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