Os artigos mais lidos

Quarta, Janeiro 30, 2013 Helio Loureiro Blog 19317
XGH é uma das coisas mais genias que surgiu nos últimos tempos, descrevendo a estupidez que se aplica em métodos ágeis, mas que reflete bem o ambiente corporativo. Infelizmente o site foi abandonado...
Sábado, Maio 03, 2008 Helio Loureiro FreeBSD 16582
This article is kindly linked by: http://tuxmobil.org/hp_compaq.html http://tuxmobil.org/mobile_bsd.html Barely finished my last article about FreeBSD (PRERELEASE at that time) on Compaq laptops,...
Quarta, Junho 11, 2014 Helio Loureiro Blog 16135
Nem só de discussões de licença, flamewares, releases de kernel e ativistas sapatênis vive o mundo do software livre. Às vezes é envolto de mistério também. Um dos enigmas recentes foi o sumiço da...
Sexta, Maio 20, 2005 Helio Loureiro Python 15689
Esse é o script usado para autenticar automaticamente no Speedy da Telefonica (região de São Paulo). Evita que usuários residenciais, como eu, precisem estar em frente à máquina, permitindo que a...

À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 :-)