]> git.evergreen-ils.org Git - working/Evergreen.git/blob - OpenSRF/src/perlmods/OpenSRF/System.pm
using a single jabber connection per process, since we do not actually care about...
[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 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         return "OpenSRF::Transport::Listener->new( '$app' )->initialize()->listen()";
93 }
94
95
96 # ----------------------------------------------
97 # Boot up the system
98
99 sub load_bootstrap_config {
100
101         if(OpenSRF::Utils::Config->current) {
102                 return;
103         }
104
105         warn "Loading $bootstrap_config_file\n";
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 => "", 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 }
119
120 sub bootstrap {
121
122         my $self = __PACKAGE__->instance();
123         load_bootstrap_config();
124         OpenSRF::Utils::Logger::set_config();
125         my $bsconfig = OpenSRF::Utils::Config->current;
126
127         # Start a process group and make me the captain
128         setpgrp( 0, 0 ); 
129         $0 = "System";
130
131         # -----------------------------------------------
132         # Launch the settings sever if necessary...
133         my $are_settings_server = 0;
134         if( (my $cfile = $bsconfig->bootstrap->settings_config) ) {
135                 my $parser = OpenSRF::Utils::SettingsParser->new();
136
137                 # since we're (probably) the settings server, we can go ahead and load the real config file
138                 $parser->initialize( $cfile );
139                 $OpenSRF::Utils::SettingsClient::host_config = 
140                         $parser->get_server_config($bsconfig->env->hostname);
141
142                 my $client = OpenSRF::Utils::SettingsClient->new();
143                 my $apps = $client->config_value("activeapps", "appname");
144                 if(!ref($apps) eq "ARRAY") { $apps = [$apps]; }
145
146                 for my $app (@$apps) {
147                         # verify we are a settings server and launch 
148                         if( $app eq "settings" ) {
149                                 $are_settings_server = 1;
150                                 $self->launch_settings();
151                                 sleep 1;
152                                 $self->launch_settings_listener();
153                                 last;
154                         } 
155                 }
156         }
157
158         # Launch everything else
159         my $client = OpenSRF::Utils::SettingsClient->new();
160         my $apps = $client->config_value("activeapps", "appname" );
161         if(!ref($apps)) { $apps = [$apps]; }
162         my $server_type = $client->config_value("server_type");
163         $server_type ||= "basic";
164
165         OpenSRF::Transport::PeerHandle->retrieve->disconnect;
166
167         if(  $server_type eq "prefork" ) { 
168                 $server_type = "Net::Server::PreFork"; 
169         } else { 
170                 $server_type = "Net::Server::Single"; 
171         }
172
173         _log( " * Server type: $server_type", INTERNAL );
174
175         eval "use $server_type";
176
177         if( $@ ) {
178                 throw OpenSRF::EX::PANIC ("Cannot set $server_type: $@" );
179         }
180
181         push @OpenSRF::UnixServer::ISA, $server_type;
182
183         _log( " * System boostrap" );
184         
185         # --- Boot the Unix servers
186         $self->launch_unix($apps);
187
188         _sleep();
189
190         # --- Boot the listeners
191         $self->launch_listener($apps);
192
193         _sleep();
194
195         _log( " * System is ready..." );
196
197         while( 1 ) { sleep; }
198         exit;
199 }
200         
201         
202
203 # ----------------------------------------------
204 # Bootstraps a single client connection.  
205
206 sub bootstrap_client {
207
208         my $self = __PACKAGE__->instance();
209         load_bootstrap_config();
210         OpenSRF::Utils::Logger::set_config();
211
212         my $client_type = shift;
213         my $app;
214
215         if( defined($client_type) and $client_type ) {
216                 $app = $client_type;
217         } else {
218                 $app = "client";
219         }
220
221         OpenSRF::Transport::PeerHandle->construct( $app );
222
223 }
224
225 sub bootstrap_logger {
226
227         $0 = "Log Server";
228         OpenSRF::Utils::LogServer->serve();
229
230 }
231
232
233 # ----------------------------------------------
234 # Cycle through the known processes, reap the dead child 
235 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
236
237 sub process_automation {
238
239         my $self = __PACKAGE__->instance();
240
241         foreach my $pid ( keys %{$self->pid_hash} ) {
242
243                 if( waitpid( $pid, WNOHANG ) == $pid ) {
244
245                         my $method = $self->pid_hash->{$pid};
246                         delete $self->pid_hash->{$pid};
247
248                         my $newpid =  OpenSRF::Utils::safe_fork();
249
250                         OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
251                         _log( "Relaunching => $method" );
252
253                         if( $newpid ) {
254                                 $self->pid_hash( $newpid, $method );
255                         }
256                         else { $0 = $method; eval $method; exit; }
257                 }
258         }
259
260         $SIG{CHLD} = \&process_automation;
261 }
262
263
264
265 sub launch_settings {
266
267         #       XXX the $self like this and pid automation will not work with this setup....
268         my($self) = @_;
269         @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::PreFork);
270
271         my $pid = OpenSRF::Utils::safe_fork();
272         if( $pid ) {
273                 $self->pid_hash( $pid , "launch_settings()" );
274         }
275         else {
276                 my $apname = "settings";
277                 $apname =~ tr/[a-z]/[A-Z]/;
278                 $0 = "OpenSRF App ($apname)";
279                 eval _unixserver( "settings" );
280                 if($@) { die "$@\n"; }
281                 exit;
282         }
283
284         @OpenSRF::UnixServer::ISA = qw(OpenSRF);
285
286 }
287
288
289 sub launch_settings_listener {
290
291         my $self = shift;
292         my $app = "settings";
293         my $pid = OpenSRF::Utils::safe_fork();
294         if ( $pid ) {
295                 $self->pid_hash( $pid , _listener( $app ) );
296         }
297         else {
298                 my $apname = $app;
299                 $apname =~ tr/[a-z]/[A-Z]/;
300                 $0 = "Listener ($apname)";
301                 eval _listener( $app );
302                 exit;
303         }
304
305 }
306
307 # ----------------------------------------------
308 # Launch the Unix Servers
309
310 sub launch_unix {
311         my( $self, $apps ) = @_;
312
313         foreach my $app ( @$apps ) {
314
315                 if( $app eq "settings" ) { next; }
316
317                 _log( " * Starting UnixServer for $app..." );
318
319                 my $pid = OpenSRF::Utils::safe_fork();
320                 if( $pid ) {
321                         $self->pid_hash( $pid , _unixserver( $app ) );
322                 }
323                 else {
324                         my $apname = $app;
325                         $apname =~ tr/[a-z]/[A-Z]/;
326                         $0 = "OpenSRF App ($apname)";
327                         eval _unixserver( $app );
328                         exit;
329                 }
330         }
331 }
332
333 # ----------------------------------------------
334 # Launch the inbound clients
335
336 sub launch_listener {
337
338         my( $self, $apps ) = @_;
339
340         foreach my $app ( @$apps ) {
341
342                 if( $app eq "settings" ) { next; }
343
344                 _log( " * Starting Listener for $app..." );
345
346                 my $pid = OpenSRF::Utils::safe_fork();
347                 if ( $pid ) {
348                         $self->pid_hash( $pid , _listener( $app ) );
349                 }
350                 else {
351                         my $apname = $app;
352                         $apname =~ tr/[a-z]/[A-Z]/;
353                         $0 = "Listener ($apname)";
354                         eval _listener( $app );
355                         exit;
356                 }
357         }
358 }
359
360 # ----------------------------------------------
361
362 =head comment
363 sub launch_shell {
364
365         my $self = shift;
366
367         my $pid = OpenSRF::Utils::safe_fork();
368
369         if( $pid ) { $self->pid_hash( $pid , _shell() ); }
370         else {
371                 $0 = "System Shell";
372                 for( my $x = 0; $x != 10; $x++ ) {
373                         eval _shell();
374                         if( ! $@ ) { last; }
375                 }
376                 exit;
377         }
378 }
379 =cut
380
381
382 # ----------------------------------------------
383
384 sub pid_hash {
385         my( $self, $pid, $method ) = @_;
386         $self->{'pid_hash'}->{$pid} = $method
387                 if( $pid and $method );
388         return $self->{'pid_hash'};
389 }
390
391 # ----------------------------------------------
392 # If requested, the System can shut down.
393
394 sub killall {
395
396         $SIG{CHLD} = 'IGNORE';
397         $SIG{INT} = 'IGNORE';
398         kill( 'INT', -$$ ); #kill all in process group
399         exit;
400
401 }
402
403 # ----------------------------------------------
404 # Handle $SIG{HUP}
405 sub hupall {
406
407         _log( "HUPping brood" );
408         $SIG{CHLD} = 'IGNORE';
409         $SIG{HUP} = 'IGNORE';
410         set_config(); # reload config
411         kill( 'HUP', -$$ );
412 #       $SIG{CHLD} = \&process_automation;
413         $SIG{HUP} = sub{ instance()->hupall(); };
414 }
415
416
417 # ----------------------------------------------
418 # Log to debug, and stdout
419
420 sub _log {
421         my $string = shift;
422         OpenSRF::Utils::Logger->debug( $string, INFO );
423         print $string . "\n";
424 }
425
426 # ----------------------------------------------
427
428 sub _sleep {
429         select( undef, undef, undef, 0.3 );
430 }
431
432 1;
433
434