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()";
92 return "OpenSRF::Transport::Listener->new( '$app' )->initialize()->listen()";
96 # ----------------------------------------------
99 sub load_bootstrap_config {
101 if(OpenSRF::Utils::Config->current) {
105 warn "Loading $bootstrap_config_file\n";
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 JSON->register_class_hint( name => "OpenSRF::Application", hint => "", 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" );
122 my $self = __PACKAGE__->instance();
123 load_bootstrap_config();
124 OpenSRF::Utils::Logger::set_config();
125 my $bsconfig = OpenSRF::Utils::Config->current;
127 # Start a process group and make me the captain
131 # -----------------------------------------------
132 # Launch the settings sever if necessary...
133 my $are_settings_server = 0;
134 if( (my $cfile = $bsconfig->bootstrap->settings_config) ) {
135 my $parser = OpenSRF::Utils::SettingsParser->new();
137 # since we're (probably) the settings server, we can go ahead and load the real config file
138 $parser->initialize( $cfile );
139 $OpenSRF::Utils::SettingsClient::host_config =
140 $parser->get_server_config($bsconfig->env->hostname);
142 my $client = OpenSRF::Utils::SettingsClient->new();
143 my $apps = $client->config_value("activeapps", "appname");
144 if(!ref($apps) eq "ARRAY") { $apps = [$apps]; }
146 for my $app (@$apps) {
147 # verify we are a settings server and launch
148 if( $app eq "settings" ) {
149 $are_settings_server = 1;
150 $self->launch_settings();
152 $self->launch_settings_listener();
158 # Launch everything else
159 my $client = OpenSRF::Utils::SettingsClient->new();
160 my $apps = $client->config_value("activeapps", "appname" );
161 if(!ref($apps)) { $apps = [$apps]; }
162 my $server_type = $client->config_value("server_type");
163 $server_type ||= "basic";
166 if( $server_type eq "prefork" ) {
167 $server_type = "Net::Server::PreFork";
169 $server_type = "Net::Server::Single";
172 _log( " * Server type: $server_type", INTERNAL );
174 eval "use $server_type";
177 throw OpenSRF::EX::PANIC ("Cannot set $server_type: $@" );
180 push @OpenSRF::UnixServer::ISA, $server_type;
182 _log( " * System boostrap" );
184 # --- Boot the Unix servers
185 $self->launch_unix($apps);
189 # --- Boot the listeners
190 $self->launch_listener($apps);
194 _log( " * System is ready..." );
196 while( 1 ) { sleep; }
202 # ----------------------------------------------
203 # Bootstraps a single client connection.
205 sub bootstrap_client {
207 my $self = __PACKAGE__->instance();
208 load_bootstrap_config();
209 OpenSRF::Utils::Logger::set_config();
211 my $client_type = shift;
214 if( defined($client_type) and $client_type ) {
220 OpenSRF::Transport::PeerHandle->construct( $app );
224 sub bootstrap_logger {
227 OpenSRF::Utils::LogServer->serve();
232 # ----------------------------------------------
233 # Cycle through the known processes, reap the dead child
234 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
236 sub process_automation {
238 my $self = __PACKAGE__->instance();
240 foreach my $pid ( keys %{$self->pid_hash} ) {
242 if( waitpid( $pid, WNOHANG ) == $pid ) {
244 my $method = $self->pid_hash->{$pid};
245 delete $self->pid_hash->{$pid};
247 my $newpid = OpenSRF::Utils::safe_fork();
249 OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
250 _log( "Relaunching => $method" );
253 $self->pid_hash( $newpid, $method );
255 else { $0 = $method; eval $method; exit; }
259 $SIG{CHLD} = \&process_automation;
264 sub launch_settings {
266 # XXX the $self like this and pid automation will not work with this setup....
268 @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::PreFork);
270 my $pid = OpenSRF::Utils::safe_fork();
272 $self->pid_hash( $pid , "launch_settings()" );
275 my $apname = "settings";
276 $apname =~ tr/[a-z]/[A-Z]/;
277 $0 = "OpenSRF App ($apname)";
278 eval _unixserver( "settings" );
279 if($@) { die "$@\n"; }
283 @OpenSRF::UnixServer::ISA = qw(OpenSRF);
288 sub launch_settings_listener {
291 my $app = "settings";
292 my $pid = OpenSRF::Utils::safe_fork();
294 $self->pid_hash( $pid , _listener( $app ) );
298 $apname =~ tr/[a-z]/[A-Z]/;
299 $0 = "Listener ($apname)";
300 eval _listener( $app );
306 # ----------------------------------------------
307 # Launch the Unix Servers
310 my( $self, $apps ) = @_;
312 foreach my $app ( @$apps ) {
314 if( $app eq "settings" ) { next; }
316 _log( " * Starting UnixServer for $app..." );
318 my $pid = OpenSRF::Utils::safe_fork();
320 $self->pid_hash( $pid , _unixserver( $app ) );
324 $apname =~ tr/[a-z]/[A-Z]/;
325 $0 = "OpenSRF App ($apname)";
326 eval _unixserver( $app );
332 # ----------------------------------------------
333 # Launch the inbound clients
335 sub launch_listener {
337 my( $self, $apps ) = @_;
339 foreach my $app ( @$apps ) {
341 if( $app eq "settings" ) { next; }
343 _log( " * Starting Listener for $app..." );
345 my $pid = OpenSRF::Utils::safe_fork();
347 $self->pid_hash( $pid , _listener( $app ) );
351 $apname =~ tr/[a-z]/[A-Z]/;
352 $0 = "Listener ($apname)";
353 eval _listener( $app );
359 # ----------------------------------------------
366 my $pid = OpenSRF::Utils::safe_fork();
368 if( $pid ) { $self->pid_hash( $pid , _shell() ); }
371 for( my $x = 0; $x != 10; $x++ ) {
381 # ----------------------------------------------
384 my( $self, $pid, $method ) = @_;
385 $self->{'pid_hash'}->{$pid} = $method
386 if( $pid and $method );
387 return $self->{'pid_hash'};
390 # ----------------------------------------------
391 # If requested, the System can shut down.
395 $SIG{CHLD} = 'IGNORE';
396 $SIG{INT} = 'IGNORE';
397 kill( 'INT', -$$ ); #kill all in process group
402 # ----------------------------------------------
406 _log( "HUPping brood" );
407 $SIG{CHLD} = 'IGNORE';
408 $SIG{HUP} = 'IGNORE';
409 set_config(); # reload config
411 # $SIG{CHLD} = \&process_automation;
412 $SIG{HUP} = sub{ instance()->hupall(); };
416 # ----------------------------------------------
417 # Log to debug, and stdout
421 OpenSRF::Utils::Logger->debug( $string, INFO );
422 print $string . "\n";
425 # ----------------------------------------------
428 select( undef, undef, undef, 0.3 );