c85d4a00e9403b08160f27a76dbeeace84c70f6a
[Evergreen.git] / OpenSRF / src / perlmods / OpenSRF / System.pm
1 package OpenSRF::System;
2 use strict; use warnings;
3 use base 'OpenSRF';
4 use OpenSRF::Utils::Logger qw(:level);
5 use OpenSRF::Transport::Listener;
6 use OpenSRF::Transport;
7 use OpenSRF::UnixServer;
8 use OpenSRF::Utils;
9 use OpenSRF::Utils::LogServer;
10 use OpenSRF::DOM;
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 OpenSRF::Application;
17 use Net::Server::PreFork;
18 use strict;
19
20 my $bootstrap_config_file;
21 sub import {
22         my( $self, $config ) = @_;
23         $bootstrap_config_file = $config;
24 }
25
26 =head2 Name/Description
27
28 OpenSRF::System
29
30 To start the system: OpenSRF::System->bootstrap();
31
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.  
36
37 Currently automated processes include launching the internal Unix Servers, launching the inbound 
38 connections for each application, and starting the system shell.
39
40
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().
46
47 =cut
48
49 $| = 1;
50
51 sub DESTROY {}
52
53 # ----------------------------------------------
54
55 $SIG{INT} = sub { instance()->killall(); };
56
57 $SIG{HUP} = sub{ instance()->hupall(); };
58
59 #$SIG{CHLD} = \&process_automation;
60
61
62
63         # --- 
64         # put $instance in a closure and return it for requests to new()
65         # since there should only be one System instance running
66         # ----- 
67         my $instance;
68         sub instance { return __PACKAGE__->new(); }
69         sub new {
70                 my( $class ) = @_;
71
72                 if( ! $instance ) {
73                         $class = ref( $class ) || $class;
74                         my $self = {};
75                         $self->{'pid_hash'} = {};
76                         bless( $self, $class );
77                         $instance = $self;
78                 }
79                 return $instance;
80         }
81 }
82
83 # ----------------------------------------------
84 # Commands to execute at system launch
85
86 sub _unixserver {
87         my( $app ) = @_;
88         return "OpenSRF::UnixServer->new( '$app')->serve()";
89 }
90
91 sub _listener {
92         my( $app ) = @_;
93         return "OpenSRF::Transport::Listener->new( '$app' )->initialize()->listen()";
94 }
95
96
97 # ----------------------------------------------
98 # Boot up the system
99
100 sub load_bootstrap_config {
101
102         if(OpenSRF::Utils::Config->current) {
103                 return;
104         }
105
106         warn "Loading $bootstrap_config_file\n";
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);";
110         }
111
112         OpenSRF::Utils::Config->load( config_file => $bootstrap_config_file );
113
114         JSON->register_class_hint( name => "OpenSRF::Application", hint => "method", type => "hash" );
115
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');
120 }
121
122 sub bootstrap {
123
124         my $self = __PACKAGE__->instance();
125         load_bootstrap_config();
126         OpenSRF::Utils::Logger::set_config();
127         my $bsconfig = OpenSRF::Utils::Config->current;
128
129         # Start a process group and make me the captain
130         setpgrp( 0, 0 ); 
131         $0 = "OpenSRF System";
132
133         # -----------------------------------------------
134         # Launch the settings sever if necessary...
135         my $are_settings_server = 0;
136         if( (my $cfile = $bsconfig->bootstrap->settings_config) ) {
137                 my $parser = OpenSRF::Utils::SettingsParser->new();
138
139                 # since we're (probably) the settings server, we can go ahead and load the real config file
140                 $parser->initialize( $cfile );
141                 $OpenSRF::Utils::SettingsClient::host_config = 
142                         $parser->get_server_config($bsconfig->env->hostname);
143
144                 my $client = OpenSRF::Utils::SettingsClient->new();
145                 my $apps = $client->config_value("activeapps", "appname");
146                 if(ref($apps) ne "ARRAY") { $apps = [$apps]; }
147
148                 if(!defined($apps) || @$apps == 0) {
149                         print "No apps to load, exiting...";
150                         return;
151                 }
152
153                 for my $app (@$apps) {
154                         # verify we are a settings server and launch 
155                         if( $app eq "opensrf.settings" and 
156                                 $client->config_value("apps","opensrf.settings", "language") =~ /perl/i ) {
157
158                                 $are_settings_server = 1;
159                                 $self->launch_settings();
160                                 sleep 1;
161                                 $self->launch_settings_listener();
162                                 last;
163                         } 
164                 }
165         }
166
167         # Launch everything else
168         OpenSRF::System->bootstrap_client(client_name => "system_client");
169         my $client = OpenSRF::Utils::SettingsClient->new();
170         my $apps = $client->config_value("activeapps", "appname" );
171         if(!ref($apps)) { $apps = [$apps]; }
172
173         if(!defined($apps) || @$apps == 0) {
174                 print "No apps to load, exiting...";
175                 return;
176         }
177
178         my $server_type = $client->config_value("server_type");
179         $server_type ||= "basic";
180
181         my $con = OpenSRF::Transport::PeerHandle->retrieve;
182         if($con) {
183                 $con->disconnect;
184         }
185
186
187
188         if(  $server_type eq "prefork" ) { 
189                 $server_type = "Net::Server::PreFork"; 
190         } else { 
191                 $server_type = "Net::Server::Single"; 
192         }
193
194         _log( " * Server type: $server_type", INTERNAL );
195
196         eval "use $server_type";
197
198         if( $@ ) {
199                 throw OpenSRF::EX::PANIC ("Cannot set $server_type: $@" );
200         }
201
202         push @OpenSRF::UnixServer::ISA, $server_type;
203
204         _log( " * System boostrap" );
205         
206         # --- Boot the Unix servers
207         $self->launch_unix($apps);
208
209
210         _sleep();
211         sleep 2;
212
213         # --- Boot the listeners
214         $self->launch_listener($apps);
215
216         _sleep();
217
218         _log( " * System is ready..." );
219
220         sleep 1;
221         my $ps = `ps ax | grep " Open" | grep -v grep | sort -r -k5`;
222
223         print "\n --- PS --- \n$ps --- PS ---\n\n";
224
225         while( 1 ) { sleep; }
226         exit;
227 }
228         
229         
230
231 # ----------------------------------------------
232 # Bootstraps a single client connection.  
233
234 # named params are 'config_file' and 'client_name'
235 #
236 sub bootstrap_client {
237         my $self = shift;
238
239         my %params = @_;
240
241         $bootstrap_config_file = 
242                 $params{config_file} || $bootstrap_config_file;
243
244         my $app = $params{client_name} || "client";
245
246
247         load_bootstrap_config();
248         OpenSRF::Utils::Logger::set_config();
249         OpenSRF::Transport::PeerHandle->construct( $app );
250
251 }
252
253 sub bootstrap_logger {
254         $0 = "Log Server";
255         OpenSRF::Utils::LogServer->serve();
256 }
257
258
259 # ----------------------------------------------
260 # Cycle through the known processes, reap the dead child 
261 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
262
263 sub process_automation {
264
265         my $self = __PACKAGE__->instance();
266
267         foreach my $pid ( keys %{$self->pid_hash} ) {
268
269                 if( waitpid( $pid, WNOHANG ) == $pid ) {
270
271                         my $method = $self->pid_hash->{$pid};
272                         delete $self->pid_hash->{$pid};
273
274                         my $newpid =  OpenSRF::Utils::safe_fork();
275
276                         OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
277                         _log( "Relaunching => $method" );
278
279                         if( $newpid ) {
280                                 $self->pid_hash( $newpid, $method );
281                         }
282                         else { eval $method; exit; }
283                 }
284         }
285
286         $SIG{CHLD} = \&process_automation;
287 }
288
289
290
291 sub launch_settings {
292
293         #       XXX the $self like this and pid automation will not work with this setup....
294         my($self) = @_;
295         @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::PreFork);
296
297         my $pid = OpenSRF::Utils::safe_fork();
298         if( $pid ) {
299                 $self->pid_hash( $pid , "launch_settings()" );
300         }
301         else {
302                 my $apname = "opensrf.settings";
303                 #$0 = "OpenSRF App [$apname]";
304                 eval _unixserver( $apname );
305                 if($@) { die "$@\n"; }
306                 exit;
307         }
308
309         @OpenSRF::UnixServer::ISA = qw(OpenSRF);
310
311 }
312
313
314 sub launch_settings_listener {
315
316         my $self = shift;
317         my $app = "opensrf.settings";
318         my $pid = OpenSRF::Utils::safe_fork();
319         if ( $pid ) {
320                 $self->pid_hash( $pid , _listener( $app ) );
321         }
322         else {
323                 my $apname = $app;
324                 $0 = "OpenSRF listener [$apname]";
325                 eval _listener( $app );
326                 exit;
327         }
328
329 }
330
331 # ----------------------------------------------
332 # Launch the Unix Servers
333
334 sub launch_unix {
335         my( $self, $apps ) = @_;
336
337         my $client = OpenSRF::Utils::SettingsClient->new();
338
339         foreach my $app ( @$apps ) {
340
341                 next unless $app;
342                 my $lang = $client->config_value( "apps", $app, "language");
343
344                 next unless $lang =~ /perl/i;
345                 next unless $app ne "opensrf.settings";
346
347                 _log( " * Starting UnixServer for $app..." );
348
349                 my $pid = OpenSRF::Utils::safe_fork();
350                 if( $pid ) {
351                         $self->pid_hash( $pid , _unixserver( $app ) );
352                 }
353                 else {
354                         my $apname = $app;
355                         $0 = "OpenSRF App ($apname)";
356                         eval _unixserver( $app );
357                         exit;
358                 }
359         }
360 }
361
362 # ----------------------------------------------
363 # Launch the inbound clients
364
365 sub launch_listener {
366
367         my( $self, $apps ) = @_;
368
369         foreach my $app ( @$apps ) {
370
371                 next unless defined($app);
372
373                 if( $app eq "opensrf.settings" ) { next; }
374
375                 _log( " * Starting Listener for $app..." );
376
377                 my $pid = OpenSRF::Utils::safe_fork();
378                 if ( $pid ) {
379                         $self->pid_hash( $pid , _listener( $app ) );
380                 }
381                 else {
382                         my $apname = $app;
383                         $0 = "OpenSRF listener [$apname]";
384                         eval _listener( $app );
385                         exit;
386                 }
387         }
388 }
389
390 # ----------------------------------------------
391
392 =head comment
393 sub launch_shell {
394
395         my $self = shift;
396
397         my $pid = OpenSRF::Utils::safe_fork();
398
399         if( $pid ) { $self->pid_hash( $pid , _shell() ); }
400         else {
401                 $0 = "System Shell";
402                 for( my $x = 0; $x != 10; $x++ ) {
403                         eval _shell();
404                         if( ! $@ ) { last; }
405                 }
406                 exit;
407         }
408 }
409 =cut
410
411
412 # ----------------------------------------------
413
414 sub pid_hash {
415         my( $self, $pid, $method ) = @_;
416         $self->{'pid_hash'}->{$pid} = $method
417                 if( $pid and $method );
418         return $self->{'pid_hash'};
419 }
420
421 # ----------------------------------------------
422 # If requested, the System can shut down.
423
424 sub killall {
425
426         $SIG{CHLD} = 'IGNORE';
427         $SIG{INT} = 'IGNORE';
428         kill( 'INT', -$$ ); #kill all in process group
429         exit;
430
431 }
432
433 # ----------------------------------------------
434 # Handle $SIG{HUP}
435 sub hupall {
436
437         _log( "HUPping brood" );
438         $SIG{CHLD} = 'IGNORE';
439         $SIG{HUP} = 'IGNORE';
440         set_config(); # reload config
441         kill( 'HUP', -$$ );
442 #       $SIG{CHLD} = \&process_automation;
443         $SIG{HUP} = sub{ instance()->hupall(); };
444 }
445
446
447 # ----------------------------------------------
448 # Log to debug, and stdout
449
450 sub _log {
451         my $string = shift;
452         OpenSRF::Utils::Logger->debug( $string, INFO );
453         print $string . "\n";
454 }
455
456 # ----------------------------------------------
457
458 sub _sleep {
459         select( undef, undef, undef, 0.3 );
460 }
461
462 1;
463
464