Publi

Conociendo el proceso que me ha enviado una señal (signal)

Hace tiempo hablábamos de capturar señales, aunque en ocasiones, es necesario saber quién me envía esa señal, si por ejemplo nos envían un SIGINT o SIGTERM, tal vez queremos saber qué proceso nos quiere muerto y qué usuario lo ha invocado. O tal vez estamos esperando una señal de control (SIGUSR1, por ejemplo) por parte de un proceso cliente específico.

El problema es que por nuestro modo actual de direccionar señales (con signal(señal, funcion)) sólo comunicamos el número de señal que se ha recibido, sin más información.

signals_from

Para capturar esta información necesitamos utilizar sigaction para establecer la función a la que llamaremos cuando llegue la señal, para utilizarla podemos utilizar el siguiente código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>

void recibe_signal(int signum, siginfo_t *siginfo, void *context)
{
  /* Mostramos toda la información necesaria */
  printf ("[%d] Recibida %d (%s) de %d (UID: %d) ¿Error? %d \n",
      getpid(),
      signum,
      strsignal(signum),
      siginfo->si_pid,
      siginfo->si_uid,
      siginfo->si_errno);
}


int main(int argc, char *argv[])
{
    struct sigaction sact;
   
    sigfillset(&sact.sa_mask);
    /* Definimos la función callback */
    sact.sa_sigaction = recibe_signal;
    /* Queremos usar el callback sigaction en lugar del dado por signal() */
    sact.sa_flags     = SA_SIGINFO;

    sigaction(SIGUSR1, &sact, NULL);

    /* Bucle infinito, para cerrar el proceso, utilizar kill -9 PID */
    while (1)
      pause;

    return 0;
}

Como vemos sigaction tiene 3 parámetros:

  • La señal que queremos capturar
  • Un registro de tipo struct sigaction
  • La acción que se ejecutaba antes, por si queremos guardarla para restaurarla más tarde

Dentro del registro, tendremos como valores importantes (struct sigaction act):

  • sact.sa_sigaction : que especifica la función que la llamaremos, esta función será del tipo void funcion(int, siginfo_t*, void*), donde el primer int recibirá la señal que nos han enviado (tal y como hacía la función que asignábamos con signal), el segundo parámetro será información asociada a la señal, y la tercera el contexto de ejecución.
  • sact.sa_handler : será la función a la que se llama, es la misma que cuando hacemos signal(señal, funcion)
  • sact.sa_flags : Es un conjunto de flags, si especificamos SA_SIGINFO, estaremos dando paso a la función especificada por sa_sigaction, si no utilizaremos sa_handler. Aunque esta variable vale para más cosas, para esta práctica nos vale con saber esto. (man sigaction para más info)
  • sact.sa_mask
  • : Nos proporciona una máscara de las señales que serán bloquedas, podemos utilizar sigfillset() para rellenar toda la máscara automáticamente. Si queremos que unas se bloqueen y otras no podemos utilizar sigaddset() y sigdelset()

Ahora bien, la función que se llamará cuando venga una señal tendrá el prototipo que hemos visto antes con tres parámetros: la señal recibida, la información de la señal con un puntero a siginfo_t y el contexto de ejecución, que se pasará con un puntero a void, que por ahora no utilizaremos. La estructura que controla la información de la señal entre otros muchos datos nos facilitará lo siguiente:

  • si_pid : PID que nos envía la señal (y esta acción da título al post)
  • si_uid : Usuario que ejecuta el proceso que nos manda la señal
  • si_errno : Algún error que haya causado la señal
  • … para más información, man sigaction

Para preparar la sentencia tendremos que escribir varias líneas definiendo los valores del registro así como la llamada a la función sigaction(), por lo tanto podremos crear una función que tenga los parámetros más comunes a la hora de definir este tipo de acciones, así lo llamamos como a signal():

1
2
3
4
5
6
7
8
9
10
11
12
int sigact(int signum, void funcion(int, siginfo_t *, void *))
{
    struct sigaction sact;
   
    sigfillset(&sact.sa_mask);
    /* Definimos la función callback */
    sact.sa_sigaction = funcion;
    /* Queremos usar el callback sigaction en lugar del dado por signal() */
    sact.sa_flags     = SA_SIGINFO;

    return sigaction(signum, &sact, NULL);
}

En esta función es importante poner bien el prototipo de la función como parámetro, eso lo comenté en un post anterior sobre callbacks

Ahora, vamos a dejar el código más bonito, este programa implementará el control de errores y nos permitirá salir cuando recibamos 5 SIGINT (pulsemos 5 veces control+C):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>

void error(char *cadena);
int sigact(int signum, void funcion(int, siginfo_t *, void *));
void llegasigint();
void recibe_signal(int signum, siginfo_t *siginfo, void *context);

int main(int argc, char *argv[])
{
  if (sigact(SIGUSR1, recibe_signal)==-1)
    error("No puedo establecer SIGUSR1");

  if (sigact(SIGINT, recibe_signal)==-1)
    error("No puedo establecer SIGINT");
  if (sigact(SIGTERM, recibe_signal)==-1)
    error("No puedo establecer SIGTERM");
  if (sigact(SIGUSR2, recibe_signal)==-1)
    error("No puedo establecer SIGUSR2");
  if (sigact(SIGQUIT, recibe_signal)==-1)
    error("No puedo establecer SIGQUIT");

    /* Bucle infinito, para cerrar el proceso, utilizar kill -9 PID */
    while (1)
      pause();

    return 0;
}

void error(char *cadena)
{
  fprintf(stderr, "ERROR: %s (errno: %d: %s)\n", cadena, errno, strerror(errno));
  exit(1);
}

void llegasigint()
{
  static int num=0;     /* No he querido usar variables globales */

  num++;
  if (num==5)
    {
      printf ("FIN del programa\n");
      exit(1);
    }
}

void recibe_signal(int signum, siginfo_t *siginfo, void *context)
{
  /* Mostramos toda la información necesaria */
  printf ("[%d] Recibida %d (%s) de %d (UID: %d) ¿Error? %d \n",
      getpid(),     /* PID del proceso actual */
      signum,       /* Número de señal recibida */
      strsignal(signum),    /* Señal escrita en texto (inglés) */
      siginfo->si_pid,  /* PID del proceso que nos manda la señal */
      siginfo->si_uid,  /* Usuario que nos manda la señal */
      siginfo->si_errno);   /* Error producido */

  if (signum==SIGINT)
    llegasigint();
}

int sigact(int signum, void funcion(int, siginfo_t *, void *))
{
    struct sigaction sact;
   
    sigfillset(&sact.sa_mask);
    /* Definimos la función callback */
    sact.sa_sigaction = funcion;
    /* Queremos usar el callback sigaction en lugar del dado por signal() */
    sact.sa_flags     = SA_SIGINFO;

    return sigaction(signum, &sact, NULL);
}

También podría interesarte...

Only 1 comment left Ir a comentario

  1. Pingback: Bitacoras.com /

Leave a Reply