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