]> git.evergreen-ils.org Git - working/Evergreen.git/blob - OpenSRF/src/perlmods/OpenSRF/System.pm
more opensearch fixup; bugfix for "no need to re-bootstrap"
[working/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         if(!$bootstrap_config_file) {
107                 die "Please provide a bootstrap config file to OpenSRF::System!\n" . 
108                         "use OpenSRF::System qw(/path/to/bootstrap_config);";
109         }
110
111         OpenSRF::Utils::Config->load( config_file => $bootstrap_config_file );
112
113         JSON->register_class_hint( name => "OpenSRF::Application", hint => "method", type => "hash" );
114
115         OpenSRF::Transport->message_envelope(  "OpenSRF::Transport::SlimJabber::MessageWrapper" );
116         OpenSRF::Transport::PeerHandle->set_peer_client(  "OpenSRF::Transport::SlimJabber::PeerConnection" );
117         OpenSRF::Transport::Listener->set_listener( "OpenSRF::Transport::SlimJabber::Inbound" );
118         OpenSRF::Application->server_class('client');
119 }
120
121 sub bootstrap {
122
123         my $self = __PACKAGE__->instance();
124         load_bootstrap_config();
125         OpenSRF::Utils::Logger::set_config();
126         my $bsconfig = OpenSRF::Utils::Config->current;
127
128         # Start a process group and make me the captain
129         setpgrp( 0, 0 ); 
130         $0 = "OpenSRF System";
131
132         # -----------------------------------------------
133         # Launch the settings sever if necessary...
134         my $are_settings_server = 0;
135         if( (my $cfile = $bsconfig->bootstrap->settings_config) ) {
136                 my $parser = OpenSRF::Utils::SettingsParser->new();
137
138                 # since we're (probably) the settings server, we can go ahead and load the real config file
139                 $parser->initialize( $cfile );
140                 $OpenSRF::Utils::SettingsClient::host_config = 
141                         $parser->get_server_config($bsconfig->env->hostname);
142
143                 my $client = OpenSRF::Utils::SettingsClient->new();
144                 my $apps = $client->config_value("activeapps", "appname");
145                 if(ref($apps) ne "ARRAY") { $apps = [$apps]; }
146
147                 if(!defined($apps) || @$apps == 0) {
148                         print "No apps to load, exiting...";
149                         return;
150                 }
151
152                 for my $app (@$apps) {
153                         # verify we are a settings server and launch 
154                         if( $app eq "opensrf.settings" and 
155                                 $client->config_value("apps","opensrf.settings", "language") =~ /perl/i ) {
156
157                                 $are_settings_server = 1;
158                                 $self->launch_settings();
159                                 sleep 1;
160                                 $self->launch_settings_listener();
161                                 last;
162                         } 
163                 }
164         }
165
166         # Launch everything else
167         OpenSRF::System->bootstrap_client(client_name => "system_client");
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 $con = OpenSRF::Transport::PeerHandle->retrieve;
239         if($con and $con->tcp_connected) {
240                 warn "PeerHandle is already connected in 'bootstrap_client'... returning\n";
241                 _log( "PeerHandle is already connected in 'bootstrap_client'... returning");
242                 return;
243         }
244
245         my %params = @_;
246
247         $bootstrap_config_file = 
248                 $params{config_file} || $bootstrap_config_file;
249
250         my $app = $params{client_name} || "client";
251
252
253         load_bootstrap_config();
254         OpenSRF::Utils::Logger::set_config();
255         OpenSRF::Transport::PeerHandle->construct( $app );
256
257 }
258
259 sub bootstrap_logger {
260         $0 = "Log Server";
261         OpenSRF::Utils::LogServer->serve();
262 }
263
264
265 # ----------------------------------------------
266 # Cycle through the known processes, reap the dead child 
267 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
268
269 sub process_automation {
270
271         my $self = __PACKAGE__->instance();
272
273         foreach my $pid ( keys %{$self->pid_hash} ) {
274
275                 if( waitpid( $pid, WNOHANG ) == $pid ) {
276
277                         my $method = $self->pid_hash->{$pid};
278                         delete $self->pid_hash->{$pid};
279
280                         my $newpid =  OpenSRF::Utils::safe_fork();
281
282                         OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
283                         _log( "Relaunching => $method" );
284
285                         if( $newpid ) {
286                                 $self->pid_hash( $newpid, $method );
287                         }
288                         else { eval $method; exit; }
289                 }
290         }
291
292         $SIG{CHLD} = \&process_automation;
293 }
294
295
296
297 sub launch_settings {
298
299         #       XXX the $self like this and pid automation will not work with this setup....
300         my($self) = @_;
301         @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::PreFork);
302
303         my $pid = OpenSRF::Utils::safe_fork();
304         if( $pid ) {
305                 $self->pid_hash( $pid , "launch_settings()" );
306         }
307         else {
308                 my $apname = "opensrf.settings";
309                 #$0 = "OpenSRF App [$apname]";
310                 eval _unixserver( $apname );
311                 if($@) { die "$@\n"; }
312                 exit;
313         }
314
315         @OpenSRF::UnixServer::ISA = qw(OpenSRF);
316
317 }
318
319
320 sub launch_settings_listener {
321
322         my $self = shift;
323         my $app = "opensrf.settings";
324         my $pid = OpenSRF::Utils::safe_fork();
325         if ( $pid ) {
326                 $self->pid_hash( $pid , _listener( $app ) );
327         }
328         else {
329                 my $apname = $app;
330                 $0 = "OpenSRF listener [$apname]";
331                 eval _listener( $app );
332                 exit;
333         }
334
335 }
336
337 # ----------------------------------------------
338 # Launch the Unix Servers
339
340 sub launch_unix {
341         my( $self, $apps ) = @_;
342
343         my $client = OpenSRF::Utils::SettingsClient->new();
344
345         foreach my $app ( @$apps ) {
346
347                 next unless $app;
348                 my $lang = $client->config_value( "apps", $app, "language");
349                 next unless $lang =~ /perl/i;
350                 next if $app eq "opensrf.settings";
351
352                 _log( " * Starting UnixServer for $app..." );
353
354                 my $pid = OpenSRF::Utils::safe_fork();
355                 if( $pid ) {
356                         $self->pid_hash( $pid , _unixserver( $app ) );
357                 }
358                 else {
359                         my $apname = $app;
360                         $0 = "OpenSRF App ($apname)";
361                         eval _unixserver( $app );
362                         exit;
363                 }
364         }
365 }
366
367 # ----------------------------------------------
368 # Launch the inbound clients
369
370 sub launch_listener {
371
372         my( $self, $apps ) = @_;
373         my $client = OpenSRF::Utils::SettingsClient->new();
374
375         foreach my $app ( @$apps ) {
376
377                 next unless $app;
378                 my $lang = $client->config_value( "apps", $app, "language");
379                 next unless $lang =~ /perl/i;
380                 next if $app eq "opensrf.settings";
381
382                 _log( " * Starting Listener for $app..." );
383
384                 my $pid = OpenSRF::Utils::safe_fork();
385                 if ( $pid ) {
386                         $self->pid_hash( $pid , _listener( $app ) );
387                 }
388                 else {
389                         my $apname = $app;
390                         $0 = "OpenSRF listener [$apname]";
391                         eval _listener( $app );
392                         exit;
393                 }
394         }
395 }
396
397 # ----------------------------------------------
398
399 =head comment
400 sub launch_shell {
401
402         my $self = shift;
403
404         my $pid = OpenSRF::Utils::safe_fork();
405
406         if( $pid ) { $self->pid_hash( $pid , _shell() ); }
407         else {
408                 $0 = "System Shell";
409                 for( my $x = 0; $x != 10; $x++ ) {
410                         eval _shell();
411                         if( ! $@ ) { last; }
412                 }
413                 exit;
414         }
415 }
416 =cut
417
418
419 # ----------------------------------------------
420
421 sub pid_hash {
422         my( $self, $pid, $method ) = @_;
423         $self->{'pid_hash'}->{$pid} = $method
424                 if( $pid and $method );
425         return $self->{'pid_hash'};
426 }
427
428 # ----------------------------------------------
429 # If requested, the System can shut down.
430
431 sub killall {
432
433         $SIG{CHLD} = 'IGNORE';
434         $SIG{INT} = 'IGNORE';
435         kill( 'INT', -$$ ); #kill all in process group
436         exit;
437
438 }
439
440 # ----------------------------------------------
441 # Handle $SIG{HUP}
442 sub hupall {
443
444         _log( "HUPping brood" );
445         $SIG{CHLD} = 'IGNORE';
446         $SIG{HUP} = 'IGNORE';
447         set_config(); # reload config
448         kill( 'HUP', -$$ );
449 #       $SIG{CHLD} = \&process_automation;
450         $SIG{HUP} = sub{ instance()->hupall(); };
451 }
452
453
454 # ----------------------------------------------
455 # Log to debug, and stdout
456
457 sub _log {
458         my $string = shift;
459         OpenSRF::Utils::Logger->debug( $string, INFO );
460         #print $string . "\n";
461 }
462
463 # ----------------------------------------------
464
465 sub _sleep {
466         select( undef, undef, undef, 0.3 );
467 }
468
469 1;
470
471