Criando um daemon no Linux

No Linux, quero adicionar um daemon que não possa ser parado e que monitore as alterações do sistema de arquivos. Se alguma alteração for detectada, ela deverá gravar o caminho para o console onde ela foi iniciada, além de uma nova linha.

Eu já tenho o código de mudança do sistema de arquivos quase pronto, mas não consigo descobrir como criar um daemon.

Meu código é daqui: http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html

O que fazer depois do garfo?

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

   

No Linux, eu quero adicionar um daemon que não pode ser parado e que monitora as mudanças no sistema de arquivos. Se alguma alteração for detectada, ela deverá gravar o caminho para o console no qual foi iniciada + uma nova linha.

Daemons trabalham em segundo plano e (geralmente …) não pertencem a um TTY, e é por isso que você não pode usar stdout / stderr da maneira que você provavelmente quer. Normalmente, um daemon syslog ( syslogd ) é usado para registrar mensagens em arquivos (debug, error, …).

Além disso, há algumas etapas necessárias para daemonizar um processo.


Se bem me lembro, estes passos são:

  • bifurque o processo pai e deixe-o terminar se a bifurcação foi bem-sucedida. -> Como o processo pai foi finalizado, o processo filho agora é executado em segundo plano.
  • setsid – cria uma nova session. O processo de chamada se torna o líder da nova session e o líder do grupo de processos do novo grupo de processos. O processo é agora separado de seu terminal de controle (CTTY).
  • Pegar sinais – Ignorar e / ou manipular sinais.
  • bifurque novamente e deixe o processo pai terminar para garantir que você se livre do processo principal da session. (Somente líderes de session podem receber um TTY novamente)
  • chdir – Altere o diretório de trabalho do daemon.
  • umask – Altere a máscara do modo de arquivo de acordo com as necessidades do daemon.
  • close – Fecha todos os descritores de arquivos abertos que podem ser herdados do processo pai.

Para lhe dar um ponto de partida: observe este código esqueleto que mostra as etapas básicas:

 /* * daemonize.c * This example daemonizes a process, writes a few log messages, * sleeps 20 seconds and terminates afterwards. */ #include  #include  #include  #include  #include  #include  #include  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; } 

  • Compile o código: gcc -o firstdaemon daemonize.c
  • Inicie o daemon: ./firstdaemon
  • Verifique se tudo está funcionando corretamente: ps -xj | grep firstdaemon ps -xj | grep firstdaemon

  • A saída deve ser semelhante a esta:

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

O que você deve ver aqui é:

  • O daemon não possui terminal de controle ( TTY =? )
  • O ID do processo pai ( PPID ) é 1 (o processo init)
  • O PID! = SID, o que significa que o nosso processo NÃO é o líder da session
    (por causa do segundo garfo ())
  • Porque PID! = SID nosso processo não pode assumir o controle de um TTY novamente

Lendo o syslog:

  • Localize o seu arquivo syslog. O meu está aqui: /var/log/syslog
  • Faça um: grep firstdaemon /var/log/syslog

  • A saída deve ser semelhante a esta:

   firstdaemon [3387]: O primeiro daemon foi iniciado.
   firstdaemon [3387]: Primeiro daemon terminado.

Uma nota: Na realidade, você também deseja implementar um manipulador de sinal e configurar o registro corretamente (Arquivos, níveis de registro …).

Leitura adicional:

  • Linux-UNIX-Programmierung – alemão
  • Programação do Servidor Daemon Unix

man 7 daemon descreve como criar daemon em grande detalhe. Minha resposta é apenas um trecho deste manual.

Existem pelo menos dois tipos de daemons:

  1. daemons tradicionais de SysV ( estilo antigo ),
  2. daemons systemd ( novo estilo ).

SysV Daemons

Se você estiver interessado no daemon SysV tradicional, você deve implementar as seguintes etapas :

  1. Feche todos os descritores de arquivos abertos, exceto input , saída e erro padrão (ou seja, os três primeiros descritores de arquivo 0, 1, 2). Isso garante que nenhum descritor de arquivo passado acidentalmente permaneça no processo do daemon. No Linux, isso é melhor implementado pela iteração através de /proc/self/fd , com um retorno de iteração do descritor de arquivo 3 para o valor retornado por getrlimit() para RLIMIT_NOFILE .
  2. Redefinir todos os manipuladores de sinal para seu padrão. Isso é feito da melhor maneira, iterando os sinais disponíveis até o limite de _NSIG e redefinindo-os para SIG_DFL .
  3. Redefinir a máscara de sinal usando sigprocmask() .
  4. Sanitize o bloco de ambiente, removendo ou reconfigurando variables ​​de ambiente que podem impactar negativamente o tempo de execução do daemon.
  5. Chame o fork() , para criar um processo em segundo plano.
  6. Na criança, chame setsid() para se desconectar de qualquer terminal e crie uma session independente.
  7. No filho, chame fork() novamente, para garantir que o daemon nunca possa readquirir novamente um terminal.
  8. Chame exit() no primeiro filho, para que apenas o segundo filho (o processo do daemon real) permaneça por perto. Isso garante que o processo do daemon seja re-criado para o init / PID 1, como todos os daemons devem ser.
  9. No processo daemon, conecte /dev/null à input , saída e erro padrão.
  10. No processo do daemon, reconfigure o umask para 0, para que os modos de arquivo passados ​​para open() , mkdir() e similares controlem diretamente o modo de access dos arquivos e diretórios criados.
  11. No processo do daemon, altere o diretório atual para o diretório-raiz ( / ), para evitar que o daemon involuntariamente bloqueie os pontos de assembly de serem desmontados.
  12. No processo daemon, escreva o daemon PID (conforme retornado por getpid() ) para um arquivo PID, por exemplo /run/foobar.pid (para um daemon hipotético “foobar”) para assegurar que o daemon não possa ser iniciado mais de uma vez . Isso deve ser implementado de forma livre de corrida para que o arquivo PID seja atualizado apenas quando for verificado ao mesmo tempo em que o PID anteriormente armazenado no arquivo PID não existir mais ou pertencer a um processo externo.
  13. No processo daemon, elimine privilégios, se possível e aplicável.
  14. A partir do processo daemon, notifique o processo original iniciado que a boot esteja concluída. Isso pode ser implementado por meio de um canal sem nome ou canal de comunicação similar que é criado antes do primeiro fork() e, portanto, disponível nos processos original e daemon.
  15. Chame exit() no processo original. O processo que chamou o daemon deve ser capaz de confiar que essa exit() acontece após a boot ser concluída e todos os canais de comunicação externos estarem estabelecidos e acessíveis.

Observe este aviso:

A function daemon() BSD daemon() não deve ser usada, pois implementa apenas um subconjunto dessas etapas.

Um daemon que precisa fornecer compatibilidade com sistemas SysV deve implementar o esquema apontado acima. No entanto, é recomendável tornar esse comportamento opcional e configurável por meio de um argumento de linha de comando para facilitar a debugging, bem como simplificar a integração em sistemas que usam o systemd.

Note que o daemon() não é compatível com POSIX .


Daemons de Novo Estilo

Para daemons de estilo novo, as etapas a seguir são recomendadas:

  1. Se SIGTERM for recebido, desligue o daemon e saia corretamente.
  2. Se SIGHUP for recebido, recarregue os arquivos de configuração, se isso se aplicar.
  3. Forneça um código de saída correto do processo do daemon principal, pois ele é usado pelo sistema init para detectar erros e problemas de serviço. Recomenda-se seguir o esquema de código de saída, conforme definido nas recomendações do LSB para scripts init do SysV .
  4. Se possível e aplicável, exponha a interface de controle do daemon através do sistema D-Bus IPC e pegue um nome de bus como último passo de boot.
  5. Para integração no systemd, forneça um arquivo de unidade de serviço que contenha informações sobre como iniciar, parar e manter o daemon. Veja systemd.service(5) para detalhes.
  6. Tanto quanto possível, confie na funcionalidade do sistema init para limitar o access do daemon a arquivos, serviços e outros resources, ou seja, no caso do systemd, confie no controle de limite de resources do systemd em vez de implementar o próprio, dependa do privilégio do systemd código em vez de implementá-lo no daemon e semelhante. Veja systemd.exec(5) para os controles disponíveis.
  7. Se o D-Bus for usado, torne seu ativador do daemon barramento fornecendo um arquivo de configuração de ativação do serviço D-Bus. Isso tem várias vantagens: seu daemon pode ser iniciado preguiçosamente sob demanda; pode ser iniciado em paralelo com outros daemons que o exigem – o que maximiza a velocidade de paralelização e boot ; seu daemon pode ser reiniciado em caso de falha sem perder pedidos de barramento, pois o barramento enfileira pedidos para serviços ativáveis. Veja abaixo para detalhes.
  8. Se o daemon fornecer serviços para outros processos locais ou clientes remotos por meio de um soquete, ele deverá ser ativado por soquete, seguindo o esquema apontado abaixo . Assim como a ativação do D-Bus, isso permite a boot de serviços sob demanda, além de permitir a paralelização aprimorada da boot do serviço. Além disso, para protocolos sem estado (como syslog, DNS), um daemon que implemente a ativação baseada em soquete pode ser reiniciado sem perder uma única solicitação. Veja abaixo para detalhes.
  9. Se aplicável, um daemon deve notificar o sistema init sobre a conclusão da boot ou atualizações de status por meio da interface sd_notify(3) .
  10. Em vez de usar a chamada syslog() para efetuar login diretamente no serviço syslog do sistema, um daemon de novo estilo pode optar por simplesmente efetuar o log do erro padrão via fprintf() , que é então encaminhado para o syslog pelo sistema init. Se os níveis de log forem necessários, eles podem ser codificados prefixando linhas de log individuais com strings como “<4>” (para nível de log 4 “WARNING” no esquema de prioridade do syslog), seguindo um estilo similar ao nível printk() do kernel Linux sistema. Para detalhes, consulte o sd-daemon(3) e systemd.exec(5) .

Para aprender mais leia o man 7 daemon inteiro man 7 daemon .

Eu posso parar no primeiro requisito “Um daemon que não pode ser parado …”

Não é possível meu amigo; no entanto, você pode conseguir o mesmo com uma ferramenta muito melhor, um módulo do kernel.

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

Todos os daemons podem ser parados. Alguns são mais facilmente parados do que outros. Mesmo um daemon emparelhado com o parceiro em espera, reaparecendo o parceiro se perdido, pode ser parado. Você só tem que trabalhar um pouco mais para isso.

Você não pode criar um processo no linux que não pode ser morto. O usuário root (uid = 0) pode enviar um sinal para um processo, e há dois sinais que não podem ser capturados, SIGKILL = 9, SIGSTOP = 19. E outros sinais (quando não são detectados) também podem resultar na finalização do processo.

Você pode querer uma function de daemonize mais geral, onde você pode especificar um nome para seu programa / daemon e um caminho para executar seu programa (talvez “/” ou “/ tmp”). Você também pode querer fornecer arquivo (s) para stderr e stdout (e possivelmente um caminho de controle usando stdin).

Aqui estão os necessários incluem:

 #include  //printf(3) #include  //exit(3) #include  //fork(3), chdir(3), sysconf(3) #include  //signal(3) #include  //umask(3) #include  //syslog(3), openlog(3), closelog(3) 

E aqui está uma function mais geral,

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

Aqui está um programa de exemplo, que se torna um daemon, fica por ai e depois sai.

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

Note que SIG_IGN indica para capturar e ignorar o sinal. Você pode criar um manipulador de sinal que pode registrar o recebimento de sinal e definir sinalizadores (como um sinalizador para indicar o desligamento normal).

Tente usar a function daemon :

 #include  int daemon(int nochdir, int noclose); 

Na página man :

A function daemon () é para programas que desejam se separar do terminal de controle e executados em segundo plano como daemons do sistema.

Se nochdir for zero, daemon () alterará o diretório de trabalho atual do processo de chamada para o diretório raiz (“/”); caso contrário, o diretório de trabalho atual permanecerá inalterado.

Se noclose for zero, o daemon () redireciona a input padrão, a saída padrão e o erro padrão para / dev / null; caso contrário, nenhuma alteração será feita nesses descritores de arquivos.

Se o seu aplicativo é um dos seguintes:

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

e você não se importa com uma dependência do NodeJS, em seguida, instale o NodeJS e, em seguida:

 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 colors pm2 list 

Para manter todos os aplicativos em execução na reboot (e daemonise pm2):

 pm2 startup pm2 save 

Agora você pode:

 service pm2 stop|restart|start|status 

(também permite que você preste atenção a alterações de código no diretório do aplicativo e reinicie automaticamente o processo do aplicativo quando uma alteração de código acontece)

Um daemon é apenas um processo em segundo plano. Se você quer iniciar seu programa quando o SO inicializa, no linux, você adiciona seu comando start em /etc/rc.d/rc.local (executado após todos os outros scripts) ou /etc/startup.sh

No Windows, você faz um serviço, registra o serviço e o configura para iniciar automaticamente na boot no painel administration -> services.

Ao chamar fork (), você criou um processo filho. Se o fork for bem-sucedido (a fork retornou um PID diferente de zero), a execução continuará a partir desse ponto a partir do processo filho. Neste caso, queremos sair do processo pai e continuar nosso trabalho no processo filho.

Talvez isso ajude: http://www.netzmafia.de/skripten/unix/linux-daemon-howto.html