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