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 Net::Server::PreFork;
19 my $bootstrap_config_file;
21 my( $self, $config ) = @_;
22 $bootstrap_config_file = $config;
25 =head2 Name/Description
29 To start the system: OpenSRF::System->bootstrap();
31 Simple system process management and automation. After instantiating the class, simply call
32 bootstrap() to launch the system. Each launched process is stored as a process-id/method-name
33 pair in a local hash. When we receive a SIG{CHILD}, we loop through this hash and relaunch
34 any child processes that may have terminated.
36 Currently automated processes include launching the internal Unix Servers, launching the inbound
37 connections for each application, and starting the system shell.
40 Note: There should be only one instance of this class
41 alive at any given time. It is designed as a globel process handler and, hence, will cause much
42 oddness if you call the bootstrap() method twice or attempt to create two of these by trickery.
43 There is a single instance of the class created on the first call to new(). This same instance is
44 returned on subsequent calls to new().
52 # ----------------------------------------------
54 $SIG{INT} = sub { instance()->killall(); };
56 $SIG{HUP} = sub{ instance()->hupall(); };
58 #$SIG{CHLD} = \&process_automation;
63 # put $instance in a closure and return it for requests to new()
64 # since there should only be one System instance running
67 sub instance { return __PACKAGE__->new(); }
72 $class = ref( $class ) || $class;
74 $self->{'pid_hash'} = {};
75 bless( $self, $class );
82 # ----------------------------------------------
83 # Commands to execute at system launch
87 return "OpenSRF::UnixServer->new( '$app')->serve()";
96 my $password = "jkjkasdf";
97 my $jserver = "elroy";
99 my $hostname = "elroy.pls-hq.org";
100 my $unix_file = "/pines/var/sock/$app" . "_unix.sock";
101 my $router = 'router\@elroy/router';
102 return "exec(\"/pines/cvs/ILS/OpenSRF/src/forker/oils_inbound $service $username " .
103 "$password $jserver $port $hostname $unix_file $router\")";
106 return "OpenSRF::Transport::Listener->new( '$app' )->initialize()->listen()";
110 # ----------------------------------------------
113 sub load_bootstrap_config {
115 if(OpenSRF::Utils::Config->current) {
119 warn "Loading $bootstrap_config_file\n";
120 if(!$bootstrap_config_file) {
121 die "Please provide a bootstrap config file to OpenSRF::System!\n" .
122 "use OpenSRF::System qw(/path/to/bootstrap_config);";
125 OpenSRF::Utils::Config->load( config_file => $bootstrap_config_file );
127 JSON->register_class_hint( name => "OpenSRF::Application", hint => "method", type => "hash" );
129 OpenSRF::Transport->message_envelope( "OpenSRF::Transport::SlimJabber::MessageWrapper" );
130 OpenSRF::Transport::PeerHandle->set_peer_client( "OpenSRF::Transport::SlimJabber::PeerConnection" );
131 OpenSRF::Transport::Listener->set_listener( "OpenSRF::Transport::SlimJabber::Inbound" );
136 my $self = __PACKAGE__->instance();
137 load_bootstrap_config();
138 OpenSRF::Utils::Logger::set_config();
139 my $bsconfig = OpenSRF::Utils::Config->current;
141 # Start a process group and make me the captain
145 # -----------------------------------------------
146 # Launch the settings sever if necessary...
147 my $are_settings_server = 0;
148 if( (my $cfile = $bsconfig->bootstrap->settings_config) ) {
149 my $parser = OpenSRF::Utils::SettingsParser->new();
151 # since we're (probably) the settings server, we can go ahead and load the real config file
152 $parser->initialize( $cfile );
153 $OpenSRF::Utils::SettingsClient::host_config =
154 $parser->get_server_config($bsconfig->env->hostname);
156 my $client = OpenSRF::Utils::SettingsClient->new();
157 my $apps = $client->config_value("activeapps", "appname");
158 if(!ref($apps) eq "ARRAY") { $apps = [$apps]; }
160 for my $app (@$apps) {
161 # verify we are a settings server and launch
162 if( $app eq "settings" ) {
163 $are_settings_server = 1;
164 $self->launch_settings();
166 $self->launch_settings_listener();
172 # Launch everything else
173 my $client = OpenSRF::Utils::SettingsClient->new();
174 my $apps = $client->config_value("activeapps", "appname" );
175 if(!ref($apps)) { $apps = [$apps]; }
176 my $server_type = $client->config_value("server_type");
177 $server_type ||= "basic";
179 my $con = OpenSRF::Transport::PeerHandle->retrieve;
186 if( $server_type eq "prefork" ) {
187 $server_type = "Net::Server::PreFork";
189 $server_type = "Net::Server::Single";
192 _log( " * Server type: $server_type", INTERNAL );
194 eval "use $server_type";
197 throw OpenSRF::EX::PANIC ("Cannot set $server_type: $@" );
200 push @OpenSRF::UnixServer::ISA, $server_type;
202 _log( " * System boostrap" );
204 # --- Boot the Unix servers
205 $self->launch_unix($apps);
209 # --- Boot the listeners
210 $self->launch_listener($apps);
214 _log( " * System is ready..." );
217 my $ps = `ps ax | grep " Open" | grep -v grep | sort -r -k5`;
219 print "\n --- PS --- \n$ps --- PS ---\n\n";
221 while( 1 ) { sleep; }
227 # ----------------------------------------------
228 # Bootstraps a single client connection.
230 # named params are 'config_file' and 'client_name'
232 sub bootstrap_client {
237 $bootstrap_config_file =
238 $params{config_file} || $bootstrap_config_file;
240 my $app = $params{client_name} || "client";
243 load_bootstrap_config();
244 OpenSRF::Utils::Logger::set_config();
245 OpenSRF::Transport::PeerHandle->construct( $app );
249 sub bootstrap_logger {
251 OpenSRF::Utils::LogServer->serve();
255 # ----------------------------------------------
256 # Cycle through the known processes, reap the dead child
257 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
259 sub process_automation {
261 my $self = __PACKAGE__->instance();
263 foreach my $pid ( keys %{$self->pid_hash} ) {
265 if( waitpid( $pid, WNOHANG ) == $pid ) {
267 my $method = $self->pid_hash->{$pid};
268 delete $self->pid_hash->{$pid};
270 my $newpid = OpenSRF::Utils::safe_fork();
272 OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
273 _log( "Relaunching => $method" );
276 $self->pid_hash( $newpid, $method );
278 else { eval $method; exit; }
282 $SIG{CHLD} = \&process_automation;
287 sub launch_settings {
289 # XXX the $self like this and pid automation will not work with this setup....
291 @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::PreFork);
293 my $pid = OpenSRF::Utils::safe_fork();
295 $self->pid_hash( $pid , "launch_settings()" );
298 my $apname = "settings";
299 #$0 = "OpenSRF App [$apname]";
300 eval _unixserver( "settings" );
301 if($@) { die "$@\n"; }
305 @OpenSRF::UnixServer::ISA = qw(OpenSRF);
310 sub launch_settings_listener {
313 my $app = "settings";
314 my $pid = OpenSRF::Utils::safe_fork();
316 $self->pid_hash( $pid , _listener( $app ) );
320 $0 = "OpenSRF listener [$apname]";
321 eval _listener( $app );
327 # ----------------------------------------------
328 # Launch the Unix Servers
331 my( $self, $apps ) = @_;
333 foreach my $app ( @$apps ) {
336 if( $app eq "settings" ) { next; }
338 _log( " * Starting UnixServer for $app..." );
340 my $pid = OpenSRF::Utils::safe_fork();
342 $self->pid_hash( $pid , _unixserver( $app ) );
346 $0 = "OpenSRF App ($apname)";
347 eval _unixserver( $app );
353 # ----------------------------------------------
354 # Launch the inbound clients
356 sub launch_listener {
358 my( $self, $apps ) = @_;
360 foreach my $app ( @$apps ) {
362 if( $app eq "settings" ) { next; }
364 _log( " * Starting Listener for $app..." );
366 my $pid = OpenSRF::Utils::safe_fork();
368 $self->pid_hash( $pid , _listener( $app ) );
372 $0 = "OpenSRF listener [$apname]";
373 eval _listener( $app );
379 # ----------------------------------------------
386 my $pid = OpenSRF::Utils::safe_fork();
388 if( $pid ) { $self->pid_hash( $pid , _shell() ); }
391 for( my $x = 0; $x != 10; $x++ ) {
401 # ----------------------------------------------
404 my( $self, $pid, $method ) = @_;
405 $self->{'pid_hash'}->{$pid} = $method
406 if( $pid and $method );
407 return $self->{'pid_hash'};
410 # ----------------------------------------------
411 # If requested, the System can shut down.
415 $SIG{CHLD} = 'IGNORE';
416 $SIG{INT} = 'IGNORE';
417 kill( 'INT', -$$ ); #kill all in process group
422 # ----------------------------------------------
426 _log( "HUPping brood" );
427 $SIG{CHLD} = 'IGNORE';
428 $SIG{HUP} = 'IGNORE';
429 set_config(); # reload config
431 # $SIG{CHLD} = \&process_automation;
432 $SIG{HUP} = sub{ instance()->hupall(); };
436 # ----------------------------------------------
437 # Log to debug, and stdout
441 OpenSRF::Utils::Logger->debug( $string, INFO );
442 print $string . "\n";
445 # ----------------------------------------------
448 select( undef, undef, undef, 0.3 );