web-dev-qa-db-fra.com

Création d'un démon sous Linux

Sous Linux, je souhaite ajouter un démon qui ne peut pas être arrêté et qui surveille les modifications du système de fichiers. Si des modifications sont détectées, il convient d'écrire le chemin d'accès à la console où elle a été démarrée, ainsi qu'une nouvelle ligne.

Le système de fichiers est déjà prêt à changer de code, mais je ne sais pas comment créer un démon.

Mon code vient d'ici: http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html

Que faire après la fourchette?

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;
}
92
chrisMe

Sous Linux, je veux ajouter un démon qui ne peut pas être arrêté et qui surveille les modifications du système de fichiers. Si des modifications sont détectées, le chemin d'accès à la console où elle a été démarrée doit être écrit + une nouvelle ligne.

Les démons fonctionnent en arrière-plan et (généralement ...) n'appartiennent pas à un téléscripteur. C'est pourquoi vous ne pouvez pas utiliser stdout/stderr comme vous le souhaitez probablement. Généralement, un démon syslog ( syslogd ) est utilisé pour consigner les messages dans des fichiers (débogage, erreur, ...).

En plus de cela, il y a quelques étapes requises pour démoniser un processus.


Si je me souviens bien, ces étapes sont les suivantes:

  • divisez le processus parent et laissez-le se terminer si le forking a réussi. -> Le processus parent étant terminé, le processus enfant s'exécute maintenant en arrière-plan.
  • setsid - Crée une nouvelle session. Le processus appelant devient le responsable de la nouvelle session et le responsable du groupe de processus du nouveau groupe de processus. Le processus est maintenant détaché de son terminal de contrôle (CTTY).
  • Capturer les signaux - Ignorer et/ou gérer les signaux.
  • branchez à nouveau & laissez le processus parent se terminer pour vous assurer de vous débarrasser du processus de conduite de session. (Seuls les responsables de session peuvent à nouveau obtenir un ATS.)
  • chdir - Modifie le répertoire de travail du démon.
  • umask - Modifie le masque de mode de fichier en fonction des besoins du démon.
  • close - Ferme tous les descripteurs de fichiers ouverts pouvant être hérités du processus parent.

Pour vous donner un point de départ: Regardez ce code squelette qui montre les étapes de base. Ce code peut maintenant aussi être créé dans GitHub: squelette de base d’un démon linux

/*
 * 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;
}


  • Compilez le code: gcc -o firstdaemon daemonize.c
  • Démarrez le démon: ./firstdaemon
  • Vérifiez si tout fonctionne correctement: ps -xj | grep firstdaemon

  • Le résultat devrait ressembler à celui-ci:

 + ------ + ------ + ------ + ------ + ----- + ------- + - ---- + ------ + ------ + ----- + 
 | PPID | PID | PGID | SID | ATS | TPGID | STAT | UID | TEMPS | CMD | 
 + ------ + ------ + ------ + ------ + ----- + ------- + ------ + ------ + ------ + ----- + 
 | 1 | 3387 | 3386 | 3386 | ? | -1 | S | 1000 | 0:00 | ./ | 
 + ------ + ------ + ------ + ------ + ----- + ------- + ------ + ------ + ------ + ----- + 

Ce que vous devriez voir ici est:

  • Le démon n'a pas de terminal de contrôle ( TTY =? )
  • L'ID du processus parent (PPID) est 1 (l'init processus)
  • Le PID! = SID , ce qui signifie que notre processus n'est PAS le leader de la session
    (à cause du second fork ())
  • Parce que PID! = SID, notre processus ne peut plus reprendre le contrôle d'un téléscripteur

Lecture du journal système:

  • Localisez votre fichier syslog. Le mien est ici: /var/log/syslog
  • Faites un: grep firstdaemon /var/log/syslog

  • Le résultat devrait ressembler à celui-ci:

 firstdaemon [3387]: premier démon démarré. 
 firstdaemon [3387]: premier démon terminé. 


Remarque: En réalité, vous voudriez également implémenter un gestionnaire de signal et configurer correctement la journalisation (fichiers, niveaux de journalisation). .).

Lectures supplémentaires:

188
Pascal Werkl

Vous ne pouvez pas créer un processus sous Linux qui ne puisse pas être tué. L'utilisateur root (uid = 0) peut envoyer un signal à un processus. Deux signaux ne peuvent pas être capturés, SIGKILL = 9, SIGSTOP = 19. Et d'autres signaux (non capturés) peuvent également entraîner la fin du processus.

Vous voudrez peut-être une fonction de démonisation plus générale, dans laquelle vous pourrez spécifier un nom pour votre programme/démon et un chemin pour l’exécuter (par exemple "/" ou "/ tmp"). Vous pouvez également vouloir fournir des fichiers pour stderr et stdout (et éventuellement un chemin de contrôle utilisant stdin).

Voici le nécessaire comprend:

#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)

Et voici une fonction plus générale,

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);
}

Voici un exemple de programme qui devient un démon, qui traîne, puis s'en 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);
}

Notez que SIG_IGN indique d'attraper et d'ignorer le signal. Vous pouvez créer un gestionnaire de signal pouvant enregistrer la réception du signal et définir des indicateurs (tels qu'un indicateur indiquant un arrêt progressif).

7
ChuckCottrill

Je peux m'arrêter à la première condition "Un démon qui ne peut pas être arrêté ..."

Pas possible mon ami; Cependant, vous pouvez réaliser la même chose avec un outil bien meilleur, un module de noyau.

http://www.infoq.com/articles/inotify-linux-file-system-evyst-monitoring

Tous les démons peuvent être arrêtés. Certains sont plus facilement arrêtés que d'autres. Même une paire de démons avec le partenaire en attente, permettant de rétablir le partenaire s'il est perdu, peut être arrêtée. Vous devez juste travailler un peu plus fort à ce sujet.

5
Edwin Buck

Essayez d’utiliser la fonction daemon:

#include <unistd.h>

int daemon(int nochdir, int noclose);

De la page de manuel :

La fonction daemon () est destinée aux programmes souhaitant se détacher du terminal de contrôle et s'exécuter en arrière-plan en tant que démons système.

Si nochdir est à zéro, daemon () modifie le répertoire de travail actuel du processus appelant dans le répertoire racine ("/"); sinon, le répertoire de travail actuel reste inchangé.

Si noclose vaut zéro, daemon () redirige l'entrée standard, la sortie standard et l'erreur standard vers/dev/null; sinon, aucune modification n'est apportée à ces descripteurs de fichier.

5
weiyin

Si votre application est l'une des suivantes:

{
  ".sh": "bash",
  ".py": "python",
  ".rb": "Ruby",
  ".coffee" : "coffee",
  ".php": "php",
  ".pl" : "Perl",
  ".js" : "node"
}

et vous ne craignez pas une dépendance NodeJS, puis installez NodeJS puis:

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

Pour que toutes les applications continuent de s'exécuter au redémarrage (et à démoniser pm2):

pm2 startup

pm2 save

Maintenant vous pouvez:

service pm2 stop|restart|start|status

(vous permet également de surveiller facilement les modifications de code dans votre répertoire d'applications et de redémarrer automatiquement le processus d'application lorsqu'une modification de code se produit)

5
danday74

En appelant fork (), vous avez créé un processus enfant. Si le fork a réussi (le fork a retourné un PID différent de zéro), l'exécution se poursuivra à partir de ce point depuis le processus enfant. Dans ce cas, nous voulons quitter le processus parent de façon élégante, puis poursuivre notre travail dans le processus enfant.

Peut-être que cela aidera: http://www.netzmafia.de/skripten/unix/linux-daemon-howto.html

2
Doug Morrow

Un démon est juste un processus en arrière-plan. Si vous souhaitez démarrer votre programme au démarrage du système d’exploitation, sous Linux, vous ajoutez votre commande de démarrage à /etc/rc.d/rc.local (exécuté après tous les autres scripts) ou à /etc/startup.sh.

Sous Windows, vous créez un service, enregistrez-le, puis vous le configurez pour qu'il démarre automatiquement au démarrage dans le volet administration -> services.

1
Magn3s1um