edited to reflect the moving around of the demo files
[urisagit/Stem.git] / lib / Stem / Boot.pm
1 #  File: Stem/Boot.pm
2
3 #  This file is part of Stem.
4 #  Copyright (C) 1999, 2000, 2001 Stem Systems, Inc.
5
6 #  Stem is free software; you can redistribute it and/or modify
7 #  it under the terms of the GNU General Public License as published by
8 #  the Free Software Foundation; either version 2 of the License, or
9 #  (at your option) any later version.
10
11 #  Stem is distributed in the hope that it will be useful,
12 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 #  GNU General Public License for more details.
15
16 #  You should have received a copy of the GNU General Public License
17 #  along with Stem; if not, write to the Free Software
18 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20 #  For a license to use the Stem under conditions other than those
21 #  described here, to purchase support for this software, or to purchase a
22 #  commercial warranty contract, please contact Stem Systems at:
23
24 #       Stem Systems, Inc.              781-643-7504
25 #       79 Everett St.                  info@stemsystems.com
26 #       Arlington, MA 02474
27 #       USA
28
29 package Stem::Boot ;
30
31 use strict ;
32 use Carp ;
33 use Symbol ;
34
35 my $attr_spec = [
36
37         {
38                 'name'          => 'reg_name',
39                 'help'          => <<HELP,
40 This is the name under which this Cell was registered.
41 HELP
42         },
43         {
44                 'name'          => 'boot_file',
45                 'required'      => 1,
46                 'help'          => <<HELP,
47 This is the file that describes the processes to bootstrap
48 HELP
49         },
50         {
51                 'name'          => 'name',
52                 'help'          => <<HELP,
53 Name of this boot entry
54 HELP
55         },
56         {
57                 'name'          => 'cmd',
58                 'help'          => <<HELP,
59 Path to command that will be booted
60 HELP
61         },
62         {
63                 'name'          => 'log',
64                 'help'          => <<HELP,
65 Default Name of logical log to send all status and process output
66 HELP
67         },
68         {
69                 'name'          => 'delay',
70                 'help'          => <<HELP,
71 Default delay (in seconds) between spawning processes
72 HELP
73         },
74         {
75                 'name'          => 'user',
76                 'help'          => <<HELP,
77 Default user id to run the processes
78 HELP
79         },
80         {
81                 'name'          => 'wrap',
82                 'default'       => '/bin/sh -c',
83                 'help'          => <<HELP,
84 Default command wrapper for each process
85 HELP
86         },
87         {
88                 'name'          => 'chdir',
89                 'help'          => <<HELP,
90 Default dir to chdir to before running each process
91 HELP
92         },
93         {
94                 'name'          => 'boot_now',
95                 'type'          => 'boolean',
96                 'default'       => 1,
97                 'help'          => <<HELP,
98 Boot this program when this object is created
99 HELP
100         },
101         {
102                 'name'          => 'restart',
103                 'help'          => <<HELP,
104 Restart this program when it exits
105 HELP
106         },
107 ] ;
108
109 my %name2boot ;
110
111
112 sub new {
113
114         my( $class ) = shift ;
115
116         my $self = Stem::Class::parse_args( $attr_spec, @_ ) ;
117         return $self unless ref $self ;
118
119         my $boot_info = Stem::Util::load_file( $self->{'boot_file'} ) ;
120         return $boot_info unless ref $boot_info ;
121
122         foreach my $boot ( @{$boot_info} ) {
123
124                 die "boot entry is not a hash\n" unless ref $boot eq 'HASH' ;
125
126                 if ( my $skip = $boot->{'skip'} ) {
127
128                         next if lc $skip eq 'yes' ;
129                 }
130
131                 my $boot_obj = Stem::Class::parse_args( $attr_spec,
132                                         %{$self},
133                                         %{$boot}
134                 ) ;
135
136                 die "boot entry error: $boot_obj\n" unless ref $boot_obj ;
137
138                 my $cmd = $boot_obj->{'cmd'} ;
139                 die "boot entry is missing 'cmd'\n" unless $cmd ;
140
141                 my $name = $boot_obj->{'name'} ;
142                 die "boot entry is missing 'name'\n" unless $name ;
143
144                 $name2boot{ $name } = $boot_obj ;
145
146                 if ( $boot_obj->{'boot_now'} ) {
147
148                         $boot_obj->run_cmd() ;
149                 }
150         }
151
152         return ;
153 }
154
155
156 sub run_cmd {
157
158         my( $self ) = @_ ;
159
160 #print Store $self ;
161
162         my $cmd ;
163
164         if ( my $user = $self->{'user'} ) {
165
166                 if ( getpwuid($<) ne $user ) {
167
168                         $cmd .= "su - $user ; " ;
169                 }
170         }
171
172         if ( my $wrap = $self->{'wrap'} ) {
173
174                 $cmd .= qq{$wrap "} ;
175                 $self->{'wrap_end'} ||= '"' ;
176         }
177
178         if ( my $chdir = $self->{'chdir'} ) {
179
180                 $cmd .= "cd $chdir ; " ;
181         }
182
183         if ( my $stem_env = $self->{'stem_env'} ) {
184
185                 my $cmd_env = join ' ', map(
186                                 "$_='$stem_env->{$_}'", keys %{$stem_env} ) ;
187
188                 $cmd =~ s/run_stem/run_stem $cmd_env/ ;
189         }
190
191         $cmd .= $self->{'cmd'} ;
192
193         $cmd .= $self->{'wrap_end'} if $self->{'wrap_end'} ;
194
195         my $handle = gensym ;
196
197 #print "$cmd\n" ;
198
199         if ( my $pid = open( $handle, '-|' ) ) {
200
201 #print "pid $pid\n" ;
202                 $self->{'pid'} = $pid ;
203                 $self->{'handle'} = $handle ;
204         }
205         elsif ( defined( $pid ) ) {
206
207                 local( %ENV ) = ( %ENV, %{ $self->{'env'} || {} } ) ;
208
209                 open( STDERR, '>&STDOUT' ) ;
210
211                 exec $cmd ;
212                 die "Couldn't exec [$cmd]\n" ;
213         }
214         else {
215
216                 die "couldn't fork\n" ;
217         }
218
219         my $aio = Stem::AsyncIO->new(
220
221                         'object'        => $self,
222                         'read_fh'       => $handle,
223                         'read_method'   => 'boot_read',
224                         'closed_method' => 'boot_closed',
225         ) ;
226
227         $self->{'aio'} = $aio ;
228
229         if ( my $log = $self->{'log'} ) {
230
231                 Stem::Log::Entry->new(
232                         'logs'  => $log,
233                         'label' => 'boot',
234                         'text'  =>
235                         "Booting $self->{'name'} PID = $self->{'pid'}: $cmd\n",
236                 ) ;
237         }
238
239         return ;
240 }
241
242 sub boot_read {
243
244         my( $self, $data ) = @_ ;
245
246 #print "BOOT READ [$$data]\n" ;
247
248         if ( my $log = $self->{'log'} ) {
249
250                 Stem::Log::Entry->new(
251                         'logs'  => $log,
252                         'label' => 'boot',
253                         'text'  => "Output for $self->{'name'}\n[${$data}]\n",
254                 ) ;
255         }
256
257         return ;
258 }
259
260 sub boot_closed {
261
262         my( $self ) = @_ ;
263
264 #print "BOOT closed\n" ;
265
266         $self->{'aio'}->shut_down() ;
267         delete $self->{'aio'} ;
268
269         my $boot_pid = $self->{'pid'} ;
270         my $pid = waitpid( $boot_pid, 0 ) ;
271
272 #print "WAIT [$pid]\n" ;
273
274         if ( my $log = $self->{'log'} ) {
275
276                 Stem::Log::Entry->new(
277                         'logs'  => $log,
278                         'label' => 'boot',
279                         'text'  => "Boot $self->{'name'} exited PID = $pid",
280                 ) ;
281         }
282
283 # do restart if needed
284
285
286
287
288         return ;
289 }
290
291 1 ;