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