]> git.evergreen-ils.org Git - Evergreen.git/blob - OpenSRF/src/perlmods/OpenSRF/System.pm
checking <language> for perl in listener and unix children
[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                 next unless $lang =~ /perl/i;
344                 next if $app eq "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         my $client = OpenSRF::Utils::SettingsClient->new();
368
369         foreach my $app ( @$apps ) {
370
371                 next unless $app;
372                 my $lang = $client->config_value( "apps", $app, "language");
373                 next unless $lang =~ /perl/i;
374                 next if $app eq "opensrf.settings";
375
376                 _log( " * Starting Listener for $app..." );
377
378                 my $pid = OpenSRF::Utils::safe_fork();
379                 if ( $pid ) {
380                         $self->pid_hash( $pid , _listener( $app ) );
381                 }
382                 else {
383                         my $apname = $app;
384                         $0 = "OpenSRF listener [$apname]";
385                         eval _listener( $app );
386                         exit;
387                 }
388         }
389 }
390
391 # ----------------------------------------------
392
393 =head comment
394 sub launch_shell {
395
396         my $self = shift;
397
398         my $pid = OpenSRF::Utils::safe_fork();
399
400         if( $pid ) { $self->pid_hash( $pid , _shell() ); }
401         else {
402                 $0 = "System Shell";
403                 for( my $x = 0; $x != 10; $x++ ) {
404                         eval _shell();
405                         if( ! $@ ) { last; }
406                 }
407                 exit;
408         }
409 }
410 =cut
411
412
413 # ----------------------------------------------
414
415 sub pid_hash {
416         my( $self, $pid, $method ) = @_;
417         $self->{'pid_hash'}->{$pid} = $method
418                 if( $pid and $method );
419         return $self->{'pid_hash'};
420 }
421
422 # ----------------------------------------------
423 # If requested, the System can shut down.
424
425 sub killall {
426
427         $SIG{CHLD} = 'IGNORE';
428         $SIG{INT} = 'IGNORE';
429         kill( 'INT', -$$ ); #kill all in process group
430         exit;
431
432 }
433
434 # ----------------------------------------------
435 # Handle $SIG{HUP}
436 sub hupall {
437
438         _log( "HUPping brood" );
439         $SIG{CHLD} = 'IGNORE';
440         $SIG{HUP} = 'IGNORE';
441         set_config(); # reload config
442         kill( 'HUP', -$$ );
443 #       $SIG{CHLD} = \&process_automation;
444         $SIG{HUP} = sub{ instance()->hupall(); };
445 }
446
447
448 # ----------------------------------------------
449 # Log to debug, and stdout
450
451 sub _log {
452         my $string = shift;
453         OpenSRF::Utils::Logger->debug( $string, INFO );
454         print $string . "\n";
455 }
456
457 # ----------------------------------------------
458
459 sub _sleep {
460         select( undef, undef, undef, 0.3 );
461 }
462
463 1;
464
465