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