RasPi: creando scripts de arranque

Es posible que queramos que un determinado script o aplicación se ejecute en el arranque del sistema. Normalmente, cuando instalamos una aplicación mediante apt-get o un instalador, la propia aplicación genere un script de arranque si se trata de un servicio, como por ejemplo apache2 o samba. En otras ocasiones no es así, como por ejemplo si es un programa propio o un script que simplemente deseamos ejecutar en tiempo de arranque, para que realice alguna tarea de mantenimiento.

Para que un script se ejecute en el arranque del sistema, existe un directorio que se destina a tal efecto. Se trata del /etc/init.d y en él residen todos los daemos o scripts de arranque de servicios del sistema. Normalmente son scripts que aceptan parámetros de entrada para indicar la acción que deseamos ejecutar, como start, stop, restart

Linux, y en especial Raspbian, que es la distribución con la que trabajamos con Raspberry Pi, nos acerca un poco mas a su mantenimiento, y nos ofrece un esqueleto que podemos utilizar como bootstrap (punto de inicio) a la hora de crear nuestros propios scripts. Es algo complejo, y podéis encontrarlo en /etc/init.d/skeleton.

Lo idóneo es utilizar este esqueleto. Así que lo que vamos a hacer es editarlo para ajustarlo a nuestras necesidades.

Primero lo copiamos sobre un nuevo script, que nombraremos según nos convenga. En nuestro ejemplo, myscript:

jordi@raspberrypi ~ $ sudo cp /etc/init.d/skeleton /etc/init.d/myscript

Ahora podemos editarlo:

jordi@raspberrypi ~ $ sudo nano /etc/init.d/myscript

Y lo editamos, de una manera parecida a esta, según nuestras necesidades. Yo he creado, o editado mejor dicho, el siguiente script para adaptarlo al servicio del cliente No-ip.com que hemos creado en esta entrada de mi blog:

#! /bin/sh
### BEGIN INIT INFO
# Provides:          skeleton
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Example initscript
# Description:       This file should be used to construct scripts to be
#                    placed in /etc/init.d.
### END INIT INFO

# Author: Jordi <jordi@jormc.es>

# Do NOT "set -e"

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="No-ip.com client service"
NAME=noip2
DAEMON=/usr/sbin/$NAME
DAEMON_ARGS="--options args"
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions

#
# Function that starts the daemon/service
#
do_start()
{
	# Return
	#   0 if daemon has been started
	#   1 if daemon was already running
	#   2 if daemon could not be started
	start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
		|| return 1
	start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
		$DAEMON_ARGS \
		|| return 2
	# Add code here, if necessary, that waits for the process to be ready
	# to handle requests from services started subsequently which depend
	# on this one.  As a last resort, sleep for some time.
}

#
# Function that stops the daemon/service
#
do_stop()
{
	# Return
	#   0 if daemon has been stopped
	#   1 if daemon was already stopped
	#   2 if daemon could not be stopped
	#   other if a failure occurred
	start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
	RETVAL="$?"
	[ "$RETVAL" = 2 ] && return 2
	# Wait for children to finish too if this is a daemon that forks
	# and if the daemon is only ever run from this initscript.
	# If the above conditions are not satisfied then add some other code
	# that waits for the process to drop all resources that could be
	# needed by services started subsequently.  A last resort is to
	# sleep for some time.
	start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
	[ "$?" = 2 ] && return 2
	# Many daemons don't delete their pidfiles when they exit.
	rm -f $PIDFILE
	return "$RETVAL"
}

#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
	#
	# If the daemon can reload its configuration without
	# restarting (for example, when it is sent a SIGHUP),
	# then implement that here.
	#
	start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
	return 0
}

case "$1" in
  start)
	[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
	do_start
	case "$?" in
		0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
		2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
	esac
	;;
  stop)
	[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
	do_stop
	case "$?" in
		0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
		2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
	esac
	;;
  status)
	status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
	;;
  #reload|force-reload)
	#
	# If do_reload() is not implemented then leave this commented out
	# and leave 'force-reload' as an alias for 'restart'.
	#
	#log_daemon_msg "Reloading $DESC" "$NAME"
	#do_reload
	#log_end_msg $?
	#;;
  restart|force-reload)
	#
	# If the "reload" option is implemented then remove the
	# 'force-reload' alias
	#
	log_daemon_msg "Restarting $DESC" "$NAME"
	do_stop
	case "$?" in
	  0|1)
		do_start
		case "$?" in
			0) log_end_msg 0 ;;
			1) log_end_msg 1 ;; # Old process is still running
			*) log_end_msg 1 ;; # Failed to start
		esac
		;;
	  *)
		# Failed to stop
		log_end_msg 1
		;;
	esac
	;;
  *)
	#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
	echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
	exit 3
	;;
esac

:

No os asustéis, después de todo este tocho solo he tocado 3 líneas:

  • línea 13, para poner mi contacto (aunque el script no es mío, o no al menos el servicio al que llama)
  • línea 19, para añadir una pequeña descripción del script
  • línea 20, el nombre del script en sí, noip2
  • línea 21, para indicar dónde se encuentra el ejecutable de nuestro servicio, en /usr/local/bin

Ahora ya podemos guardarlo y prepararlo para ser ejecutado:

jordi@raspberrypi ~ $ sudo chmod +x /etc/init.d/noip2
jordi@raspberrypi ~ $ sudo update-rc.d -f noip2 defaults 99
update-rc.d: using dependency based boot sequencing
insserv: Script noip2 is broken: incomplete LSB comment.
insserv: missing `Required-Start:' entry: please add even if empty.
insserv: missing `Required-Stop:'  entry: please add even if empty.
insserv: missing `Default-Start:'  entry: please add even if empty.
insserv: missing `Default-Stop:'   entry: please add even if empty.
insserv: script noip2 provides system facility $remote_fs, skipped!
insserv: script noip2 provides system facility $syslog, skipped!
insserv: script noip2 provides system facility $remote_fs, skipped!
insserv: script noip2 provides system facility $syslog, skipped!
insserv: Default-Start undefined, assuming empty start runlevel(s) for script `noip2'
insserv: Default-Stop  undefined, assuming empty stop  runlevel(s) for script `noip2'
insserv: warning: script 'mathkernel' missing LSB tags and overrides

Le hemos dado permisos de ejecución (línea 1) y hemos informado al sistema que debe ejecutarlo en el arranque, para que esté siempre disponible (línea 2).

Ahora ya podemos tratarlo como un servicio mas:

jordi@raspberrypi ~ $ sudo service noip2 status
jordi@raspberrypi ~ $ sudo service noip2 stop
jordi@raspberrypi ~ $ sudo service noip2 start
jordi@raspberrypi ~ $ sudo service noip2 status

Notad que con este ejemplo no obtendremos nada a la salida, ya que el propio ejecutable del noip2 es “silencioso”.

Y con esto ya tenemos nuestro script funcionando. Una cosa muy importante, este proceso no implica un respawn automático. Es decir, si el servicio se mata con in kill -9, no se reiniciará, habrá que ejecutarlo a mano… Eos lo veremos en otro artículo más adelante.

Espero haberos sido de ayuda 😉

Jordi

Share Button

Un comentario

  1. Pingback: RasPi: cómo gestionar un DNS dinámico | jormc.es

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.