son - ejecutar script como servicio linux
Creando un daemon en Linux (8)
En Linux, quiero agregar un daemon que no se puede detener y que monitorea los cambios en el sistema de archivos. Si se detectan cambios, debe escribir la ruta a la consola donde se inició más una nueva línea.
Ya tengo el código de cambio del sistema de archivos casi listo, pero no puedo entender cómo crear un daemon.
Mi código es de aquí: http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html
¿Qué hacer después del tenedor?
int main (int argc, char **argv) {
pid_t pID = fork();
if (pID == 0) { // child
// Code only executed by child process
sIdentifier = "Child Process: ";
}
else if (pID < 0) {
cerr << "Failed to fork" << endl;
exit(1);
// Throw exception
}
else // parent
{
// Code only executed by parent process
sIdentifier = "Parent Process:";
}
return 0;
}
En Linux quiero agregar un daemon que no se puede detener y que monitorea los cambios en el sistema de archivos. Si se detectaran cambios, debería escribir la ruta a la consola donde se inició + una nueva línea.
Los daemons funcionan en segundo plano y (normalmente ...) no pertenecen a un TTY, por eso no puede usar stdout / stderr de la manera que probablemente quiera. Por lo general, un daemon syslog ( syslogd ) se utiliza para registrar mensajes en archivos (depuración, error, ...).
Además de eso, hay algunos pasos necesarios para demonizar un proceso.
Si mal no recuerdo estos pasos son:
- bifurque el proceso principal y déjelo terminar si el bifurcación fue exitoso. -> Debido a que el proceso principal ha finalizado, el proceso hijo ahora se ejecuta en segundo plano.
- setsid - Crea una nueva sesión. El proceso de llamada se convierte en el líder de la nueva sesión y el líder del grupo de procesos del nuevo grupo de procesos. El proceso ahora está separado de su terminal de control (CTTY).
- Señales de captura - Ignora y / o maneja señales.
- bifurque nuevamente y deje que el proceso principal finalice para asegurarse de que se deshace del proceso principal de la sesión. (Solo los líderes de la sesión pueden obtener un TTY nuevamente).
- chdir - Cambia el directorio de trabajo del daemon.
- umask - Cambia la máscara de modo de archivo según las necesidades del daemon.
- cerrar : cierre todos los descriptores de archivos abiertos que puedan heredarse del proceso principal.
Para darle un punto de partida: mire este código esqueleto que muestra los pasos básicos:
/*
* daemonize.c
* This example daemonizes a process, writes a few log messages,
* sleeps 20 seconds and terminates afterwards.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>
static void skeleton_daemon()
{
pid_t pid;
/* Fork off the parent process */
pid = fork();
/* An error occurred */
if (pid < 0)
exit(EXIT_FAILURE);
/* Success: Let the parent terminate */
if (pid > 0)
exit(EXIT_SUCCESS);
/* On success: The child process becomes session leader */
if (setsid() < 0)
exit(EXIT_FAILURE);
/* Catch, ignore and handle signals */
//TODO: Implement a working signal handler */
signal(SIGCHLD, SIG_IGN);
signal(SIGHUP, SIG_IGN);
/* Fork off for the second time*/
pid = fork();
/* An error occurred */
if (pid < 0)
exit(EXIT_FAILURE);
/* Success: Let the parent terminate */
if (pid > 0)
exit(EXIT_SUCCESS);
/* Set new file permissions */
umask(0);
/* Change the working directory to the root directory */
/* or another appropriated directory */
chdir("/");
/* Close all open file descriptors */
int x;
for (x = sysconf(_SC_OPEN_MAX); x>=0; x--)
{
close (x);
}
/* Open the log file */
openlog ("firstdaemon", LOG_PID, LOG_DAEMON);
}
int main()
{
skeleton_daemon();
while (1)
{
//TODO: Insert daemon code here.
syslog (LOG_NOTICE, "First daemon started.");
sleep (20);
break;
}
syslog (LOG_NOTICE, "First daemon terminated.");
closelog();
return EXIT_SUCCESS;
}
- Compila el código:
gcc -o firstdaemon daemonize.c
- Comience el daemon:
./firstdaemon
Compruebe si todo funciona correctamente:
ps -xj | grep firstdaemon
ps -xj | grep firstdaemon
La salida debería ser similar a esta:
+------+------+------+------+-----+-------+------+------+------+-----+ | PPID | PID | PGID | SID | TTY | TPGID | STAT | UID | TIME | CMD | +------+------+------+------+-----+-------+------+------+------+-----+ | 1 | 3387 | 3386 | 3386 | ? | -1 | S | 1000 | 0:00 | ./ | +------+------+------+------+-----+-------+------+------+------+-----+
Lo que debes ver aquí es:
- El daemon no tiene terminal de control ( TTY =? )
- El ID de proceso padre ( PPID ) es 1 (El proceso de inicio)
- El PID! = SID, lo que significa que nuestro proceso NO es el líder de la sesión
(debido a la segunda bifurcación ()) - Debido a que PID! = SID nuestro proceso no puede tomar el control de un TTY de nuevo
Leyendo el syslog:
- Ubique su archivo syslog. El mío está aquí:
/var/log/syslog
Hacer a:
grep firstdaemon /var/log/syslog
La salida debería ser similar a esta:
firstdaemon[3387]: First daemon started. firstdaemon[3387]: First daemon terminated.
Una nota: en realidad, también debería implementar un manejador de señal y configurar el registro correctamente (archivos, niveles de registro ...).
Otras lecturas:
Al llamar a fork (), ha creado un proceso secundario. Si la horquilla es exitosa (la horquilla devolvió un PID distinto de cero) la ejecución continuará desde este punto desde el proceso secundario. En este caso, deseamos salir del proceso padre y luego continuar nuestro trabajo en el proceso secundario.
Tal vez esto ayude: http://www.netzmafia.de/skripten/unix/linux-daemon-howto.html
Intenta usar la función daemon
:
#include <unistd.h>
int daemon(int nochdir, int noclose);
Desde la daemon() :
La función daemon () es para programas que desean desprenderse del terminal de control y ejecutarlos en segundo plano como daemons del sistema.
Si nochdir es cero, daemon () cambia el directorio de trabajo actual del proceso de llamada al directorio raíz ("/"); de lo contrario, el directorio de trabajo actual no se modifica.
Si noclose es cero, daemon () redirige la entrada estándar, la salida estándar y el error estándar a / dev / null; de lo contrario, no se realizan cambios en estos descriptores de archivos.
No puedes crear un proceso en Linux que no se pueda eliminar. El usuario raíz (uid = 0) puede enviar una señal a un proceso, y hay dos señales que no se pueden capturar, SIGKILL = 9, SIGSTOP = 19. Y otras señales (cuando no se detectan) también pueden dar como resultado la terminación del proceso.
Es posible que desee una función de demonización más general, donde puede especificar un nombre para su programa / daemon y una ruta para ejecutar su programa (tal vez "/" o "/ tmp"). También es posible que desee proporcionar archivos para stderr y stdout (y posiblemente una ruta de control con stdin).
Aquí están los necesarios incluye:
#include <stdio.h> //printf(3)
#include <stdlib.h> //exit(3)
#include <unistd.h> //fork(3), chdir(3), sysconf(3)
#include <signal.h> //signal(3)
#include <sys/stat.h> //umask(3)
#include <syslog.h> //syslog(3), openlog(3), closelog(3)
Y aquí hay una función más general,
int
daemonize(char* name, char* path, char* outfile, char* errfile, char* infile )
{
if(!path) { path="/"; }
if(!name) { name="medaemon"; }
if(!infile) { infile="/dev/null"; }
if(!outfile) { outfile="/dev/null"; }
if(!errfile) { errfile="/dev/null"; }
//printf("%s %s %s %s/n",name,path,outfile,infile);
pid_t child;
//fork, detach from process group leader
if( (child=fork())<0 ) { //failed fork
fprintf(stderr,"error: failed fork/n");
exit(EXIT_FAILURE);
}
if (child>0) { //parent
exit(EXIT_SUCCESS);
}
if( setsid()<0 ) { //failed to become session leader
fprintf(stderr,"error: failed setsid/n");
exit(EXIT_FAILURE);
}
//catch/ignore signals
signal(SIGCHLD,SIG_IGN);
signal(SIGHUP,SIG_IGN);
//fork second time
if ( (child=fork())<0) { //failed fork
fprintf(stderr,"error: failed fork/n");
exit(EXIT_FAILURE);
}
if( child>0 ) { //parent
exit(EXIT_SUCCESS);
}
//new file permissions
umask(0);
//change to path directory
chdir(path);
//Close all open file descriptors
int fd;
for( fd=sysconf(_SC_OPEN_MAX); fd>0; --fd )
{
close(fd);
}
//reopen stdin, stdout, stderr
stdin=fopen(infile,"r"); //fd=0
stdout=fopen(outfile,"w+"); //fd=1
stderr=fopen(errfile,"w+"); //fd=2
//open syslog
openlog(name,LOG_PID,LOG_DAEMON);
return(0);
}
Aquí hay un programa de ejemplo, que se convierte en un daemon, se cuelga y luego se va.
int
main()
{
int res;
int ttl=120;
int delay=5;
if( (res=daemonize("mydaemon","/tmp",NULL,NULL,NULL)) != 0 ) {
fprintf(stderr,"error: daemonize failed/n");
exit(EXIT_FAILURE);
}
while( ttl>0 ) {
//daemon code here
syslog(LOG_NOTICE,"daemon ttl %d",ttl);
sleep(delay);
ttl-=delay;
}
syslog(LOG_NOTICE,"daemon ttl expired");
closelog();
return(EXIT_SUCCESS);
}
Tenga en cuenta que SIG_IGN indica capturar e ignorar la señal. Puede construir un manejador de señal que pueda registrar el recibo de la señal y establecer indicadores (como un indicador para indicar un apagado correcto).
Puedo detenerme en el primer requisito "Un daemon que no se puede detener ..."
No es posible mi amigo; sin embargo, puede lograr lo mismo con una herramienta mucho mejor, un módulo kernel.
http://www.infoq.com/articles/inotify-linux-file-system-event-monitoring
Todos los daemons pueden ser detenidos. Algunos se detienen más fácilmente que otros. Incluso un par de daemon con el compañero en espera, reapareciendo el compañero si se pierde, se puede detener. Solo tienes que trabajar un poco más duro.
Si su aplicación es una de:
{
".sh": "bash",
".py": "python",
".rb": "ruby",
".coffee" : "coffee",
".php": "php",
".pl" : "perl",
".js" : "node"
}
y no le molesta la dependencia de NodeJS, instale NodeJS y luego:
npm install -g pm2
pm2 start yourapp.yourext --name "fred" # where .yourext is one of the above
pm2 start yourapp.yourext -i 0 --name "fred" # run your app on all cores
pm2 list
Para mantener todas las aplicaciones ejecutándose al reiniciar (y daemonise pm2):
pm2 startup
pm2 save
Ahora usted puede:
service pm2 stop|restart|start|status
(también le permite ver fácilmente los cambios de código en el directorio de su aplicación y reiniciar automáticamente el proceso de la aplicación cuando ocurre un cambio de código)
Un daemon es solo un proceso en el fondo. Si desea iniciar su programa cuando se inicia el sistema operativo, en Linux, agregue su comando de inicio a /etc/rc.d/rc.local (ejecutar después de todos los demás scripts) o /etc/startup.sh
En Windows, realiza un servicio, registra el servicio y luego lo configura para que se inicie automáticamente en el arranque en la administración -> panel de servicios.
man 7 daemon
describe cómo crear daemon con gran detalle. Mi respuesta es solo un extracto de este manual.
Hay al menos dos tipos de daemons:
Daemons SysV
Si está interesado en el daemon SysV tradicional, debe implementar los siguientes pasos :
- Cierre todos los descriptores de archivos abiertos, excepto la entrada , salida y error estándar (es decir, los primeros tres descriptores de archivos 0, 1, 2). Esto asegura que ningún descriptor de archivo pasado accidentalmente permanece en el proceso del daemon. En Linux, esto se implementa mejor iterando a través de
/proc/self/fd
, con un retorno de la iteración del descriptor de archivo 3 al valor devuelto porgetrlimit()
paraRLIMIT_NOFILE
.- Reset todos los manejadores de señal a sus valores predeterminados. Esto se realiza mejor al iterar a través de las señales disponibles hasta el límite de
_NSIG
y restablecerlas aSIG_DFL
.- Restablezca la máscara de señal usando
sigprocmask()
.- Desinfecte el bloque de entorno, eliminando o restableciendo variables de entorno que puedan afectar negativamente el tiempo de ejecución de daemon.
- Call
fork()
, para crear un proceso en segundo plano.- En el niño, llame a
setsid()
para desconectarse de cualquier terminal y crear una session independiente.- En el niño, vuelva a llamar a
fork()
para asegurarse de que el daemon nunca vuelva a adquirir un terminal.- Llame a
exit()
en el primer elemento secundario, para que solo el segundo elemento secundario (el proceso real del daemon) permanezca cerca. Esto garantiza que el proceso del daemon se vuelva a criar en init / PID 1, como deberían ser todos los daemons.- En el proceso del daemon, conecte
/dev/null
a la entrada , salida y error estándar.- En el proceso del daemon, reinicie
umask
a 0, para que los modos de archivo pasados aopen()
,mkdir()
y similares controlen directamente el modo de acceso de los archivos y directorios creados.- En el proceso del daemon, change el directorio actual al directorio raíz (
/
), para evitar que el daemon impida involuntariamente que los puntos de montaje sean desmontados.- En el proceso del daemon, escriba el PID del daemon (como lo devuelve
getpid()
) en un archivo PID, por ejemplo/run/foobar.pid
(para un daemon hipotético "foobar") para garantizar que el daemon no pueda iniciarse más de una vez . Esto debe implementarse de manera libre de competencia para que el archivo PID solo se actualice cuando se verifique al mismo tiempo que el PID previamente almacenado en el archivo PID ya no existe o pertenece a un proceso en el extranjero.- En el proceso de daemon, descarte los privilegios, si es posible y aplicable.
- Desde el proceso del daemon, notifique al proceso original iniciado que la inicialización se completó. Esto se puede implementar a través de un conducto sin nombre o un canal de comunicación similar que se crea antes de la primera
fork()
y, por lo tanto, está disponible tanto en el proceso original como en el de daemon.- Call
exit()
en el proceso original. El proceso que invocó al daemon debe ser capaz de confiar en que estaexit()
ocurre después de que se completa la inicialización y que todos los canales de comunicación externos estén establecidos y sean accesibles.
Tenga en cuenta esta advertencia:
La función de
daemon()
BSD no se debe utilizar, ya que implementa solo un subconjunto de estos pasos.Un daemon que necesita proporcionar compatibilidad con sistemas SysV debería implementar el esquema indicado anteriormente. Sin embargo, se recomienda que este comportamiento sea opcional y configurable a través de un argumento de línea de comando para facilitar la depuración y simplificar la integración en los sistemas que usan systemd.
Tenga en cuenta que daemon()
no es POSIX con POSIX .
Daemons de Nuevo Estilo
Para daemons de estilo nuevo, se recomiendan los siguientes pasos :
- Si se recibe
SIGTERM
, apague el daemon y salga limpiamente.- Si se recibe
SIGHUP
, vuelva a cargar los archivos de configuración, si corresponde.- Proporcione un código de salida correcto del proceso principal del daemon, ya que el sistema init lo usa para detectar problemas y problemas del servicio. Se recomienda seguir el esquema del código de salida tal como se define en las recomendaciones de LSB para los scripts de inicio de SysV .
- Si es posible y aplicable, exponga la interfaz de control del daemon a través del sistema D-Bus IPC y tome un nombre de bus como último paso de inicialización.
- Para la integración en systemd, proporcione un archivo de unit que transmita información sobre cómo iniciar, detener y mantener el daemon. Ver
systemd.service(5)
para más detalles.- En la medida de lo posible, confíe en la funcionalidad del sistema init para limitar el acceso del daemon a archivos, servicios y otros recursos, es decir, en el caso de systemd, confíe en el control de límite de recursos de systemd en lugar de implementar el suyo propio, confíe en la caída de privilegios de systemd código en lugar de implementarlo en el daemon, y similar. Ver
systemd.exec(5)
para ver los controles disponibles.- Si se utiliza D-Bus, active el bus daemon mediante el suministro de un archivo de configuración de activación del servicio D-Bus. Esto tiene múltiples ventajas: su daemon puede iniciarse de forma ocasional bajo demanda; puede iniciarse en paralelo a otros daemons que lo requieran, lo que maximiza la paralelización y la velocidad de arranque ; su daemon puede reiniciarse en caso de falla sin perder ninguna solicitud de bus, ya que el bus pone en cola las solicitudes de servicios activables. Ver below para más detalles.
- Si su daemon proporciona servicios a otros procesos locales o clientes remotos a través de un socket, debe ser socket-activatable siguiendo el esquema indicado a below . Al igual que la activación de D-Bus, esto permite el inicio de servicios a pedido y permite una paralelización mejorada del inicio del servicio. Además, para protocolos sin estado (como syslog, DNS), un daemon que implementa la activación basada en sockets puede reiniciarse sin perder una sola solicitud. Ver below para más detalles.
- Si corresponde, un daemon debe notificar al sistema init sobre la finalización de la puesta en marcha o las actualizaciones de estado a través de la
sd_notify(3)
.- En lugar de utilizar la llamada a
syslog()
para iniciar sesión directamente en el servicio syslog del sistema, un daemon de estilo nuevo puede elegir simplemente iniciar sesión en el error estándar a través defprintf()
, que luego el sistema init reenvía a syslog. Si los niveles de registro son necesarios, pueden codificarse prefijando líneas de registro individuales con cadenas como "<4>" (para el nivel de registro 4 "ADVERTENCIA" en el esquema de prioridad syslog), siguiendo un estilo similar al nivelprintk()
del kernel de Linux sistema. Para obtener detalles, consultesd-daemon(3)
ysystemd.exec(5)
.
Para obtener más información, lea man 7 daemon
completo man 7 daemon
.