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 { $0 = $method; 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 $apname =~ tr/[a-z]/[A-Z]/;
295 $0 = "OpenSRF App ($apname)";
296 eval _unixserver( "settings" );
297 if($@) { die "$@\n"; }
301 @OpenSRF::UnixServer::ISA = qw(OpenSRF);
306 sub launch_settings_listener {
309 my $app = "settings";
310 my $pid = OpenSRF::Utils::safe_fork();
312 $self->pid_hash( $pid , _listener( $app ) );
316 $apname =~ tr/[a-z]/[A-Z]/;
317 $0 = "Listener ($apname)";
318 eval _listener( $app );
324 # ----------------------------------------------
325 # Launch the Unix Servers
328 my( $self, $apps ) = @_;
330 foreach my $app ( @$apps ) {
332 if( $app eq "settings" ) { next; }
334 _log( " * Starting UnixServer for $app..." );
336 my $pid = OpenSRF::Utils::safe_fork();
338 $self->pid_hash( $pid , _unixserver( $app ) );
342 $apname =~ tr/[a-z]/[A-Z]/;
343 $0 = "OpenSRF App ($apname)";
344 eval _unixserver( $app );
350 # ----------------------------------------------
351 # Launch the inbound clients
353 sub launch_listener {
355 my( $self, $apps ) = @_;
357 foreach my $app ( @$apps ) {
359 if( $app eq "settings" ) { next; }
361 _log( " * Starting Listener for $app..." );
363 my $pid = OpenSRF::Utils::safe_fork();
365 $self->pid_hash( $pid , _listener( $app ) );
369 $apname =~ tr/[a-z]/[A-Z]/;
370 $0 = "Listener ($apname)";
371 warn "Launching Listener with command:\n " . _listener($app) . "\n";
372 eval _listener( $app );
378 # ----------------------------------------------
385 my $pid = OpenSRF::Utils::safe_fork();
387 if( $pid ) { $self->pid_hash( $pid , _shell() ); }
390 for( my $x = 0; $x != 10; $x++ ) {
400 # ----------------------------------------------
403 my( $self, $pid, $method ) = @_;
404 $self->{'pid_hash'}->{$pid} = $method
405 if( $pid and $method );
406 return $self->{'pid_hash'};
409 # ----------------------------------------------
410 # If requested, the System can shut down.
414 $SIG{CHLD} = 'IGNORE';
415 $SIG{INT} = 'IGNORE';
416 kill( 'INT', -$$ ); #kill all in process group
421 # ----------------------------------------------
425 _log( "HUPping brood" );
426 $SIG{CHLD} = 'IGNORE';
427 $SIG{HUP} = 'IGNORE';
428 set_config(); # reload config
430 # $SIG{CHLD} = \&process_automation;
431 $SIG{HUP} = sub{ instance()->hupall(); };
435 # ----------------------------------------------
436 # Log to debug, and stdout
440 OpenSRF::Utils::Logger->debug( $string, INFO );
441 print $string . "\n";
444 # ----------------------------------------------
447 select( undef, undef, undef, 0.3 );