1 package OpenSRF::System;
2 use strict; use warnings;
4 use OpenSRF::Utils::Logger qw(:level);
5 use OpenSRF::Transport::Listener;
6 use OpenSRF::Transport;
7 use OpenSRF::UnixServer;
9 use OpenSRF::Utils::LogServer;
11 use OpenSRF::EX qw/:try/;
12 use POSIX ":sys_wait_h";
13 use OpenSRF::Utils::Config;
14 use OpenSRF::Utils::SettingsParser;
15 use OpenSRF::Utils::SettingsClient;
16 use OpenSRF::Application;
17 use Net::Server::PreFork;
20 my $bootstrap_config_file;
22 my( $self, $config ) = @_;
23 $bootstrap_config_file = $config;
26 =head2 Name/Description
30 To start the system: OpenSRF::System->bootstrap();
32 Simple system process management and automation. After instantiating the class, simply call
33 bootstrap() to launch the system. Each launched process is stored as a process-id/method-name
34 pair in a local hash. When we receive a SIG{CHILD}, we loop through this hash and relaunch
35 any child processes that may have terminated.
37 Currently automated processes include launching the internal Unix Servers, launching the inbound
38 connections for each application, and starting the system shell.
41 Note: There should be only one instance of this class
42 alive at any given time. It is designed as a globel process handler and, hence, will cause much
43 oddness if you call the bootstrap() method twice or attempt to create two of these by trickery.
44 There is a single instance of the class created on the first call to new(). This same instance is
45 returned on subsequent calls to new().
53 # ----------------------------------------------
55 $SIG{INT} = sub { instance()->killall(); };
57 $SIG{HUP} = sub{ instance()->hupall(); };
59 #$SIG{CHLD} = \&process_automation;
64 # put $instance in a closure and return it for requests to new()
65 # since there should only be one System instance running
68 sub instance { return __PACKAGE__->new(); }
73 $class = ref( $class ) || $class;
75 $self->{'pid_hash'} = {};
76 bless( $self, $class );
83 # ----------------------------------------------
84 # Commands to execute at system launch
88 return "OpenSRF::UnixServer->new( '$app')->serve()";
97 my $password = "jkjkasdf";
98 my $jserver = "elroy";
100 my $hostname = "elroy.pls-hq.org";
101 my $unix_file = "/pines/var/sock/$app" . "_unix.sock";
102 my $router = 'router\@elroy/router';
103 return "exec(\"/pines/cvs/ILS/OpenSRF/src/forker/oils_inbound $service $username " .
104 "$password $jserver $port $hostname $unix_file $router\")";
107 return "OpenSRF::Transport::Listener->new( '$app' )->initialize()->listen()";
111 # ----------------------------------------------
114 sub load_bootstrap_config {
116 if(OpenSRF::Utils::Config->current) {
120 warn "Loading $bootstrap_config_file\n";
121 if(!$bootstrap_config_file) {
122 die "Please provide a bootstrap config file to OpenSRF::System!\n" .
123 "use OpenSRF::System qw(/path/to/bootstrap_config);";
126 OpenSRF::Utils::Config->load( config_file => $bootstrap_config_file );
128 JSON->register_class_hint( name => "OpenSRF::Application", hint => "method", type => "hash" );
130 OpenSRF::Transport->message_envelope( "OpenSRF::Transport::SlimJabber::MessageWrapper" );
131 OpenSRF::Transport::PeerHandle->set_peer_client( "OpenSRF::Transport::SlimJabber::PeerConnection" );
132 OpenSRF::Transport::Listener->set_listener( "OpenSRF::Transport::SlimJabber::Inbound" );
133 OpenSRF::Application->server_class('client');
138 my $self = __PACKAGE__->instance();
139 load_bootstrap_config();
140 OpenSRF::Utils::Logger::set_config();
141 my $bsconfig = OpenSRF::Utils::Config->current;
143 # Start a process group and make me the captain
147 # -----------------------------------------------
148 # Launch the settings sever if necessary...
149 my $are_settings_server = 0;
150 if( (my $cfile = $bsconfig->bootstrap->settings_config) ) {
151 my $parser = OpenSRF::Utils::SettingsParser->new();
153 # since we're (probably) the settings server, we can go ahead and load the real config file
154 $parser->initialize( $cfile );
155 $OpenSRF::Utils::SettingsClient::host_config =
156 $parser->get_server_config($bsconfig->env->hostname);
158 my $client = OpenSRF::Utils::SettingsClient->new();
159 my $apps = $client->config_value("activeapps", "appname");
160 if(!ref($apps) eq "ARRAY") { $apps = [$apps]; }
162 for my $app (@$apps) {
163 # verify we are a settings server and launch
164 if( $app eq "opensrf.settings" ) {
165 $are_settings_server = 1;
166 $self->launch_settings();
168 $self->launch_settings_listener();
174 # Launch everything else
175 my $client = OpenSRF::Utils::SettingsClient->new();
176 my $apps = $client->config_value("activeapps", "appname" );
177 if(!ref($apps)) { $apps = [$apps]; }
178 my $server_type = $client->config_value("server_type");
179 $server_type ||= "basic";
181 my $con = OpenSRF::Transport::PeerHandle->retrieve;
188 if( $server_type eq "prefork" ) {
189 $server_type = "Net::Server::PreFork";
191 $server_type = "Net::Server::Single";
194 _log( " * Server type: $server_type", INTERNAL );
196 eval "use $server_type";
199 throw OpenSRF::EX::PANIC ("Cannot set $server_type: $@" );
202 push @OpenSRF::UnixServer::ISA, $server_type;
204 _log( " * System boostrap" );
206 # --- Boot the Unix servers
207 $self->launch_unix($apps);
213 # --- Boot the listeners
214 $self->launch_listener($apps);
218 _log( " * System is ready..." );
221 my $ps = `ps ax | grep " Open" | grep -v grep | sort -r -k5`;
223 print "\n --- PS --- \n$ps --- PS ---\n\n";
225 while( 1 ) { sleep; }
231 # ----------------------------------------------
232 # Bootstraps a single client connection.
234 # named params are 'config_file' and 'client_name'
236 sub bootstrap_client {
241 $bootstrap_config_file =
242 $params{config_file} || $bootstrap_config_file;
244 my $app = $params{client_name} || "client";
247 load_bootstrap_config();
248 OpenSRF::Utils::Logger::set_config();
249 OpenSRF::Transport::PeerHandle->construct( $app );
253 sub bootstrap_logger {
255 OpenSRF::Utils::LogServer->serve();
259 # ----------------------------------------------
260 # Cycle through the known processes, reap the dead child
261 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
263 sub process_automation {
265 my $self = __PACKAGE__->instance();
267 foreach my $pid ( keys %{$self->pid_hash} ) {
269 if( waitpid( $pid, WNOHANG ) == $pid ) {
271 my $method = $self->pid_hash->{$pid};
272 delete $self->pid_hash->{$pid};
274 my $newpid = OpenSRF::Utils::safe_fork();
276 OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
277 _log( "Relaunching => $method" );
280 $self->pid_hash( $newpid, $method );
282 else { eval $method; exit; }
286 $SIG{CHLD} = \&process_automation;
291 sub launch_settings {
293 # XXX the $self like this and pid automation will not work with this setup....
295 @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::PreFork);
297 my $pid = OpenSRF::Utils::safe_fork();
299 $self->pid_hash( $pid , "launch_settings()" );
302 my $apname = "opensrf.settings";
303 #$0 = "OpenSRF App [$apname]";
304 eval _unixserver( $apname );
305 if($@) { die "$@\n"; }
309 @OpenSRF::UnixServer::ISA = qw(OpenSRF);
314 sub launch_settings_listener {
317 my $app = "opensrf.settings";
318 my $pid = OpenSRF::Utils::safe_fork();
320 $self->pid_hash( $pid , _listener( $app ) );
324 $0 = "OpenSRF listener [$apname]";
325 eval _listener( $app );
331 # ----------------------------------------------
332 # Launch the Unix Servers
335 my( $self, $apps ) = @_;
337 foreach my $app ( @$apps ) {
340 if( $app eq "opensrf.settings" ) { next; }
342 _log( " * Starting UnixServer for $app..." );
344 my $pid = OpenSRF::Utils::safe_fork();
346 $self->pid_hash( $pid , _unixserver( $app ) );
350 $0 = "OpenSRF App ($apname)";
351 eval _unixserver( $app );
357 # ----------------------------------------------
358 # Launch the inbound clients
360 sub launch_listener {
362 my( $self, $apps ) = @_;
364 foreach my $app ( @$apps ) {
366 if( $app eq "opensrf.settings" ) { next; }
368 _log( " * Starting Listener for $app..." );
370 my $pid = OpenSRF::Utils::safe_fork();
372 $self->pid_hash( $pid , _listener( $app ) );
376 $0 = "OpenSRF listener [$apname]";
377 eval _listener( $app );
383 # ----------------------------------------------
390 my $pid = OpenSRF::Utils::safe_fork();
392 if( $pid ) { $self->pid_hash( $pid , _shell() ); }
395 for( my $x = 0; $x != 10; $x++ ) {
405 # ----------------------------------------------
408 my( $self, $pid, $method ) = @_;
409 $self->{'pid_hash'}->{$pid} = $method
410 if( $pid and $method );
411 return $self->{'pid_hash'};
414 # ----------------------------------------------
415 # If requested, the System can shut down.
419 $SIG{CHLD} = 'IGNORE';
420 $SIG{INT} = 'IGNORE';
421 kill( 'INT', -$$ ); #kill all in process group
426 # ----------------------------------------------
430 _log( "HUPping brood" );
431 $SIG{CHLD} = 'IGNORE';
432 $SIG{HUP} = 'IGNORE';
433 set_config(); # reload config
435 # $SIG{CHLD} = \&process_automation;
436 $SIG{HUP} = sub{ instance()->hupall(); };
440 # ----------------------------------------------
441 # Log to debug, and stdout
445 OpenSRF::Utils::Logger->debug( $string, INFO );
446 print $string . "\n";
449 # ----------------------------------------------
452 select( undef, undef, undef, 0.3 );