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;
11 use OpenSRF::EX qw/:try/;
12 use POSIX qw/setsid :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 if(!$bootstrap_config_file) {
107 die "Please provide a bootstrap config file to OpenSRF::System!\n" .
108 "use OpenSRF::System qw(/path/to/bootstrap_config);";
111 OpenSRF::Utils::Config->load( config_file => $bootstrap_config_file );
113 OpenSRF::Utils::JSON->register_class_hint( name => "OpenSRF::Application", hint => "method", type => "hash" );
115 OpenSRF::Transport->message_envelope( "OpenSRF::Transport::SlimJabber::MessageWrapper" );
116 OpenSRF::Transport::PeerHandle->set_peer_client( "OpenSRF::Transport::SlimJabber::PeerConnection" );
117 OpenSRF::Transport::Listener->set_listener( "OpenSRF::Transport::SlimJabber::Inbound" );
118 OpenSRF::Application->server_class('client');
123 my $self = __PACKAGE__->instance();
124 load_bootstrap_config();
125 OpenSRF::Utils::Logger::set_config();
126 my $bsconfig = OpenSRF::Utils::Config->current;
128 # Start a process group and make me the captain
129 exit if (OpenSRF::Utils::safe_fork());
136 $0 = "OpenSRF System";
138 # -----------------------------------------------
139 # Launch the settings sever if necessary...
140 my $are_settings_server = 0;
141 if( (my $cfile = $bsconfig->bootstrap->settings_config) ) {
142 my $parser = OpenSRF::Utils::SettingsParser->new();
144 # since we're (probably) the settings server, we can go ahead and load the real config file
145 $parser->initialize( $cfile );
146 $OpenSRF::Utils::SettingsClient::host_config =
147 $parser->get_server_config($bsconfig->env->hostname);
149 my $client = OpenSRF::Utils::SettingsClient->new();
150 my $apps = $client->config_value("activeapps", "appname");
151 if(ref($apps) ne "ARRAY") { $apps = [$apps]; }
153 if(!defined($apps) || @$apps == 0) {
154 print "No apps to load, exiting...";
158 for my $app (@$apps) {
159 # verify we are a settings server and launch
160 if( $app eq "opensrf.settings" and
161 $client->config_value("apps","opensrf.settings", "language") =~ /perl/i ) {
163 $are_settings_server = 1;
164 $self->launch_settings();
166 $self->launch_settings_listener();
172 # Launch everything else
173 OpenSRF::System->bootstrap_client(client_name => "system_client");
174 my $client = OpenSRF::Utils::SettingsClient->new();
175 my $apps = $client->config_value("activeapps", "appname" );
176 if(!ref($apps)) { $apps = [$apps]; }
178 if(!defined($apps) || @$apps == 0) {
179 print "No apps to load, exiting...";
183 my $server_type = $client->config_value("server_type");
184 $server_type ||= "basic";
186 my $con = OpenSRF::Transport::PeerHandle->retrieve;
193 if( $server_type eq "prefork" ) {
194 $server_type = "Net::Server::PreFork";
196 $server_type = "Net::Server::Single";
199 _log( " * Server type: $server_type", INTERNAL );
204 throw OpenSRF::EX::PANIC ("Cannot set $server_type: $@" );
207 push @OpenSRF::UnixServer::ISA, $server_type;
209 _log( " * System bootstrap" );
211 # --- Boot the Unix servers
212 $self->launch_unix($apps);
216 # --- Boot the listeners
217 $self->launch_listener($apps);
221 _log( " * System is ready..." );
224 # my $ps = `ps ax | grep " Open" | grep -v grep | sort -r -k5`;
225 # print "\n --- PS --- \n$ps --- PS ---\n\n";
227 while( 1 ) { sleep; }
233 # ----------------------------------------------
234 # Bootstraps a single client connection.
236 # named params are 'config_file' and 'client_name'
238 sub bootstrap_client {
241 my $con = OpenSRF::Transport::PeerHandle->retrieve;
243 if($con and $con->tcp_connected) {
249 $bootstrap_config_file =
250 $params{config_file} || $bootstrap_config_file;
252 my $app = $params{client_name} || "client";
255 load_bootstrap_config();
256 OpenSRF::Utils::Logger::set_config();
257 OpenSRF::Transport::PeerHandle->construct( $app );
262 if (my $con = OpenSRF::Transport::PeerHandle->retrieve) {
263 return 1 if ($con->tcp_connected);
268 sub bootstrap_logger {
270 OpenSRF::Utils::LogServer->serve();
274 # ----------------------------------------------
275 # Cycle through the known processes, reap the dead child
276 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
278 sub process_automation {
280 my $self = __PACKAGE__->instance();
282 foreach my $pid ( keys %{$self->pid_hash} ) {
284 if( waitpid( $pid, WNOHANG ) == $pid ) {
286 my $method = $self->pid_hash->{$pid};
287 delete $self->pid_hash->{$pid};
289 my $newpid = OpenSRF::Utils::safe_fork();
291 OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
292 _log( "Relaunching => $method" );
295 $self->pid_hash( $newpid, $method );
297 else { eval $method; exit; }
301 $SIG{CHLD} = \&process_automation;
306 sub launch_settings {
308 # XXX the $self like this and pid automation will not work with this setup....
310 @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::PreFork);
312 my $pid = OpenSRF::Utils::safe_fork();
314 $self->pid_hash( $pid , "launch_settings()" );
317 my $apname = "opensrf.settings";
318 #$0 = "OpenSRF App [$apname]";
319 eval _unixserver( $apname );
320 if($@) { die "$@\n"; }
324 @OpenSRF::UnixServer::ISA = qw(OpenSRF);
329 sub launch_settings_listener {
332 my $app = "opensrf.settings";
333 my $pid = OpenSRF::Utils::safe_fork();
335 $self->pid_hash( $pid , _listener( $app ) );
339 $0 = "OpenSRF listener [$apname]";
340 eval _listener( $app );
346 # ----------------------------------------------
347 # Launch the Unix Servers
350 my( $self, $apps ) = @_;
352 my $client = OpenSRF::Utils::SettingsClient->new();
354 foreach my $app ( @$apps ) {
357 my $lang = $client->config_value( "apps", $app, "language");
358 next unless $lang =~ /perl/i;
359 next if $app eq "opensrf.settings";
361 _log( " * Starting UnixServer for $app..." );
363 my $pid = OpenSRF::Utils::safe_fork();
365 $self->pid_hash( $pid , _unixserver( $app ) );
369 $0 = "OpenSRF App ($apname)";
370 eval _unixserver( $app );
376 # ----------------------------------------------
377 # Launch the inbound clients
379 sub launch_listener {
381 my( $self, $apps ) = @_;
382 my $client = OpenSRF::Utils::SettingsClient->new();
384 foreach my $app ( @$apps ) {
387 my $lang = $client->config_value( "apps", $app, "language");
388 next unless $lang =~ /perl/i;
389 next if $app eq "opensrf.settings";
391 _log( " * Starting Listener for $app..." );
393 my $pid = OpenSRF::Utils::safe_fork();
395 $self->pid_hash( $pid , _listener( $app ) );
399 $0 = "OpenSRF listener [$apname]";
400 eval _listener( $app );
407 # ----------------------------------------------
410 my( $self, $pid, $method ) = @_;
411 $self->{'pid_hash'}->{$pid} = $method
412 if( $pid and $method );
413 return $self->{'pid_hash'};
416 # ----------------------------------------------
417 # If requested, the System can shut down.
421 $SIG{CHLD} = 'IGNORE';
422 $SIG{INT} = 'IGNORE';
423 kill( 'INT', -$$ ); #kill all in process group
428 # ----------------------------------------------
432 _log( "HUPping brood" );
433 $SIG{CHLD} = 'IGNORE';
434 $SIG{HUP} = 'IGNORE';
436 # $SIG{CHLD} = \&process_automation;
437 $SIG{HUP} = sub{ instance()->hupall(); };
441 # ----------------------------------------------
442 # Log to debug, and stdout
446 OpenSRF::Utils::Logger->debug( $string, INFO );
447 print $string . "\n";
450 # ----------------------------------------------