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 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" and
156 $client->config_value("apps","opensrf.settings", "language") =~ /perl/i ) {
158 $are_settings_server = 1;
159 $self->launch_settings();
161 $self->launch_settings_listener();
167 # Launch everything else
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 {
240 $bootstrap_config_file =
241 $params{config_file} || $bootstrap_config_file;
243 my $app = $params{client_name} || "client";
246 load_bootstrap_config();
247 OpenSRF::Utils::Logger::set_config();
248 OpenSRF::Transport::PeerHandle->construct( $app );
252 sub bootstrap_logger {
254 OpenSRF::Utils::LogServer->serve();
258 # ----------------------------------------------
259 # Cycle through the known processes, reap the dead child
260 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
262 sub process_automation {
264 my $self = __PACKAGE__->instance();
266 foreach my $pid ( keys %{$self->pid_hash} ) {
268 if( waitpid( $pid, WNOHANG ) == $pid ) {
270 my $method = $self->pid_hash->{$pid};
271 delete $self->pid_hash->{$pid};
273 my $newpid = OpenSRF::Utils::safe_fork();
275 OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
276 _log( "Relaunching => $method" );
279 $self->pid_hash( $newpid, $method );
281 else { eval $method; exit; }
285 $SIG{CHLD} = \&process_automation;
290 sub launch_settings {
292 # XXX the $self like this and pid automation will not work with this setup....
294 @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::PreFork);
296 my $pid = OpenSRF::Utils::safe_fork();
298 $self->pid_hash( $pid , "launch_settings()" );
301 my $apname = "opensrf.settings";
302 #$0 = "OpenSRF App [$apname]";
303 eval _unixserver( $apname );
304 if($@) { die "$@\n"; }
308 @OpenSRF::UnixServer::ISA = qw(OpenSRF);
313 sub launch_settings_listener {
316 my $app = "opensrf.settings";
317 my $pid = OpenSRF::Utils::safe_fork();
319 $self->pid_hash( $pid , _listener( $app ) );
323 $0 = "OpenSRF listener [$apname]";
324 eval _listener( $app );
330 # ----------------------------------------------
331 # Launch the Unix Servers
334 my( $self, $apps ) = @_;
336 my $client = OpenSRF::Utils::SettingsClient->new();
338 foreach my $app ( @$apps ) {
341 my $lang = $client->config_value( "apps", $app, "language");
343 next unless $lang =~ /perl/i;
344 next unless $app ne "opensrf.settings";
346 _log( " * Starting UnixServer for $app..." );
348 my $pid = OpenSRF::Utils::safe_fork();
350 $self->pid_hash( $pid , _unixserver( $app ) );
354 $0 = "OpenSRF App ($apname)";
355 eval _unixserver( $app );
361 # ----------------------------------------------
362 # Launch the inbound clients
364 sub launch_listener {
366 my( $self, $apps ) = @_;
368 foreach my $app ( @$apps ) {
370 next unless defined($app);
372 if( $app eq "opensrf.settings" ) { next; }
374 _log( " * Starting Listener for $app..." );
376 my $pid = OpenSRF::Utils::safe_fork();
378 $self->pid_hash( $pid , _listener( $app ) );
382 $0 = "OpenSRF listener [$apname]";
383 eval _listener( $app );
389 # ----------------------------------------------
396 my $pid = OpenSRF::Utils::safe_fork();
398 if( $pid ) { $self->pid_hash( $pid , _shell() ); }
401 for( my $x = 0; $x != 10; $x++ ) {
411 # ----------------------------------------------
414 my( $self, $pid, $method ) = @_;
415 $self->{'pid_hash'}->{$pid} = $method
416 if( $pid and $method );
417 return $self->{'pid_hash'};
420 # ----------------------------------------------
421 # If requested, the System can shut down.
425 $SIG{CHLD} = 'IGNORE';
426 $SIG{INT} = 'IGNORE';
427 kill( 'INT', -$$ ); #kill all in process group
432 # ----------------------------------------------
436 _log( "HUPping brood" );
437 $SIG{CHLD} = 'IGNORE';
438 $SIG{HUP} = 'IGNORE';
439 set_config(); # reload config
441 # $SIG{CHLD} = \&process_automation;
442 $SIG{HUP} = sub{ instance()->hupall(); };
446 # ----------------------------------------------
447 # Log to debug, and stdout
451 OpenSRF::Utils::Logger->debug( $string, INFO );
452 print $string . "\n";
455 # ----------------------------------------------
458 select( undef, undef, undef, 0.3 );