1 package OpenSRF::System;
2 use strict; use warnings;
5 use OpenSRF::Utils::Logger qw(:level);
6 use OpenSRF::Transport::Listener;
7 use OpenSRF::Transport;
8 use OpenSRF::UnixServer;
10 use OpenSRF::Utils::LogServer;
12 use OpenSRF::EX qw/:try/;
13 use POSIX ":sys_wait_h";
14 use OpenSRF::Utils::Config;
15 use OpenSRF::Utils::SettingsParser;
16 use OpenSRF::Utils::SettingsClient;
17 use OpenSRF::Application;
18 use Net::Server::PreFork;
21 my $bootstrap_config_file;
23 my( $self, $config ) = @_;
24 $bootstrap_config_file = $config;
27 =head2 Name/Description
31 To start the system: OpenSRF::System->bootstrap();
33 Simple system process management and automation. After instantiating the class, simply call
34 bootstrap() to launch the system. Each launched process is stored as a process-id/method-name
35 pair in a local hash. When we receive a SIG{CHILD}, we loop through this hash and relaunch
36 any child processes that may have terminated.
38 Currently automated processes include launching the internal Unix Servers, launching the inbound
39 connections for each application, and starting the system shell.
42 Note: There should be only one instance of this class
43 alive at any given time. It is designed as a globel process handler and, hence, will cause much
44 oddness if you call the bootstrap() method twice or attempt to create two of these by trickery.
45 There is a single instance of the class created on the first call to new(). This same instance is
46 returned on subsequent calls to new().
54 # ----------------------------------------------
56 $SIG{INT} = sub { instance()->killall(); };
58 $SIG{HUP} = sub{ instance()->hupall(); };
60 #$SIG{CHLD} = \&process_automation;
65 # put $instance in a closure and return it for requests to new()
66 # since there should only be one System instance running
69 sub instance { return __PACKAGE__->new(); }
74 $class = ref( $class ) || $class;
76 $self->{'pid_hash'} = {};
77 bless( $self, $class );
84 # ----------------------------------------------
85 # Commands to execute at system launch
89 return "OpenSRF::UnixServer->new( '$app')->serve()";
94 return "OpenSRF::Transport::Listener->new( '$app' )->initialize()->listen()";
98 # ----------------------------------------------
101 sub load_bootstrap_config {
103 if(OpenSRF::Utils::Config->current) {
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 OpenSRF::System->bootstrap_client(client_name => "system_client");
169 my $client = OpenSRF::Utils::SettingsClient->new();
170 my $apps = $client->config_value("activeapps", "appname" );
171 if(!ref($apps)) { $apps = [$apps]; }
173 if(!defined($apps) || @$apps == 0) {
174 print "No apps to load, exiting...";
178 my $server_type = $client->config_value("server_type");
179 $server_type ||= "basic";
181 my $con = OpenSRF::Transport::PeerHandle->retrieve;
188 if( $server_type eq "prefork" ) {
189 $server_type = "Net::Server::PreFork";
191 $server_type = "Net::Server::Single";
194 _log( " * Server type: $server_type", INTERNAL );
199 throw OpenSRF::EX::PANIC ("Cannot set $server_type: $@" );
202 push @OpenSRF::UnixServer::ISA, $server_type;
204 _log( " * System boostrap" );
206 # --- Boot the Unix servers
207 $self->launch_unix($apps);
213 # --- Boot the listeners
214 $self->launch_listener($apps);
218 _log( " * System is ready..." );
221 # 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 );
260 if (my $con = OpenSRF::Transport::PeerHandle->retrieve) {
261 return 1 if ($con->tcp_connected);
266 sub bootstrap_logger {
268 OpenSRF::Utils::LogServer->serve();
272 # ----------------------------------------------
273 # Cycle through the known processes, reap the dead child
274 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
276 sub process_automation {
278 my $self = __PACKAGE__->instance();
280 foreach my $pid ( keys %{$self->pid_hash} ) {
282 if( waitpid( $pid, WNOHANG ) == $pid ) {
284 my $method = $self->pid_hash->{$pid};
285 delete $self->pid_hash->{$pid};
287 my $newpid = OpenSRF::Utils::safe_fork();
289 OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
290 _log( "Relaunching => $method" );
293 $self->pid_hash( $newpid, $method );
295 else { eval $method; exit; }
299 $SIG{CHLD} = \&process_automation;
304 sub launch_settings {
306 # XXX the $self like this and pid automation will not work with this setup....
308 @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::PreFork);
310 my $pid = OpenSRF::Utils::safe_fork();
312 $self->pid_hash( $pid , "launch_settings()" );
315 my $apname = "opensrf.settings";
316 #$0 = "OpenSRF App [$apname]";
317 eval _unixserver( $apname );
318 if($@) { die "$@\n"; }
322 @OpenSRF::UnixServer::ISA = qw(OpenSRF);
327 sub launch_settings_listener {
330 my $app = "opensrf.settings";
331 my $pid = OpenSRF::Utils::safe_fork();
333 $self->pid_hash( $pid , _listener( $app ) );
337 $0 = "OpenSRF listener [$apname]";
338 eval _listener( $app );
344 # ----------------------------------------------
345 # Launch the Unix Servers
348 my( $self, $apps ) = @_;
350 my $client = OpenSRF::Utils::SettingsClient->new();
352 foreach my $app ( @$apps ) {
355 my $lang = $client->config_value( "apps", $app, "language");
356 next unless $lang =~ /perl/i;
357 next if $app eq "opensrf.settings";
359 _log( " * Starting UnixServer for $app..." );
361 my $pid = OpenSRF::Utils::safe_fork();
363 $self->pid_hash( $pid , _unixserver( $app ) );
367 $0 = "OpenSRF App ($apname)";
368 eval _unixserver( $app );
374 # ----------------------------------------------
375 # Launch the inbound clients
377 sub launch_listener {
379 my( $self, $apps ) = @_;
380 my $client = OpenSRF::Utils::SettingsClient->new();
382 foreach my $app ( @$apps ) {
385 my $lang = $client->config_value( "apps", $app, "language");
386 next unless $lang =~ /perl/i;
387 next if $app eq "opensrf.settings";
389 _log( " * Starting Listener for $app..." );
391 my $pid = OpenSRF::Utils::safe_fork();
393 $self->pid_hash( $pid , _listener( $app ) );
397 $0 = "OpenSRF listener [$apname]";
398 eval _listener( $app );
404 # ----------------------------------------------
411 my $pid = OpenSRF::Utils::safe_fork();
413 if( $pid ) { $self->pid_hash( $pid , _shell() ); }
416 for( my $x = 0; $x != 10; $x++ ) {
426 # ----------------------------------------------
429 my( $self, $pid, $method ) = @_;
430 $self->{'pid_hash'}->{$pid} = $method
431 if( $pid and $method );
432 return $self->{'pid_hash'};
435 # ----------------------------------------------
436 # If requested, the System can shut down.
440 $SIG{CHLD} = 'IGNORE';
441 $SIG{INT} = 'IGNORE';
442 kill( 'INT', -$$ ); #kill all in process group
447 # ----------------------------------------------
451 _log( "HUPping brood" );
452 $SIG{CHLD} = 'IGNORE';
453 $SIG{HUP} = 'IGNORE';
454 set_config(); # reload config
456 # $SIG{CHLD} = \&process_automation;
457 $SIG{HUP} = sub{ instance()->hupall(); };
461 # ----------------------------------------------
462 # Log to debug, and stdout
466 OpenSRF::Utils::Logger->debug( $string, INFO );
467 print $string . "\n";
470 # ----------------------------------------------
473 select( undef, undef, undef, 0.3 );