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 if(!$bootstrap_config_file) {
107 die "Please provide a bootstrap config file to OpenSRF::System!\n" .
108 "use OpenSRF::System qw(/path/to/bootstrap_config);";
111 OpenSRF::Utils::Config->load( config_file => $bootstrap_config_file );
113 JSON->register_class_hint( name => "OpenSRF::Application", hint => "method", type => "hash" );
115 OpenSRF::Transport->message_envelope( "OpenSRF::Transport::SlimJabber::MessageWrapper" );
116 OpenSRF::Transport::PeerHandle->set_peer_client( "OpenSRF::Transport::SlimJabber::PeerConnection" );
117 OpenSRF::Transport::Listener->set_listener( "OpenSRF::Transport::SlimJabber::Inbound" );
118 OpenSRF::Application->server_class('client');
123 my $self = __PACKAGE__->instance();
124 load_bootstrap_config();
125 OpenSRF::Utils::Logger::set_config();
126 my $bsconfig = OpenSRF::Utils::Config->current;
128 # Start a process group and make me the captain
130 $0 = "OpenSRF System";
132 # -----------------------------------------------
133 # Launch the settings sever if necessary...
134 my $are_settings_server = 0;
135 if( (my $cfile = $bsconfig->bootstrap->settings_config) ) {
136 my $parser = OpenSRF::Utils::SettingsParser->new();
138 # since we're (probably) the settings server, we can go ahead and load the real config file
139 $parser->initialize( $cfile );
140 $OpenSRF::Utils::SettingsClient::host_config =
141 $parser->get_server_config($bsconfig->env->hostname);
143 my $client = OpenSRF::Utils::SettingsClient->new();
144 my $apps = $client->config_value("activeapps", "appname");
145 if(ref($apps) ne "ARRAY") { $apps = [$apps]; }
147 if(!defined($apps) || @$apps == 0) {
148 print "No apps to load, exiting...";
152 for my $app (@$apps) {
153 # verify we are a settings server and launch
154 if( $app eq "opensrf.settings" and
155 $client->config_value("apps","opensrf.settings", "language") =~ /perl/i ) {
157 $are_settings_server = 1;
158 $self->launch_settings();
160 $self->launch_settings_listener();
166 # Launch everything else
167 OpenSRF::System->bootstrap_client(client_name => "system_client");
168 my $client = OpenSRF::Utils::SettingsClient->new();
169 my $apps = $client->config_value("activeapps", "appname" );
170 if(!ref($apps)) { $apps = [$apps]; }
172 if(!defined($apps) || @$apps == 0) {
173 print "No apps to load, exiting...";
177 my $server_type = $client->config_value("server_type");
178 $server_type ||= "basic";
180 my $con = OpenSRF::Transport::PeerHandle->retrieve;
187 if( $server_type eq "prefork" ) {
188 $server_type = "Net::Server::PreFork";
190 $server_type = "Net::Server::Single";
193 _log( " * Server type: $server_type", INTERNAL );
195 eval "use $server_type";
198 throw OpenSRF::EX::PANIC ("Cannot set $server_type: $@" );
201 push @OpenSRF::UnixServer::ISA, $server_type;
203 _log( " * System boostrap" );
205 # --- Boot the Unix servers
206 $self->launch_unix($apps);
212 # --- Boot the listeners
213 $self->launch_listener($apps);
217 _log( " * System is ready..." );
220 my $ps = `ps ax | grep " Open" | grep -v grep | sort -r -k5`;
222 print "\n --- PS --- \n$ps --- PS ---\n\n";
224 while( 1 ) { sleep; }
230 # ----------------------------------------------
231 # Bootstraps a single client connection.
233 # named params are 'config_file' and 'client_name'
235 sub bootstrap_client {
238 my $con = OpenSRF::Transport::PeerHandle->retrieve;
239 if($con and $con->tcp_connected) {
240 warn "PeerHandle is already connected in 'bootstrap_client'... returning\n";
241 _log( "PeerHandle is already connected in 'bootstrap_client'... returning");
247 $bootstrap_config_file =
248 $params{config_file} || $bootstrap_config_file;
250 my $app = $params{client_name} || "client";
253 load_bootstrap_config();
254 OpenSRF::Utils::Logger::set_config();
255 OpenSRF::Transport::PeerHandle->construct( $app );
259 sub bootstrap_logger {
261 OpenSRF::Utils::LogServer->serve();
265 # ----------------------------------------------
266 # Cycle through the known processes, reap the dead child
267 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
269 sub process_automation {
271 my $self = __PACKAGE__->instance();
273 foreach my $pid ( keys %{$self->pid_hash} ) {
275 if( waitpid( $pid, WNOHANG ) == $pid ) {
277 my $method = $self->pid_hash->{$pid};
278 delete $self->pid_hash->{$pid};
280 my $newpid = OpenSRF::Utils::safe_fork();
282 OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
283 _log( "Relaunching => $method" );
286 $self->pid_hash( $newpid, $method );
288 else { eval $method; exit; }
292 $SIG{CHLD} = \&process_automation;
297 sub launch_settings {
299 # XXX the $self like this and pid automation will not work with this setup....
301 @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::PreFork);
303 my $pid = OpenSRF::Utils::safe_fork();
305 $self->pid_hash( $pid , "launch_settings()" );
308 my $apname = "opensrf.settings";
309 #$0 = "OpenSRF App [$apname]";
310 eval _unixserver( $apname );
311 if($@) { die "$@\n"; }
315 @OpenSRF::UnixServer::ISA = qw(OpenSRF);
320 sub launch_settings_listener {
323 my $app = "opensrf.settings";
324 my $pid = OpenSRF::Utils::safe_fork();
326 $self->pid_hash( $pid , _listener( $app ) );
330 $0 = "OpenSRF listener [$apname]";
331 eval _listener( $app );
337 # ----------------------------------------------
338 # Launch the Unix Servers
341 my( $self, $apps ) = @_;
343 my $client = OpenSRF::Utils::SettingsClient->new();
345 foreach my $app ( @$apps ) {
348 my $lang = $client->config_value( "apps", $app, "language");
349 next unless $lang =~ /perl/i;
350 next if $app eq "opensrf.settings";
352 _log( " * Starting UnixServer for $app..." );
354 my $pid = OpenSRF::Utils::safe_fork();
356 $self->pid_hash( $pid , _unixserver( $app ) );
360 $0 = "OpenSRF App ($apname)";
361 eval _unixserver( $app );
367 # ----------------------------------------------
368 # Launch the inbound clients
370 sub launch_listener {
372 my( $self, $apps ) = @_;
373 my $client = OpenSRF::Utils::SettingsClient->new();
375 foreach my $app ( @$apps ) {
378 my $lang = $client->config_value( "apps", $app, "language");
379 next unless $lang =~ /perl/i;
380 next if $app eq "opensrf.settings";
382 _log( " * Starting Listener for $app..." );
384 my $pid = OpenSRF::Utils::safe_fork();
386 $self->pid_hash( $pid , _listener( $app ) );
390 $0 = "OpenSRF listener [$apname]";
391 eval _listener( $app );
397 # ----------------------------------------------
404 my $pid = OpenSRF::Utils::safe_fork();
406 if( $pid ) { $self->pid_hash( $pid , _shell() ); }
409 for( my $x = 0; $x != 10; $x++ ) {
419 # ----------------------------------------------
422 my( $self, $pid, $method ) = @_;
423 $self->{'pid_hash'}->{$pid} = $method
424 if( $pid and $method );
425 return $self->{'pid_hash'};
428 # ----------------------------------------------
429 # If requested, the System can shut down.
433 $SIG{CHLD} = 'IGNORE';
434 $SIG{INT} = 'IGNORE';
435 kill( 'INT', -$$ ); #kill all in process group
440 # ----------------------------------------------
444 _log( "HUPping brood" );
445 $SIG{CHLD} = 'IGNORE';
446 $SIG{HUP} = 'IGNORE';
447 set_config(); # reload config
449 # $SIG{CHLD} = \&process_automation;
450 $SIG{HUP} = sub{ instance()->hupall(); };
454 # ----------------------------------------------
455 # Log to debug, and stdout
459 OpenSRF::Utils::Logger->debug( $string, INFO );
460 print $string . "\n";
463 # ----------------------------------------------
466 select( undef, undef, undef, 0.3 );