bootstrap_client now registers a dummy client server_class
[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
94 =head
95         my $service = $app;
96         my $username = $app;
97         my $password = "jkjkasdf";
98         my $jserver = "elroy";
99         my $port = "5222";
100         my $hostname = "elroy.pls-hq.org";
101         my $unix_file = "/pines/var/sock/$app" . "_unix.sock";
102         my $router = 'router\@elroy/router'; 
103         return "exec(\"/pines/cvs/ILS/OpenSRF/src/forker/oils_inbound $service $username " .
104                 "$password $jserver $port $hostname $unix_file $router\")";
105 =cut
106
107         return "OpenSRF::Transport::Listener->new( '$app' )->initialize()->listen()";
108 }
109
110
111 # ----------------------------------------------
112 # Boot up the system
113
114 sub load_bootstrap_config {
115
116         if(OpenSRF::Utils::Config->current) {
117                 return;
118         }
119
120         warn "Loading $bootstrap_config_file\n";
121         if(!$bootstrap_config_file) {
122                 die "Please provide a bootstrap config file to OpenSRF::System!\n" . 
123                         "use OpenSRF::System qw(/path/to/bootstrap_config);";
124         }
125
126         OpenSRF::Utils::Config->load( config_file => $bootstrap_config_file );
127
128         JSON->register_class_hint( name => "OpenSRF::Application", hint => "method", type => "hash" );
129
130         OpenSRF::Transport->message_envelope(  "OpenSRF::Transport::SlimJabber::MessageWrapper" );
131         OpenSRF::Transport::PeerHandle->set_peer_client(  "OpenSRF::Transport::SlimJabber::PeerConnection" );
132         OpenSRF::Transport::Listener->set_listener( "OpenSRF::Transport::SlimJabber::Inbound" );
133         OpenSRF::Application->server_class('client');
134 }
135
136 sub bootstrap {
137
138         my $self = __PACKAGE__->instance();
139         load_bootstrap_config();
140         OpenSRF::Utils::Logger::set_config();
141         my $bsconfig = OpenSRF::Utils::Config->current;
142
143         # Start a process group and make me the captain
144         setpgrp( 0, 0 ); 
145         $0 = "System";
146
147         # -----------------------------------------------
148         # Launch the settings sever if necessary...
149         my $are_settings_server = 0;
150         if( (my $cfile = $bsconfig->bootstrap->settings_config) ) {
151                 my $parser = OpenSRF::Utils::SettingsParser->new();
152
153                 # since we're (probably) the settings server, we can go ahead and load the real config file
154                 $parser->initialize( $cfile );
155                 $OpenSRF::Utils::SettingsClient::host_config = 
156                         $parser->get_server_config($bsconfig->env->hostname);
157
158                 my $client = OpenSRF::Utils::SettingsClient->new();
159                 my $apps = $client->config_value("activeapps", "appname");
160                 if(!ref($apps) eq "ARRAY") { $apps = [$apps]; }
161
162                 for my $app (@$apps) {
163                         # verify we are a settings server and launch 
164                         if( $app eq "settings" ) {
165                                 $are_settings_server = 1;
166                                 $self->launch_settings();
167                                 sleep 1;
168                                 $self->launch_settings_listener();
169                                 last;
170                         } 
171                 }
172         }
173
174         # Launch everything else
175         my $client = OpenSRF::Utils::SettingsClient->new();
176         my $apps = $client->config_value("activeapps", "appname" );
177         if(!ref($apps)) { $apps = [$apps]; }
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 = "settings";
303                 #$0 = "OpenSRF App [$apname]";
304                 eval _unixserver( "settings" );
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 = "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         foreach my $app ( @$apps ) {
338                 next unless $app;
339
340                 if( $app eq "settings" ) { next; }
341
342                 _log( " * Starting UnixServer for $app..." );
343
344                 my $pid = OpenSRF::Utils::safe_fork();
345                 if( $pid ) {
346                         $self->pid_hash( $pid , _unixserver( $app ) );
347                 }
348                 else {
349                         my $apname = $app;
350                         $0 = "OpenSRF App ($apname)";
351                         eval _unixserver( $app );
352                         exit;
353                 }
354         }
355 }
356
357 # ----------------------------------------------
358 # Launch the inbound clients
359
360 sub launch_listener {
361
362         my( $self, $apps ) = @_;
363
364         foreach my $app ( @$apps ) {
365
366                 if( $app eq "settings" ) { next; }
367
368                 _log( " * Starting Listener for $app..." );
369
370                 my $pid = OpenSRF::Utils::safe_fork();
371                 if ( $pid ) {
372                         $self->pid_hash( $pid , _listener( $app ) );
373                 }
374                 else {
375                         my $apname = $app;
376                         $0 = "OpenSRF listener [$apname]";
377                         eval _listener( $app );
378                         exit;
379                 }
380         }
381 }
382
383 # ----------------------------------------------
384
385 =head comment
386 sub launch_shell {
387
388         my $self = shift;
389
390         my $pid = OpenSRF::Utils::safe_fork();
391
392         if( $pid ) { $self->pid_hash( $pid , _shell() ); }
393         else {
394                 $0 = "System Shell";
395                 for( my $x = 0; $x != 10; $x++ ) {
396                         eval _shell();
397                         if( ! $@ ) { last; }
398                 }
399                 exit;
400         }
401 }
402 =cut
403
404
405 # ----------------------------------------------
406
407 sub pid_hash {
408         my( $self, $pid, $method ) = @_;
409         $self->{'pid_hash'}->{$pid} = $method
410                 if( $pid and $method );
411         return $self->{'pid_hash'};
412 }
413
414 # ----------------------------------------------
415 # If requested, the System can shut down.
416
417 sub killall {
418
419         $SIG{CHLD} = 'IGNORE';
420         $SIG{INT} = 'IGNORE';
421         kill( 'INT', -$$ ); #kill all in process group
422         exit;
423
424 }
425
426 # ----------------------------------------------
427 # Handle $SIG{HUP}
428 sub hupall {
429
430         _log( "HUPping brood" );
431         $SIG{CHLD} = 'IGNORE';
432         $SIG{HUP} = 'IGNORE';
433         set_config(); # reload config
434         kill( 'HUP', -$$ );
435 #       $SIG{CHLD} = \&process_automation;
436         $SIG{HUP} = sub{ instance()->hupall(); };
437 }
438
439
440 # ----------------------------------------------
441 # Log to debug, and stdout
442
443 sub _log {
444         my $string = shift;
445         OpenSRF::Utils::Logger->debug( $string, INFO );
446         print $string . "\n";
447 }
448
449 # ----------------------------------------------
450
451 sub _sleep {
452         select( undef, undef, undef, 0.3 );
453 }
454
455 1;
456
457