added the <language> block to the opensrf app config so the servers
[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         my $client = OpenSRF::Utils::SettingsClient->new();
169         my $apps = $client->config_value("activeapps", "appname" );
170         if(!ref($apps)) { $apps = [$apps]; }
171
172         if(!defined($apps) || @$apps == 0) {
173                 print "No apps to load, exiting...";
174                 return;
175         }
176
177         my $server_type = $client->config_value("server_type");
178         $server_type ||= "basic";
179
180         my $con = OpenSRF::Transport::PeerHandle->retrieve;
181         if($con) {
182                 $con->disconnect;
183         }
184
185
186
187         if(  $server_type eq "prefork" ) { 
188                 $server_type = "Net::Server::PreFork"; 
189         } else { 
190                 $server_type = "Net::Server::Single"; 
191         }
192
193         _log( " * Server type: $server_type", INTERNAL );
194
195         eval "use $server_type";
196
197         if( $@ ) {
198                 throw OpenSRF::EX::PANIC ("Cannot set $server_type: $@" );
199         }
200
201         push @OpenSRF::UnixServer::ISA, $server_type;
202
203         _log( " * System boostrap" );
204         
205         # --- Boot the Unix servers
206         $self->launch_unix($apps);
207
208
209         _sleep();
210         sleep 2;
211
212         # --- Boot the listeners
213         $self->launch_listener($apps);
214
215         _sleep();
216
217         _log( " * System is ready..." );
218
219         sleep 1;
220         my $ps = `ps ax | grep " Open" | grep -v grep | sort -r -k5`;
221
222         print "\n --- PS --- \n$ps --- PS ---\n\n";
223
224         while( 1 ) { sleep; }
225         exit;
226 }
227         
228         
229
230 # ----------------------------------------------
231 # Bootstraps a single client connection.  
232
233 # named params are 'config_file' and 'client_name'
234 #
235 sub bootstrap_client {
236         my $self = shift;
237
238         my %params = @_;
239
240         $bootstrap_config_file = 
241                 $params{config_file} || $bootstrap_config_file;
242
243         my $app = $params{client_name} || "client";
244
245
246         load_bootstrap_config();
247         OpenSRF::Utils::Logger::set_config();
248         OpenSRF::Transport::PeerHandle->construct( $app );
249
250 }
251
252 sub bootstrap_logger {
253         $0 = "Log Server";
254         OpenSRF::Utils::LogServer->serve();
255 }
256
257
258 # ----------------------------------------------
259 # Cycle through the known processes, reap the dead child 
260 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
261
262 sub process_automation {
263
264         my $self = __PACKAGE__->instance();
265
266         foreach my $pid ( keys %{$self->pid_hash} ) {
267
268                 if( waitpid( $pid, WNOHANG ) == $pid ) {
269
270                         my $method = $self->pid_hash->{$pid};
271                         delete $self->pid_hash->{$pid};
272
273                         my $newpid =  OpenSRF::Utils::safe_fork();
274
275                         OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
276                         _log( "Relaunching => $method" );
277
278                         if( $newpid ) {
279                                 $self->pid_hash( $newpid, $method );
280                         }
281                         else { eval $method; exit; }
282                 }
283         }
284
285         $SIG{CHLD} = \&process_automation;
286 }
287
288
289
290 sub launch_settings {
291
292         #       XXX the $self like this and pid automation will not work with this setup....
293         my($self) = @_;
294         @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::PreFork);
295
296         my $pid = OpenSRF::Utils::safe_fork();
297         if( $pid ) {
298                 $self->pid_hash( $pid , "launch_settings()" );
299         }
300         else {
301                 my $apname = "opensrf.settings";
302                 #$0 = "OpenSRF App [$apname]";
303                 eval _unixserver( $apname );
304                 if($@) { die "$@\n"; }
305                 exit;
306         }
307
308         @OpenSRF::UnixServer::ISA = qw(OpenSRF);
309
310 }
311
312
313 sub launch_settings_listener {
314
315         my $self = shift;
316         my $app = "opensrf.settings";
317         my $pid = OpenSRF::Utils::safe_fork();
318         if ( $pid ) {
319                 $self->pid_hash( $pid , _listener( $app ) );
320         }
321         else {
322                 my $apname = $app;
323                 $0 = "OpenSRF listener [$apname]";
324                 eval _listener( $app );
325                 exit;
326         }
327
328 }
329
330 # ----------------------------------------------
331 # Launch the Unix Servers
332
333 sub launch_unix {
334         my( $self, $apps ) = @_;
335
336         my $client = OpenSRF::Utils::SettingsClient->new();
337
338         foreach my $app ( @$apps ) {
339
340                 next unless $app;
341                 my $lang = $client->config_value( "apps", $app, "language");
342
343                 next unless $lang =~ /perl/i;
344                 next unless $app ne "opensrf.settings";
345
346                 _log( " * Starting UnixServer for $app..." );
347
348                 my $pid = OpenSRF::Utils::safe_fork();
349                 if( $pid ) {
350                         $self->pid_hash( $pid , _unixserver( $app ) );
351                 }
352                 else {
353                         my $apname = $app;
354                         $0 = "OpenSRF App ($apname)";
355                         eval _unixserver( $app );
356                         exit;
357                 }
358         }
359 }
360
361 # ----------------------------------------------
362 # Launch the inbound clients
363
364 sub launch_listener {
365
366         my( $self, $apps ) = @_;
367
368         foreach my $app ( @$apps ) {
369
370                 next unless defined($app);
371
372                 if( $app eq "opensrf.settings" ) { next; }
373
374                 _log( " * Starting Listener for $app..." );
375
376                 my $pid = OpenSRF::Utils::safe_fork();
377                 if ( $pid ) {
378                         $self->pid_hash( $pid , _listener( $app ) );
379                 }
380                 else {
381                         my $apname = $app;
382                         $0 = "OpenSRF listener [$apname]";
383                         eval _listener( $app );
384                         exit;
385                 }
386         }
387 }
388
389 # ----------------------------------------------
390
391 =head comment
392 sub launch_shell {
393
394         my $self = shift;
395
396         my $pid = OpenSRF::Utils::safe_fork();
397
398         if( $pid ) { $self->pid_hash( $pid , _shell() ); }
399         else {
400                 $0 = "System Shell";
401                 for( my $x = 0; $x != 10; $x++ ) {
402                         eval _shell();
403                         if( ! $@ ) { last; }
404                 }
405                 exit;
406         }
407 }
408 =cut
409
410
411 # ----------------------------------------------
412
413 sub pid_hash {
414         my( $self, $pid, $method ) = @_;
415         $self->{'pid_hash'}->{$pid} = $method
416                 if( $pid and $method );
417         return $self->{'pid_hash'};
418 }
419
420 # ----------------------------------------------
421 # If requested, the System can shut down.
422
423 sub killall {
424
425         $SIG{CHLD} = 'IGNORE';
426         $SIG{INT} = 'IGNORE';
427         kill( 'INT', -$$ ); #kill all in process group
428         exit;
429
430 }
431
432 # ----------------------------------------------
433 # Handle $SIG{HUP}
434 sub hupall {
435
436         _log( "HUPping brood" );
437         $SIG{CHLD} = 'IGNORE';
438         $SIG{HUP} = 'IGNORE';
439         set_config(); # reload config
440         kill( 'HUP', -$$ );
441 #       $SIG{CHLD} = \&process_automation;
442         $SIG{HUP} = sub{ instance()->hupall(); };
443 }
444
445
446 # ----------------------------------------------
447 # Log to debug, and stdout
448
449 sub _log {
450         my $string = shift;
451         OpenSRF::Utils::Logger->debug( $string, INFO );
452         print $string . "\n";
453 }
454
455 # ----------------------------------------------
456
457 sub _sleep {
458         select( undef, undef, undef, 0.3 );
459 }
460
461 1;
462
463