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()";
93 return "OpenSRF::Transport::Listener->new( '$app' )->initialize()->listen()";
97 # ----------------------------------------------
100 sub load_bootstrap_config {
102 if(OpenSRF::Utils::Config->current) {
106 warn "Loading $bootstrap_config_file\n";
107 if(!$bootstrap_config_file) {
108 die "Please provide a bootstrap config file to OpenSRF::System!\n" .
109 "use OpenSRF::System qw(/path/to/bootstrap_config);";
112 OpenSRF::Utils::Config->load( config_file => $bootstrap_config_file );
114 JSON->register_class_hint( name => "OpenSRF::Application", hint => "method", type => "hash" );
116 OpenSRF::Transport->message_envelope( "OpenSRF::Transport::SlimJabber::MessageWrapper" );
117 OpenSRF::Transport::PeerHandle->set_peer_client( "OpenSRF::Transport::SlimJabber::PeerConnection" );
118 OpenSRF::Transport::Listener->set_listener( "OpenSRF::Transport::SlimJabber::Inbound" );
119 OpenSRF::Application->server_class('client');
124 my $self = __PACKAGE__->instance();
125 load_bootstrap_config();
126 OpenSRF::Utils::Logger::set_config();
127 my $bsconfig = OpenSRF::Utils::Config->current;
129 # Start a process group and make me the captain
133 # -----------------------------------------------
134 # Launch the settings sever if necessary...
135 my $are_settings_server = 0;
136 if( (my $cfile = $bsconfig->bootstrap->settings_config) ) {
137 my $parser = OpenSRF::Utils::SettingsParser->new();
139 # since we're (probably) the settings server, we can go ahead and load the real config file
140 $parser->initialize( $cfile );
141 $OpenSRF::Utils::SettingsClient::host_config =
142 $parser->get_server_config($bsconfig->env->hostname);
144 my $client = OpenSRF::Utils::SettingsClient->new();
145 my $apps = $client->config_value("activeapps", "appname");
146 if(ref($apps) ne "ARRAY") { $apps = [$apps]; }
148 if(!defined($apps) || @$apps == 0) {
149 print "No apps to load, exiting...";
153 for my $app (@$apps) {
154 # verify we are a settings server and launch
155 if( $app eq "opensrf.settings" ) {
156 $are_settings_server = 1;
157 $self->launch_settings();
159 $self->launch_settings_listener();
165 # Launch everything else
166 my $client = OpenSRF::Utils::SettingsClient->new();
167 my $apps = $client->config_value("activeapps", "appname" );
168 if(!ref($apps)) { $apps = [$apps]; }
170 if(!defined($apps) || @$apps == 0) {
171 print "No apps to load, exiting...";
175 my $server_type = $client->config_value("server_type");
176 $server_type ||= "basic";
178 my $con = OpenSRF::Transport::PeerHandle->retrieve;
185 if( $server_type eq "prefork" ) {
186 $server_type = "Net::Server::PreFork";
188 $server_type = "Net::Server::Single";
191 _log( " * Server type: $server_type", INTERNAL );
193 eval "use $server_type";
196 throw OpenSRF::EX::PANIC ("Cannot set $server_type: $@" );
199 push @OpenSRF::UnixServer::ISA, $server_type;
201 _log( " * System boostrap" );
203 # --- Boot the Unix servers
204 $self->launch_unix($apps);
210 # --- Boot the listeners
211 $self->launch_listener($apps);
215 _log( " * System is ready..." );
218 my $ps = `ps ax | grep " Open" | grep -v grep | sort -r -k5`;
220 print "\n --- PS --- \n$ps --- PS ---\n\n";
222 while( 1 ) { sleep; }
228 # ----------------------------------------------
229 # Bootstraps a single client connection.
231 # named params are 'config_file' and 'client_name'
233 sub bootstrap_client {
238 $bootstrap_config_file =
239 $params{config_file} || $bootstrap_config_file;
241 my $app = $params{client_name} || "client";
244 load_bootstrap_config();
245 OpenSRF::Utils::Logger::set_config();
246 OpenSRF::Transport::PeerHandle->construct( $app );
250 sub bootstrap_logger {
252 OpenSRF::Utils::LogServer->serve();
256 # ----------------------------------------------
257 # Cycle through the known processes, reap the dead child
258 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
260 sub process_automation {
262 my $self = __PACKAGE__->instance();
264 foreach my $pid ( keys %{$self->pid_hash} ) {
266 if( waitpid( $pid, WNOHANG ) == $pid ) {
268 my $method = $self->pid_hash->{$pid};
269 delete $self->pid_hash->{$pid};
271 my $newpid = OpenSRF::Utils::safe_fork();
273 OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
274 _log( "Relaunching => $method" );
277 $self->pid_hash( $newpid, $method );
279 else { eval $method; exit; }
283 $SIG{CHLD} = \&process_automation;
288 sub launch_settings {
290 # XXX the $self like this and pid automation will not work with this setup....
292 @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::PreFork);
294 my $pid = OpenSRF::Utils::safe_fork();
296 $self->pid_hash( $pid , "launch_settings()" );
299 my $apname = "opensrf.settings";
300 #$0 = "OpenSRF App [$apname]";
301 eval _unixserver( $apname );
302 if($@) { die "$@\n"; }
306 @OpenSRF::UnixServer::ISA = qw(OpenSRF);
311 sub launch_settings_listener {
314 my $app = "opensrf.settings";
315 my $pid = OpenSRF::Utils::safe_fork();
317 $self->pid_hash( $pid , _listener( $app ) );
321 $0 = "OpenSRF listener [$apname]";
322 eval _listener( $app );
328 # ----------------------------------------------
329 # Launch the Unix Servers
332 my( $self, $apps ) = @_;
334 foreach my $app ( @$apps ) {
337 if( $app eq "opensrf.settings" ) { next; }
339 _log( " * Starting UnixServer for $app..." );
341 my $pid = OpenSRF::Utils::safe_fork();
343 $self->pid_hash( $pid , _unixserver( $app ) );
347 $0 = "OpenSRF App ($apname)";
348 eval _unixserver( $app );
354 # ----------------------------------------------
355 # Launch the inbound clients
357 sub launch_listener {
359 my( $self, $apps ) = @_;
361 foreach my $app ( @$apps ) {
363 next unless defined($app);
365 if( $app eq "opensrf.settings" ) { next; }
367 _log( " * Starting Listener for $app..." );
369 my $pid = OpenSRF::Utils::safe_fork();
371 $self->pid_hash( $pid , _listener( $app ) );
375 $0 = "OpenSRF listener [$apname]";
376 eval _listener( $app );
382 # ----------------------------------------------
389 my $pid = OpenSRF::Utils::safe_fork();
391 if( $pid ) { $self->pid_hash( $pid , _shell() ); }
394 for( my $x = 0; $x != 10; $x++ ) {
404 # ----------------------------------------------
407 my( $self, $pid, $method ) = @_;
408 $self->{'pid_hash'}->{$pid} = $method
409 if( $pid and $method );
410 return $self->{'pid_hash'};
413 # ----------------------------------------------
414 # If requested, the System can shut down.
418 $SIG{CHLD} = 'IGNORE';
419 $SIG{INT} = 'IGNORE';
420 kill( 'INT', -$$ ); #kill all in process group
425 # ----------------------------------------------
429 _log( "HUPping brood" );
430 $SIG{CHLD} = 'IGNORE';
431 $SIG{HUP} = 'IGNORE';
432 set_config(); # reload config
434 # $SIG{CHLD} = \&process_automation;
435 $SIG{HUP} = sub{ instance()->hupall(); };
439 # ----------------------------------------------
440 # Log to debug, and stdout
444 OpenSRF::Utils::Logger->debug( $string, INFO );
445 print $string . "\n";
448 # ----------------------------------------------
451 select( undef, undef, undef, 0.3 );