Written by: Helio Loureiro
Category: Python
Hits: 8748

Às vezes eu escrevo programas.  Entre esse programas, alguns são daemons.
Que erro maldito!

Além da confusão com demônios, o que são daemons?

Daemons são os programas que rodam em background no sistema, não precisando de um terminal (console) anexado.  E qualquer tipo de programa pode ser um daemon, pra qualquer finalidade.

Em geral daemons seguem as seguintes regras pra se tornarem daemons:

  1. Mudar pro diretório raiz ("/").
  2. Realizar um fork().
  3. Finalizar o processo pai, ficando somente o filho, criado no fork().
  4. Realizar outro fork().
  5. Finalizar o primeiro processo filho, deixando somente o segundo, criado no segundo fork().

O segundo fork() é feito para garantir que o programa, através do segundo processo filho, seja "herdado" pelo processo init do sistema.

Em Python, sempre incluo uma função como essa:

   def Daemonize(self):
      """
      Fork to became a daemon.
      """
      
      if not self.isDaemon:
         try:
            self.run()
         except KeyboardInterrupt:
            sys.exit(0)
         return
      
      os.chdir("/")
      pid = os.fork()
      
      if (pid > 0):
         sys.exit(os.EX_OK)
      else:
         pid = os.fork()
         
         if (pid > 0):
            sys.exit(os.EX_OK)
         else:
            self.run()

Esse é parte de um método, mas poderia ser uma função.  A idéia é usar o getopt() para verificar as opções passadas e entrar no modo de daemon ou não, dependendo da opção passada, que modifica a variável booleana self.isDaemon.

Mas um dos meus programs começou a apresentar o seguinte erro:

helio@goosfraba:~$ connect_TSP.py ccn
IP already setup... skipping root access
Running as daemon
daemonized...
close failed in file object destructor:
IOError: [Errno 10] No child processes
Error in sys.excepthook:
Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/apport_python_hook.py", line 66, in apport_excepthook
    from apport.fileutils import likely_packaged, get_recent_crashes
RuntimeError: sys.meta_path must be a list of import hooks

Original exception was:
IOError: [Errno 10] No child processes

Inicialmente achei que era problema no "apport" com meu programa, que usa python-expect.  Mesmo com tal erro, o programa funcionava perfeitamente em background, como daemon.  Várias fontes na Internet, principalmente no Launchpad, o sistema de bug report do Ubuntu, várias pessoas reclamavam de tal erro como sendo problema do apport.

Após muito buscar a origem do problema, não no Ubuntu, mas no python, descobri que alguns file descriptors estavam causando esse erro, por continuarem abertos quando ocorria o fork().  Corrigi da seguinte forma:

   def Daemonize(self):
      """
      Fork to became a daemon.
      """
      
      if not self.isDaemon:
         try:
            self.run()
         except KeyboardInterrupt:
            sys.exit(0)
         return
      
      os.chdir("/")
      pid = os.fork()
      
      if (pid > 0):
         os.close(sys.stdin.fileno())
         os.close(sys.stout.fileno())
         os.close(sys.stderr.fileno())
         sys.exit(os.EX_OK)
      else:
         pid = os.fork()
         
         if (pid > 0):
            os.close(sys.stdin.fileno())
            os.close(sys.stdout.fileno())
            os.close(sys.stderr.fileno())
            sys.exit(os.EX_OK)
         else:
            self.run()

então bastou fechar os descritores de arquivo do STDIN, STDOU e STDERR pra ter certeza que o daemon não sairia com o erro acima.

Happy hacking :-)