]> git.evergreen-ils.org Git - Evergreen.git/blob - OpenSRF/src/perlmods/OpenSRF/System.pm
fixing old fieldmapper_lookup generator
[Evergreen.git] / OpenSRF / src / perlmods / OpenSRF / System.pm
1 package OpenSRF::System;
2 use strict; use warnings;
3 use OpenSRF;
4 use base 'OpenSRF';
5 use OpenSRF::Utils::Logger qw(:level);
6 use OpenSRF::Transport::Listener;
7 use OpenSRF::Transport;
8 use OpenSRF::UnixServer;
9 use OpenSRF::Utils;
10 use OpenSRF::Utils::LogServer;
11 use OpenSRF::DOM;
12 use OpenSRF::EX qw/:try/;
13 use POSIX ":sys_wait_h";
14 use OpenSRF::Utils::Config; 
15 use OpenSRF::Utils::SettingsParser;
16 use OpenSRF::Utils::SettingsClient;
17 use OpenSRF::Application;
18 use Net::Server::PreFork;
19 use strict;
20
21 my $bootstrap_config_file;
22 sub import {
23         my( $self, $config ) = @_;
24         $bootstrap_config_file = $config;
25 }
26
27 =head2 Name/Description
28
29 OpenSRF::System
30
31 To start the system: OpenSRF::System->bootstrap();
32
33 Simple system process management and automation.  After instantiating the class, simply call
34 bootstrap() to launch the system.  Each launched process is stored as a process-id/method-name
35 pair in a local hash.  When we receive a SIG{CHILD}, we loop through this hash and relaunch
36 any child processes that may have terminated.  
37
38 Currently automated processes include launching the internal Unix Servers, launching the inbound 
39 connections for each application, and starting the system shell.
40
41
42 Note: There should be only one instance of this class
43 alive at any given time.  It is designed as a globel process handler and, hence, will cause much
44 oddness if you call the bootstrap() method twice or attempt to create two of these by trickery.
45 There is a single instance of the class created on the first call to new().  This same instance is 
46 returned on subsequent calls to new().
47
48 =cut
49
50 $| = 1;
51
52 sub DESTROY {}
53
54 # ----------------------------------------------
55
56 $SIG{INT} = sub { instance()->killall(); };
57
58 $SIG{HUP} = sub{ instance()->hupall(); };
59
60 #$SIG{CHLD} = \&process_automation;
61
62
63
64         # --- 
65         # put $instance in a closure and return it for requests to new()
66         # since there should only be one System instance running
67         # ----- 
68         my $instance;
69         sub instance { return __PACKAGE__->new(); }
70         sub new {
71                 my( $class ) = @_;
72
73                 if( ! $instance ) {
74                         $class = ref( $class ) || $class;
75                         my $self = {};
76                         $self->{'pid_hash'} = {};
77                         bless( $self, $class );
78                         $instance = $self;
79                 }
80                 return $instance;
81         }
82 }
83
84 # ----------------------------------------------
85 # Commands to execute at system launch
86
87 sub _unixserver {
88         my( $app ) = @_;
89         return "OpenSRF::UnixServer->new( '$app')->serve()";
90 }
91
92 sub _listener {
93         my( $app ) = @_;
94         return "OpenSRF::Transport::Listener->new( '$app' )->initialize()->listen()";
95 }
96
97
98 # ----------------------------------------------
99 # Boot up the system
100
101 sub load_bootstrap_config {
102
103         if(OpenSRF::Utils::Config->current) {
104                 return;
105         }
106
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         $server_type->use;
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 $con = OpenSRF::Transport::PeerHandle->retrieve;
240         if($con and $con->tcp_connected) {
241                 warn "PeerHandle is already connected in 'bootstrap_client'... returning\n";
242                 _log( "PeerHandle is already connected in 'bootstrap_client'... returning");
243                 return;
244         }
245
246         my %params = @_;
247
248         $bootstrap_config_file = 
249                 $params{config_file} || $bootstrap_config_file;
250
251         my $app = $params{client_name} || "client";
252
253
254         load_bootstrap_config();
255         OpenSRF::Utils::Logger::set_config();
256         OpenSRF::Transport::PeerHandle->construct( $app );
257
258 }
259
260 sub connected {
261         if (my $con = OpenSRF::Transport::PeerHandle->retrieve) {
262                 return 1 if ($con->tcp_connected);
263         }
264         return 0;
265 }
266
267 sub bootstrap_logger {
268         $0 = "Log Server";
269         OpenSRF::Utils::LogServer->serve();
270 }
271
272
273 # ----------------------------------------------
274 # Cycle through the known processes, reap the dead child 
275 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
276
277 sub process_automation {
278
279         my $self = __PACKAGE__->instance();
280
281         foreach my $pid ( keys %{$self->pid_hash} ) {
282
283                 if( waitpid( $pid, WNOHANG ) == $pid ) {
284
285                         my $method = $self->pid_hash->{$pid};
286                         delete $self->pid_hash->{$pid};
287
288                         my $newpid =  OpenSRF::Utils::safe_fork();
289
290                         OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
291                         _log( "Relaunching => $method" );
292
293                         if( $newpid ) {
294                                 $self->pid_hash( $newpid, $method );
295                         }
296                         else { eval $method; exit; }
297                 }
298         }
299
300         $SIG{CHLD} = \&process_automation;
301 }
302
303
304
305 sub launch_settings {
306
307         #       XXX the $self like this and pid automation will not work with this setup....
308         my($self) = @_;
309         @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::PreFork);
310
311         my $pid = OpenSRF::Utils::safe_fork();
312         if( $pid ) {
313                 $self->pid_hash( $pid , "launch_settings()" );
314         }
315         else {
316                 my $apname = "opensrf.settings";
317                 #$0 = "OpenSRF App [$apname]";
318                 eval _unixserver( $apname );
319                 if($@) { die "$@\n"; }
320                 exit;
321         }
322
323         @OpenSRF::UnixServer::ISA = qw(OpenSRF);
324
325 }
326
327
328 sub launch_settings_listener {
329
330         my $self = shift;
331         my $app = "opensrf.settings";
332         my $pid = OpenSRF::Utils::safe_fork();
333         if ( $pid ) {
334                 $self->pid_hash( $pid , _listener( $app ) );
335         }
336         else {
337                 my $apname = $app;
338                 $0 = "OpenSRF listener [$apname]";
339                 eval _listener( $app );
340                 exit;
341         }
342
343 }
344
345 # ----------------------------------------------
346 # Launch the Unix Servers
347
348 sub launch_unix {
349         my( $self, $apps ) = @_;
350
351         my $client = OpenSRF::Utils::SettingsClient->new();
352
353         foreach my $app ( @$apps ) {
354
355                 next unless $app;
356                 my $lang = $client->config_value( "apps", $app, "language");
357                 next unless $lang =~ /perl/i;
358                 next if $app eq "opensrf.settings";
359
360                 _log( " * Starting UnixServer for $app..." );
361
362                 my $pid = OpenSRF::Utils::safe_fork();
363                 if( $pid ) {
364                         $self->pid_hash( $pid , _unixserver( $app ) );
365                 }
366                 else {
367                         my $apname = $app;
368                         $0 = "OpenSRF App ($apname)";
369                         eval _unixserver( $app );
370                         exit;
371                 }
372         }
373 }
374
375 # ----------------------------------------------
376 # Launch the inbound clients
377
378 sub launch_listener {
379
380         my( $self, $apps ) = @_;
381         my $client = OpenSRF::Utils::SettingsClient->new();
382
383         foreach my $app ( @$apps ) {
384
385                 next unless $app;
386                 my $lang = $client->config_value( "apps", $app, "language");
387                 next unless $lang =~ /perl/i;
388                 next if $app eq "opensrf.settings";
389
390                 _log( " * Starting Listener for $app..." );
391
392                 my $pid = OpenSRF::Utils::safe_fork();
393                 if ( $pid ) {
394                         $self->pid_hash( $pid , _listener( $app ) );
395                 }
396                 else {
397                         my $apname = $app;
398                         $0 = "OpenSRF listener [$apname]";
399                         eval _listener( $app );
400                         exit;
401                 }
402         }
403 }
404
405 # ----------------------------------------------
406
407 =head comment
408 sub launch_shell {
409
410         my $self = shift;
411
412         my $pid = OpenSRF::Utils::safe_fork();
413
414         if( $pid ) { $self->pid_hash( $pid , _shell() ); }
415         else {
416                 $0 = "System Shell";
417                 for( my $x = 0; $x != 10; $x++ ) {
418                         eval _shell();
419                         if( ! $@ ) { last; }
420                 }
421                 exit;
422         }
423 }
424 =cut
425
426
427 # ----------------------------------------------
428
429 sub pid_hash {
430         my( $self, $pid, $method ) = @_;
431         $self->{'pid_hash'}->{$pid} = $method
432                 if( $pid and $method );
433         return $self->{'pid_hash'};
434 }
435
436 # ----------------------------------------------
437 # If requested, the System can shut down.
438
439 sub killall {
440
441         $SIG{CHLD} = 'IGNORE';
442         $SIG{INT} = 'IGNORE';
443         kill( 'INT', -$$ ); #kill all in process group
444         exit;
445
446 }
447
448 # ----------------------------------------------
449 # Handle $SIG{HUP}
450 sub hupall {
451
452         _log( "HUPping brood" );
453         $SIG{CHLD} = 'IGNORE';
454         $SIG{HUP} = 'IGNORE';
455         set_config(); # reload config
456         kill( 'HUP', -$$ );
457 #       $SIG{CHLD} = \&process_automation;
458         $SIG{HUP} = sub{ instance()->hupall(); };
459 }
460
461
462 # ----------------------------------------------
463 # Log to debug, and stdout
464
465 sub _log {
466         my $string = shift;
467         OpenSRF::Utils::Logger->debug( $string, INFO );
468         #print $string . "\n";
469 }
470
471 # ----------------------------------------------
472
473 sub _sleep {
474         select( undef, undef, undef, 0.3 );
475 }
476
477 1;
478
479