Cómo usar señales de Linux en scripts Bash

El kernel de Linux envía señales a los procesos sobre los eventos a los que deben reaccionar. Los scripts que se comportan bien manejan las señales con elegancia y solidez y pueden limpiarse por sí solos incluso si presiona Ctrl+C. Así es cómo.

Señales y Procesos

Las señales son mensajes breves, rápidos y unidireccionales que se envían a procesos como scripts, programas y demonios. Le informan al proceso sobre algo que ha sucedido. Es posible que el usuario haya presionado Ctrl+C o que la aplicación haya intentado escribir en la memoria a la que no tiene acceso.

Si el autor del proceso anticipó que se le podría enviar una determinada señal, puede escribir una rutina en el programa o script para manejar esa señal. Tal rutina se llama manejador de señales. Atrapa o atrapa la señal y realiza alguna acción en respuesta a ella.

Linux usa muchas señales, como veremos, pero desde el punto de vista de las secuencias de comandos, solo hay un pequeño subconjunto de señales que probablemente le interesen. En particular, en las secuencias de comandos no triviales, las señales que indican la la secuencia de comandos para apagar debe quedar atrapada (siempre que sea posible) y se debe realizar un apagado correcto.

Para examplelos scripts que crean archivos temporales o abren puertos de cortafuegos pueden tener la oportunidad de eliminar los archivos temporales o close los puertos antes de que se cierren. Si el script simplemente muere en el instante en que recibe la señal, su computadora puede quedar en un estado impredecible.

Así es como puede manejar las señales en sus propios scripts.

Conoce las Señales

Algunos comandos de Linux tienen nombres crípticos. No así el mando que atrapa señales. Se llama trap . También podemos usar trap con el -l (lista) opción para mostrarnos la lista completa de señales que utiliza Linux .

                      trap -l
                    

Aunque nuestra lista numerada termina en 64, en realidad hay 62 señales. Faltan las señales 32 y 33. Ellos son no implementado en Linux . Han sido reemplazados por la funcionalidad en el gcc compilador para manejar hilos en tiempo real. Todo desde la señal 34, SIGRTMIN para señalar 64, SIGRTMAX son señales en tiempo real.

Verá diferentes listas en diferentes sistemas operativos similares a Unix. En AbiertoIndiana por examplelas señales 32 y 33 están presentes, junto con un montón de señales adicionales que elevan el total a 73.

Listado de señales en OpenIndiana con trap -l

Las señales se pueden referenciar por nombre, número o por su nombre abreviado. Su nombre abreviado es simplemente su nombre sin el “SIG” inicial.

Las señales se emiten por muchas razones diferentes. Si puedes descifrarlos, su propósito está contenido en su nombre. El impacto de una señal cae en una de las siguientes categorías:

  • Terminar: El proceso se da por terminado.
  • Pasar por alto: La señal no afecta el proceso. Esta es una señal de solo información.
  • Centro: Se crea un archivo de núcleo de volcado. Esto generalmente se hace porque el proceso ha transgredido de alguna manera, como una violación de la memoria.
  • Deténgase: El proceso se detiene. Es decir, está en pausa, no terminado.
  • Continuar: Le dice a un proceso detenido que continúe la ejecución.

Estas son las señales que encontrará con más frecuencia.

  • SUSCRÍBETE : Signal 1. La conexión a un host remoto, como un servidor SSH, se interrumpió inesperadamente o el usuario cerró la sesión. Una secuencia de comandos que recibe esta señal puede terminar correctamente o puede optar por intentar volver a conectarse al host remoto.
  • SEGUIR : Signal 2. El usuario ha presionado la combinación Ctrl+C para forzar la finalización de un proceso. closeo el kill El comando se ha utilizado con la señal 2. Técnicamente, esta es una señal de interrupción, no una señal de terminación, pero un script interrumpido sin un controlador de señal generalmente terminará.
  • SIGQUITAR : Signal 3. El usuario ha presionado la combinación Ctrl+D para forzar el cierre de un proceso, o el kill El comando se ha utilizado con la señal 3.
  • SIGFPE : Signal 8. El proceso intentó realizar una operación matemática ilegal (imposible), como la división por cero.
  • sigilo : Signal 9. Esta es la señal equivalente a una guillotina. No puedes atraparlo o ignorarlo, y sucede instantáneamente. El proceso se termina inmediatamente.
  • SIGTERM : Signal 15. Esta es la versión más considerada de SIGKILL . SIGTERM también le dice a un proceso que finalice, pero puede quedar atrapado y el proceso puede ejecutar sus procesos de limpieza antes de cerrarse. Esto permite un apagado elegante. Esta es la señal predeterminada emitida por el kill dominio.

Señales en la línea de comando

Una forma de atrapar una señal es usar trap con el número o nombre de la señal, y una respuesta que quieres que suceda si se recibe la señal. Podemos demostrar esto en una ventana de terminal.

Este comando atrapa el SIGINT señal. La respuesta es imprimir una línea de texto en la ventana del terminal. estamos usando el -e (habilitar escapes) opción con echo entonces podemos usar el “ n ” especificador de formato.

                      trap 'echo -e "+c Detected."' SIGINT
                    

Trapping Ctrl+C en la línea de comando

Nuestra línea de texto se imprime cada vez que pulsamos la combinación Ctrl+C.

Para ver si hay una trampa en una señal, use el -p (imprimir trampa) opción.

                      trap -p SIGINT
                    

Comprobación de si hay una trampa en una señal

Usando trap sin opciones hace lo mismo.

Para restablecer la señal a su estado normal sin atrapar, use un guión “ - ” y el nombre de la señal atrapada.

                      trap - SIGINT
                    
                      trap -p SIGINT
                    

Eliminar una trampa de una señal

Sin salida del trap -p El comando indica que no hay una trampa configurada en esa señal.

Señales de reventado en secuencias de comandos

Podemos usar el mismo formato general trap comando dentro de un script. Este script atrapa tres señales diferentes, SIGINT , SIGQUIT y SIGTERM .

                      #!/bin/bash

trap "echo I was SIGINT terminated; exit" SIGINT
trap "echo I was SIGQUIT terminated; exit" SIGQUIT
trap "echo I was SIGTERM terminated; exit" SIGTERM

echo $$
counter=0

while true
do 
  echo "Loop number:" $((++counter))
  sleep 1
done
                    

El tres trap Las declaraciones están en la parte superior del script. Tenga en cuenta que hemos incluido el exit comando dentro de la respuesta a cada una de las señales. Esto significa que el script reacciona a la señal y luego sale.

Copie el texto en su editor y guárdelo en un archivo llamado “simple-loop.sh”, y hágalo ejecutable usando el chmod dominio. Deberá hacer eso con todos los scripts de este artículo si desea seguirlos en su propia computadora. Simplemente use el nombre del script apropiado en cada caso.

                      chmod +x simple-loop.sh
                    

Haciendo un script ejecutable con chmod

El resto del guión es muy simple. Necesitamos saber el ID de proceso del script, por lo que el script nos lo hace eco. los $$ La variable contiene el ID de proceso del script.

Creamos una variable llamada counter y ponerlo a cero.

los while el bucle se ejecutará para siempre a menos que se detenga a la fuerza. Se incrementa el counter variable, lo repite en la pantalla y duerme por un segundo.

Ejecutemos el script y enviemos diferentes señales.

                      ./simple-loop.sh
                    

Un script que lo identifica ha sido terminado con Ctrl+C

Cuando presionamos “Ctrl + C”, nuestro mensaje se imprime en la ventana del terminal y el script finaliza.

Ejecutémoslo de nuevo y enviemos el SIGQUIT señal usando el kill dominio. Tendremos que hacerlo desde otra ventana de terminal. Tendrá que usar el ID de proceso informado por su propia secuencia de comandos.

                      ./simple-loop.sh
                    
                      kill -SIGQUIT 4575
                    

Un script que lo identifica ha sido terminado con SIGQUIT

Como se esperaba, el script informa que la señal llega y luego termina. Y finalmente, para probar el punto, lo haremos de nuevo con el SIGTERM señal.

                      ./simple-loop.sh
                    
                      kill -SIGTERM 4584
                    

Un script que lo identifica ha sido terminado con SIGTERM

Hemos verificado que podemos atrapar múltiples señales en un script y reaccionar a cada una de ellas de forma independiente. El paso que promueve todo esto de interesante a útil es agregar controladores de señal.

Manejo de señales en scripts

Podemos reemplazar la cadena de respuesta con el nombre de una función en su script. los trap el comando luego llama a esa función cuando se detecta la señal.

Copie este texto en un editor y guárdelo como un archivo llamado “grace.sh”, y hágalo ejecutable con chmod .

                      #!/bin/bash

trap graceful_shutdown SIGINT SIGQUIT SIGTERM

graceful_shutdown()
{
  echo -e "nRemoving temporary file:" $temp_file
  rm -rf "$temp_file"
  exit
}

temp_file=$(mktemp -p /tmp tmp.XXXXXXXXXX)
echo "Created temp file:" $temp_file

counter=0

while true
do 
  echo "Loop number:" $((++counter))
  sleep 1
done
                    

El guión tiende una trampa para tres señales diferentes: SIGHUP , SIGINT y SIGTERM —usando un solo trap declaración. La respuesta es el nombre del graceful_shutdown() función. La función se llama cada vez que se recibe una de las tres señales atrapadas.

El script crea un archivo temporal en el directorio “/tmp”, usando mktemp . La plantilla de nombre de archivo es “tmp.XXXXXXXXXX”, por lo que el nombre del archivo será “tmp”. seguido de diez caracteres alfanuméricos aleatorios. El nombre del archivo se repite en la pantalla.

El resto del guión es igual al anterior, con un counter variable y un infinito while círculo.

                      ./grace.sh
                    

Una secuencia de comandos que realiza un cierre correcto al eliminar un archivo temporal

Cuando se envía al archivo una señal que hace que se closela graceful_shutdown() se llama la función. Esto elimina nuestro único archivo temporal. En una situación del mundo real, podría realizar cualquier limpieza que requiera su secuencia de comandos.

Además, agrupamos todas nuestras señales atrapadas y las manejamos con una sola función. Puede atrapar señales individualmente y enviarlas a sus propias funciones de controlador dedicadas.

Copie este texto y guárdelo en un archivo llamado “triple.sh”, y hágalo ejecutable usando el chmod dominio.

                      #!/bin/bash

trap sigint_handler SIGINT
trap sigusr1_handler SIGUSR1
trap exit_handler EXIT

function sigint_handler() {
  ((++sigint_count))

  echo -e "nSIGINT received $sigint_count time(s)."

  if [[ "$sigint_count" -eq 3 ]]; then
    echo "Starting close-down."
    loop_flag=1
  fi
}

function sigusr1_handler() {
  echo "SIGUSR1 sent and received $((++sigusr1_count)) time(s)."
}

function exit_handler() { 
  echo "Exit handler: Script is closing down..."
}

echo $$
sigusr1_count=0
sigint_count=0
loop_flag=0

while [[ $loop_flag -eq 0 ]]; do
  kill -SIGUSR1 $$
  sleep 1
done
                    

Definimos tres trampas en la parte superior del script.

  • una trampa SIGINT y tiene un controlador llamado sigint_handler() .
  • El segundo atrapa una señal llamada SIGUSR1 y utiliza un controlador llamado sigusr1_handler() .
  • La trampa número tres atrapa al EXIT señal. Esta señal la emite el propio script cuando se cierra. Configuración de un controlador de señal para EXIT significa que puede configurar una función que siempre se llamará cuando finalice el script (a menos que se elimine con la señal SIGKILL ). Nuestro controlador se llama exit_handler() .

SIGUSR1 y SIGUSR2 son señales proporcionadas para que pueda enviar señales personalizadas a sus scripts. La forma en que los interprete y reaccione ante ellos depende totalmente de usted.

Dejando a un lado los controladores de señales por ahora, el cuerpo del script debería resultarle familiar. Reproduce el ID del proceso en la ventana del terminal y crea algunas variables. Variable sigusr1_count registra el número de veces SIGUSR1 fue manipulado y sigint_count registra el número de veces SIGINT fue manejado. los loop_flag variable se pone a cero.

los while bucle no es un bucle infinito. Dejará de repetirse si el loop_flag variable se establece en cualquier valor distinto de cero. Cada giro de la while usos del bucle kill para enviar el SIGUSR1 señal a este script, enviándolo al ID de proceso del script. ¡Los scripts pueden enviarse señales a sí mismos!

los sigusr1_handler() La función incrementa el sigusr1_count variable y envía un mensaje a la ventana del terminal.

Cada vez que el SIGINT se recibe la señal, el siguint_handler() La función incrementa el sigint_count variable y hace eco de su valor en la ventana de terminal.

Si el sigint_count variable es igual a tres, la loop_flag la variable se establece en uno y se envía un mensaje a la ventana de la terminal que le informa al usuario que se ha iniciado el proceso de apagado.

Porque loop_flag ya no es igual a cero, el while el ciclo termina y el script finaliza. Pero esa acción eleva automáticamente la EXIT señal y la exit_handler() se llama la función.

                      ./triple.sh
                    

Un script que usa SIGUSR1, que requiere tres combinaciones de Ctrl+C para closey captar la señal de SALIDA al apagar

Después de tres pulsaciones de Ctrl+C, el script finaliza e invoca automáticamente el exit_handler() función.

Leer las señales

Al atrapar señales y manejarlas en funciones de controlador sencillas, puede hacer que sus scripts de Bash se ordenen detrás de sí mismos, incluso si se terminan inesperadamente. Eso le da un sistema de archivos más limpio. También evita la inestabilidad la próxima vez que ejecute la secuencia de comandos y, según cuál sea el propósito de la secuencia de comandos, incluso podría evitar brechas de seguridad.

Cómo auditar la seguridad de su sistema Linux con Lynis

Related Posts