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