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