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";
165 OpenSRF::Transport::PeerHandle->retrieve->disconnect;
167 if( $server_type eq "prefork" ) {
168 $server_type = "Net::Server::PreFork";
170 $server_type = "Net::Server::Single";
173 _log( " * Server type: $server_type", INTERNAL );
175 eval "use $server_type";
178 throw OpenSRF::EX::PANIC ("Cannot set $server_type: $@" );
181 push @OpenSRF::UnixServer::ISA, $server_type;
183 _log( " * System boostrap" );
185 # --- Boot the Unix servers
186 $self->launch_unix($apps);
190 # --- Boot the listeners
191 $self->launch_listener($apps);
195 _log( " * System is ready..." );
197 while( 1 ) { sleep; }
203 # ----------------------------------------------
204 # Bootstraps a single client connection.
206 sub bootstrap_client {
208 my $self = __PACKAGE__->instance();
209 load_bootstrap_config();
210 OpenSRF::Utils::Logger::set_config();
212 my $client_type = shift;
215 if( defined($client_type) and $client_type ) {
221 OpenSRF::Transport::PeerHandle->construct( $app );
225 sub bootstrap_logger {
228 OpenSRF::Utils::LogServer->serve();
233 # ----------------------------------------------
234 # Cycle through the known processes, reap the dead child
235 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
237 sub process_automation {
239 my $self = __PACKAGE__->instance();
241 foreach my $pid ( keys %{$self->pid_hash} ) {
243 if( waitpid( $pid, WNOHANG ) == $pid ) {
245 my $method = $self->pid_hash->{$pid};
246 delete $self->pid_hash->{$pid};
248 my $newpid = OpenSRF::Utils::safe_fork();
250 OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
251 _log( "Relaunching => $method" );
254 $self->pid_hash( $newpid, $method );
256 else { $0 = $method; eval $method; exit; }
260 $SIG{CHLD} = \&process_automation;
265 sub launch_settings {
267 # XXX the $self like this and pid automation will not work with this setup....
269 @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::PreFork);
271 my $pid = OpenSRF::Utils::safe_fork();
273 $self->pid_hash( $pid , "launch_settings()" );
276 my $apname = "settings";
277 $apname =~ tr/[a-z]/[A-Z]/;
278 $0 = "OpenSRF App ($apname)";
279 eval _unixserver( "settings" );
280 if($@) { die "$@\n"; }
284 @OpenSRF::UnixServer::ISA = qw(OpenSRF);
289 sub launch_settings_listener {
292 my $app = "settings";
293 my $pid = OpenSRF::Utils::safe_fork();
295 $self->pid_hash( $pid , _listener( $app ) );
299 $apname =~ tr/[a-z]/[A-Z]/;
300 $0 = "Listener ($apname)";
301 eval _listener( $app );
307 # ----------------------------------------------
308 # Launch the Unix Servers
311 my( $self, $apps ) = @_;
313 foreach my $app ( @$apps ) {
315 if( $app eq "settings" ) { next; }
317 _log( " * Starting UnixServer for $app..." );
319 my $pid = OpenSRF::Utils::safe_fork();
321 $self->pid_hash( $pid , _unixserver( $app ) );
325 $apname =~ tr/[a-z]/[A-Z]/;
326 $0 = "OpenSRF App ($apname)";
327 eval _unixserver( $app );
333 # ----------------------------------------------
334 # Launch the inbound clients
336 sub launch_listener {
338 my( $self, $apps ) = @_;
340 foreach my $app ( @$apps ) {
342 if( $app eq "settings" ) { next; }
344 _log( " * Starting Listener for $app..." );
346 my $pid = OpenSRF::Utils::safe_fork();
348 $self->pid_hash( $pid , _listener( $app ) );
352 $apname =~ tr/[a-z]/[A-Z]/;
353 $0 = "Listener ($apname)";
354 eval _listener( $app );
360 # ----------------------------------------------
367 my $pid = OpenSRF::Utils::safe_fork();
369 if( $pid ) { $self->pid_hash( $pid , _shell() ); }
372 for( my $x = 0; $x != 10; $x++ ) {
382 # ----------------------------------------------
385 my( $self, $pid, $method ) = @_;
386 $self->{'pid_hash'}->{$pid} = $method
387 if( $pid and $method );
388 return $self->{'pid_hash'};
391 # ----------------------------------------------
392 # If requested, the System can shut down.
396 $SIG{CHLD} = 'IGNORE';
397 $SIG{INT} = 'IGNORE';
398 kill( 'INT', -$$ ); #kill all in process group
403 # ----------------------------------------------
407 _log( "HUPping brood" );
408 $SIG{CHLD} = 'IGNORE';
409 $SIG{HUP} = 'IGNORE';
410 set_config(); # reload config
412 # $SIG{CHLD} = \&process_automation;
413 $SIG{HUP} = sub{ instance()->hupall(); };
417 # ----------------------------------------------
418 # Log to debug, and stdout
422 OpenSRF::Utils::Logger->debug( $string, INFO );
423 print $string . "\n";
426 # ----------------------------------------------
429 select( undef, undef, undef, 0.3 );