Archivo

Entradas Etiquetadas ‘captura’

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

Jueves, 24 de Noviembre de 2011 Gaspar Fernández Sin comentarios

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);
}

Jugando con ImageMagick (I): Dimensiones, captura, color y efectos

Jueves, 26 de Agosto de 2010 Gaspar Fernández Sin comentarios

A menudo es necesario hacer una manipulación básica de imágenes y da mucha pereza ejecutar GIMP u otro editor de imágenes para una tontería; e incluso a veces necesitamos modificar gran cantidad de imágenes y necesitamos automatizar el proceso:

Ejecución

Aunque ImageMagick, que seguro que lo encontráis en los repositorios de vuestra distribución favorita, da mucho más de sí, aquí veremos ejemplos con convert, import y mogrify

Redimensionado de imágenes

hamburgueson_2$ convert -resize [dimensión/porcentaje] origen destino

Por ejemplo para redimensionar con un ancho de 320 pixels:

$ convert -resize 320 hamburgueson.jpg hamburgueson_mini.jpg

De esta forma, si especificamos la altura, se ignorará, ya que convert intentará mantener el aspecto de la imagen.

También podemos imponer la altura y que se calcule automáticamente el ancho:

$ convert -resize x500 hamburgueson.jpg hamburgueson_mini.jpg

O si queremos, podemos también redimensionar con un porcentaje del tamaño original:

$ convert -resize 40% hamburgueson.jpg hamburgueson_mini.jpg

hamburgueson_21También es posible la redimensión sin mantener el aspecto (relación ancho/alto) de la siguiente forma:

$ convert -resize \!600×300 hamburgueson.jpg hamburgueson_deforme.jpg

Y esto mismo, también por porcentaje:

$ convert -resize \!100×30% hamburgueson.jpg hamburgueson_deforme.jpg

Capturando el escritorio

Puede que nuestro entorno preferido de escritorio no venga con esta funcionalidad, o incluso que estemos desde una máquina remota y queramos ver lo que pasa en el escritorio, una captura de pantalla no viene mal. Para ello podemos hacer lo siguiente:

screenshot-26-08-2010-100842

$ import -window root captura.jpg

O si queremos capturar una ventana en especial:

$ import ventana.jpg

Nos aparecerá un puntero especial que nos permitirá seleccionar la ventana a capturar.

O si queremos capturar una ventana en especial sin necesidad de hacer click (muy útil si estamos en un equipo remoto):

$ import -window “Titulo de la ventana” ventana.jpg

También podemos sacar el ID de la ventana con:

$ wmctrl -l

Y poner en lugar del título, el ID. Es necesario que nos encontremos en el mismo escritorio de la ventana para hacer la captura. Cómo podemos hacer eso remotamente ?

$ wmctrl -l # Nos dirá en la segunda columna el escritorio donde está la ventana

$ wmctrl -s [numero] # Saltaremos al escritorio [numero], el primero es el cero.

Espacios de color

Podemos cambiar el formato del color de forma rápida usando colorspaces, dependiendo del formato de salida y la forma en la que la imagen será utilizada, para vídeo, por ejemplo. Aunque también tenemos algunos espacios interesantes como:hamburgueson_3

$ convert -colorspace gray hamburgueson.jpg hamburgueson_gris.jpg

El tono de gris estará calculado para adecuarse al ojo humano: Gris = 0.299*Rojo+0.587*Verde+0.114*Azul

Aunque también podremos transformar a YUV, XYZ, LAB, HSL, etc; también necesitamos que el formato de salida sea compatible con esos espacios de color. Para ver una lista de todos los colorspaces disponibles:

$ covert -list colorspace

hamburgueson_31También podemos ecualizar la imagen con respecto a los diferentes espacios de color de la siguiente forma:

$ convert -colorspace [espacio] -equalize origen destino

En el ejemplo:

$ convert -colorspace hsl -equalice hamburgueson.jpg hamburgueson_hsl.jpg

Y si no queremos decir el colorspace, usaremos el actual de la imagen:

$ convert -equalize hamburgueson.jpg hamburgueson_e.jpg

Recortar imágenes

Es más cómodo hacerlo con el ratón, pero en ocasiones querremos hamburgueson_4automatizar el proceso, y podemos hacerlo de la siguiente forma:

$ convert -crop [ancho]x[alto]+[x]+[y]

Por ejemplo lo siguiente:

$ convert -crop 300×300+100+100

Creará una imagen de 300×300 y empezará a partir del punto 100×100 (desde la izquierda/arriba).

Bordes

hamburgueson_41Para añadir un borde podemos hacer lo siguiente:

$ convert -bordercolor [color] -border [ancho]x[alto] origen destino

En el ejemplo tenemos:

$ convert -bordercolor black -border 10×10 hamburgueson.jpg hamburgueson_borde.jpg

Para especificar el color podemos hacerlo de varias formas:

$ convert -bordercolor black/red/green/blue… # Por su nombre. Si queremos saber los nombres de color disponibles:

$ convert -list color

$ convert -bordercolor “#aacc11″ # Por su equivalente hexadecimal

$ convert -bordercolor “rgb(100,150,200)” # En función de sus valores RGB

$ convert -bordercolor “hsl(100,150,200)” # En función de sus valores HSL

$convert -bordercolor “cmyk(100,200,150,230)” # En función de sus valores CMYK

… etcétera… tantos formatos como espacios de color.

Ahora bien, queremos quitar el reborde añadido:

$ convert -trim hamburgueson_borde.jpg hamburgueson_nuevo.jpg

Pero es posible que por la compresión jpeg no se detecte bien el borde (es una compresión con pérdida de calidad, y se modifica la imagen; para nosotros el borde es negro, pero para el ordenador hay muchos tonos de negro), entonces podemos hacer lo siguiente:

$ convert -fuzz 10% -trim hamburgueson_borde.jpg hamburgueson_nuevo.jpg

Con esto damos una tolerancia de un 10% al color, es decir, si se parecen los tonos de negro en menos de un 10% serán considerados iguales.

hamburgueson_42También podemos añadir un biselado como el del ejemplo de la siguiente manera:

$ convert -mattecolor red -frame 20×20+10+10 hamburgueson.jpg hamburgueson_rojo.jpg

Filtros y efectos

hamburgueson_32Por si fuera poco, podemos aplicar los filtros típicos a las imágenes como blur, motion-blur, blur gaussiano (gaussian-blur), ruido (noise), carboncillo (charcoal).

En el ejemplo, se ha añadido motion-blur de la siguiente forma:

$ convert -motion-blur 10×40+10+0 hamburgueson.jpg hamburgueson_movido.jpg

Visita otras webs de la red