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 Net::Server::PreFork;
19 my $bootstrap_config_file;
21 my( $self, $config ) = @_;
22 $bootstrap_config_file = $config;
25 =head2 Name/Description
29 To start the system: OpenSRF::System->bootstrap();
31 Simple system process management and automation. After instantiating the class, simply call
32 bootstrap() to launch the system. Each launched process is stored as a process-id/method-name
33 pair in a local hash. When we receive a SIG{CHILD}, we loop through this hash and relaunch
34 any child processes that may have terminated.
36 Currently automated processes include launching the internal Unix Servers, launching the inbound
37 connections for each application, and starting the system shell.
40 Note: There should be only one instance of this class
41 alive at any given time. It is designed as a globel process handler and, hence, will cause much
42 oddness if you call the bootstrap() method twice or attempt to create two of these by trickery.
43 There is a single instance of the class created on the first call to new(). This same instance is
44 returned on subsequent calls to new().
52 # ----------------------------------------------
54 $SIG{INT} = sub { instance()->killall(); };
56 $SIG{HUP} = sub{ instance()->hupall(); };
58 $SIG{CHLD} = \&process_automation;
63 # put $instance in a closure and return it for requests to new()
64 # since there should only be one System instance running
67 sub instance { return __PACKAGE__->new(); }
72 $class = ref( $class ) || $class;
74 $self->{'pid_hash'} = {};
75 bless( $self, $class );
82 # ----------------------------------------------
83 # Commands to execute at system launch
87 return "OpenSRF::UnixServer->new( '$app')->serve()";
96 my $password = "jkjkasdf";
97 my $jserver = "elroy";
99 my $hostname = "elroy.pls-hq.org";
100 my $unix_file = "/pines/var/sock/$app" . "_unix.sock";
101 my $router = 'router\@elroy/router';
102 return "exec(\"/pines/cvs/ILS/OpenSRF/src/forker/oils_inbound $service $username " .
103 "$password $jserver $port $hostname $unix_file $router\")";
106 return "OpenSRF::Transport::Listener->new( '$app' )->initialize()->listen()";
110 # ----------------------------------------------
113 sub load_bootstrap_config {
115 if(OpenSRF::Utils::Config->current) {
119 warn "Loading $bootstrap_config_file\n";
120 if(!$bootstrap_config_file) {
121 die "Please provide a bootstrap config file to OpenSRF::System!\n" .
122 "use OpenSRF::System qw(/path/to/bootstrap_config);";
125 OpenSRF::Utils::Config->load( config_file => $bootstrap_config_file );
127 JSON->register_class_hint( name => "OpenSRF::Application", hint => "", type => "hash" );
129 OpenSRF::Transport->message_envelope( "OpenSRF::Transport::SlimJabber::MessageWrapper" );
130 OpenSRF::Transport::PeerHandle->set_peer_client( "OpenSRF::Transport::SlimJabber::PeerConnection" );
131 OpenSRF::Transport::Listener->set_listener( "OpenSRF::Transport::SlimJabber::Inbound" );
136 my $self = __PACKAGE__->instance();
137 load_bootstrap_config();
138 OpenSRF::Utils::Logger::set_config();
139 my $bsconfig = OpenSRF::Utils::Config->current;
141 # Start a process group and make me the captain
145 # -----------------------------------------------
146 # Launch the settings sever if necessary...
147 my $are_settings_server = 0;
148 if( (my $cfile = $bsconfig->bootstrap->settings_config) ) {
149 my $parser = OpenSRF::Utils::SettingsParser->new();
151 # since we're (probably) the settings server, we can go ahead and load the real config file
152 $parser->initialize( $cfile );
153 $OpenSRF::Utils::SettingsClient::host_config =
154 $parser->get_server_config($bsconfig->env->hostname);
156 my $client = OpenSRF::Utils::SettingsClient->new();
157 my $apps = $client->config_value("activeapps", "appname");
158 if(!ref($apps) eq "ARRAY") { $apps = [$apps]; }
160 for my $app (@$apps) {
161 # verify we are a settings server and launch
162 if( $app eq "settings" ) {
163 $are_settings_server = 1;
164 $self->launch_settings();
166 $self->launch_settings_listener();
172 # Launch everything else
173 my $client = OpenSRF::Utils::SettingsClient->new();
174 my $apps = $client->config_value("activeapps", "appname" );
175 if(!ref($apps)) { $apps = [$apps]; }
176 my $server_type = $client->config_value("server_type");
177 $server_type ||= "basic";
179 my $con = OpenSRF::Transport::PeerHandle->retrieve;
186 if( $server_type eq "prefork" ) {
187 $server_type = "Net::Server::PreFork";
189 $server_type = "Net::Server::Single";
192 _log( " * Server type: $server_type", INTERNAL );
194 eval "use $server_type";
197 throw OpenSRF::EX::PANIC ("Cannot set $server_type: $@" );
200 push @OpenSRF::UnixServer::ISA, $server_type;
202 _log( " * System boostrap" );
204 # --- Boot the Unix servers
205 $self->launch_unix($apps);
209 # --- Boot the listeners
210 $self->launch_listener($apps);
214 _log( " * System is ready..." );
216 while( 1 ) { sleep; }
222 # ----------------------------------------------
223 # Bootstraps a single client connection.
225 sub bootstrap_client {
227 my $self = __PACKAGE__->instance();
228 load_bootstrap_config();
229 OpenSRF::Utils::Logger::set_config();
231 my $client_type = shift;
234 if( defined($client_type) and $client_type ) {
240 OpenSRF::Transport::PeerHandle->construct( $app );
244 sub bootstrap_logger {
246 OpenSRF::Utils::LogServer->serve();
250 # ----------------------------------------------
251 # Cycle through the known processes, reap the dead child
252 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
254 sub process_automation {
256 my $self = __PACKAGE__->instance();
258 foreach my $pid ( keys %{$self->pid_hash} ) {
260 if( waitpid( $pid, WNOHANG ) == $pid ) {
262 my $method = $self->pid_hash->{$pid};
263 delete $self->pid_hash->{$pid};
265 my $newpid = OpenSRF::Utils::safe_fork();
267 OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
268 _log( "Relaunching => $method" );
271 $self->pid_hash( $newpid, $method );
273 else { eval $method; exit; }
277 $SIG{CHLD} = \&process_automation;
282 sub launch_settings {
284 # XXX the $self like this and pid automation will not work with this setup....
286 @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::PreFork);
288 my $pid = OpenSRF::Utils::safe_fork();
290 $self->pid_hash( $pid , "launch_settings()" );
293 my $apname = "settings";
294 $0 = "OpenSRF App ($apname)";
295 eval _unixserver( "settings" );
296 if($@) { die "$@\n"; }
300 @OpenSRF::UnixServer::ISA = qw(OpenSRF);
305 sub launch_settings_listener {
308 my $app = "settings";
309 my $pid = OpenSRF::Utils::safe_fork();
311 $self->pid_hash( $pid , _listener( $app ) );
315 $0 = "OpenSRF listener ($apname)";
316 eval _listener( $app );
322 # ----------------------------------------------
323 # Launch the Unix Servers
326 my( $self, $apps ) = @_;
328 foreach my $app ( @$apps ) {
330 if( $app eq "settings" ) { next; }
332 _log( " * Starting UnixServer for $app..." );
334 my $pid = OpenSRF::Utils::safe_fork();
336 $self->pid_hash( $pid , _unixserver( $app ) );
340 $0 = "OpenSRF App ($apname)";
341 eval _unixserver( $app );
347 # ----------------------------------------------
348 # Launch the inbound clients
350 sub launch_listener {
352 my( $self, $apps ) = @_;
354 foreach my $app ( @$apps ) {
356 if( $app eq "settings" ) { next; }
358 _log( " * Starting Listener for $app..." );
360 my $pid = OpenSRF::Utils::safe_fork();
362 $self->pid_hash( $pid , _listener( $app ) );
366 $0 = "Listener ($apname)";
367 eval _listener( $app );
373 # ----------------------------------------------
380 my $pid = OpenSRF::Utils::safe_fork();
382 if( $pid ) { $self->pid_hash( $pid , _shell() ); }
385 for( my $x = 0; $x != 10; $x++ ) {
395 # ----------------------------------------------
398 my( $self, $pid, $method ) = @_;
399 $self->{'pid_hash'}->{$pid} = $method
400 if( $pid and $method );
401 return $self->{'pid_hash'};
404 # ----------------------------------------------
405 # If requested, the System can shut down.
409 $SIG{CHLD} = 'IGNORE';
410 $SIG{INT} = 'IGNORE';
411 kill( 'INT', -$$ ); #kill all in process group
416 # ----------------------------------------------
420 _log( "HUPping brood" );
421 $SIG{CHLD} = 'IGNORE';
422 $SIG{HUP} = 'IGNORE';
423 set_config(); # reload config
425 # $SIG{CHLD} = \&process_automation;
426 $SIG{HUP} = sub{ instance()->hupall(); };
430 # ----------------------------------------------
431 # Log to debug, and stdout
435 OpenSRF::Utils::Logger->debug( $string, INFO );
436 print $string . "\n";
439 # ----------------------------------------------
442 select( undef, undef, undef, 0.3 );