Creación de un demonio en Raspberry pi

@cmdearcos
Grupo TIECs

 

Estamos desarrollando una aplicación el Raspbian (SS.OO. de Raspberry pi) y necesitamos crear un demonio. Supuse que todos los participantes en el desarrollo conocían lo que es un demonio, pero después de varias horas comprobé que no era así.

Después de tantos años de desarrollo un demonio no deja de ser de forma simplificada una llamada a la función fork en C y “matar” al padre. Pero esta explicación tampoco ayudo mucho.

Voy a intentar exponer de forma sencilla (basicamente apoyandome en otros textos) que es un demonio. Buscando información por internet me he encontrado con esta página “Crear Demonio en Linux (Servicios)”. El artículo es interesante aunque no es exáctamente lo que buscaba.

El artículo empieza definiendo lo que es un demonio mucho mejor que lo que he hecho yo antes: “Un demonio, daemon o dæmon (de sus siglas en inglés Disk And Execution MONitor), es un tipo especial de proceso informático no interactivo, es decir, que se ejecuta en segundo plano en vez de ser controlado directamente por el usuario. Este tipo de programas se ejecutan de forma continua (infinita), vale decir, que aunque se intente cerrar o matar el proceso, este continuará en ejecución o se reiniciará automáticamente. Todo esto sin intervención de terceros y sin dependencia de consola alguna. (Wikipedia)

Resumiendo es una aplicación que se ejecuta al iniciar el sistema y en segundo plano. Los demonios son útiles para tener levantados servicios como SSH, CUPS(servicio de impresión), etc.”

Otra definición es la que da W. Richard Stevens en “Advanced Programming in the UNIX Environment: Second Edition”: “Daemons are processes that live for a long time. They are often started when the system is bootstrapped and terminate only when the system is shut down. Because they don’t have a controlling terminal, we say that they run in the background. UNIX systems have numerous daemons that perform day-to-day activities.” A tenor de estas definiciones un demonio solo podrá comunicarse a través de un log (de sistema o propietario).

El artículo anterior nos servirá para una vez programado el demonio poder ver su estado desde la consola y poder arrancarlo y pararlo.

Si hacemos un “ps –ef” podremos ver que algunos procesos tienen como TTY el carácter ‘?’. Estos procesos son demonios. Aquí os dejo algunos pantallazos.

¿Cómo es el código en C que permite crear un demonio?. Os dejo la explicación y el código del capítulo 13.3 del libro “Advanced Programming in the UNIX Environment: Second Edition” para que la tengáis en el momento que necesitéis crear un demonio:

Some basic rules to coding a daemon prevent unwanted interactions from happening. We state these rules and then show a function, daemonize, that implements them.

The first thing to do is call umask to set the file mode creation mask to 0. The file mode creation mask that’s inherited could be set to deny certain permissions. If the daemon process is going to create files, it may want to set specific permissions. For example, if it specifically creates files with group-read and group-write enabled, a file mode creation mask that turns off either of these permissions would undo its efforts.

Call fork and have the parent exit. This does several things. First, if the daemon was started as a simple shell command, having the parent terminate makes the shell think that the command is done. Second, the child inherits the process group ID of the parent but gets a new process ID, so we’re guaranteed that the child is not a process group leader. This is a prerequisite for the call to setsid that is done next.

Call setsid to create a new session. The three steps listed in Section 9.5 occur. The process (a) becomes a session leader of a new session, (b) becomes the process group leader of a new process group, and (c) has no controlling terminal.

Under System Vbased systems, some people recommend calling fork again at this point and having the parent terminate. The second child continues as the daemon. This guarantees that the daemon is not a session leader, which prevents it from acquiring a controlling terminal under the System V rules (Section 9.6). Alternatively, to avoid acquiring a controlling terminal, be sure to specify O_NOCTTY whenever opening a terminal device.

Change the current working directory to the root directory. The current working directory inherited from the parent could be on a mounted file system. Since daemons normally exist until the system is rebooted, if the daemon stays on a mounted file system, that file system cannot be unmounted.

Alternatively, some daemons might change the current working directory to some specific location, where they will do all their work. For example, line printer spooling daemons often change to their spool directory.

Unneeded file descriptors should be closed. This prevents the daemon from holding open any descriptors that it may have inherited from its parent (which could be a shell or some other process). We can use our open_max function (Figure 2.16) or the getrlimit function (Section 7.11) to determine the highest descriptor and close all descriptors up to that value.

Some daemons open file descriptors 0, 1, and 2 to /dev/null so that any library routines that try to read from standard input or write to standard output or standard error will have no effect. Since the daemon is not associated with a terminal device, there is nowhere for output to be displayed; nor is there anywhere to receive input from an interactive user. Even if the daemon was started from an interactive session, the daemon runs in the background, and the login session can terminate without affecting the daemon. If other users log in on the same terminal device, we wouldn’t want output from the daemon showing up on the terminal, and the users wouldn’t expect their input to be read by the daemon.

#include “apue.h”

#include

#include

#include <sys/resource.h>

void daemonize(const char *cmd) {    

int i, fd0, fd1, fd2;    

pid_t  pid;

struct rlimit rl;

struct sigaction    sa; 

/* 

* Clear file creation mask.     

*/ 

umask(0);

 

/*     

* Get maximum number of file descriptors.     

*/    

if (getrlimit(RLIMIT_NOFILE, &rl) < 0)        

err_quit(“%s: can’t get file limit”, cmd);

 

/*     

* Become a session leader to lose controlling TTY.     

*/    

if ((pid = fork()) < 0)        

err_quit(“%s: can’t fork”, cmd);    

else if (pid != 0) /* parent */        

exit(0);    

setsid();

 

/*     

* Ensure future opens won’t allocate controlling TTYs.     

*/    

sa.sa_handler = SIG_IGN;    

sigemptyset(&sa.sa_mask);    

sa.sa_flags = 0;    

if (sigaction(SIGHUP, &sa, NULL) < 0)        

err_quit(“%s: can’t ignore SIGHUP”);    

if ((pid = fork()) < 0)        

err_quit(“%s: can’t fork”, cmd);    

else if (pid != 0) /* parent */        

exit(0);

 

/*     

* Change the current working directory to the root so     

* we won’t prevent file systems from being unmounted.     

*/    

if (chdir(“/”) < 0)        

 err_quit(“%s: can’t change directory to /”);

 

/*     

* Close all open file descriptors.     

*/    

if (rl.rlim_max == RLIM_INFINITY)        

rl.rlim_max = 1024;    

for (i = 0; i < rl.rlim_max; i++)        

close(i);

 

/*     

* Attach file descriptors 0, 1, and 2 to /dev/null.     

*/    

fd0 = open(“/dev/null”, O_RDWR);    

fd1 = dup(0);    

fd2 = dup(0);

 

/*     

* Initialize the log file.     

*/    

openlog(cmd, LOG_CONS, LOG_DAEMON);    

if (fd0 != 0 || fd1 != 1 || fd2 != 2) {        

syslog(LOG_ERR, “unexpected file descriptors %d %d %d”,           fd0, fd1, fd2);        

exit(1);    

}

 }

 

 

@cmdearcos
Grupo TIECs

Deja un comentario

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