Tworzenie demona w systemie Linux

110

W Linuksie chcę dodać demona, którego nie można zatrzymać i który monitoruje zmiany w systemie plików. Jeśli zostaną wykryte jakiekolwiek zmiany, powinien zapisać ścieżkę do konsoli, na której został uruchomiony, oraz znak nowej linii.

Mam już prawie gotowy kod zmieniający system plików, ale nie mogę wymyślić, jak utworzyć demona.

Mój kod pochodzi stąd: http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html

Co robić po widelcu?

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;
}
chrisMe
źródło
1
Możliwy duplikat: stackoverflow.com/q/5384168/1076451
Chimera
1
możliwy duplikat: stackoverflow.com/questions/5384168/… dla części demonizującej, stackoverflow.com/questions/931093/… dla zegarka systemu plików
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Jeśli nie potrzebujesz zgodności z POSIX, możesz być zainteresowany inotifyAPI. Patrz: inotify_init, inotify_add_watch, inotify_rm_watch.
patryk.beza

Odpowiedzi:

216

W Linuksie chcę dodać demona, którego nie można zatrzymać i który monitoruje zmiany w systemie plików. Jeśli zostaną wykryte jakiekolwiek zmiany, powinien zapisać ścieżkę do konsoli, na której została uruchomiona + nową linię.

Demony działają w tle i (zwykle ...) nie należą do TTY, dlatego nie możesz używać stdout / stderr w sposób, w jaki prawdopodobnie chcesz. Zwykle demon syslog ( syslogd ) jest używany do logowania komunikatów do plików (debugowanie, błąd, ...).

Poza tym istnieje kilka wymaganych kroków, aby zdemonizować proces.


Jeśli dobrze pamiętam te kroki to:

  • rozwidlenia procesu nadrzędnego i pozwól mu się zakończyć, jeśli rozwidlenie zakończyło się powodzeniem. -> Ponieważ proces nadrzędny został zakończony, proces potomny działa teraz w tle.
  • zestawy - Utwórz nową sesję. Proces wywołujący staje się liderem nowej sesji i liderem grupy procesów nowej grupy procesów. Proces jest teraz odłączony od terminala sterującego (CTTY).
  • Sygnały złapania - Ignoruj ​​i / lub obsługuj sygnały.
  • fork ponownie i pozwól procesowi nadrzędnemu zakończyć, aby pozbyć się procesu prowadzącego sesję. (Tylko liderzy sesji mogą ponownie otrzymać TTY.)
  • chdir - Zmień katalog roboczy demona.
  • umask - Zmień maskę trybu pliku zgodnie z potrzebami demona.
  • close - zamyka wszystkie otwarte deskryptory plików, które mogą być dziedziczone z procesu nadrzędnego.

Aby dać ci punkt wyjścia: spójrz na ten szkielet kodu, który przedstawia podstawowe kroki. Ten kod można teraz również rozwidlić na GitHub: Podstawowy szkielet demona linuxa

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


  • Skompiluj kod: gcc -o firstdaemon daemonize.c
  • Uruchom demona: ./firstdaemon
  • Sprawdź, czy wszystko działa poprawnie: ps -xj | grep firstdaemon

  • Wynik powinien być podobny do tego:

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

Powinieneś tu zobaczyć:

  • Demon nie ma kontrolującego terminala ( TTY =? )
  • Identyfikator procesu nadrzędnego ( PPID ) to 1 (proces inicjujący)
  • PID! = SID co oznacza, że nasz proces nie jest liderem sesji
    (ze względu na drugi fork ())
  • Ponieważ PID! = SID, nasz proces nie może ponownie przejąć kontroli nad TTY

Czytanie syslog:

  • Zlokalizuj plik syslog. Mój jest tutaj:/var/log/syslog
  • Zrób: grep firstdaemon /var/log/syslog

  • Wynik powinien być podobny do tego:

  firstdaemon [3387]: Uruchomiono pierwszy demon.
  firstdaemon [3387]: Pierwszy demon zakończony.


Uwaga: W rzeczywistości chciałbyś również zaimplementować obsługę sygnału i poprawnie skonfigurować rejestrowanie (pliki, poziomy dziennika ...).

Dalsze czytanie:

Pascal Werkl
źródło
Wow dzięki! To wspaniale. Więc muszę umieścić mój kod w pętli while i to wszystko?
chrisMe
Zasadniczo tak. Ale ten kod to tylko przykład. Zależy to całkowicie od tego, co chcesz osiągnąć za pomocą procesu demona. Koniecznie
Pascal Werkl
1
Zamiast drugiego fork(), dlaczego po prostu nie użyć setsid()?
Chimera
1
Należy zauważyć, że funkcja zapewnia bardziej wszechstronne i niezawodny mechanizm regulacji sygnałów; nowe aplikacje powinny używać zamiast . sigaction()sigaction()signal()
patryk.beza 08.08.2016
4
Należy zwrócić uwagę widzów, że jest to metoda „stara”. Nowym zalecanym sposobem tworzenia demona jest użycie „demona nowego stylu”, który można znaleźć tutaj: 0pointer.de/public/systemd-man/daemon.html#New-Style%20Daemons lub
Starlord
30

man 7 daemonopisuje szczegółowo, jak stworzyć demona. Moja odpowiedź to tylko fragment tej instrukcji.

Istnieją co najmniej dwa typy demonów:

  1. tradycyjne demony SysV (w starym stylu ),
  2. demony systemowe (w nowym stylu ).

SysV Daemons

Jeśli interesuje Cię tradycyjny demon SysV , powinieneś zaimplementować następujące kroki :

  1. Zamknij wszystkie otwarte deskryptory plików z wyjątkiem standardowego wejścia , wyjścia i błędu (tj. Pierwsze trzy deskryptory plików 0, 1, 2). Zapewnia to, że żaden przypadkowo przekazany deskryptor pliku nie pozostaje w procesie demona. W systemie Linux najlepiej jest to zaimplementować, wykonując iterację /proc/self/fd, z powrotem iteracji z deskryptora pliku 3 do wartości zwracanej przez getrlimit()for RLIMIT_NOFILE.
  2. Zresetuj wszystkie programy obsługi sygnału do ich wartości domyślnych. Najlepiej jest to zrobić, przechodząc przez dostępne sygnały aż do limitu _NSIGi resetując je do SIG_DFL.
  3. Zresetuj maskę sygnału za pomocą sigprocmask().
  4. Oczyść blok środowiska, usuwając lub resetując zmienne środowiskowe, które mogą negatywnie wpłynąć na działanie demona.
  5. Zadzwoń fork(), aby utworzyć proces w tle.
  6. W dziecku zadzwoń, setsid()aby odłączyć się od dowolnego terminala i stworzyć niezależną sesję .
  7. W dziecku zadzwoń fork()ponownie, aby upewnić się, że demon nigdy nie może ponownie zdobyć terminala.
  8. Wezwij exit()pierwsze dziecko, tak aby pozostało tylko drugie dziecko (rzeczywisty proces demona). Zapewnia to, że proces demona jest ponownie rodzicielski na init / PID 1, tak jak powinny być wszystkie demony.
  9. W procesie demona połącz się /dev/nullze standardowym wejściem , wyjściem i błędem .
  10. W procesie demona, zresetować umaskdo 0, tak że tryby plików przekazane open(), mkdir()i tym podobnych bezpośrednio kontrolować tryb dostępu tworzonych plików i katalogów.
  11. W procesie demona zmień bieżący katalog na katalog główny ( /), aby uniknąć przypadkowego zablokowania przez demona punktów podłączenia przed odmontowaniem.
  12. W procesie demona zapisz PID demona (zwrócony przez getpid()) do pliku PID, na przykład /run/foobar.pid(dla hipotetycznego demona „foobar”), aby upewnić się, że demon nie może zostać uruchomiony więcej niż jeden raz. Należy to zaimplementować w sposób wolny od wyścigów, tak aby plik PID był aktualizowany tylko wtedy, gdy zostanie zweryfikowany w tym samym czasie, gdy PID poprzednio przechowywany w pliku PID już nie istnieje lub należy do obcego procesu.
  13. W procesie demona usuń uprawnienia, jeśli to możliwe i mają zastosowanie.
  14. Z poziomu procesu demona powiadom pierwotny proces o zakończeniu inicjowania. Można to zaimplementować za pomocą nienazwanego potoku lub podobnego kanału komunikacyjnego, który jest tworzony przed pierwszym fork()i dlatego jest dostępny zarówno w oryginalnym, jak iw procesie demona.
  15. Wezwij exit()oryginalny proces. Proces, który wywołał demona, musi móc polegać na tym, że exit()dzieje się to po zakończeniu inicjalizacji i ustanowieniu i udostępnieniu wszystkich zewnętrznych kanałów komunikacji.

Zwróć uwagę na to ostrzeżenie:

Nie należy używać daemon()funkcji BSD , ponieważ implementuje ona tylko podzbiór tych kroków.

Demon, który musi zapewnić kompatybilność z systemami SysV, powinien implementować powyższy schemat. Zaleca się jednak, aby to zachowanie było opcjonalne i konfigurowalne za pomocą argumentu wiersza poleceń, aby ułatwić debugowanie, a także uprościć integrację z systemami używającymi systemd.

Zauważ, że daemon()nie jest to zgodne z POSIX .


Demony w nowym stylu

W przypadku demonów nowego stylu zalecane są następujące kroki :

  1. Jeśli SIGTERMzostanie odebrany, zamknij demona i wyjdź bezproblemowo.
  2. Jeśli SIGHUPzostanie odebrany, załaduj ponownie pliki konfiguracyjne, jeśli dotyczy.
  3. Podaj poprawny kod wyjścia z procesu głównego demona, ponieważ jest on używany przez system init do wykrywania błędów i problemów związanych z usługą. Zaleca się postępowanie zgodnie ze schematem kodu wyjścia zdefiniowanym w zaleceniach LSB dla skryptów inicjalizacyjnych SysV .
  4. Jeśli to możliwe i ma to zastosowanie, ujawnij interfejs sterowania demona za pośrednictwem systemu IPC D-Bus i pobierz nazwę magistrali jako ostatni krok inicjalizacji.
  5. W celu integracji z systemd należy udostępnić plik jednostki usługowej , który zawiera informacje o uruchamianiu, zatrzymywaniu i utrzymywaniu demona w inny sposób. Zobacz szczegóły.systemd.service(5)
  6. O ile to możliwe, polegaj na funkcjonalności systemu init, aby ograniczyć dostęp demona do plików, usług i innych zasobów, tj. W przypadku systemd polegaj na kontroli limitów zasobów systemd zamiast implementować własne, polegaj na utracie uprawnień systemd kod zamiast implementować go w demonie i tym podobne. Zobacz systemd.exec(5)dostępne elementy sterujące.
  7. Jeśli używana jest magistrala D-Bus , uaktywnij magistralę demonów, dostarczając plik konfiguracyjny aktywacji usługi D-Bus . Ma to wiele zalet: twój demon może być uruchamiany leniwie na żądanie; może być uruchamiany równolegle z innymi demonami wymagającymi tego - co maksymalizuje zrównoleglenie i prędkość uruchamiania ; Twój demon może zostać ponownie uruchomiony w przypadku awarii bez utraty żądań magistrali, ponieważ magistrala kolejkuje żądania usług aktywowanych. Szczegóły poniżej .
  8. Jeśli twój demon dostarcza usługi innym lokalnym procesom lub zdalnym klientom poprzez gniazdo, powinien być aktywowany przez gniazdo zgodnie ze schematem przedstawionym poniżej . Podobnie jak aktywacja D-Bus, umożliwia to uruchamianie usług na żądanie, a także umożliwia lepszą równoległość uruchamiania usług. Ponadto w przypadku protokołów bezstanowych (takich jak syslog, DNS) demon implementujący aktywację opartą na gniazdach można ponownie uruchomić bez utraty ani jednego żądania. Szczegóły poniżej .
  9. Jeśli ma to zastosowanie, demon powinien powiadomić system init o zakończeniu uruchamiania lub aktualizacjach statusu za pośrednictwem sd_notify(3)interfejsu.
  10. Zamiast używać syslog()wywołania do logowania się bezpośrednio do systemowej usługi syslog, demon nowego stylu może po prostu zalogować się do standardowego błędu za pośrednictwem fprintf(), który jest następnie przekazywany do syslog przez system init. Jeśli potrzebne są poziomy dziennika, można je zakodować, poprzedzając poszczególne wiersze dziennika ciągami, takimi jak „<4>” (dla poziomu dziennika 4 „WARNING” w schemacie priorytetów syslog), zgodnie ze stylem podobnym do systemu printk()poziomu jądra Linuksa . Aby uzyskać szczegółowe informacje, zobacz sd-daemon(3)i systemd.exec(5).

Aby dowiedzieć się więcej, przeczytaj całość man 7 daemon.

patryk.beza
źródło
11

W Linuksie nie można stworzyć procesu, którego nie można zabić. Użytkownik root (uid = 0) może wysłać sygnał do procesu, a są dwa sygnały, których nie można przechwycić, SIGKILL = 9, SIGSTOP = 19. Inne sygnały (jeśli nie zostaną złapane) również mogą spowodować zakończenie procesu.

Możesz potrzebować bardziej ogólnej funkcji daemonize, w której możesz określić nazwę swojego programu / demona i ścieżkę do uruchomienia programu (na przykład „/” lub „/ tmp”). Możesz również chcieć dostarczyć plik (i) dla stderr i stdout (i prawdopodobnie ścieżkę kontrolną za pomocą stdin).

Oto niezbędne obejmuje:

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

A oto bardziej ogólna funkcja,

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

Oto przykładowy program, który staje się demonem, kręci się w pobliżu, a następnie wychodzi.

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

Zauważ, że SIG_IGN wskazuje na złapanie i zignorowanie sygnału. Możesz zbudować procedurę obsługi sygnału, która może rejestrować odbiór sygnału i ustawiać flagi (takie jak flaga wskazująca bezpieczne zamknięcie).

ChuckCottrill
źródło
8

Spróbuj użyć daemonfunkcji:

#include <unistd.h>

int daemon(int nochdir, int noclose);

Od strony man :

Funkcja daemon () jest przeznaczona dla programów, które chcą odłączyć się od kontrolującego terminala i działać w tle jako demony systemowe.

Jeśli nochdir ma wartość zero, daemon () zmienia bieżący katalog roboczy wywołującego procesu na katalog główny („/”); w przeciwnym razie bieżący katalog roboczy pozostaje niezmieniony.

Jeśli noclose ma wartość zero, daemon () przekierowuje standardowe wejście, standardowe wyjście i standardowe wyjście błędów do / dev / null; w przeciwnym razie żadne zmiany nie są wprowadzane do tych deskryptorów plików.

weiyin
źródło
2
Zwróć uwagę, że daemon(7)instrukcja wspomina o krokach tworzenia demona i ostrzega, że: Funkcja BSD daemon()nie powinna być używana, ponieważ implementuje tylko podzbiór tych kroków. daemonfunkcja pojawiła się po raz pierwszy w 4.4BSD i nie jest zgodna z POSIX .
patryk.beza
2
Zauważ również, że ostrzeżenie o używaniu daemon () znajduje się w sekcji SysV w starym stylu na stronie man daemon (7) . Używanie daemon () nie jest odradzane w przypadku systemd.
Greg McPherran
7

Mogę zatrzymać się na pierwszym wymaganiu „Demon, którego nie można zatrzymać ...”

Niemożliwe, przyjacielu; jednak możesz osiągnąć to samo dzięki znacznie lepszemu narzędziu, modułowi jądra.

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

Wszystkie demony można zatrzymać. Niektórych łatwiej jest zatrzymać niż innych. Nawet para demonów z partnerem w przytrzymaniu, odradzająca partnera w przypadku utraty, może zostać zatrzymana. Musisz tylko trochę bardziej nad tym popracować.

Edwin Buck
źródło
7
Myślę, że mówiąc „Demon, którego nie można zatrzymać”, autor ma na myśli, że demon zawsze działa w tle, gdy sesja jest kończona.
FaceBro
6

Jeśli Twoja aplikacja jest jedną z:

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

i nie masz nic przeciwko zależności NodeJS, a następnie zainstaluj NodeJS, a następnie:

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

Aby wszystkie aplikacje działały po ponownym uruchomieniu (i demonizowaniu pm2):

pm2 startup

pm2 save

Teraz możesz:

service pm2 stop|restart|start|status

(pozwala również łatwo obserwować zmiany kodu w katalogu aplikacji i automatycznie ponownie uruchamiać proces aplikacji, gdy nastąpi zmiana kodu)

danday74
źródło
2
To nie ma nic wspólnego z C.
melpomene
4
Doceniam, że jest tag C. Jednak OP nie wspomina o wymaganiu dotyczącym C w pytaniu. Tytuł tworzy demona w systemie Linux. Ta odpowiedź to zadowala.
danday74
1
Och, masz rację. Jest oznaczony jako C, ale faktycznym wymaganiem jest C ++ (o czym świadczy kod OP i powiązany artykuł).
melpomene
3

Wywołując fork () utworzyłeś proces potomny. Jeśli rozwidlenie zakończyło się pomyślnie (fork zwrócił niezerowy PID), wykonanie będzie kontynuowane od tego momentu z poziomu procesu potomnego. W tym przypadku chcemy wdzięcznie wyjść z procesu nadrzędnego, a następnie kontynuować naszą pracę w procesie potomnym.

Może to pomoże: http://www.netzmafia.de/skripten/unix/linux-daemon-howto.html

Doug Morrow
źródło
2

Demon to tylko proces w tle. Jeśli chcesz uruchomić program podczas uruchamiania systemu operacyjnego, w systemie Linux dodaj polecenie start do /etc/rc.d/rc.local (uruchom po wszystkich innych skryptach) lub /etc/startup.sh

W systemie Windows tworzysz usługę, rejestrujesz usługę, a następnie ustawiasz jej automatyczne uruchamianie przy starcie w panelu Administracja -> Usługi.

Magn3s1um
źródło
1
Dzięki. Czy więc nie ma różnicy między „demonem” a zwykłym programem? Nie chcę, żeby się łatwo zamykało.
chrisMe
1
Nie, demon to tylko proces w tle. Dokładniej, rozwidlasz od rodzica, uruchamiasz proces potomny i przerywasz rodzica (aby nie było dostępu terminala do programu). nie jest to jednak konieczne, aby być „demonem”: en.wikipedia.org/wiki/Daemon_(computing)
Magn3s1um
1

Szablon demona

Napisałem szablon demona podążając za daemonem w nowym stylu: link

Cały kod szablonu można znaleźć na GitHub: tutaj

Main.cpp

// This function will be called when the daemon receive a SIGHUP signal.
void reload() {
    LOG_INFO("Reload function called.");
}

int main(int argc, char **argv) {
    // The Daemon class is a singleton to avoid be instantiate more than once
    Daemon& daemon = Daemon::instance();
    // Set the reload function to be called in case of receiving a SIGHUP signal
    daemon.setReloadFunction(reload);
    // Daemon main loop
    int count = 0;
    while(daemon.IsRunning()) {
        LOG_DEBUG("Count: ", count++);
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    LOG_INFO("The daemon process ended gracefully.");
}

Daemon.hpp

class Daemon {
    public:

    static Daemon& instance() {
        static Daemon instance;
        return instance;
    }

    void setReloadFunction(std::function<void()> func);

    bool IsRunning();

    private:

    std::function<void()> m_reloadFunc;
    bool m_isRunning;
    bool m_reload;

    Daemon();
    Daemon(Daemon const&) = delete;
    void operator=(Daemon const&) = delete;

    void Reload();

    static void signalHandler(int signal);
};

Daemon.cpp

Daemon::Daemon() {
    m_isRunning = true;
    m_reload = false;
    signal(SIGINT, Daemon::signalHandler);
    signal(SIGTERM, Daemon::signalHandler);
    signal(SIGHUP, Daemon::signalHandler);
}

void Daemon::setReloadFunction(std::function<void()> func) {
    m_reloadFunc = func;
}

bool Daemon::IsRunning() {
    if (m_reload) {
        m_reload = false;
        m_reloadFunc();
    }
    return m_isRunning;
}

void Daemon::signalHandler(int signal) {
    LOG_INFO("Interrup signal number [", signal,"] recived.");
    switch(signal) {
        case SIGINT:
        case SIGTERM: {
            Daemon::instance().m_isRunning = false;
            break;
        }
        case SIGHUP: {
            Daemon::instance().m_reload = true;
            break;
        }
    }
}

daemon-template.service

[Unit]
Description=Simple daemon template
After=network.taget

[Service]
Type=simple
ExecStart=/usr/bin/daemon-template --conf_file /etc/daemon-template/daemon-tenplate.conf
ExecReload=/bin/kill -HUP $MAINPID
User=root
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=daemon-template

[Install]
WantedBy=multi-user.target
Fabiano Traple
źródło