terça-feira, 31 de julho de 2012

Linux: Como criar um daemon

Em sistemas operacionais multitarefas, um daemon, acrônimo de Disk And Execution MONitor (Monitor de Execução e de Disco), é um programa que roda de forma independente em segundo plano (aqueles programinhas que deixam o Windows extremamente lento com o tempo). Tipicamente, daemons tem nomes que terminam com a letra "d"; por exemplo, syslogd é o daemon que gerencia o log do sistema ("system log").
Em um ambiente Unix, o "processo pai" de um daemon é normalmente o processo init (PID=1).

De forma geral, os sistemas operacionais iniciam daemons durante o processo de boot. Os daemons podem fornecer todo tipo de funcionalidade para o sistema. Como agendamento de tarefas (como o cron), servidor web (como o httpd), servidor de banco de dados, entre muitos outros. Se você quiser, você pode criar um daemon seu para a finalidade que você preferir. É o básico disso que vamos ensinar como fazer aqui.


A origem do termo Daemon

A palavra daemon é uma forma alternativa de escrever demon (demônio em inglês). O termo foi trazido para a computação pelos programadores do projeto MAC do MIT. Eles tiraram a idéia do demônio de Maxwell, um ser imaginário de um experimento teórico muito famoso, no qual um demônio trabalharia constantemente em segundo plano, selecionando as partículas de uma caixa.

De qualquer forma, os sistemas Unix herdaram essa nomenclatura e, conforme os sistemas baseados em Unix se espalharam, o termo também se espalhou por toda parte.

Algo que também ajudou muito na expansão do termo, foi o fato de o BSD e algumas variantes dele terem adotado um daemon como mascote, porém, temos que ressaltar que o símbolo do BSD é uma variante de artes cristãs. Pois as interpretações, formas e comportamentos de daemons são extremamente diferentes de uma crença para outra.

É interessante notar que os daemons são seres presentes na mitologia grega também, alguns deles eram responsáveis por realizar tarefas de forma a livrar os deuses do incômodo de realizá-las eles mesmos (faziam o trabalho sujo).

Alguns nomes alternativos para daemons são serviços (Windows), subsistema (IBM z/OS), servidor virtual (IBM VM) e tarefa fantasma (XDS UTS).

Implementação no Linux (Ubuntu)

Para a implementação de daemons no Linux vamos utilizar o comando chkconfig. Pelo menos no Xubuntu, que é a distribuição que estou usando no momento, ele não vem instalado por padrão. Por isso, vamos de apt-get (ou yum ou como for na sua distro):
sudo apt-get install chkconfig

chkconfig

O chkconfig é um aplicativo de linha de comando que nos permite manipular a hierarquia do diretório /etc/rc[0-6].d liberando o administrador do sistema da tarefa de lidar diretamente com uma grande diversidade de links simbólicos usados nesses diretórios. Em português mais claro: o chkconfig serve para criar, listar ou modificar daemons.

A implementação do chkconfig foi feita inspirada no comando chkconfig presente no sistema operacional IRIX. Ao invés de manter as informações de configurações fora da hierarquia de /etc/rc[0-6].d, o chkconfig gere diretamente os links simbólicos em /etc/rc[0-6].d. Isto faz com que todas as configurações relativas a quais serviços serão iniciados com o sistema fiquem reunidas em um só lugar.

Listar os serviços

Quando o chkconfig é executado sem nenhuma opção, ele lista os daemons e seus estados. Veja o resultado parcial da execução no meu computador:
jayme@jayme-laptop:~$ chkconfig
acpi-support                2345
acpid                       off
alsa-mixer-save             off
anacron                     off
apache2                     on
apparmor                    on
apport                      off
atd                         off
aumix                       on
avahi-daemon                off
binfmt-support              on
kerneloops                  on
killprocs                   on
rcS                         off
rsync                       on
umountfs                    0
umountnfs.sh                0
umountroot                  0
unattended-upgrades         0
urandom                     0S
wpa-ifupdown                0
x11-common                  on
...

A lista completa é mais que o triplo disso (haja tranqueira!).

Agora, se um nome de serviço é fornecido, ele mostra as informações somente desse serviço, se ele existir.  Veja:
jayme@jayme-laptop:~$ chkconfig rsync
rsync                       on
jayme@jayme-laptop:~$ chkconfig daemoninexistente
daemoninexistente: unknown service

Podemos visualizar a lista de serviços passando o parâmetro --list. Ele mostra a mesma lista de quando executamos sem passar parâmetro algum, mais a lista de níveis de execução em que esse daemon está trabalhando. Não entraremos em detalhes sobre níveis de execução neste artigo, para mais informações leia este artigo aqui. O que é preciso saber mesmo é que cada nível de execução se refere um dos 7 arquivos do /etc/rc.d/rc0.d/ ao /etc/rc.d/rc6.d/ que são chamados em etapas definidas da inicialização do sistema.

Exemplo do uso do --list:
jayme@jayme-laptop:~$ chkconfig --list
abrtd   0:off   1:off   2:off   3:on    4:off   5:on    6:off
acpid   0:off   1:off   2:off   3:off   4:off   5:off   6:off
atd     0:off   1:off   2:off   3:on    4:on    5:on    6:off


Para verificar quais são os processos que são inicializados juntos com o seu sistema é preciso saber em qual o nível de execução seu sistema é carregado. Geralmente isso ocorre no nível 3 e, por isso, nosso exemplo será nesse nível:

jayme@jayme-laptop:~$ chkconfig --list | grep 3:on
acpi-support              0:off  1:off  2:on   3:on   4:on   5:on   6:off
apache2                   0:off  1:off  2:on   3:on   4:on   5:on   6:off
aumix                     0:off  1:off  2:on   3:on   4:on   5:on   6:off
binfmt-support            0:off  1:off  2:on   3:on   4:on   5:on   6:off


Para verificar a configuração de inicialização de um serviço em especial, use, por exemplo:
jayme@jayme-laptop:~$ chkconfig --list apache2
apache2                   0:off  1:off  2:on   3:on   4:on   5:on   6:off

Adicionar um novo serviço para a inicialização

Utilizamos a opção --add para adicionar um novo serviço a inicialização do sistema.
O exemplo a seguir mostra como adicionar um novo serviço a lista de inicialização do sistema:

jayme@jayme-laptop:~$ chkconfig --list iptables

jayme@jayme-laptop:~$ chkconfig --add iptables

jayme@jayme-laptop:~$ chkconfig --list iptables
iptables       0:off   1:off   2:on    3:on    4:on    5:on    6:off


Nota: chkconfig --add só adiciona um serviço já existente a lista de inicialização. Se o serviço não existir, primeiro você deverá instalá-lo ou criá-lo. É bem óbvio, mas é no óbvio, no básico, onde costumamos mais errar. Fique de olho.

Remover um serviço da lista de inicialização

O exemplo a seguir mostra que o serviço ip6tables está configurado para ser inicializado com o sistema:

jayme@jayme-laptop:~$ chkconfig --list ip6tables
ip6tables       0:off   1:off   2:off   3:on   4:off   5:off   6:off
Para removê-lo da lista de inicialização, use a opção --del:
jayme@jayme-laptop:~$ chkconfig --del ip6tables
jayme@jayme-laptop:~$ chkconfig --list | grep ip6tables


Ativar e desativar um serviço em algum nível de execução

Eventualmente você pode não querer apagar o serviço como um todo. Você pode querer só desativá-lo em alguns níveis de execução. O exemplo a seguir vai desativar o serviço nfsserver no nível 5:

jayme@jayme-laptop:~$ chkconfig --level 5 nfsserver off


Você pode combinar vários níveis de uma vez também. Por exemplo, os níveis 3 e 5:

jayme@jayme-laptop:~$ chkconfig --level 35 nfsserver off

Scripts dentro da hierarquia de rc.d

Toda vez que você adicionar ou remover um serviço utilizando o chkconfig, ele faz o seguinte aos arquivos nos subdiretórios de /etc/rc.d:

  • chkconfig -add: cria links simbólicos para os comandos de início e parada do serviço adicionado nos diretórios rc correspondentes aos níveis definidos.
  • chkconfig -del: remove o link simbólicos dos diretórios rc referentes aos níveis especificados.

O exemplo a seguir mostra um serviço chamado xinetd que esta habilitado para rodar tanto no level 3 quanto no 5.
Logo, o xinetd vai ter dois arquivos dentro do diretório rc3.d, e dois arquivos dentro de rc5.d. O arquivo cujo nome começa com K é usado ao desligar a máquina (K vem de kill). Já o arquivo que é usado para inicializar o seu processo é iniciado com S (S vem de start).
jayme@jayme-laptop:~$ chkconfig --list xinetd
xinetd                    0:off  1:off  2:off  3:on   4:off  5:on   6:off
Serviços baseados no xinetd:
jayme@jayme-laptop:~$ cd /etc/rc.d/rc3.d
jayme@jayme-laptop:~$ ls | grep xinetd
K08xinetd
S14xinetd
jayme@jayme-laptop:~$ cd /etc/rc.d/rc5.d
jayme@jayme-laptop:~$ ls | grep xinetd
K08xinetd
S14xinetd
Veja os nomes dos arquivos obtidos com as iniciais para inicialização e desligamento. Para ilustrar, veja a listagem do conteúdo de /etc/rc4.d/ do meu sistema:
jayme@jayme-laptop:~$ chkconfig --list | grep 3:on

jayme@jayme-laptop:~$ sudo ls -la /etc/rc4.d/*
-rw-r--r-- 1 root root 677 2012-01-20 08:03 /etc/rc4.d/README
lrwxrwxrwx 1 root root  20 2012-07-26 07:22 /etc/rc4.d/S20fancontrol -> ../init.d/fancontrol
lrwxrwxrwx 1 root root  20 2012-07-26 07:22 /etc/rc4.d/S20kerneloops -> ../init.d/kerneloops
lrwxrwxrwx 1 root root  15 2012-07-26 07:22 /etc/rc4.d/S21aumix -> ../init.d/aumix
lrwxrwxrwx 1 root root  19 2012-07-26 07:22 /etc/rc4.d/S25bluetooth -> ../init.d/bluetooth
lrwxrwxrwx 1 root root  14 2012-07-26 07:22 /etc/rc4.d/S50cups -> ../init.d/cups
lrwxrwxrwx 1 root root  20 2012-07-26 07:22 /etc/rc4.d/S50pulseaudio -> ../init.d/pulseaudio
lrwxrwxrwx 1 root root  15 2012-07-26 07:22 /etc/rc4.d/S50rsync -> ../init.d/rsync
lrwxrwxrwx 1 root root  15 2012-07-26 07:22 /etc/rc4.d/S50saned -> ../init.d/saned
lrwxrwxrwx 1 root root  19 2012-07-26 07:22 /etc/rc4.d/S70dns-clean -> ../init.d/dns-clean
lrwxrwxrwx 1 root root  18 2012-07-26 07:22 /etc/rc4.d/S70pppd-dns -> ../init.d/pppd-dns
lrwxrwxrwx 1 root root  24 2012-07-26 13:31 /etc/rc4.d/S90binfmt-support -> ../init.d/binfmt-support
lrwxrwxrwx 1 root root  17 2012-07-26 13:31 /etc/rc4.d/S91apache2 -> ../init.d/apache2
lrwxrwxrwx 1 root root  22 2012-07-26 07:22 /etc/rc4.d/S99acpi-support -> ../init.d/acpi-support
lrwxrwxrwx 1 root root  21 2012-07-26 07:22 /etc/rc4.d/S99grub-common -> ../init.d/grub-common
lrwxrwxrwx 1 root root  18 2012-07-26 07:22 /etc/rc4.d/S99ondemand -> ../init.d/ondemand
lrwxrwxrwx 1 root root  18 2012-07-26 07:22 /etc/rc4.d/S99rc.local -> ../init.d/rc.local
Note que no meu sistema, o caminho não é /etc/rc.d/rc4.d é /etc/rc4.d/. Conforme a distribuição com que você estiver trabalhando, a diferença de local pode ser maior ainda. Então fique atento.


Como iniciar um daemon/processo com o sistema

Bem, agora que já demos uma idéia básica sobre o chkconfig, vamos iniciar um. Primeiro, execute o comando:
insserv #se retornar isto
sudo: insserv: command not found

Não precisa entrar em pânico. É só um link simbólico faltando no seu sistema que pode ser criado com o seguinte comando:
ln -s /usr/lib/insserv/insserv /sbin/insserv

Tente executar novamente. É para estar tudo ok. Aqui no meu computador é mostrada uma lista enorme de erros quando executo. Mas é porque há muitos serviços criados por métodos alternativos ao uso de chkconfig. Para sumir com as mensagens, seria necessário converter todos os serviços para chkconfig. O que, cá entre nós, é muito trabalho para pouco retorno.

Para iniciar o nosso daemon usando o chkconfig teremos que escrever um shell script chamando os serviços que queremos e seguir um layout padrão dele. Aqui está o script que montei para executar o django-celery na inicialização do sistema:
### BEGIN INIT INFO
# Provides: djcelery
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start django celery daemon at boot time
# Description: Enable service provided by daemon.
### END INIT INFO
 
# chkconfig: 345 80 20
# description: django-celery daemon
# processname: djcelery

# /etc/init.d/djcelery

case "$1" in
  start)
        echo -e "Starting djcelery service\n"
        #To run it as root:
        python /home/meuprojeto/wsgi/dev/meuprojeto/async_tasks/start_djcelery.py
 ;;
  stop)
        echo -e "Stopping djcelery\n"
        #To run it as root:
        #/path/to/command/to/stop/counter
        #Or to run it as some other user:
        #/bin/su - username -c /path/to/command/to/stop/counter
        #echo "."
        kill -9 `ps -ef | grep -i 'meuprojeto/manage.py celeryd' | grep -v grep | awk '{print $2}'`
 ;;

  *)
        echo "Usage: /etc/init.d/djcelery {start|stop}"
        exit 1
esac

exit 0

No caso, o daemon já estava pronto. Era o daemon do django-celery que faz a comunicação entre o django e o celery (ferramenta similar ao cron). E permite que dentro do django possamos agendar tarefas. Bem útil isso. Enfim. O assunto é outro.

Note que há dois comandos aí dentro no case: start e stop. Sabe quando vamos iniciar ou parar, por exemplo, o apache? /etc/init.d/apache2 stop ou /etc/init.d/apache2 start. Vem daí. É preciso implementar cada um dos casos que quiser. Só o start e o stop que são obrigatórios. Já que é preciso iniciar e parar o daemon em algum momento e o sistema é quem fará isso por você.

Por padrão, crie o seu arquivo para chamar o seu daemon em /etc/init.d/. Depois dê a ele permissão de execução e execute o chkconfig:
chmod +x /etc/init.d/seuScript

chkconfig seuScript

Se não houver nenhum erro de sintaxe, seu daemon/processo deverá ter sido adicionado a inicialização do sistema.

Como finalmente criar um daemon

Bem, um daemon é só um processo que não termina. Fica sempre realizando algumas operações de tempo em tempo ou o tempo todo. Ou seja, um daemon em si é muito simples de ser feito. O problema é integrá-lo ao sistema.
Um exemplo de daemon bem simplérrimo em bash script:
while [ 1 ]; do
     echo "DAEMON EXEMPLO RODANDO";
     sleep 10; #pausa de 10 segundos
done;

Com isso, você pode por exemplo, fazer todas as suas operações na linguagem que preferir, e chamá-la de tempo em tempo através de um script simples como esse e gerenciá-lo através do outro script mais acima (o para a integração utilizando chkconfig).


Implementação de daemons(serviços) no Windows


Alguns exemplos e fontes para se criar seus próprios serviços no windows:



Implementação de daemons independentes de SO

Como temos diversas linguagens feitas para trabalharem na web como Python, Java, PHP, ASP, etc. Podemos elaborar daemons utilizando-as, uma vez que vão funcionar em qualquer sistema operacional. Infelizmente, às vezes ocorrem alguns problemas por causa de alguma pequena diferença na implementação da linguagem de um sistema para outro, além de restrições do seu servidor.

Um exemplo de implementação de um daemon em PHP pode ser visto aqui. Mas tome cuidado, um erro num laço infinito e toda a memória da sua máquina vai ser consumida rapidamente até sua máquina travar.

Neste fórum tem uma discussão de como criar daemons em Java.

E neste site tem um esqueleto de um código para criar daemons em Python.

Basta dar uma pesquisada por aí que tem até que bastante material sobre isso.
Sei que é chato ler isso, mas realmente tem bastante material sobre o assunto. ^^



Obs: no artigo sobre JSP  que enviei há algum tempo atrás, falei sobre como criar um daemon no linux. Porém, a forma que utilizei lá não está correta. Ela não funciona e demorei a descobrir pois utilizei para uma funcionalidade num sistema de um cliente que só foi passar por testes agora. Como o artigo é grande e há vários códigos fontes com caracteres especiais que o blogger insiste em remover, é melhor não editá-lo mais. Por isso a solução veio como um novo artigo. Desculpem-me por qualquer problema que tenha lhes causado.


Espero ter sido útil! ;)

Fontes:
http://en.wikipedia.org/wiki/Daemon_(computing)http://pt.wikipedia.org/wiki/Daemonhttp://pt.wikipedia.org/wiki/Daemon_(computa%C3%A7%C3%A3o)http://www.thegeekstuff.com/2011/06/chkconfig-examples/http://www.linuxquestions.org/questions/linux-server-73/creating-an-initscript-that-chkconfig-identifies-829496/http://loginroot.com/ubuntu-12-04-64bit-sbininsserv-no-such-file-or-directory/http://carlo-hamalainen.net/blog/2012/05/13/insserv-warning-script-k01vmware-missing-lsb-tags-and-overrides/




4 comentários: