Commit | Line | Data |
4536f655 |
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 ; |