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
131 $0 = "OpenSRF System";
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 OpenSRF::System->bootstrap_client(client_name => "system_client");
145 my $client = OpenSRF::Utils::SettingsClient->new();
146 my $apps = $client->config_value("activeapps", "appname");
147 if(ref($apps) ne "ARRAY") { $apps = [$apps]; }
149 if(!defined($apps) || @$apps == 0) {
150 print "No apps to load, exiting...";
154 for my $app (@$apps) {
155 # verify we are a settings server and launch
156 if( $app eq "opensrf.settings" and
157 $client->config_value("apps","opensrf.settings", "language") =~ /perl/i ) {
159 $are_settings_server = 1;
160 $self->launch_settings();
162 $self->launch_settings_listener();
168 # Launch everything else
169 my $client = OpenSRF::Utils::SettingsClient->new();
170 my $apps = $client->config_value("activeapps", "appname" );
171 if(!ref($apps)) { $apps = [$apps]; }
173 if(!defined($apps) || @$apps == 0) {
174 print "No apps to load, exiting...";
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 my $client = OpenSRF::Utils::SettingsClient->new();
339 foreach my $app ( @$apps ) {
342 my $lang = $client->config_value( "apps", $app, "language");
344 next unless $lang =~ /perl/i;
345 next unless $app ne "opensrf.settings";
347 _log( " * Starting UnixServer for $app..." );
349 my $pid = OpenSRF::Utils::safe_fork();
351 $self->pid_hash( $pid , _unixserver( $app ) );
355 $0 = "OpenSRF App ($apname)";
356 eval _unixserver( $app );
362 # ----------------------------------------------
363 # Launch the inbound clients
365 sub launch_listener {
367 my( $self, $apps ) = @_;
369 foreach my $app ( @$apps ) {
371 next unless defined($app);
373 if( $app eq "opensrf.settings" ) { next; }
375 _log( " * Starting Listener for $app..." );
377 my $pid = OpenSRF::Utils::safe_fork();
379 $self->pid_hash( $pid , _listener( $app ) );
383 $0 = "OpenSRF listener [$apname]";
384 eval _listener( $app );
390 # ----------------------------------------------
397 my $pid = OpenSRF::Utils::safe_fork();
399 if( $pid ) { $self->pid_hash( $pid , _shell() ); }
402 for( my $x = 0; $x != 10; $x++ ) {
412 # ----------------------------------------------
415 my( $self, $pid, $method ) = @_;
416 $self->{'pid_hash'}->{$pid} = $method
417 if( $pid and $method );
418 return $self->{'pid_hash'};
421 # ----------------------------------------------
422 # If requested, the System can shut down.
426 $SIG{CHLD} = 'IGNORE';
427 $SIG{INT} = 'IGNORE';
428 kill( 'INT', -$$ ); #kill all in process group
433 # ----------------------------------------------
437 _log( "HUPping brood" );
438 $SIG{CHLD} = 'IGNORE';
439 $SIG{HUP} = 'IGNORE';
440 set_config(); # reload config
442 # $SIG{CHLD} = \&process_automation;
443 $SIG{HUP} = sub{ instance()->hupall(); };
447 # ----------------------------------------------
448 # Log to debug, and stdout
452 OpenSRF::Utils::Logger->debug( $string, INFO );
453 print $string . "\n";
456 # ----------------------------------------------
459 select( undef, undef, undef, 0.3 );