Crear un demonio en el linux embebido en placas

Ahora que están de moda las placas que contienen un linux embebido como las beagleboard, las raspberry pilas foneras, he creido interesante escribir un artículo en el cual se explica cómo ejecutar una tarea por tiempo indefinido desde que arranca la placa  y sin necesidad de acceder por shell a la misma.

Presupongo que tienes el compilador cruzado que genera ejecutables para la plataforma de la placa y que tienes acceso a ella mediante shell con el usuario root.

Nuestro objetivo es sencillo, crear lo que se denomina en entornos linux y unix un demonio (en windows es un servicio) lo más sencillo posible. Un demonio no es nada más que una aplicación que se ejecuta en segundo plano y no muestra directamente datos al usuario ni este puede interactuar con ella.

Como queremos que el ejemplo sea bastante sencillo nuestro demonio simplemente va a escribir en un fichero una cadena cada cierto tiempo. Vosotros podreis modificarlo para que haga lo que querais (leer y escribir datos en un puerto serie, analizar imágenes de una webcam, manejar GPIO, crear un servicio para internet, etc).

Scripts de demonios:

Normalmente cuando se arranca un sistema linux, después de cargar el kernel se ejecuta el proceso init, que es el proceso padre del que dependerán el resto de procesos que se ejecuten en el sistema. Este proceso lee el fichero /etc/inittab para saber entre otras cosas en qué nivel de arranque (runlevel de 1 a 5) debe iniciar el sistema.

Dependiendo de ese nivel de arranque se ejecutarán unos scripts ubicados en la carpeta correspondiente. Así por ejemplo si el nivel es 3, los scripts de la carpeta /etc/rc3.d que empiecen por S (Start) se ejecutarían al arrancar. Estos scripts tienen un número que indica en qué orden se ejecutan (se puede repetir el número).

Pero… un momento… ¡¡¡ Si estos ficheros son en realidad son enlaces simbólicos !!!.

Efectivamente, en realidad son accesos directos que apuntan al directorio /etc/init.d donde realmente están los scripts para los demonios. El proceso init lee de la carpeta /etc/rc3.d los enlaces que empiezan por S y ejecuta el script al que apunta con el parámetro start. Igualmente cuando se apaga o se reinicia el sistema el proceso init lee de la carpeta /etc/rc0.d o /etc/rc6.d  respectivamente los enlaces que empiezan por K y ejecuta el script al que apunta con el parámetro stop.

Los scripts que se encuentran en la carpeta /etc/init.d son en realidad scripts shell que dependiendo del parámetro que se le pase (start, stop, reload, etc) ejecuta el comando necesario para lo que se requiere. Ni que decir tiene que igualmente nosotros podemos ejecutar por nuestra cuenta el script con los parámetros mencionados para parar o arrancar el demonio desde una consola.

Por tanto vamos a crear un sencillo script que arranque o pare nuestro demonio. Lo crearemos dentro del directorio /etc/init.d y lo llamaremos nohacenada con el siguiente contenido:

Este ejemplo muestra un sencillo script donde se espera como parámetro una cadena start o stop. Si es start simplemente arranca la aplicación. Si es stop comprueba si existe el fichero /var/run/nohacenada.pid para saber si la aplicación está corriendo, después lee su contenido, que es el identificador del proceso, para posteriormente mandarle una señal kill y que este pueda cerrarse por si sóla limpiamente. Si no es ninguno de los dos parámetros anteriores es que el usuario intentó ejecutar el script por su cuenta sin poner start o stop como parámetro.

El script debe tener permisos 755  y pertenecer a root por lo que debemos dárselo de esta forma desde una consola shell con el usuario root:

Además hay que crear los enlaces simbólicos en el directorio correspondiente para que init sepa que tiene que arrancarlo (niveles 2, 3, 4 y 5) y pararlo (niveles 0, 1 y 6):

o si te lo permite el linux que tienes instalado, simplemente:

Con estos pasos ya hemos terminado nuestro script para que el proceso init pueda arrancar o parar nuestro demonio.

El demonio:

Para crear nuestro demonio usaremos el lenguaje C, que es el lenguaje estandar de linux. Nuestra aplicación se convertirá en demonio para quedar residente en el sistema permanentemente hasta que se termine mediante kill.

Programaremos nuestra aplicación para que informe de los errores en el log, genere un fichero pid, cree un hilo de ejecución que se convierta en demonio y escriba una cadena cada segundo mientras espera a que se termine la aplicación mediante una señal.

Dado que la aplicación no muestra datos al usuario este no sabe que está pasando por dentro, por eso habrá que informarle a través de los log. De esto se encargan las funciones openlog y syslog. Toda la información podrá ser consultada en el fichero /var/log/syslog.

Muchos demonios generan en el directorio /var/run un fichero con el nombre de la aplicación y extensión pid. Dentro guardan el PID o identificador de proceso en formato numérico. Con esto se obtienen dos ventajas: por un lado la misma aplicación sabe si ya hay una copia de ella misma ejecutandose para no duplicar procesos; por otro lado como guarda el pid dentro del fichero cualquier proceso puede leerlo para poder comunicarse con la aplicación (por ejemplo el comando kill que explicaré más adelante).

Para crear un demonio la aplicación principal crea un hilo de ejecución con fork y la aplicación principal se saldrá. Esto provoca que el hilo de ejecución se quede huerfano. Después se ejecuta umask para no heredar los permisos de los ficheros del proceso padre. A continuación se ejecuta setsid para que el proceso huerfano tenga su propio SID y sea un proceso independiente para el sistema operativo.  Por otro lado cambiaremos de directorio de ejecución para apuntar a la raiz con un chdir ya que es el único directorio seguro que existe en linux.  Finalmente cerraremos con un close la entrada estandar, la salida estandar y la salida de errores ya que no las necesitamos.

Con la función signal haremos que cuando el usuario (o init) nos envíe un comando kill (sin el -9) salgamos de la aplicación limpiamente. Esto es debido a que cuando se ejecuta kill <pid> lo que se está haciendo realmente es enviar una señal SIGTERM a la aplicación. La aplicación recupera la señal y ejecuta la función asociada mediante la función signal. En el ejemplo de más abajo veremos que se asocia a la función adios que simplemente cambia una variable para que se salga del bucle principal. Mientra se espera a recibir la señal, cada segundo se está escribiendo en el fichero /tmp/nohacenada.txt una cadena.

Al salir del bucle se escribirá en el fichero anterior otra cadena y cerramos ese fichero. Igualmente cerraremos el log y se borrará el fichero pid para que se pueda volver a ejecutar la aplicación nuevamente (en un arranque del sistema o nosotros mismos).

Dejo el código fuente de la aplicación llamado nohacenada.c para que os hagais una idea y poder hacer vuestros demonios basados en este:

Para compilarlo depende del toolchain para cada plataforma, pero sería algo parecido a esto:

Como seguramente hayais instalado un compilador cruzado en vuestro ordenador de sobremesa, habrá que subir el fichero ejecutable a la placa. Yo recomiendo que useis scp en linux o winscp en windows si la placa tiene acceso a la red ethernet o wifi ya que hace uso del demonio sshd (el que se usa para las sesiones de consola remotas por ssh).

Una vez subido el fichero a la placa debemos darle permisos de ejecución, asignarle como propietario al usuario root y moverlo a la ruta /usr/sbin a través de la consola shell con el usuario root de la siguiente manera:

Ahora podeis probar si funciona realmente simplemente ejecutando:

Podeis comprobar en la carpeta /tmp si hay un fichero llamado nohacenada.txt y consultarlo, también podeis mirar en la carpeta /var/run si existe un fichero llamado nohacenada.pid y visualizarlo. Si no veis nada podeis mirar el fichero /var/log/syslog para ver si hubiese algún error de la aplicación nohacenada.

Y para pararlo:

Con esto espero que le saqueis bastante provecho a vuestras placas con linux embebido ya que así conseguis que nada más encenderla se ejecute el programa sin tener que hacer vosotros nada por vuestra cuenta. Quizá en el futuro explique cómo programar módulos para el kernel y extender la funcionalidad de vuestra placa aún más lejos (drivers para hardware, sistemas de ficheros, llamadas al sistema, etc).

12 comentarios en “Crear un demonio en el linux embebido en placas

  1. carlos

    buenas amigo muchas gracias por el aporte… pero una pregunta como puedo hacer para que cuando el S.O arranque el se siga ejecutando eh intentado algunas cosas pero no veo como hacerlo… Gracias

    Responder
      1. carlos

        mmmm… si pero un cosa.. cuando reinicio la pc el demonio nohacenada no deberia volver a ejecutarse… eso es lo que quiero yo le cambie la dirección de /tmp/ a /home/ para que ahi se guarde el txt… pero como hago para que yo apenas prende la pc ese demonio se ejecute..

        Responder
          1. carlos

            introduciendo los paquetes a mano no me ejecuta el demonio… lo que hice fue ejecutar el comando update-rc.d nohacenada defaults 76

            Pero para que el comando pudiera ejecutarse correctamente coloque en el demonio:

            #!/bin/sh
            ### BEGIN INIT INFO
            # Provides : nohacenada
            # Required-Start: $syslog
            # Required-Stop: $syslog
            # Default-Start: 2 3 4 5
            # Default-Stop: 0 1 6
            # Short-Description: Crear un log
            # Description: activar el documentador de errores
            #
            ### END INIT INFO

            case «$1» in
            start)
            /usr/sbin/nohacenada
            ;;
            stop)
            test -e /var/run/nohacenada.pid || exit 2
            kill cat /var/run/nohacenada.pid
            ;;
            *)
            echo «El uso es: $0 {start|stop}»
            exit 1
            ;;
            esac
            exit 0

            porque sin los comentarios el comando no se ejecuta…
            bueno de nuevo muchas gracias por su ejemplo me a servido de mucha ayuda 😀

  2. Rafa

    Hola nuevamente, ya funciono pero cree sólo un enlace simbólico asi;

    ln -s /etc/init.d/nohacenada /etc/rc.d/S76nohacenada

    no sé, sí sea correcto. El contenido de mi directorio /etc es:

    mx27# cd /etc
    mx27# ls
    adjtime hotplug.d passwd
    busybox.conf inetd.conf printcap
    devfsd.conf init.d profile
    dropbear inittab protocols
    exports inputrc rc.d
    fdprm issue resolv.conf
    fstab issue.net securetty
    fstab~ ltib-release services
    group mime.types shadow
    host.conf modprobe.conf shells
    hosts modprobe.conf.dist udev
    hosts.allow modules.devfs udhcpd.conf
    hosts.deny mtab vsftpd.conf
    hotplug nsswitch.conf xinetd.d
    mx27# cd rc.d
    mx27# ls
    S76nohacenada rc.conf rc.local rc.serial
    init.d rc.conf.orig rc.modules rcS
    mx27# pwd
    /etc/rc.d

    Responder
    1. Oscar Autor

      Puede ser, aunque en tu caso ya no hace falta que el enlace simbólico sea S76nohacenada, con que se llama también noahcenada te sirve.

      Responder
  3. Mario

    Hola estoy bastante perdido y no domino mucho del tema,estaría muy agradecido si alguien pudiese ayudarme.

    Tengo una beaglebone (con Debian), y lo que quiero es que al alimentarla (sin que este conectada a la red ni a un ordenador ni nada), se habilite los permisos para un fichero.
    Algo asi como: chmod 666 /sys/class/leds/beaglebone::usr2/brigtness.

    Y no se muy bien que tengo que hacer, ¿si necesito crear el script ‘nohacenada’ y ya esta o necesito también de un demonio?…

    Ayudadme por favor y muchas gracias.

    Mario.

    Responder

Deja un comentario

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