]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/perlmods/OpenSRF/System.pm
moved to new config settings
[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 strict;
17
18 my $bootstrap_config_file;
19 sub import {
20         my( $self, $config ) = @_;
21         $bootstrap_config_file = $config;
22 }
23
24 =head2 Name/Description
25
26 OpenSRF::System
27
28 To start the system: OpenSRF::System->bootstrap();
29
30 Simple system process management and automation.  After instantiating the class, simply call
31 bootstrap() to launch the system.  Each launched process is stored as a process-id/method-name
32 pair in a local hash.  When we receive a SIG{CHILD}, we loop through this hash and relaunch
33 any child processes that may have terminated.  
34
35 Currently automated processes include launching the internal Unix Servers, launching the inbound 
36 connections for each application, and starting the system shell.
37
38
39 Note: There should be only one instance of this class
40 alive at any given time.  It is designed as a globel process handler and, hence, will cause much
41 oddness if you call the bootstrap() method twice or attempt to create two of these by trickery.
42 There is a single instance of the class created on the first call to new().  This same instance is 
43 returned on subsequent calls to new().
44
45 =cut
46
47 $| = 1;
48
49 sub DESTROY {}
50
51 # ----------------------------------------------
52
53 $SIG{INT} = sub { instance()->killall(); };
54
55 $SIG{HUP} = sub{ instance()->hupall(); };
56
57 #$SIG{CHLD} = \&process_automation;
58
59
60
61         # --- 
62         # put $instance in a closure and return it for requests to new()
63         # since there should only be one System instance running
64         # ----- 
65         my $instance;
66         sub instance { return __PACKAGE__->new(); }
67         sub new {
68                 my( $class ) = @_;
69
70                 if( ! $instance ) {
71                         $class = ref( $class ) || $class;
72                         my $self = {};
73                         $self->{'pid_hash'} = {};
74                         bless( $self, $class );
75                         $instance = $self;
76                 }
77                 return $instance;
78         }
79 }
80
81 # ----------------------------------------------
82 # Commands to execute at system launch
83
84 sub _unixserver {
85         my( $app ) = @_;
86         return "OpenSRF::UnixServer->new( '$app')->serve()";
87 }
88
89 sub _listener {
90         my( $app ) = @_;
91         return "OpenSRF::Transport::Listener->new( '$app' )->initialize()->listen()";
92 }
93
94
95 # ----------------------------------------------
96 # Boot up the system
97
98 sub load_bootstrap_config {
99
100         if(OpenSRF::Utils::Config->current) {
101                 return;
102         }
103
104         warn "Loading $bootstrap_config_file\n";
105         if(!$bootstrap_config_file) {
106                 die "Please provide a bootstrap config file to OpenSRF::System!\n" . 
107                         "use OpenSRF::System qw(/path/to/bootstrap_config);";
108         }
109
110         OpenSRF::Utils::Config->load( config_file => $bootstrap_config_file );
111
112         OpenSRF::Transport->message_envelope(  "OpenSRF::Transport::SlimJabber::MessageWrapper" );
113         OpenSRF::Transport::PeerHandle->set_peer_client(  "OpenSRF::Transport::SlimJabber::PeerConnection" );
114         OpenSRF::Transport::Listener->set_listener( "OpenSRF::Transport::SlimJabber::Inbound" );
115 }
116
117 sub bootstrap {
118
119         my $self = __PACKAGE__->instance();
120         load_bootstrap_config();
121         OpenSRF::Utils::Logger::set_config();
122         my $bsconfig = OpenSRF::Utils::Config->current;
123
124         # Start a process group and make me the captain
125         setpgrp( 0, 0 ); 
126         $0 = "System";
127
128         # -----------------------------------------------
129         # Launch the settings sever if necessary...
130         my $are_settings_server = 0;
131         if( (my $cfile = $bsconfig->bootstrap->settings_config) ) {
132                 my $parser = OpenSRF::Utils::SettingsParser->new();
133
134                 # since we're (probably) the settings server, we can go ahead and load the real config file
135                 $parser->initialize( $cfile );
136                 $OpenSRF::Utils::SettingsClient::host_config = 
137                         $parser->get_server_config($bsconfig->env->hostname);
138
139                 my $client = OpenSRF::Utils::SettingsClient->new();
140                 my $apps = $client->config_value("activeapps", "appname");
141                 if(!ref($apps) eq "ARRAY") { $apps = [$apps]; }
142
143                 for my $app (@$apps) {
144                         # verify we are a settings server and launch 
145                         if( $app eq "settings" ) {
146                                 $are_settings_server = 1;
147                                 $self->launch_settings();
148                                 sleep 1;
149                                 $self->launch_settings_listener();
150                                 last;
151                         } 
152                 }
153         }
154
155         # Launch everything else
156         my $client = OpenSRF::Utils::SettingsClient->new();
157         my $apps = $client->config_value("activeapps", "appname" );
158         if(!ref($apps)) { $apps = [$apps]; }
159         my $server_type = $client->config_value("server_type");
160         $server_type ||= "basic";
161
162
163         if(  $server_type eq "prefork" ) { 
164                 $server_type = "Net::Server::PreFork"; 
165         } else { 
166                 $server_type = "Net::Server::Single"; 
167         }
168
169         _log( " * Server type: $server_type", INTERNAL );
170
171         eval "use $server_type";
172
173         if( $@ ) {
174                 throw OpenSRF::EX::PANIC ("Cannot set $server_type: $@" );
175         }
176
177         push @OpenSRF::UnixServer::ISA, $server_type;
178
179         _log( " * System boostrap" );
180         
181         # --- Boot the Unix servers
182         $self->launch_unix($apps);
183
184         _sleep();
185
186         # --- Boot the listeners
187         $self->launch_listener($apps);
188
189         _sleep();
190
191         _log( " * System is ready..." );
192
193         while( 1 ) { sleep; }
194         exit;
195 }
196         
197         
198
199 # ----------------------------------------------
200 # Bootstraps a single client connection.  
201
202 sub bootstrap_client {
203
204         my $self = __PACKAGE__->instance();
205         load_bootstrap_config();
206         OpenSRF::Utils::Logger::set_config();
207
208         my $client_type = shift;
209         my $app;
210
211         if( defined($client_type) and $client_type ) {
212                 $app = $client_type;
213         } else {
214                 $app = "client";
215         }
216
217         OpenSRF::Transport::PeerHandle->construct( $app );
218
219 }
220
221 sub bootstrap_logger {
222
223         $0 = "Log Server";
224         OpenSRF::Utils::LogServer->serve();
225
226 }
227
228
229 # ----------------------------------------------
230 # Cycle through the known processes, reap the dead child 
231 # and put a new child in its place. (MMWWAHAHHAHAAAA!)
232
233 sub process_automation {
234
235         my $self = __PACKAGE__->instance();
236
237         foreach my $pid ( keys %{$self->pid_hash} ) {
238
239                 if( waitpid( $pid, WNOHANG ) == $pid ) {
240
241                         my $method = $self->pid_hash->{$pid};
242                         delete $self->pid_hash->{$pid};
243
244                         my $newpid =  OpenSRF::Utils::safe_fork();
245
246                         OpenSRF::Utils::Logger->debug( "Relaunching $method", ERROR );
247                         _log( "Relaunching => $method" );
248
249                         if( $newpid ) {
250                                 $self->pid_hash( $newpid, $method );
251                         }
252                         else { $0 = $method; eval $method; exit; }
253                 }
254         }
255
256         $SIG{CHLD} = \&process_automation;
257 }
258
259
260
261 sub launch_settings {
262
263         #       XXX the $self like this and pid automation will not work with this setup....
264         my($self) = @_;
265         use Net::Server::Single;
266         @OpenSRF::UnixServer::ISA = qw(OpenSRF Net::Server::Single);
267
268         my $pid = OpenSRF::Utils::safe_fork();
269         if( $pid ) {
270                 $self->pid_hash( $pid , "launch_settings()" );
271         }
272         else {
273                 my $apname = "settings";
274                 $apname =~ tr/[a-z]/[A-Z]/;
275                 $0 = "OpenSRF App ($apname)";
276                 eval _unixserver( "settings" );
277                 if($@) { die "$@\n"; }
278                 exit;
279         }
280
281         @OpenSRF::UnixServer::ISA = qw(OpenSRF);
282
283 }
284
285
286 sub launch_settings_listener {
287
288         my $self = shift;
289         my $app = "settings";
290         my $pid = OpenSRF::Utils::safe_fork();
291         if ( $pid ) {
292                 $self->pid_hash( $pid , _listener( $app ) );
293         }
294         else {
295                 my $apname = $app;
296                 $apname =~ tr/[a-z]/[A-Z]/;
297                 $0 = "Listener ($apname)";
298                 eval _listener( $app );
299                 exit;
300         }
301
302 }
303
304 # ----------------------------------------------
305 # Launch the Unix Servers
306
307 sub launch_unix {
308         my( $self, $apps ) = @_;
309
310         foreach my $app ( @$apps ) {
311
312                 if( $app eq "settings" ) { next; }
313
314                 _log( " * Starting UnixServer for $app..." );
315
316                 my $pid = OpenSRF::Utils::safe_fork();
317                 if( $pid ) {
318                         $self->pid_hash( $pid , _unixserver( $app ) );
319                 }
320                 else {
321                         my $apname = $app;
322                         $apname =~ tr/[a-z]/[A-Z]/;
323                         $0 = "OpenSRF App ($apname)";
324                         eval _unixserver( $app );
325                         exit;
326                 }
327         }
328 }
329
330 # ----------------------------------------------
331 # Launch the inbound clients
332
333 sub launch_listener {
334
335         my( $self, $apps ) = @_;
336
337         foreach my $app ( @$apps ) {
338
339                 if( $app eq "settings" ) { next; }
340
341                 _log( " * Starting Listener for $app..." );
342
343                 my $pid = OpenSRF::Utils::safe_fork();
344                 if ( $pid ) {
345                         $self->pid_hash( $pid , _listener( $app ) );
346                 }
347                 else {
348                         my $apname = $app;
349                         $apname =~ tr/[a-z]/[A-Z]/;
350                         $0 = "Listener ($apname)";
351                         eval _listener( $app );
352                         exit;
353                 }
354         }
355 }
356
357 # ----------------------------------------------
358
359 =head comment
360 sub launch_shell {
361
362         my $self = shift;
363
364         my $pid = OpenSRF::Utils::safe_fork();
365
366         if( $pid ) { $self->pid_hash( $pid , _shell() ); }
367         else {
368                 $0 = "System Shell";
369                 for( my $x = 0; $x != 10; $x++ ) {
370                         eval _shell();
371                         if( ! $@ ) { last; }
372                 }
373                 exit;
374         }
375 }
376 =cut
377
378
379 # ----------------------------------------------
380
381 sub pid_hash {
382         my( $self, $pid, $method ) = @_;
383         $self->{'pid_hash'}->{$pid} = $method
384                 if( $pid and $method );
385         return $self->{'pid_hash'};
386 }
387
388 # ----------------------------------------------
389 # If requested, the System can shut down.
390
391 sub killall {
392
393         $SIG{CHLD} = 'IGNORE';
394         $SIG{INT} = 'IGNORE';
395         kill( 'INT', -$$ ); #kill all in process group
396         exit;
397
398 }
399
400 # ----------------------------------------------
401 # Handle $SIG{HUP}
402 sub hupall {
403
404         _log( "HUPping brood" );
405         $SIG{CHLD} = 'IGNORE';
406         $SIG{HUP} = 'IGNORE';
407         set_config(); # reload config
408         kill( 'HUP', -$$ );
409 #       $SIG{CHLD} = \&process_automation;
410         $SIG{HUP} = sub{ instance()->hupall(); };
411 }
412
413
414 # ----------------------------------------------
415 # Log to debug, and stdout
416
417 sub _log {
418         my $string = shift;
419         OpenSRF::Utils::Logger->debug( $string, INFO );
420         print $string . "\n";
421 }
422
423 # ----------------------------------------------
424
425 sub _sleep {
426         select( undef, undef, undef, 0.3 );
427 }
428
429 1;
430
431