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 => "", 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 sub bootstrap_client {
235 $bootstrap_config_file =
236 $params{config_file} || $bootstrap_config_file;
238 my $app = $params{client_name} || "client";
241 load_bootstrap_config();
242 OpenSRF::Utils::Logger::set_config();
243 OpenSRF::Transport::PeerHandle->construct( $app );
247 sub bootstrap_logger {
249 OpenSRF::Utils::LogServer->serve();
253 # ----------------------------------------------
254 # Cycle through the known processes, reap the dead child
255 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
257 sub process_automation {
259 my $self = __PACKAGE__->instance();
261 foreach my $pid ( keys %{$self->pid_hash} ) {
263 if( waitpid( $pid, WNOHANG ) == $pid ) {
265 my $method = $self->pid_hash->{$pid};
266 delete $self->pid_hash->{$pid};
268 my $newpid = OpenSRF::Utils::safe_fork();
270 OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
271 _log( "Relaunching => $method" );
274 $self->pid_hash( $newpid, $method );
276 else { eval $method; exit; }
280 $SIG{CHLD} = \&process_automation;
285 sub launch_settings {
287 # XXX the $self like this and pid automation will not work with this setup....
289 @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::PreFork);
291 my $pid = OpenSRF::Utils::safe_fork();
293 $self->pid_hash( $pid , "launch_settings()" );
296 my $apname = "settings";
297 $0 = "OpenSRF App ($apname)";
298 eval _unixserver( "settings" );
299 if($@) { die "$@\n"; }
303 @OpenSRF::UnixServer::ISA = qw(OpenSRF);
308 sub launch_settings_listener {
311 my $app = "settings";
312 my $pid = OpenSRF::Utils::safe_fork();
314 $self->pid_hash( $pid , _listener( $app ) );
318 $0 = "OpenSRF listener ($apname)";
319 eval _listener( $app );
325 # ----------------------------------------------
326 # Launch the Unix Servers
329 my( $self, $apps ) = @_;
331 foreach my $app ( @$apps ) {
334 if( $app eq "settings" ) { next; }
336 _log( " * Starting UnixServer for $app..." );
338 my $pid = OpenSRF::Utils::safe_fork();
340 $self->pid_hash( $pid , _unixserver( $app ) );
344 $0 = "OpenSRF App ($apname)";
345 eval _unixserver( $app );
351 # ----------------------------------------------
352 # Launch the inbound clients
354 sub launch_listener {
356 my( $self, $apps ) = @_;
358 foreach my $app ( @$apps ) {
360 if( $app eq "settings" ) { next; }
362 _log( " * Starting Listener for $app..." );
364 my $pid = OpenSRF::Utils::safe_fork();
366 $self->pid_hash( $pid , _listener( $app ) );
370 $0 = "OpenSRF listener ($apname)";
371 eval _listener( $app );
377 # ----------------------------------------------
384 my $pid = OpenSRF::Utils::safe_fork();
386 if( $pid ) { $self->pid_hash( $pid , _shell() ); }
389 for( my $x = 0; $x != 10; $x++ ) {
399 # ----------------------------------------------
402 my( $self, $pid, $method ) = @_;
403 $self->{'pid_hash'}->{$pid} = $method
404 if( $pid and $method );
405 return $self->{'pid_hash'};
408 # ----------------------------------------------
409 # If requested, the System can shut down.
413 $SIG{CHLD} = 'IGNORE';
414 $SIG{INT} = 'IGNORE';
415 kill( 'INT', -$$ ); #kill all in process group
420 # ----------------------------------------------
424 _log( "HUPping brood" );
425 $SIG{CHLD} = 'IGNORE';
426 $SIG{HUP} = 'IGNORE';
427 set_config(); # reload config
429 # $SIG{CHLD} = \&process_automation;
430 $SIG{HUP} = sub{ instance()->hupall(); };
434 # ----------------------------------------------
435 # Log to debug, and stdout
439 OpenSRF::Utils::Logger->debug( $string, INFO );
440 print $string . "\n";
443 # ----------------------------------------------
446 select( undef, undef, undef, 0.3 );