fixproc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. #!/usr/bin/perl
  2. #
  3. # fixproc [-min n] [-max n] [-check | -kill | -restart | -exist | -fix] proc ...
  4. #
  5. # fixproc exit code:
  6. # 0 ok
  7. # 1 check failed
  8. # 2 cannot restart
  9. # 3 cannot kill
  10. # 4 fix failed if fix is defined as kill or restart, then
  11. # cannot kill or cannot restart is return instead
  12. # 10 fixproc error
  13. #
  14. #
  15. # Fixes a process named "proc" by performing the specified action. The
  16. # actions can be check, kill, restart, exist, or fix. The action is specified
  17. # on the command line or is read from a default database, which describes
  18. # the default action to take for each process. The database format and
  19. # the meaning of each action are described below.
  20. #
  21. # database format
  22. # ---------------
  23. #
  24. # name foo required
  25. # cmd /a/b/name args required
  26. # min number optional, defaults to 1
  27. # max number optional, defaults to 1
  28. #
  29. # check {null, exist, shell} optional, defaults to exist if not defined
  30. # [shell command shell commands needed only if check=shell
  31. # ...
  32. # shell command
  33. # end_shell] keyword end_shell marks end of shell commands
  34. # fix {kill, restart, shell} required
  35. # [shell command shell commands needed only if fix=shell
  36. # ...
  37. # shell command
  38. # end_shell] keyword end_shell marks end of shell commands
  39. #
  40. # Blank lines and lines beginning with "#" are ignored.
  41. #
  42. #
  43. # Example:
  44. #
  45. # name test1
  46. # cmd nice /home/kong/z/test1 > /dev/null &
  47. # max 2
  48. # fix shell
  49. # xterm&
  50. # nice /home/kong/z/test1 > /dev/null &
  51. # end_shell
  52. #
  53. #
  54. # actions
  55. # -------
  56. # There are 5 possible actions: kill, restart, fix, exist, check. Fix is
  57. # defined to be the kill action, the restart action, or a series of shell
  58. # commands. Check is optionally defined in the database. If check is not
  59. # defined, it defaults to exist.
  60. #
  61. # If the action is specified on the cmd line, it is executed regardless of
  62. # check. The commands executed for each action type is as follow:
  63. #
  64. # switch action:
  65. # kill:
  66. # kill process, wait 5 seconds, kill -9 if still exist
  67. # if still exist
  68. # return "cannot kill"
  69. # else
  70. # return "ok"
  71. #
  72. # restart:
  73. # execute kill
  74. # if kill returned "cannot kill"
  75. # return "cannot kill"
  76. # restart by issuing cmd to shell
  77. # if check defined
  78. # execute check
  79. # if check succeeds
  80. # return "ok"
  81. # else
  82. # return "cannot restart"
  83. #
  84. # fix:
  85. # if fix=kill
  86. # execute kill
  87. # else if fix=restart
  88. # execute restart
  89. # else
  90. # execute shell commands
  91. # execute check
  92. #
  93. # check:
  94. # if check defined as null
  95. # return "fixproc error"
  96. # else
  97. # execute check
  98. # if check succeeds
  99. # return (execute exist)
  100. # return "check failed"
  101. #
  102. # exist:
  103. # if proc exists in ps && (min <= num. of processes <= max)
  104. # return "ok"
  105. # else
  106. # return "check failed"
  107. #
  108. #
  109. # If the action is not specified on the cmd line, the default action is the
  110. # fix action defined in the database. Fix is only executed if check fails:
  111. #
  112. # if fix defined
  113. # if check is not defined as null
  114. # execute check
  115. # if check succeeds
  116. # return "ok"
  117. # execute action defined for fix
  118. # else
  119. # return "fixproc error"
  120. #
  121. #
  122. # If proc is not specified on the command line, return "fixproc error."
  123. # Multiple proc's can be defined on the cmd line. When an error occurs
  124. # when multiple proc's are specified, the first error encountered halts the
  125. # script.
  126. #
  127. # For check shell scripts, any non-zero exit code means the check has failed.
  128. #
  129. #
  130. # Timothy Kong 3/1995
  131. use File::Temp qw(tempfile);
  132. $database_file = '/local/etc/fixproc.conf';
  133. $debug = 0; # specify debug level using -dN
  134. # currently defined: -d1
  135. $no_error = 0;
  136. $check_failed_error = 1;
  137. $cannot_restart_error = 2;
  138. $cannot_kill_error = 3;
  139. $cannot_fix_error = 4;
  140. $fixproc_error = 10;
  141. $min = 1;
  142. $max = 1;
  143. $cmd_line_action = '';
  144. %min = ();
  145. %max = ();
  146. %cmd = ();
  147. %check = ();
  148. %fix = ();
  149. $shell_lines = ();
  150. @proc_list = ();
  151. $shell_header = "#!/bin/sh\n";
  152. $shell_end_marker = 'shell_end_marker';
  153. &read_args();
  154. &read_database();
  155. # &dump_database(); # debug only
  156. # change the default min. and max. number of processes allowed
  157. if ($min != 1)
  158. {
  159. for $name ( keys (%min) )
  160. {
  161. $min{$name} = $min;
  162. }
  163. }
  164. if ($max != 1)
  165. {
  166. for $name ( keys (%max) )
  167. {
  168. $max{$name} = $max;
  169. }
  170. }
  171. # work on one process at a time
  172. for $proc ( @proc_list )
  173. {
  174. $error_code = &work_on_proc ($proc);
  175. ############# uncomment next line when fully working ############
  176. # exit $error_code if ($error_code);
  177. die "error_code = $error_code\n" if ($error_code);
  178. }
  179. # create an executable shell script file
  180. sub create_sh_script
  181. {
  182. local ($file) = pop (@_);
  183. local ($fh) = pop (@_);
  184. local ($i) = pop (@_);
  185. printf (STDERR "create_sh_script\n") if ($debug > 0);
  186. $! = $fixproc_error;
  187. while ( $shell_lines[$i] ne $shell_end_marker )
  188. {
  189. printf ($fh "%s", $shell_lines[$i]);
  190. $i++;
  191. }
  192. close ($fh);
  193. chmod 0755, $file;
  194. }
  195. sub do_fix
  196. {
  197. local ($proc) = pop(@_);
  198. printf (STDERR "do_fix\n") if ($debug > 0);
  199. if ($fix{$proc} eq '')
  200. {
  201. $! = $fixproc_error;
  202. die "$0: internal error 4\n";
  203. }
  204. if ($fix{$proc} eq 'kill')
  205. {
  206. return &do_kill ($proc);
  207. }
  208. elsif ($fix{$proc} eq 'restart')
  209. {
  210. return &do_restart ($proc);
  211. }
  212. else
  213. {
  214. # it must be "shell", so execute the shell script defined in database
  215. local ($tmpfh, $tmpfile) = tempfile("fix_XXXXXXXX", DIR => "/tmp");
  216. &create_sh_script ($fix{$proc}, $tmpfh, $tmpfile);
  217. # return code is number divided by 256
  218. $error_code = (system "$tmpfile") / 256;
  219. unlink($tmpfile);
  220. return ($fix_failed_error) if ($error_code != 0);
  221. # sleep needed here?
  222. return &do_exist ($proc);
  223. }
  224. }
  225. sub do_check
  226. {
  227. local ($proc) = pop(@_);
  228. printf (STDERR "do_check\n") if ($debug > 0);
  229. if ($check{$proc} eq '')
  230. {
  231. $! = $fixproc_error;
  232. die "$0: internal error 2\n";
  233. }
  234. if ($check{$proc} ne 'exist')
  235. {
  236. # if not "exist", then it must be "shell", so execute the shell script
  237. # defined in database
  238. local ($tmpfh, $tmpfile) = tempfile("check_XXXXXXXX", DIR => "/tmp");
  239. &create_sh_script ($fix{$proc}, $tmpfh, $tmpfile);
  240. # return code is number divided by 256
  241. $error_code = (system "$tmpfile") / 256;
  242. unlink($tmpfile);
  243. return ($check_failed_error) if ($error_code != 0);
  244. # check passed, continue
  245. }
  246. return &do_exist ($proc);
  247. }
  248. sub do_exist
  249. {
  250. local ($proc) = pop(@_);
  251. printf (STDERR "do_exist\n") if ($debug > 0);
  252. # do ps, check to see if min <= no. of processes <= max
  253. $! = $fixproc_error;
  254. open (COMMAND, "/bin/ps -e | /bin/grep $proc | /bin/wc -l |")
  255. || die "$0: can't run ps-grep-wc command\n";
  256. $proc_count = <COMMAND>;
  257. if (($proc_count < $min{$proc}) || ($proc_count > $max{$proc}))
  258. {
  259. return $check_failed_error;
  260. }
  261. return $no_error;
  262. }
  263. sub do_kill
  264. {
  265. local ($proc) = pop(@_);
  266. local ($second_kill_needed);
  267. printf (STDERR "do_kill\n") if ($debug > 0);
  268. # first try kill
  269. $! = $fixproc_error;
  270. open (COMMAND, "/bin/ps -e | /bin/grep $proc |")
  271. || die "$0: can't run ps-grep-awk command\n";
  272. while (<COMMAND>)
  273. {
  274. # match the first field of ps -e
  275. $! = $fixproc_error;
  276. /^\s*(\d+)\s/ || die "$0: can't match ps -e output\n";
  277. system "kill $1";
  278. }
  279. # if process still exist, try kill -9
  280. sleep 2;
  281. $! = $fixproc_error;
  282. open (COMMAND, "/bin/ps -e | /bin/grep $proc |")
  283. || die "$0: can't run ps-grep-awk command\n";
  284. $second_kill_needed = 0;
  285. while (<COMMAND>)
  286. {
  287. # match the first field of ps -e
  288. $! = $fixproc_error;
  289. /^\s*(\d+)\s/ || die "$0: can't match ps -e output\n";
  290. system "kill -9 $1";
  291. $second_kill_needed = 1;
  292. }
  293. return ($no_error) if ($second_kill_needed == 0);
  294. # see if kill -9 worked
  295. sleep 2;
  296. $! = $fixproc_error;
  297. open (COMMAND, "/bin/ps -e | /bin/grep $proc |")
  298. || die "$0: can't run ps-grep-awk command\n";
  299. while (<COMMAND>)
  300. { # a process still exist, return error
  301. return $cannot_kill_error;
  302. }
  303. return $no_error; # good, all dead
  304. }
  305. sub do_restart
  306. {
  307. local ($proc) = pop(@_);
  308. local ($error_code);
  309. printf (STDERR "do_restart\n") if ($debug > 0);
  310. $error_code = &do_kill ($proc);
  311. return $error_code if ($error_code != $no_error);
  312. die "$0: internal error 3\n" if ($cmd{$proc} eq '');
  313. system "$cmd{$proc}";
  314. # sleep needed here?
  315. if ($check{$proc} ne 'null')
  316. {
  317. return $no_error if (&do_check($proc) == $no_error);
  318. return $cannot_restart_error;
  319. }
  320. }
  321. sub work_on_proc
  322. {
  323. local ($proc) = pop(@_);
  324. local ($error_code);
  325. printf (STDERR "work_on_proc\n") if ($debug > 0);
  326. if ($cmd_line_action eq '')
  327. {
  328. # perform action from database
  329. if ($check{$proc} ne 'null')
  330. {
  331. $error_code = &do_check ($proc);
  332. if ($error_code != $check_failed_error)
  333. {
  334. return $error_code;
  335. }
  336. }
  337. return &do_fix ($proc);
  338. }
  339. else
  340. {
  341. # perform action from command line
  342. $error_code = $no_error;
  343. if ($cmd_line_action eq 'kill')
  344. {
  345. $error_code = &do_kill ($proc);
  346. }
  347. elsif ($cmd_line_action eq 'restart')
  348. {
  349. $error_code = &do_restart ($proc);
  350. }
  351. elsif ($cmd_line_action eq 'fix')
  352. {
  353. $error_code = &do_fix ($proc);
  354. }
  355. elsif ($cmd_line_action eq 'check')
  356. {
  357. if ( $check{$proc} eq 'null' )
  358. {
  359. exit $fixproc_error;
  360. }
  361. $error_code = &do_check ($proc);
  362. }
  363. elsif ($cmd_line_action eq 'exist')
  364. {
  365. $error_code = &do_exist ($proc);
  366. }
  367. else
  368. {
  369. $! = $fixproc_error;
  370. die "$0: internal error 1\n";
  371. }
  372. }
  373. }
  374. sub dump_database
  375. {
  376. local ($name);
  377. for $name (keys(%cmd))
  378. {
  379. printf ("name\t%s\n", $name);
  380. printf ("cmd\t%s\n", $cmd{$name});
  381. printf ("min\t%s\n", $min{$name});
  382. printf ("max\t%s\n", $max{$name});
  383. if ( $check{$name} =~ /[0-9]+/ )
  384. {
  385. printf ("check\tshell\n");
  386. $i = $check{$name};
  387. while ( $shell_lines[$i] ne $shell_end_marker )
  388. {
  389. printf ("%s", $shell_lines[$i]);
  390. $i++;
  391. }
  392. }
  393. else
  394. {
  395. printf ("check\t%s\n", $check{$name});
  396. }
  397. if ( $fix{$name} =~ /[0-9]+/ )
  398. {
  399. printf ("fix\tshell\n");
  400. $i = $fix{$name};
  401. while ( $shell_lines[$i] ne $shell_end_marker )
  402. {
  403. printf ("%s", $shell_lines[$i]);
  404. $i++;
  405. }
  406. }
  407. else
  408. {
  409. printf ("fix\t%s\n", $fix{$name});
  410. }
  411. printf ("\n");
  412. }
  413. }
  414. sub read_database
  415. {
  416. local ($in_check_shell_lines) = 0;
  417. local ($in_fix_shell_lines) = 0;
  418. local ($name) = '';
  419. local ($str1);
  420. local ($str2);
  421. $! = $fixproc_error;
  422. open (DB, $database_file) || die 'cannot open database file $database_file\n';
  423. while (<DB>)
  424. {
  425. if ((! /\S/) || (/^[ \t]*#.*$/))
  426. {
  427. # ignore blank lines or lines beginning with "#"
  428. }
  429. elsif ($in_check_shell_lines)
  430. {
  431. if ( /^\s*end_shell\s*$/ )
  432. {
  433. $in_check_shell_lines = 0;
  434. push (@shell_lines, $shell_end_marker);
  435. }
  436. else
  437. {
  438. push (@shell_lines, $_);
  439. }
  440. }
  441. elsif ($in_fix_shell_lines)
  442. {
  443. if ( /^\s*end_shell\s*$/ )
  444. {
  445. $in_fix_shell_lines = 0;
  446. push (@shell_lines, $shell_end_marker);
  447. }
  448. else
  449. {
  450. push (@shell_lines, $_);
  451. }
  452. }
  453. else
  454. {
  455. if ( ! /^\s*(\S+)\s+(\S.*)\s*$/ )
  456. {
  457. $! = $fixproc_error;
  458. die "$0: syntax error in database\n$_";
  459. }
  460. $str1 = $1;
  461. $str2 = $2;
  462. if ($str1 eq 'name')
  463. {
  464. &finish_db_entry($name);
  465. $name = $str2;
  466. }
  467. elsif ($str1 eq 'cmd')
  468. {
  469. $! = $fixproc_error;
  470. die "$0: cmd specified before name in database\n$_\n"
  471. if ($name eq '');
  472. die "$0: cmd specified multiple times for $name in database\n"
  473. if ($cmd{$name} ne '');
  474. $cmd{$name} = $str2;
  475. }
  476. elsif ($str1 eq 'min')
  477. {
  478. $! = $fixproc_error;
  479. die "$0: min specified before name in database\n$_\n"
  480. if ($name eq '');
  481. die "$0: min specified multiple times in database\n$_\n"
  482. if ($min{$name} ne '');
  483. die "$0: non-numeric min value in database\n$_\n"
  484. if ( ! ($str2 =~ /[0-9]+/ ));
  485. $min{$name} = $str2;
  486. }
  487. elsif ($str1 eq 'max')
  488. {
  489. $! = $fixproc_error;
  490. die "$0: max specified before name in database\n$_\n"
  491. if ($name eq '');
  492. die "$0: max specified multiple times in database\n$_\n"
  493. if ($max{$name} ne '');
  494. die "$0: non-numeric max value in database\n$_\n"
  495. if ( ! ($str2 =~ /[0-9]+/ ));
  496. $max{$name} = $str2;
  497. }
  498. elsif ($str1 eq 'check')
  499. {
  500. $! = $fixproc_error;
  501. die "$0: check specified before name in database\n$_\n"
  502. if ($name eq '');
  503. die "$0: check specified multiple times in database\n$_\n"
  504. if ($check{$name} ne '');
  505. if ( $str2 eq 'shell' )
  506. {
  507. # if $check{$name} is a number, it is a pointer into
  508. # $shell_lines[] where the shell commands are kept
  509. $shell_lines[$#shell_lines+1] = $shell_header;
  510. $check{$name} = $#shell_lines;
  511. $in_check_shell_lines = 1;
  512. }
  513. else
  514. {
  515. $check{$name} = $str2;
  516. }
  517. }
  518. elsif ($str1 eq 'fix')
  519. {
  520. $! = $fixproc_error;
  521. die "$0: fix specified before name in database\n$_\n"
  522. if ($name eq '');
  523. die "$0: fix specified multiple times in database\n$_\n"
  524. if ($fix{$name} ne '');
  525. if ( $str2 eq 'shell' )
  526. {
  527. # if $fix{$name} is a number, it is a pointer into
  528. # $shell_lines[] where the shell commands are kept
  529. $shell_lines[$#shell_lines+1] = $shell_header;
  530. $fix{$name} = $#shell_lines;
  531. $in_fix_shell_lines = 1;
  532. }
  533. else
  534. {
  535. $fix{$name} = $str2;
  536. }
  537. }
  538. }
  539. }
  540. &finish_db_entry($name);
  541. }
  542. sub finish_db_entry
  543. {
  544. local ($name) = pop(@_);
  545. if ($name ne '')
  546. {
  547. $! = $fixproc_error;
  548. die "$0: fix not defined for $name in database\n"
  549. if ($fix{$name} eq '');
  550. die "$0: cmd not defined for $name in database\n"
  551. if ($cmd{$name} eq '');
  552. $check{$name} = 'exist' if ($check{$name} eq '');
  553. $max{$name} = 1 if ($max{$name} eq '');
  554. $min{$name} = 1 if ($min{$name} eq '');
  555. }
  556. }
  557. sub read_args
  558. {
  559. local ($i) = 0;
  560. local ($arg);
  561. local ($action_arg_count) = 0;
  562. while ( $i <= $#ARGV )
  563. {
  564. $arg = $ARGV[$i];
  565. if (($arg eq '-min') || ($arg eq '-max'))
  566. {
  567. if (($i == $#ARGV - 1) || ($ARGV[$i+1] =~ /\D/)) # \D is non-numeric
  568. {
  569. $! = $fixproc_error;
  570. die "$0: numeric arg missing after -min or -max\n";
  571. }
  572. if ($arg eq '-min')
  573. {
  574. $min = $ARGV[$i+1];
  575. }
  576. else
  577. {
  578. $max = $ARGV[$i+1];
  579. }
  580. $i += 2;
  581. }
  582. elsif ($arg eq '-kill')
  583. {
  584. $cmd_line_action = 'kill';
  585. $action_arg_count++;
  586. $i++;
  587. }
  588. elsif ($arg eq '-check')
  589. {
  590. $cmd_line_action = 'check';
  591. $action_arg_count++;
  592. $i++;
  593. }
  594. elsif ($arg eq '-restart')
  595. {
  596. $cmd_line_action = 'restart';
  597. $action_arg_count++;
  598. $i++;
  599. }
  600. elsif ($arg eq '-exist')
  601. {
  602. $cmd_line_action = 'exist';
  603. $action_arg_count++;
  604. $i++;
  605. }
  606. elsif ($arg eq '-fix')
  607. {
  608. $cmd_line_action = 'fix';
  609. $action_arg_count++;
  610. $i++;
  611. }
  612. elsif ($arg =~ /-d(\d)$/)
  613. {
  614. $debug = $1;
  615. $i++;
  616. }
  617. elsif ($arg =~ /^-/)
  618. {
  619. $! = $fixproc_error;
  620. die "$0: unknown switch $arg\n";
  621. }
  622. else
  623. {
  624. push (@proc_list, $arg);
  625. $i++;
  626. }
  627. }
  628. $! = $fixproc_error;
  629. die "$0: no process specified\n" if ($#proc_list == -1);
  630. die "$0: more than one action specified\n" if ($action_arg_count > 1);
  631. }