Echando código: ¿Ejecución de programas de linea de comandos más inteligente?

Vamos a ver el siguiente problema: Usted rutinariamente ejecuta cierto programa en su sistema, el cual corre por una cantidad ilimitada de tiempo (como un demonio en UNIX); Rato más tarde se decide a matar el proceso y debe entonces empezar a buscar en que máquina lo corrio y cual es el PID (program ID) de el proceso, para luego matarlo con kill. ¿Suena ladilla verdad? (sobre todo si lo hace todos los dias).

Seria genial tener un programa que nos permita ejecutar un comando (sin importar en donde estemos parados en el sistema de archivos) y si necesitamos matarlo entonces sólo llamamos a otro script con el nombre de el programa anterior. La idea es mantener el rastro de el primer programa (donde se ejecutó y el PID) y para matarlo poder utilizar esa información. En el caso de demonios normalmente ejecutamos una instancia (y es esa la que tiene hijos), así que nuestra solución se va a valer de eso.

La solución a el problema se reduce a dos scripts:

  • Uno para correr los programas, este mantendrá la máquina en donde lo ejecutamos y el PID de el programa que queremos correr.
  • Un script que sabe que hizo el script que lanzó el programa anterior, y con esa información lo mata

Esto se puede hacer en cualquier script languaje, pero veamos que tan lejos podemos llegar con Perl 🙂

Supongamos que nuestro «demonio» es solamente un programita que no hace nada, excepto contar:

   1:#!/bin/bash
2:declare -ri MAX=999999
3:for ((i=0; i <= MAX; i++))
4:do
5: printf "Counter: %i\n" $i
6: sleep 10
7:done

El programa que lo arranca:

   1:#!/usr/bin/perl -w
2:
3:use strict;
4:use File::Basename;
5:use Sys::Hostname;
6:
7:if (scalar(@ARGV) <= 0) {
8: die "Provide the full path of the program to be executed!";
9:}
10:
11:use constant LOCKDIR => "/tmp";
12:
13:my $currentHost = hostname;
14:
15:my $progName = basename($ARGV[0]);
16:printf "Executing '%s'\n", $progName;
17:my $lockfile = LOCKDIR . "/" . $progName . ".lock";
18:# There is a lockfile, but is the process really running?
19:if ( -f $lockfile ) {
20: my ($pid, $hostname, $prog);
21: open(LOCK, "$lockfile") || die "$@, $!";
22: while(<LOCK>) {
23: ($pid, $hostname, $prog) = split(";", $_);
24: }
25: close(LOCK);
26: my $cnt = kill 0, $pid;
27: if ($cnt != 0) {
28: die "Sorry, but the programm $progName is already running as the PID: $pid"
29: }
30:}
31:open(LOCK, "> $lockfile") || die "$@, $!";
32:print LOCK ("$$" . ";" . $currentHost . ";" . $progName);
33:close(LOCK);
34:my $prog = shift(@ARGV);
35:exec("$prog", @ARGV);
36:__END__
37:=head1 NAME
38:
39:Run a single instance of a UNIX process, leaving information behind
40:to ease its termination.
41:
42:=head1 DESCRIPTION
43:
44:Will run the desired command and will create a lock file. To kill the program,
45:either use the information on the lock file or call the smart_kill script
46:
47:To run a program:
48:
49:./smart_run.pl script.sh
50:
51:To run on the background:
52:
53:./smart_run.pl script.sh &
54:
55:=head1 AuTHOR
56:
57:Jose Vicente Nunez Zuleta
58:
59:=head1 BLOG
60:
61:KodeGeeK - http://kodegeek.com
62:
63:=head1 LICENSE
64:
65:LGPL
66:
67:=cut

Y el programa que lo mata:

   1:#!/usr/bin/perl
2:
3:use strict;
4:use File::Basename;
5:use Sys::Hostname;
6:
7:if (scalar(@ARGV) != 1) {
8: die "Provide the name of the program to be killed!";
9:}
10:
11:use constant LOCKDIR => "/tmp";
12:
13:my $currentHost = hostname;
14:
15:my $progName = $ARGV[0];
16:my $lockfile = LOCKDIR . "/" . $progName . ".lock";
17:# There is a lockfile, but is the process really running?
18:if ( -f $lockfile ) {
19: printf "Trying to kill '%s'\n", $progName;
20: my ($pid, $hostname, $prog);
21: open(LOCK, "$lockfile") || die "$@, $!";
22: while(<LOCK>) {
23: ($pid, $hostname, $prog) = split(";", $_);
24: }
25: close(LOCK);
26: my $cnt = kill 6, $pid;
27: if ($cnt != 0) {
28: printf "process %i killed\n", $pid;
29: unlink $lockfile;
30: } else {
31: printf "Unable to kill %i\n", $pid;
32: exit 192;
33: }
34:}
35:__END__
36:=head1 NAME
37:
38:Terminates a single instance of a UNIX process, if it was started by smart_run.pl
39:It will kill the process only if the current process name and host matches what is
40:inside the lock file (and also if you are the owner of the process).
41:
42:To kill a program:
43:
44:./smart_kill.pl script.sh
45:
46:(Note than the full path of the script is not required, just the name).
47:
48:=head1 AuTHOR
49:
50:Jose Vicente Nunez Zuleta
51:
52:=head1 BLOG
53:
54:KodeGeeK - http://kodegeek.com
55:
56:=head1 LICENSE
57:
58:LGPL
59:
60:=cut

Una salida de ejemplo de los dos programas en acción:

[josevnz@localhost perl]$ ./smart_run.pl /home/josevnz/do_nothing.sh &
[1] 7998
[josevnz@localhost perl]$ Executing ‘do_nothing.sh’
Counter: 0
[josevnz@localhost perl]$ ./smart_kill.pl do_nothing.sh
Trying to kill ‘do_nothing.sh’
process 7998 killed
Aborted
[josevnz@localhost perl]$

Me tomó 30 minutos hacer los dos scripts (usted es más inteligente, así que seguro le tomará menos). Los scripts no son perfectos y de hecho pudieramos pensar en tener uno sólo que haga las dos cosas en vez de dos.

¿Como los mejoraría usted?. Como siempre se puede bajar el código desde aquí.

Buscar en Technorati: