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 qw/setsid :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 OpenSRF::Utils::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
130 exit if (OpenSRF::Utils::safe_fork());
137 $0 = "OpenSRF System";
139 # -----------------------------------------------
140 # Launch the settings sever if necessary...
141 my $are_settings_server = 0;
142 if( (my $cfile = $bsconfig->bootstrap->settings_config) ) {
143 my $parser = OpenSRF::Utils::SettingsParser->new();
145 # since we're (probably) the settings server, we can go ahead and load the real config file
146 $parser->initialize( $cfile );
147 $OpenSRF::Utils::SettingsClient::host_config =
148 $parser->get_server_config($bsconfig->env->hostname);
150 my $client = OpenSRF::Utils::SettingsClient->new();
151 my $apps = $client->config_value("activeapps", "appname");
152 if(ref($apps) ne "ARRAY") { $apps = [$apps]; }
154 if(!defined($apps) || @$apps == 0) {
155 print "No apps to load, exiting...";
159 for my $app (@$apps) {
160 # verify we are a settings server and launch
161 if( $app eq "opensrf.settings" and
162 $client->config_value("apps","opensrf.settings", "language") =~ /perl/i ) {
164 $are_settings_server = 1;
165 $self->launch_settings();
167 $self->launch_settings_listener();
173 # Launch everything else
174 OpenSRF::System->bootstrap_client(client_name => "system_client");
175 my $client = OpenSRF::Utils::SettingsClient->new();
176 my $apps = $client->config_value("activeapps", "appname" );
177 if(!ref($apps)) { $apps = [$apps]; }
179 if(!defined($apps) || @$apps == 0) {
180 print "No apps to load, exiting...";
184 my $server_type = $client->config_value("server_type");
185 $server_type ||= "basic";
187 my $con = OpenSRF::Transport::PeerHandle->retrieve;
194 if( $server_type eq "prefork" ) {
195 $server_type = "Net::Server::PreFork";
197 $server_type = "Net::Server::Single";
200 _log( " * Server type: $server_type", INTERNAL );
205 throw OpenSRF::EX::PANIC ("Cannot set $server_type: $@" );
208 push @OpenSRF::UnixServer::ISA, $server_type;
210 _log( " * System bootstrap" );
212 # --- Boot the Unix servers
213 $self->launch_unix($apps);
217 # --- Boot the listeners
218 $self->launch_listener($apps);
222 _log( " * System is ready..." );
225 # my $ps = `ps ax | grep " Open" | grep -v grep | sort -r -k5`;
226 # print "\n --- PS --- \n$ps --- PS ---\n\n";
228 while( 1 ) { sleep; }
234 # ----------------------------------------------
235 # Bootstraps a single client connection.
237 # named params are 'config_file' and 'client_name'
239 sub bootstrap_client {
242 my $con = OpenSRF::Transport::PeerHandle->retrieve;
244 if($con and $con->tcp_connected) {
250 $bootstrap_config_file =
251 $params{config_file} || $bootstrap_config_file;
253 my $app = $params{client_name} || "client";
256 load_bootstrap_config();
257 OpenSRF::Utils::Logger::set_config();
258 OpenSRF::Transport::PeerHandle->construct( $app );
263 if (my $con = OpenSRF::Transport::PeerHandle->retrieve) {
264 return 1 if ($con->tcp_connected);
269 sub bootstrap_logger {
271 OpenSRF::Utils::LogServer->serve();
275 # ----------------------------------------------
276 # Cycle through the known processes, reap the dead child
277 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
279 sub process_automation {
281 my $self = __PACKAGE__->instance();
283 foreach my $pid ( keys %{$self->pid_hash} ) {
285 if( waitpid( $pid, WNOHANG ) == $pid ) {
287 my $method = $self->pid_hash->{$pid};
288 delete $self->pid_hash->{$pid};
290 my $newpid = OpenSRF::Utils::safe_fork();
292 OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
293 _log( "Relaunching => $method" );
296 $self->pid_hash( $newpid, $method );
298 else { eval $method; exit; }
302 $SIG{CHLD} = \&process_automation;
307 sub launch_settings {
309 # XXX the $self like this and pid automation will not work with this setup....
311 @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::PreFork);
313 my $pid = OpenSRF::Utils::safe_fork();
315 $self->pid_hash( $pid , "launch_settings()" );
318 my $apname = "opensrf.settings";
319 #$0 = "OpenSRF App [$apname]";
320 eval _unixserver( $apname );
321 if($@) { die "$@\n"; }
325 @OpenSRF::UnixServer::ISA = qw(OpenSRF);
330 sub launch_settings_listener {
333 my $app = "opensrf.settings";
334 my $pid = OpenSRF::Utils::safe_fork();
336 $self->pid_hash( $pid , _listener( $app ) );
340 $0 = "OpenSRF listener [$apname]";
341 eval _listener( $app );
347 # ----------------------------------------------
348 # Launch the Unix Servers
351 my( $self, $apps ) = @_;
353 my $client = OpenSRF::Utils::SettingsClient->new();
355 foreach my $app ( @$apps ) {
358 my $lang = $client->config_value( "apps", $app, "language");
359 next unless $lang =~ /perl/i;
360 next if $app eq "opensrf.settings";
362 _log( " * Starting UnixServer for $app..." );
364 my $pid = OpenSRF::Utils::safe_fork();
366 $self->pid_hash( $pid , _unixserver( $app ) );
370 $0 = "OpenSRF App ($apname)";
371 eval _unixserver( $app );
377 # ----------------------------------------------
378 # Launch the inbound clients
380 sub launch_listener {
382 my( $self, $apps ) = @_;
383 my $client = OpenSRF::Utils::SettingsClient->new();
385 foreach my $app ( @$apps ) {
388 my $lang = $client->config_value( "apps", $app, "language");
389 next unless $lang =~ /perl/i;
390 next if $app eq "opensrf.settings";
392 _log( " * Starting Listener for $app..." );
394 my $pid = OpenSRF::Utils::safe_fork();
396 $self->pid_hash( $pid , _listener( $app ) );
400 $0 = "OpenSRF listener [$apname]";
401 eval _listener( $app );
408 # ----------------------------------------------
411 my( $self, $pid, $method ) = @_;
412 $self->{'pid_hash'}->{$pid} = $method
413 if( $pid and $method );
414 return $self->{'pid_hash'};
417 # ----------------------------------------------
418 # If requested, the System can shut down.
422 $SIG{CHLD} = 'IGNORE';
423 $SIG{INT} = 'IGNORE';
424 kill( 'INT', -$$ ); #kill all in process group
429 # ----------------------------------------------
433 _log( "HUPping brood" );
434 $SIG{CHLD} = 'IGNORE';
435 $SIG{HUP} = 'IGNORE';
437 # $SIG{CHLD} = \&process_automation;
438 $SIG{HUP} = sub{ instance()->hupall(); };
442 # ----------------------------------------------
443 # Log to debug, and stdout
447 OpenSRF::Utils::Logger->debug( $string, INFO );
448 print $string . "\n";
451 # ----------------------------------------------