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);
211 # --- Boot the listeners
212 $self->launch_listener($apps);
216 _log( " * System is ready..." );
219 my $ps = `ps ax | grep " Open" | grep -v grep | sort -r -k5`;
221 print "\n --- PS --- \n$ps --- PS ---\n\n";
223 while( 1 ) { sleep; }
229 # ----------------------------------------------
230 # Bootstraps a single client connection.
232 # named params are 'config_file' and 'client_name'
234 sub bootstrap_client {
239 $bootstrap_config_file =
240 $params{config_file} || $bootstrap_config_file;
242 my $app = $params{client_name} || "client";
245 load_bootstrap_config();
246 OpenSRF::Utils::Logger::set_config();
247 OpenSRF::Transport::PeerHandle->construct( $app );
251 sub bootstrap_logger {
253 OpenSRF::Utils::LogServer->serve();
257 # ----------------------------------------------
258 # Cycle through the known processes, reap the dead child
259 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
261 sub process_automation {
263 my $self = __PACKAGE__->instance();
265 foreach my $pid ( keys %{$self->pid_hash} ) {
267 if( waitpid( $pid, WNOHANG ) == $pid ) {
269 my $method = $self->pid_hash->{$pid};
270 delete $self->pid_hash->{$pid};
272 my $newpid = OpenSRF::Utils::safe_fork();
274 OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
275 _log( "Relaunching => $method" );
278 $self->pid_hash( $newpid, $method );
280 else { eval $method; exit; }
284 $SIG{CHLD} = \&process_automation;
289 sub launch_settings {
291 # XXX the $self like this and pid automation will not work with this setup....
293 @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::PreFork);
295 my $pid = OpenSRF::Utils::safe_fork();
297 $self->pid_hash( $pid , "launch_settings()" );
300 my $apname = "settings";
301 #$0 = "OpenSRF App [$apname]";
302 eval _unixserver( "settings" );
303 if($@) { die "$@\n"; }
307 @OpenSRF::UnixServer::ISA = qw(OpenSRF);
312 sub launch_settings_listener {
315 my $app = "settings";
316 my $pid = OpenSRF::Utils::safe_fork();
318 $self->pid_hash( $pid , _listener( $app ) );
322 $0 = "OpenSRF listener [$apname]";
323 eval _listener( $app );
329 # ----------------------------------------------
330 # Launch the Unix Servers
333 my( $self, $apps ) = @_;
335 foreach my $app ( @$apps ) {
338 if( $app eq "settings" ) { next; }
340 _log( " * Starting UnixServer for $app..." );
342 my $pid = OpenSRF::Utils::safe_fork();
344 $self->pid_hash( $pid , _unixserver( $app ) );
348 $0 = "OpenSRF App ($apname)";
349 eval _unixserver( $app );
355 # ----------------------------------------------
356 # Launch the inbound clients
358 sub launch_listener {
360 my( $self, $apps ) = @_;
362 foreach my $app ( @$apps ) {
364 if( $app eq "settings" ) { next; }
366 _log( " * Starting Listener for $app..." );
368 my $pid = OpenSRF::Utils::safe_fork();
370 $self->pid_hash( $pid , _listener( $app ) );
374 $0 = "OpenSRF listener [$apname]";
375 eval _listener( $app );
381 # ----------------------------------------------
388 my $pid = OpenSRF::Utils::safe_fork();
390 if( $pid ) { $self->pid_hash( $pid , _shell() ); }
393 for( my $x = 0; $x != 10; $x++ ) {
403 # ----------------------------------------------
406 my( $self, $pid, $method ) = @_;
407 $self->{'pid_hash'}->{$pid} = $method
408 if( $pid and $method );
409 return $self->{'pid_hash'};
412 # ----------------------------------------------
413 # If requested, the System can shut down.
417 $SIG{CHLD} = 'IGNORE';
418 $SIG{INT} = 'IGNORE';
419 kill( 'INT', -$$ ); #kill all in process group
424 # ----------------------------------------------
428 _log( "HUPping brood" );
429 $SIG{CHLD} = 'IGNORE';
430 $SIG{HUP} = 'IGNORE';
431 set_config(); # reload config
433 # $SIG{CHLD} = \&process_automation;
434 $SIG{HUP} = sub{ instance()->hupall(); };
438 # ----------------------------------------------
439 # Log to debug, and stdout
443 OpenSRF::Utils::Logger->debug( $string, INFO );
444 print $string . "\n";
447 # ----------------------------------------------
450 select( undef, undef, undef, 0.3 );