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) eq "ARRAY") { $apps = [$apps]; }
148 for my $app (@$apps) {
149 # verify we are a settings server and launch
150 if( $app eq "opensrf.settings" ) {
151 $are_settings_server = 1;
152 $self->launch_settings();
154 $self->launch_settings_listener();
160 # Launch everything else
161 my $client = OpenSRF::Utils::SettingsClient->new();
162 my $apps = $client->config_value("activeapps", "appname" );
163 if(!ref($apps)) { $apps = [$apps]; }
164 my $server_type = $client->config_value("server_type");
165 $server_type ||= "basic";
167 my $con = OpenSRF::Transport::PeerHandle->retrieve;
174 if( $server_type eq "prefork" ) {
175 $server_type = "Net::Server::PreFork";
177 $server_type = "Net::Server::Single";
180 _log( " * Server type: $server_type", INTERNAL );
182 eval "use $server_type";
185 throw OpenSRF::EX::PANIC ("Cannot set $server_type: $@" );
188 push @OpenSRF::UnixServer::ISA, $server_type;
190 _log( " * System boostrap" );
192 # --- Boot the Unix servers
193 $self->launch_unix($apps);
199 # --- Boot the listeners
200 $self->launch_listener($apps);
204 _log( " * System is ready..." );
207 my $ps = `ps ax | grep " Open" | grep -v grep | sort -r -k5`;
209 print "\n --- PS --- \n$ps --- PS ---\n\n";
211 while( 1 ) { sleep; }
217 # ----------------------------------------------
218 # Bootstraps a single client connection.
220 # named params are 'config_file' and 'client_name'
222 sub bootstrap_client {
227 $bootstrap_config_file =
228 $params{config_file} || $bootstrap_config_file;
230 my $app = $params{client_name} || "client";
233 load_bootstrap_config();
234 OpenSRF::Utils::Logger::set_config();
235 OpenSRF::Transport::PeerHandle->construct( $app );
239 sub bootstrap_logger {
241 OpenSRF::Utils::LogServer->serve();
245 # ----------------------------------------------
246 # Cycle through the known processes, reap the dead child
247 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
249 sub process_automation {
251 my $self = __PACKAGE__->instance();
253 foreach my $pid ( keys %{$self->pid_hash} ) {
255 if( waitpid( $pid, WNOHANG ) == $pid ) {
257 my $method = $self->pid_hash->{$pid};
258 delete $self->pid_hash->{$pid};
260 my $newpid = OpenSRF::Utils::safe_fork();
262 OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
263 _log( "Relaunching => $method" );
266 $self->pid_hash( $newpid, $method );
268 else { eval $method; exit; }
272 $SIG{CHLD} = \&process_automation;
277 sub launch_settings {
279 # XXX the $self like this and pid automation will not work with this setup....
281 @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::PreFork);
283 my $pid = OpenSRF::Utils::safe_fork();
285 $self->pid_hash( $pid , "launch_settings()" );
288 my $apname = "opensrf.settings";
289 #$0 = "OpenSRF App [$apname]";
290 eval _unixserver( $apname );
291 if($@) { die "$@\n"; }
295 @OpenSRF::UnixServer::ISA = qw(OpenSRF);
300 sub launch_settings_listener {
303 my $app = "opensrf.settings";
304 my $pid = OpenSRF::Utils::safe_fork();
306 $self->pid_hash( $pid , _listener( $app ) );
310 $0 = "OpenSRF listener [$apname]";
311 eval _listener( $app );
317 # ----------------------------------------------
318 # Launch the Unix Servers
321 my( $self, $apps ) = @_;
323 foreach my $app ( @$apps ) {
326 if( $app eq "opensrf.settings" ) { next; }
328 _log( " * Starting UnixServer for $app..." );
330 my $pid = OpenSRF::Utils::safe_fork();
332 $self->pid_hash( $pid , _unixserver( $app ) );
336 $0 = "OpenSRF App ($apname)";
337 eval _unixserver( $app );
343 # ----------------------------------------------
344 # Launch the inbound clients
346 sub launch_listener {
348 my( $self, $apps ) = @_;
350 foreach my $app ( @$apps ) {
352 if( $app eq "opensrf.settings" ) { next; }
354 _log( " * Starting Listener for $app..." );
356 my $pid = OpenSRF::Utils::safe_fork();
358 $self->pid_hash( $pid , _listener( $app ) );
362 $0 = "OpenSRF listener [$apname]";
363 eval _listener( $app );
369 # ----------------------------------------------
376 my $pid = OpenSRF::Utils::safe_fork();
378 if( $pid ) { $self->pid_hash( $pid , _shell() ); }
381 for( my $x = 0; $x != 10; $x++ ) {
391 # ----------------------------------------------
394 my( $self, $pid, $method ) = @_;
395 $self->{'pid_hash'}->{$pid} = $method
396 if( $pid and $method );
397 return $self->{'pid_hash'};
400 # ----------------------------------------------
401 # If requested, the System can shut down.
405 $SIG{CHLD} = 'IGNORE';
406 $SIG{INT} = 'IGNORE';
407 kill( 'INT', -$$ ); #kill all in process group
412 # ----------------------------------------------
416 _log( "HUPping brood" );
417 $SIG{CHLD} = 'IGNORE';
418 $SIG{HUP} = 'IGNORE';
419 set_config(); # reload config
421 # $SIG{CHLD} = \&process_automation;
422 $SIG{HUP} = sub{ instance()->hupall(); };
426 # ----------------------------------------------
427 # Log to debug, and stdout
431 OpenSRF::Utils::Logger->debug( $string, INFO );
432 print $string . "\n";
435 # ----------------------------------------------
438 select( undef, undef, undef, 0.3 );