source: gs2-extensions/parallel-building/trunk/src/opt/Perseus/perseus-medusa.pl@ 29649

Last change on this file since 29649 was 29649, checked in by jmt12, 9 years ago

Perseus was an attempt to add functionality to automatically and remote fix Medusa is processes went run-away. The idea was the Persus server would either run as a daemon or be periodically called by cron, it would look for a sentinel file or be contacted by a client, and then it would slay processes belonging to a certain user. Never actually used, because the problems were found to be NFS/system level and impossible to solve without a hard reset anyway

  • Property svn:executable set to *
File size: 8.4 KB
Line 
1#!/usr/bin/perl
2
3# Pragma
4use strict;
5use warnings;
6$|++; # Unbuffer STDOUT
7
8# Perl Built-in Modules
9use Getopt::Long;
10use IO::Socket::INET;
11use Pod::Usage;
12
13my $start_time = time();
14print "[" . localtime($start_time) . "]\n";
15print "===== Persus =====\n";
16print "To slay a Medusa you need an appropriately shiney shield - this is\n";
17print "that shield.\n";
18print "\n";
19
20# 1. Configuration
21my $debug = 0;
22my $help = 0;
23my $kill_delay = 5;
24my $listen_time = 60;
25my $man = 0;
26my $search_path = '/tmp/perseus.txt';
27my $search_port = 0;
28my $search_url = 'http://www.cms.waikato.ac.nz/~jmt12/perseus.txt';
29GetOptions ('debug' => \$debug,
30 'kill_delay=i' => \$kill_delay,
31 'listen_port=i' => \$search_port,
32 'listen_time=i' => \$listen_time,
33 'path=s' => \$search_path,
34 'url=s' => \$search_url,
35 'help|?' => \$help,
36 man => \$man
37 )
38or pod2usage(2);
39if ($help)
40{
41 pod2usage(1);
42}
43if ($man)
44{
45 pod2usage(-exitval => 0, -verbose => 2);
46}
47print " * Configuration\n";
48print " - Debug? " . ($debug ? "Yes" : "No") . "\n";
49print " - Search path: " . $search_path . "\n";
50print " - Search URL: " . $search_url . "\n";
51print " - Search port: " . $search_port . "\n";
52print " - Listen time: " . $listen_time . " seconds\n";
53print " - Delay before KILL: " . $kill_delay . " seconds\n";
54
55# 2. Search
56print " * Looking for Instructions\n";
57my $instructions = [];
58print " - Search for sentinel file... ";
59if (&searchPath())
60{
61 print "Found\n";
62}
63else
64{
65 print "Not found\n";
66}
67
68if (scalar(@{$instructions}) == 0)
69{
70 print " - Search for internet page... ";
71 if (&searchURL())
72 {
73 print "Found\n";
74 }
75 else
76 {
77 print "Not found\n";
78 }
79 print "Done!\n";
80}
81
82if (scalar(@{$instructions}) == 0)
83{
84 if ($search_port > 0)
85 {
86 print " - Listen on port... ";
87 if (&searchPort())
88 {
89 print "Contacted\n";
90 }
91 else
92 {
93 print "No contact\n";
94 }
95 print "Done!\n";
96 }
97 else
98 {
99 print " - Skipping port search\n";
100 }
101}
102
103
104# 3. Action
105if (scalar(@{$instructions}))
106{
107 print " * Processing Instructions\n";
108 my @instructions2;
109 # - terms and kills first
110 my $instruction_ptr = pop(@{$instructions});
111 while (defined $instruction_ptr)
112 {
113 my @instruction = @{$instruction_ptr};
114 if ($instruction[0] eq 'pid')
115 {
116 &commandTerminate($instruction[1]);
117 }
118 else
119 {
120 push(@instructions2, $instruction_ptr);
121 }
122 $instruction_ptr = pop(@{$instructions});
123 }
124 # - then slays
125 $instruction_ptr = pop(@instructions2);
126 while (defined $instruction_ptr)
127 {
128 my @instruction = @{$instruction_ptr};
129 if ($instruction[0] eq 'user')
130 {
131 &commandSlay($instruction[1]);
132 }
133 else
134 {
135 push(@{$instructions}, $instruction_ptr);
136 }
137 $instruction_ptr = pop(@instructions2);
138 }
139 # - then commands (reboot)
140 $instruction_ptr = pop(@{$instructions});
141 while (defined $instruction_ptr)
142 {
143 my @instruction = @{$instruction_ptr};
144 if ($instruction[0] eq 'cmd' && $instruction[1] eq 'reboot')
145 {
146 &commandReboot();
147 }
148 else
149 {
150 push(@instructions2, $instruction_ptr);
151 }
152 $instruction_ptr = pop(@{$instructions});
153 }
154 # - left overs
155 foreach $instruction_ptr (@instructions2)
156 {
157 my @instruction = @{$instruction_ptr};
158 print "Error! Unrecognized instruction \"" . $instruction[0] . ":" . $instruction[1] . "\"\n";
159 }
160}
161else
162{
163 print " * No Instructions!\n";
164}
165
166# 4. Complete!
167my $end_time = time();
168my $duration = $end_time - $start_time;
169print "===== Complete in " . $duration . " seconds =====\n";
170print "[" . localtime($end_time) . "]\n\n";
171exit;
172
173
174## @function commandIsRunning
175#
176sub commandIsRunning
177{
178 my ($pid) = @_;
179 my $cmd = 'ps -p ' . $pid . ' > /dev/null';
180 `$cmd`;
181 my $retval = $?;
182 return ($retval == 0);
183}
184## commandIsRunning() ##
185
186## @function commandReboot
187#
188# Wait five minutes then reboot the machine
189#
190sub commandReboot
191{
192 print " - rebooting\n";
193 print &shellCommand('shutdown -r +5', 1);
194}
195## commandReboot ##
196
197## @function commandSlay
198#
199sub commandSlay
200{
201 my ($user) = @_;
202 print " - slay all processes by: " . $user . "\n";
203 print &shellCommand('slay -clean ' . $user);
204}
205## commandSlay() ##
206
207## @function commandTerminate
208#
209sub commandTerminate
210{
211 my ($pid) = @_;
212 if (&commandIsRunning($pid))
213 {
214 print " - terminate process: " . $pid . "... ";
215 # send SIGTERM
216 print &shellCommand('kill -s SIGTERM ' . $pid);
217 # wait delay seconds
218 sleep($kill_delay);
219 # see if process still there
220 if (&commandIsRunning($pid))
221 {
222 print "Failed\n";
223 print " - kill process: " . $pid . "... ";
224 print &shellCommand('kill -s SIGKILL ' . $pid);
225 sleep($kill_delay);
226 if (&commandIsRunning($pid))
227 {
228 print "Failed\n";
229 }
230 else
231 {
232 print "Killed\n";
233 }
234 }
235 else
236 {
237 print "Terminated\n";
238 }
239 }
240 else
241 {
242 print " - can't terminate, no such process: " . $pid . "\n";
243 }
244 # if it is still running then it must be uninterruptable... nothing we can
245 # do from here
246}
247## commandTerminate() ##
248
249
250## @function parseInstructions
251#
252sub parseInstructions
253{
254 my ($content) = @_;
255 my $instruction_count = 0;
256 while ($content =~ s/(pid|user|cmd)\:([^\s]+)//i)
257 {
258 my $type = lc($1);
259 my $value = $2;
260 push(@{$instructions}, [$1, $2]);
261 $instruction_count++;
262 }
263 return $instruction_count;
264}
265## parseInstructions() ##
266
267
268## @function printDebug()
269#
270sub printDebug
271{
272 my ($msg) = @_;
273 if ($debug)
274 {
275 print "[DEBUG] " . $msg . " [" . time() . "]\n";
276 }
277}
278## printDebug() ##
279
280
281## @function searchPath()
282#
283sub searchPath
284{
285 my $result = 0;
286 # A user can ask us to do stuff by dumping a file here
287 if (-e $search_path)
288 {
289 # read instructions (<type>:<value>\n) from the file
290 if (open(FIN, '<:utf8', $search_path))
291 {
292 my $content;
293 sysread(FIN, $content, -s $search_path);
294 $result = &parseInstructions($content);
295 close(FIN);
296 }
297 else
298 {
299 print STDERR "Error! Failed to open file for reading: " . $search_path . "\n";
300 }
301 # remove the file (if we can)
302 #unlink($search_path);
303 }
304 return $result;
305}
306## searchPath() ##
307
308
309## @function searchPort
310#
311sub searchPort
312{
313 my $result = 0;
314 # timeout isn't implemented in Perl sockets, so we cheat and use an alarm
315 # that interrupts an eval block
316 eval {
317 local $SIG{ALRM} = sub { die 'timeout' };
318 alarm $listen_time;
319 my $socket = IO::Socket::INET->new(Proto => 'tcp',
320 LocalPort => $search_port,
321 Listen => 1,
322 ReuseAddr => 1
323 );
324 if (defined $socket)
325 {
326 print "[listening on port " . $search_port . "... ";
327 while (my $client = $socket->accept())
328 {
329 print "connected... ";
330 $client->autoflush(1);
331 my $line;
332 while ($line = <$client>)
333 {
334 $result += &parseInstruction($line);
335 }
336 close $client;
337 print "complete]";
338 }
339 }
340 else
341 {
342 print STDERR "Error! Failed to open socket for listening: " . $search_port . "\n";
343 }
344 alarm 0; # reset alarm
345 };
346 alarm 0; # reset alarm
347 if (defined $@)
348 {
349 if ($@ =~ /timeout/)
350 {
351 print "timed out]";
352 }
353 else
354 {
355 print "Error! " . @! . "\n";
356 }
357 }
358 return $result;
359}
360## searchPort() ##
361
362
363## @function searchURL
364#
365sub searchURL
366{
367 # we use wget as it is simple, and doesn't require Perl module changes
368 my $cmd = 'wget -q -O - "' . $search_url . '"';
369 &printDebug($cmd);
370 my $content = `$cmd`;
371 my $result = &parseInstructions($content);
372 return $result;
373}
374## searchURL() ##
375
376
377## @function shellCommand()
378#
379sub shellCommand
380{
381 my ($cmd, $background) = @_;
382 my $result = '';
383 if ($debug)
384 {
385 &printDebug('shellCommand("' . $cmd . '")');
386 }
387 elsif (defined $background)
388 {
389 $result = `$cmd 2>&1 &`;
390 }
391 else
392 {
393 $result = `$cmd 2>&1`;
394 }
395 return $result;
396}
397## shellCommand() ##
398
399__END__
400=head1 NAME
401sample - Using GetOpt::Long and Pod::Usage
402=head1 SYNOPSIS
403sample [options] [file ...]
404 Options:
405 -help brief help message
406 -man full documentation
407=head1 OPTIONS
408=over 8
409=item B<-help>
410Print a brief help message and exits.
411=item B<-man>
412Prints the manual page and exits.
413=back
414=head1 DESCRIPTION
415B<This program> will read the given input file(s) and do something useful with the contents thereof.
416=cut
Note: See TracBrowser for help on using the repository browser.