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;
18 my $bootstrap_config_file;
20 my( $self, $config ) = @_;
21 $bootstrap_config_file = $config;
24 =head2 Name/Description
28 To start the system: OpenSRF::System->bootstrap();
30 Simple system process management and automation. After instantiating the class, simply call
31 bootstrap() to launch the system. Each launched process is stored as a process-id/method-name
32 pair in a local hash. When we receive a SIG{CHILD}, we loop through this hash and relaunch
33 any child processes that may have terminated.
35 Currently automated processes include launching the internal Unix Servers, launching the inbound
36 connections for each application, and starting the system shell.
39 Note: There should be only one instance of this class
40 alive at any given time. It is designed as a globel process handler and, hence, will cause much
41 oddness if you call the bootstrap() method twice or attempt to create two of these by trickery.
42 There is a single instance of the class created on the first call to new(). This same instance is
43 returned on subsequent calls to new().
51 # ----------------------------------------------
53 $SIG{INT} = sub { instance()->killall(); };
55 $SIG{HUP} = sub{ instance()->hupall(); };
57 #$SIG{CHLD} = \&process_automation;
62 # put $instance in a closure and return it for requests to new()
63 # since there should only be one System instance running
66 sub instance { return __PACKAGE__->new(); }
71 $class = ref( $class ) || $class;
73 $self->{'pid_hash'} = {};
74 bless( $self, $class );
81 # ----------------------------------------------
82 # Commands to execute at system launch
86 return "OpenSRF::UnixServer->new( '$app')->serve()";
91 return "OpenSRF::Transport::Listener->new( '$app' )->initialize()->listen()";
95 # ----------------------------------------------
98 sub load_bootstrap_config {
100 if(OpenSRF::Utils::Config->current) {
104 warn "Loading $bootstrap_config_file\n";
105 if(!$bootstrap_config_file) {
106 die "Please provide a bootstrap config file to OpenSRF::System!\n" .
107 "use OpenSRF::System qw(/path/to/bootstrap_config);";
110 OpenSRF::Utils::Config->load( config_file => $bootstrap_config_file );
112 OpenSRF::Transport->message_envelope( "OpenSRF::Transport::SlimJabber::MessageWrapper" );
113 OpenSRF::Transport::PeerHandle->set_peer_client( "OpenSRF::Transport::SlimJabber::PeerConnection" );
114 OpenSRF::Transport::Listener->set_listener( "OpenSRF::Transport::SlimJabber::Inbound" );
119 my $self = __PACKAGE__->instance();
120 load_bootstrap_config();
121 OpenSRF::Utils::Logger::set_config();
122 my $bsconfig = OpenSRF::Utils::Config->current;
124 # Start a process group and make me the captain
128 # -----------------------------------------------
129 # Launch the settings sever if necessary...
130 my $are_settings_server = 0;
131 if( (my $cfile = $bsconfig->bootstrap->settings_config) ) {
132 my $parser = OpenSRF::Utils::SettingsParser->new();
134 # since we're (probably) the settings server, we can go ahead and load the real config file
135 $parser->initialize( $cfile );
136 $OpenSRF::Utils::SettingsClient::host_config =
137 $parser->get_server_config($bsconfig->env->hostname);
139 my $client = OpenSRF::Utils::SettingsClient->new();
140 my $apps = $client->config_value("activeapps", "appname");
141 if(!ref($apps) eq "ARRAY") { $apps = [$apps]; }
143 for my $app (@$apps) {
144 # verify we are a settings server and launch
145 if( $app eq "settings" ) {
146 $are_settings_server = 1;
147 $self->launch_settings();
149 $self->launch_settings_listener();
155 # Launch everything else
156 my $client = OpenSRF::Utils::SettingsClient->new();
157 my $apps = $client->config_value("activeapps", "appname" );
158 if(!ref($apps)) { $apps = [$apps]; }
159 my $server_type = $client->config_value("server_type");
160 $server_type ||= "basic";
163 if( $server_type eq "prefork" ) {
164 $server_type = "Net::Server::PreFork";
166 $server_type = "Net::Server::Single";
169 _log( " * Server type: $server_type", INTERNAL );
171 eval "use $server_type";
174 throw OpenSRF::EX::PANIC ("Cannot set $server_type: $@" );
177 push @OpenSRF::UnixServer::ISA, $server_type;
179 _log( " * System boostrap" );
181 # --- Boot the Unix servers
182 $self->launch_unix($apps);
186 # --- Boot the listeners
187 $self->launch_listener($apps);
191 _log( " * System is ready..." );
193 while( 1 ) { sleep; }
199 # ----------------------------------------------
200 # Bootstraps a single client connection.
202 sub bootstrap_client {
204 my $self = __PACKAGE__->instance();
205 load_bootstrap_config();
206 OpenSRF::Utils::Logger::set_config();
208 my $client_type = shift;
211 if( defined($client_type) and $client_type ) {
217 OpenSRF::Transport::PeerHandle->construct( $app );
221 sub bootstrap_logger {
224 OpenSRF::Utils::LogServer->serve();
229 # ----------------------------------------------
230 # Cycle through the known processes, reap the dead child
231 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
233 sub process_automation {
235 my $self = __PACKAGE__->instance();
237 foreach my $pid ( keys %{$self->pid_hash} ) {
239 if( waitpid( $pid, WNOHANG ) == $pid ) {
241 my $method = $self->pid_hash->{$pid};
242 delete $self->pid_hash->{$pid};
244 my $newpid = OpenSRF::Utils::safe_fork();
246 OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
247 _log( "Relaunching => $method" );
250 $self->pid_hash( $newpid, $method );
252 else { $0 = $method; eval $method; exit; }
256 $SIG{CHLD} = \&process_automation;
261 sub launch_settings {
263 # XXX the $self like this and pid automation will not work with this setup....
265 use Net::Server::Single;
266 @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::Single);
268 my $pid = OpenSRF::Utils::safe_fork();
270 $self->pid_hash( $pid , "launch_settings()" );
273 my $apname = "settings";
274 $apname =~ tr/[a-z]/[A-Z]/;
275 $0 = "OpenSRF App ($apname)";
276 eval _unixserver( "settings" );
277 if($@) { die "$@\n"; }
281 @OpenSRF::UnixServer::ISA = qw(OpenSRF);
286 sub launch_settings_listener {
289 my $app = "settings";
290 my $pid = OpenSRF::Utils::safe_fork();
292 $self->pid_hash( $pid , _listener( $app ) );
296 $apname =~ tr/[a-z]/[A-Z]/;
297 $0 = "Listener ($apname)";
298 eval _listener( $app );
304 # ----------------------------------------------
305 # Launch the Unix Servers
308 my( $self, $apps ) = @_;
310 foreach my $app ( @$apps ) {
312 if( $app eq "settings" ) { next; }
314 _log( " * Starting UnixServer for $app..." );
316 my $pid = OpenSRF::Utils::safe_fork();
318 $self->pid_hash( $pid , _unixserver( $app ) );
322 $apname =~ tr/[a-z]/[A-Z]/;
323 $0 = "OpenSRF App ($apname)";
324 eval _unixserver( $app );
330 # ----------------------------------------------
331 # Launch the inbound clients
333 sub launch_listener {
335 my( $self, $apps ) = @_;
337 foreach my $app ( @$apps ) {
339 if( $app eq "settings" ) { next; }
341 _log( " * Starting Listener for $app..." );
343 my $pid = OpenSRF::Utils::safe_fork();
345 $self->pid_hash( $pid , _listener( $app ) );
349 $apname =~ tr/[a-z]/[A-Z]/;
350 $0 = "Listener ($apname)";
351 eval _listener( $app );
357 # ----------------------------------------------
364 my $pid = OpenSRF::Utils::safe_fork();
366 if( $pid ) { $self->pid_hash( $pid , _shell() ); }
369 for( my $x = 0; $x != 10; $x++ ) {
379 # ----------------------------------------------
382 my( $self, $pid, $method ) = @_;
383 $self->{'pid_hash'}->{$pid} = $method
384 if( $pid and $method );
385 return $self->{'pid_hash'};
388 # ----------------------------------------------
389 # If requested, the System can shut down.
393 $SIG{CHLD} = 'IGNORE';
394 $SIG{INT} = 'IGNORE';
395 kill( 'INT', -$$ ); #kill all in process group
400 # ----------------------------------------------
404 _log( "HUPping brood" );
405 $SIG{CHLD} = 'IGNORE';
406 $SIG{HUP} = 'IGNORE';
407 set_config(); # reload config
409 # $SIG{CHLD} = \&process_automation;
410 $SIG{HUP} = sub{ instance()->hupall(); };
414 # ----------------------------------------------
415 # Log to debug, and stdout
419 OpenSRF::Utils::Logger->debug( $string, INFO );
420 print $string . "\n";
423 # ----------------------------------------------
426 select( undef, undef, undef, 0.3 );