                          Scripts rc.d praticos no BSD

  Yar Tikhiy

   <yar@FreeBSD.org>

   Revisao: dc5a2690b8

   Copyright (c) 2005-2006, 2012 The FreeBSD Project

   FreeBSD is a registered trademark of the FreeBSD Foundation.

   NetBSD is a registered trademark of the NetBSD Foundation.

   Many of the designations used by manufacturers and sellers to distinguish
   their products are claimed as trademarks. Where those designations appear
   in this document, and the FreeBSD Project was aware of the trademark
   claim, the designations have been followed by the "(TM)" or the "(R)"
   symbol.

   2018-09-12 00:52:17 +0000 por Edson Brandi.
   Resumo

   Os iniciantes podem achar dificil relacionar os fatos da documentac,ao
   formal do framework rc.d do BSD com as tarefas praticas do script rc.d.
   Neste artigo, consideramos alguns casos tipicos de complexidade crescente,
   vamos mostrar os recursos do rc.d adequados para cada caso e vamos
   discutir como eles funcionam. Esse exame deve fornecer pontos de
   referencia para um estudo mais aprofundado do design e da aplicac,ao
   eficiente do rc.d.

   [ Documento HTML em partes / Documento HTML completo ]

     ----------------------------------------------------------------------

   Indice

   1. Introduc,ao

   2. Esboc,ando a tarefa

   3. Um script ficticio

   4. Um script ficticio configuravel

   5. Inicializac,ao e desligamento de um daemon simples

   6. Inicializac,ao e desligamento de um daemon avanc,ado

   7. Conectando um script ao framework rc.d

   8. Dando mais flexibilidade a um script rc.d

   9. Leitura adicional

1. Introduc,ao

   Historicamente o BSD tinha um script de inicializac,ao monolitico, o
   /etc/rc. Ele era chamado pelo init( 8) no momento da inicializac,ao do
   sistema e executava todas as tarefas necessarias para a operac,ao
   multi-usuario: verificac,ao e montagem do sistemas de arquivos,
   configurac,ao de rede, iniciava daemons e assim por diante. A lista
   precisa de tarefas nao era a mesma em todos os sistemas; os
   administradores precisavam personaliza-lo. Com poucas excec,oes, o /etc/rc
   teve que ser modificado, e os verdadeiros hackers gostaram disso.

   O problema real com a abordagem monolitica era que ela nao fornecia nenhum
   controle sobre os componentes individuais iniciados a partir do /etc/rc.
   Por exemplo, o /etc/rc nao podia reiniciar um unico daemon. O
   administrador do sistema tinha que encontrar o processo daemon
   manualmente, mata-lo, esperar ate que ele realmente finalizasse, entao
   procurar pelas flags no /etc/rc, e finalmente digitar a linha de comando
   completa para iniciar o daemon novamente. A tarefa se tornaria ainda mais
   dificil e propensa a erros se o servic,o de reinicializac,ao consistisse
   em mais de um daemon ou exigisse ac,oes adicionais. Em poucas palavras, o
   unico script nao cumpriu o objetivo dos scripts: tornar a vida do
   administrador do sistema mais facil.

   Mais tarde, houve uma tentativa de dividir algumas partes do /etc/rc para
   iniciar os subsistemas mais importantes separadamente. O exemplo notorio
   foi o /etc/netstart para configurar a rede. Ele permitia acessar a rede a
   partir do modo single-user, mas nao se integrou bem ao processo de
   inicializac,ao automatica porque partes de seu codigo precisavam
   intercalar com ac,oes essencialmente nao relacionadas `a rede. Foi por
   isso que o /etc/netstart mudou para /etc/rc.network. Este ultimo nao era
   mais um script comum; ele era composto por um emaranhado de func,oes sh(1)
   chamadas pelo /etc/rc em diferentes estagios da inicializac,ao do sistema.
   No entanto, a medida que as tarefas de inicializac,ao cresciam variadas e
   sofisticadas, a abordagem "quase modular" tornou-se ainda mais engessada
   do que o monolitico /etc/rc.

   Sem um framework limpo e bem projetado, os scripts de inicializac,ao
   tiveram que se curvar para satisfazer as necessidades de desenvolvimento
   rapido dos sistemas operacionais baseados no BSD. Tornou-se obvio,
   finalmente, que mais passos eram necessarios no caminho para construc,ao
   de um sistema rc extensivel e customizavel. Assim nasceu o BSD rc.d. Seus
   pais reconhecidos foram o Luke Mewburn e a comunidade do NetBSD. Mais
   tarde ele foi importado para o FreeBSD. Seu nome se refere `a localizac,ao
   dos scripts do sistema para servic,os individuais, que e o /etc/rc.d. Em
   breve, vamos aprender sobre mais componentes do sistema rc.d e vamos ver
   como os scripts individuais sao invocados.

   As ideias basicas por tras do BSD rc.d sao modularidade fina e
   reutilizac,ao de codigo. Modularidade fina significa que cada "servic,o
   basico", como um daemon do sistema ou uma tarefa de inicializac,ao
   primitiva, obtem seu proprio script sh() capaz de iniciar o servic,o,
   para-lo, recarrega-lo e verificar seu status. Uma ac,ao especifica e
   escolhida pelo argumento da linha de comando para o script. O script
   /etc/rc ainda comanda a inicializac,ao do sistema, mas agora ele
   simplesmente invoca os scripts menores um por um com o argumento start. E
   facil executar tarefas de desligamento executando o mesmo conjunto de
   scripts com o argumento stop, o que e feito pelo /etc/rc.shutdown. Observe
   como isso segue de perto o modo Unix de ter um conjunto de pequenas
   ferramentas especializadas, cada uma cumprindo sua tarefa da melhor forma
   possivel. Reutilizac,ao de codigo significa que operac,oes comuns sao
   implementadas como func,oes sh(1) e coletadas em /etc/rc.subr . Agora, um
   script tipico pode conter apenas algumas linhas de codigo sh(1).
   Finalmente, uma parte importante do framework do rc.d e rcorder(8), o qual
   ajuda o /etc/rc a executar os pequenos scripts ordenadamente em relac,ao
   `as dependencias entre eles. Ele tambem pode ajudar o /etc/rc.shutdown,
   porque a ordem apropriada para a sequencia de encerramento e oposta `a da
   inicializac,ao.

   O design do BSD rc.d e descrito no artigo original de Luke Mewburn, e os
   componentes do rc.d sao documentados em grande detalhe nas respectivas
   paginas de manual. No entanto, pode nao parecer obvio para um novato em
   rc.d como amarrar os inumeros pedac,os juntos para criar um script bem
   estilizado para uma tarefa especifica. Portanto, este artigo tentara uma
   abordagem diferente para descrever o rc.d. Ele mostrara quais recursos
   devem ser usados em varios casos tipicos e por que. Note que este nao e um
   documento explicativo porque nosso objetivo nao e fornecer receitas
   prontas, mas mostrar algumas entradas faceis no dominio do rc.d. Nem este
   artigo e um substituto para as paginas de manual relevantes. Nao hesite em
   consulta-los para obter uma documentac,ao mais formal e completa ao ler
   este artigo.

   Existem pre-requisitos para entender este artigo. Primeiro de tudo, voce
   deve estar familiarizado com a linguagem de script sh(1) para poder
   dominar o rc.d. Alem disso, voce deve saber como o sistema executa as
   tarefas de inicializac,ao e encerramento do userland, o que esta descrito
   em rc(8).

   Este artigo foca no branch rc.d do FreeBSD. No entanto, ele tambem pode
   ser util para os desenvolvedores do NetBSD, porque os dois branchs rc.d do
   BSD nao apenas compartilham o mesmo design, mas tambem permanecem
   similares em seus aspectos visiveis aos autores do script.

2. Esboc,ando a tarefa

   Um pouco de considerac,ao antes de iniciar o $EDITOR nao ira prejudicar.
   Para escrever um script rc.d corretamente customizado para um servic,o do
   sistema, devemos poder responder as seguintes questoes primeiro:

     * O servic,o e obrigatorio ou opcional?

     * O script servira um unico programa, por exemplo, um daemon, ou
       realizara ac,oes mais complexas?

     * De quais outros servic,os nosso servic,o dependera e vice-versa?

   A partir dos exemplos que se seguem, veremos o porque e importante
   conhecer as respostas a essas perguntas.

3. Um script ficticio

   O script a seguir apenas emite uma mensagem toda vez que o sistema e
   inicializado:

 #!/bin/sh1

 . /etc/rc.subr2

 name="dummy"3
 start_cmd="${name}_start"4
 stop_cmd=":"5

 dummy_start()6
 {
         echo "Nothing started."
 }

 load_rc_config $name7
 run_rc_command "$1"8

   Os pontos a serem observadas sao:

   1 Um script interpretado deve comec,ar com a linha magica "shebang". Essa  
     linha especifica o programa interpretador para o script. Devido a linha  
     shebang, o script pode ser invocado exatamente como um programa binario, 
     desde que tenha o bit de execuc,ao definido. (Veja chmod(1).) Por        
     exemplo, um administrador do sistema pode executar nosso script          
     manualmente, a partir da linha de comando:                               
                                                                              
     # /etc/rc.d/dummy start                                                  
                                                                              
       Nota:                                                                  
                                                                              
     Para ser adequadamente gerenciado pelo framework do rc.d, seus scripts   
     precisam ser escritos na linguagem sh(1). Se voce tiver um servic,o ou   
     port que use um utilitario de controle binario ou uma rotina de          
     inicializac,ao escrita em outra linguagem, instale este elemento em      
     /usr/sbin (para o sistema) ou em /usr/local/sbin (para um port) e        
     invoque-o por meio de um script sh(1) no diretorio apropriado do rc.d.   
                                                                              
       Dica:                                                                  
                                                                              
     Caso voce queira aprender os detalhes do porque os scripts rc.d devem    
     ser escritos na linguagem sh(1), veja como o /etc/rc invoca-os por meio  
     de run_rc_script, e entao estude a implementac,ao de run_rc_script em    
     /etc/rc. subr.                                                           
   2 Em /etc/rc.subr, varias func,oes sh(1) estao definidas para serem        
     utilizadas por um script rc.d. As func,oes estao documentadas em         
     rc.subr(8). Embora seja teoricamente possivel escrever um script rc.d    
     sem usar o rc.subr(8), as suas func,oes sao extremamente uteis e tornam  
     o trabalho mais facil. Portanto, nao e de surpreender que todos recorram 
     a scripts rc.subr(8) em rc.d. Nos nao vamos ser uma excec,ao.            
                                                                              
     Um script rc.d deve "incluir" o /etc/rc.subr (isto por ser feito usando  
     o comando ".") antes que ele chame as func,oes do rc.subr(8) para que o  
     sh(1) tenha a oportunidade para aprender as func,oes. O estilo preferido 
     e incluir o /etc/rc.subr antes de tudo.                                  
                                                                              
       Nota:                                                                  
                                                                              
     Algumas func,oes uteis relacionadas a rede sao fornecidas por outro      
     arquivo include, o /etc/network.subr.                                    
   3 A variavel obrigatoria name especifica o nome do nosso script. Ela e     
     exigida pelo rc.subr(8). Ou seja, cada script rc.d deve definir a        
     variavel name antes de chamar func,oes do rc.subr(8).                    
                                                                              
     Agora e o momento certo para escolher um nome exclusivo para o nosso     
     script de uma vez por todas. Vamos usa-lo em varios lugares enquanto     
     desenvolvemos o script. Para comec,ar, tambem vamos dar o mesmo nome ao  
     arquivo de script.                                                       
                                                                              
       Nota:                                                                  
                                                                              
     O estilo atual do script rc.d e incluir valores atribuidos as variaveis  
     entre aspas duplas. Tenha em mente que e apenas um problema de estilo    
     que nem sempre pode ser aplicavel. Voce pode omitir com seguranc,a as    
     aspas das palavras simples sem os metacaracteres do sh(1) nelas,         
     enquanto em certos casos voce precisara de aspas simples para evitar     
     qualquer interpretac,ao do valor pelo sh(1). Um programador deve ser     
     capaz de dizer a sintaxe da linguagem a partir das convenc,oes de estilo 
     e bem como de usa-las sabiamente.                                        
   4 A ideia principal por tras do rc.subr(8) e que um script rc.d fornece    
     manipuladores, ou metodos, para o rc.subr(8) invocar. Em particular,     
     start, stop e outros argumentos para um script rc.d sao tratados desta   
     maneira. Um metodo e uma expressao sh(1) armazenada em uma variavel      
     denominada argument_cmd, no qual argument corresponde ao que pode ser    
     especificado na linha de comando do script. Vamos ver mais adiante como  
     o rc.subr(8) fornece metodos default para os argumentos padrao.          
                                                                              
       Nota:                                                                  
                                                                              
     Para tornar o codigo em rc.d mais uniforme, e comum usar ${name} onde    
     for apropriado. Assim, varias linhas podem ser copiadas de um script     
     para outro.                                                              
   5 Devemos ter em mente que o rc.subr(8) fornece metodos default para os    
     argumentos padroes. Consequentemente, devemos sobrescrever um metodo     
     default com uma expressao no-op sh() se desejarmos que ele nao fac,a     
     nada.                                                                    
   6 O corpo de um metodo sofisticado pode ser implementado como uma func,ao. 
     E uma boa ideia tornar o nome da func,ao significativo.                  
                                                                              
       Importante:                                                            
                                                                              
     E altamente recomendado adicionar o prefixo ${name} aos nomes de todas   
     as func,oes definidas em nosso script, para que eles nunca entrem em     
     conflito com as func,oes do rc.subr(8) ou outro arquivo de inclusao      
     comum.                                                                   
   7 Essa chamada ao rc.subr(8) carrega as variaveis do rc.conf(5). Nosso     
     script nao faz uso delas ainda, mas ainda assim e recomendado carregar o 
     rc.conf(5) pois podem haver variaveis rc.conf(5) controlando o           
     rc.subr(8) propriamente dito.                                            
   8 Geralmente este e o ultimo comando em um script rc.d. Ele invoca o       
     maquinario rc.subr(8) para executar a ac,ao solicitada usando as         
     variaveis e metodos que nosso script forneceu.                           

4. Um script ficticio configuravel

   Agora vamos adicionar alguns controles ao nosso script ficticio. Como voce
   deve saber, os scripts rc.d sao controlados pelo rc.conf(5). Felizmente, o
   rc.subr(8) esconde todas as complicac,oes de nos. O script a seguir usa o
   rc.conf(5) via rc.subr(8) para ver se ele esta habilitado em primeiro
   lugar, e buscar uma mensagem para mostrar no momento da inicializac,ao.
   Estas duas tarefas sao de fato independentes. Por um lado, um script rc.d
   pode apenas suportar a ativac,ao e desativac,ao de seu servic,o. Por outro
   lado, um script rc.d obrigatorio pode ter variaveis de configurac,ao. Nos
   vamos fazer as duas coisas no mesmo script:

 #!/bin/sh

 . /etc/rc.subr

 name=dummy
 rcvar=dummy_enable1

 start_cmd="${name}_start"
 stop_cmd=":"

 load_rc_config $name2
 : ${dummy_enable:=no} 3
 : ${dummy_msg="Nothing started."}4

 dummy_start()
 {
         echo "$dummy_msg"5
 }

 run_rc_command "$1"

   O que mudou neste exemplo?

   1 A variavel rcvar especifica o nome da variavel do botao ON/OFF.          
   2 Agora o load_rc_config e invocado anteriormente no script, antes que     
     qualquer variavel do rc.conf(5) seja acessada.                           
                                                                              
       Nota:                                                                  
                                                                              
     Ao examinar os scripts rc.d, tenha em mente que o sh(1) adia a           
     avaliac,ao de expressoes em uma func,ao ate que a func,ao seja chamada.  
     Portanto, nao e um erro invocar load_rc_config tao tarde quanto antes do 
     run_rc_comman e ainda acessar as variaveis do rc.conf(5) a partir do     
     metodo das func,oes exportadas para o run_rc_command. Isto ocorre porque 
     as func,oes do metodo devem ser chamadas por run_rc_command, que e       
     chamado apos o load_rc_config.                                           
   3 Um aviso sera emitido pelo run_rc_command se o proprio rcvar estiver     
     definido, mas a variavel de knob indicada nao estiver definida. Se o seu 
     script rc.d for para o sistema base, voce deve adicionar uma             
     configurac,ao padrao para o knob no /etc/defaults/rc.conf e documenta-lo 
     em rc.conf(5). Caso contrario, sera o seu script que devera fornecer uma 
     configurac,ao padrao para o knob. A abordagem canonica para o ultimo     
     caso e mostrada no exemplo.                                              
                                                                              
       Nota:                                                                  
                                                                              
     Voce pode fazer o rc.subr(8) agir como se o knob fosse definido como ON, 
     independentemente da sua configurac,ao atual, prefixando o argumento     
     para o script com one ou force, como em onestart ou forcestop. Tenha em  
     mente que o force tem outros efeitos perigosos que mencionaremos abaixo, 
     enquanto one apenas sobrescreve o knob ON/OFF. Por exemplo, suponha que  
     dummy_enable seja OFF. O comando a seguir executara o metodo start       
     apesar da configurac,ao:                                                 
                                                                              
     # /etc/rc.d/dummy onestart                                               
   4 Agora, a mensagem a ser mostrada no momento da inicializac,ao nao e mais 
     codificada no script. Ela e especificada por uma variavel do rc.conf(5)  
     chamada dummy_msg. Este e um exemplo trivial de como as variaveis do     
     rc.conf(5) podem controlar um script rc.d.                               
                                                                              
       Importante:                                                            
                                                                              
     Os nomes de todas as variaveis do rc.conf(5) usadas exclusivamente pelo  
     nosso script devem possuir o mesmo prefixo: $ {name} _. Por exemplo:     
     dummy_mode, dummy_state_file, e assim por diante.                        
                                                                              
       Nota:                                                                  
                                                                              
     Embora seja possivel usar um nome mais curto internamente, por exemplo,  
     apenas msg, adicionar o prefixo exclusivo ${name}_ a todos os nomes      
     globais introduzidos pelo nosso script nos salvara de possiveis colisoes 
     com o nome das func,oes existentes no rc.subr(8).                        
                                                                              
     Como regra, os scripts rc.d do sistema base nao precisam fornecer        
     valores padroes para as suas variaveis rc.conf(5) porque os padroes      
     devem ser definidos em /etc/defaults/rc.conf. Por outro lado, os scripts 
     rc.d para os ports devem fornecer os valores padroes, conforme mostrado  
     no exemplo.                                                              
   5 Aqui usamos dummy_msg para realmente controlar nosso script, ou seja,    
     para emitir uma mensagem variavel. O uso de uma func,ao de shell e um    
     exagero aqui, ja que ele so executa um unico comando; uma alternativa    
     igualmente valida seria:                                                 
                                                                              
     start_cmd="echo \"$dummy_msg\""                                          

5. Inicializac,ao e desligamento de um daemon simples

   Dissemos anteriormente que o rc.subr(8) poderia fornecer metodos padrao.
   Obviamente, estes padroes nao podem ser muito gerais. Eles sao adequados
   para o caso comum de iniciar e encerrar um programa daemon simples. Vamos
   supor agora que precisamos escrever um script rc.d para um daemon chamado
   mumbled. Aqui esta:

 #!/bin/sh

 . /etc/rc.subr

 name=mumbled
 rcvar=mumbled_enable

 command="/usr/sbin/${name}"1

 load_rc_config $name
 run_rc_command "$1"

   Agradavelmente simples, nao e? Vamos examinar nosso pequeno script. A
   unica coisa nova a observar e o seguinte:

   1 A variavel command e significativa para o rc.subr(8). Se estiver         
     definido, o rc.subr(8) agira de acordo com o cenario de servir um daemon 
     convencional. Em particular, os metodos padrao serao fornecidos para     
     tais argumentos: start, stop, restart, poll, e status.                   
                                                                              
     O daemon sera iniciado executando $command com os sinalizadores de linha 
     de comando especificados por $mumbled_flags. Assim, todos os dados de    
     entrada para o metodo padrao start estao disponiveis nas variaveis       
     configuradas pelo nosso script. Ao contrario do start, outros metodos    
     podem requerer informac,oes adicionais sobre o processo iniciado. Por    
     exemplo, stop deve conhecer o PID do processo para termina-lo. No        
     presente caso, rc.subr(8) varrera a lista de todos os processos,         
     procurando por um processo com seu nome igual a $procname. Esta ultima e 
     outra variavel de significado para rc.subr(8), e seu valor e padronizado 
     para command. Em outras palavras, quando definimos o command, procname e 
     efetivamente definido para o mesmo valor. Isso permite que nosso script  
     mate o daemon e verifique se ele esta sendo executado em primeiro lugar. 
                                                                              
       Nota:                                                                  
                                                                              
     Alguns programas sao, na verdade, scripts executaveis. O sistema executa 
     esse script iniciando seu interpretador e passando o nome do script para 
     ele como um argumento de linha de comando. Isso e refletido na lista de  
     processos, que podem confundir o rc.subr(8). Voce tambem deve definir o  
     command_interpreter para permitir que o rc.subr(8) saiba o nome real do  
     processo se o $command e um script.                                      
                                                                              
     Para cada script rc.d, existe uma variavel rc.conf() que tem precedencia 
     sobre command. Seu nome e construido da seguinte forma: ${name}_program, 
     onde name e a variavel obrigatoria que discutimos anteriormente. Por     
     exemplo, neste caso, sera mumbled_program . E rc.subr(8)que              
     organiza${name}_program para substituir o comando.                       
                                                                              
     Obviamente, o sh(1) permitira que voce defina ${name}_program a partir   
     do rc.conf (5) ou o proprio script, mesmo que o command esteja           
     indefinido. Nesse caso, as propriedades especiais de ${name}_program sao 
     perdidas e se tornam uma variavel comum que seu script pode usar para    
     seus proprios propositos. No entanto, o uso exclusivo de ${name}_program 
     e desencorajado porque usa-lo junto com o command tornou-se um idioma na 
     escrita de scripts rc.d.                                                 
                                                                              
     Para obter informac,oes mais detalhadas sobre metodos padroes, consulte  
     rc.subr(8).                                                              

6. Inicializac,ao e desligamento de um daemon avanc,ado

   Vamos adicionar um pouco de carne aos ossos do script anterior e torna-lo
   mais complexo e cheio de funcionalidades. Os metodos padroes podem fazer
   um bom trabalho para nos, mas podemos precisar ajustar alguns dos seus
   aspectos. Agora vamos aprender como ajustar os metodos padroes para as
   nossas necessidades.

 #!/bin/sh

 . /etc/rc.subr

 name=mumbled
 rcvar=mumbled_enable

 command="/usr/sbin/${name}"
 command_args="mock arguments > /dev/null 2>&1"1

 pidfile="/var/run/${name}.pid"2

 required_files="/etc/${name}.conf /usr/share/misc/${name}.rules"3

 sig_reload="USR1"4

 start_precmd="${name}_prestart"5
 stop_postcmd="echo Bye-bye"6

 extra_commands="reload plugh xyzzy"7

 plugh_cmd="mumbled_plugh"8
 xyzzy_cmd="echo 'Nothing happens.'"

 mumbled_prestart()
 {
         if checkyesno mumbled_smart; then9
                 rc_flags="-o smart ${rc_flags}"10
         fi
         case "$mumbled_mode" in
         foo)
                 rc_flags="-frotz ${rc_flags}"
                 ;;
         bar)
                 rc_flags="-baz ${rc_flags}"
                 ;;
         *)
                 warn "Invalid value for mumbled_mode"11
                 return 112
                 ;;
         esac
         run_rc_command xyzzy13
         return 0
 }

 mumbled_plugh()14
 {
         echo 'A hollow voice says "plugh".'
 }

 load_rc_config $name
 run_rc_command "$1"

1  Argumentos adicionais para $command podem ser passados em command_args. Eles serao adicionados a 
   linha de comando apos $mumbled_flags. Como a linha de comando final e passada para eval para sua 
   execuc,ao real, os redirecionamentos de entrada e saida podem ser especificados em command_args. 
                                                                                                    
     Nota:                                                                                          
                                                                                                    
   Nunca inclua opc,oes tracejadas, como -X ou --foo, em command_args. O conteudo de command_args   
   aparecera no final da linha de comando final, portanto e provavel que eles sigam os argumentos   
   presentes em ${name}_flags; mas a maioria dos comandos nao reconhecera opc,oes tracejadas apos   
   argumentos comuns. Uma maneira melhor de passar opc,oes adicionais para $command e adiciona-las  
   ao inicio de ${name}_flags . Outra maneira e modificar rc_flags como mostrado posteriormente .   
2  Um daemon de boas maneiras deve criar um pidfile para que seu processo possa ser encontrado com  
   mais facilidade e confiabilidade. A variavel pidfile, se configurada, informa ao rc.subr(8) onde 
   pode encontrar o pidfile para seus metodos padrao possam usar.                                   
                                                                                                    
     Nota:                                                                                          
                                                                                                    
   De fato, o rc.subr(8) tambem usara o pidfile para ver se o daemon ja esta em execuc,ao antes de  
   inicia-lo. Esta verificac,ao pode ser ignorada usando o argumento faststart.                     
3  Se o daemon nao puder ser executado a menos que existam certos arquivos, apenas liste-os em      
   required_files, e rc.subr(8) ira verificar se esses arquivos existem antes de iniciar o daemon.  
   Tambem existem required_dirs e required_vars para diretorios e variaveis de ambiente,            
   respectivamente. Todos eles sao descritos em detalhes em rc.subr(8).                             
                                                                                                    
     Nota:                                                                                          
                                                                                                    
   O metodo padrao de rc.subr(8) pode ser forc,ado a ignorar as verificac,oes de pre-requisitos     
   usando forcestart como o argumento para o script.                                                
4  Podemos personalizar sinais para enviar para o daemon caso eles sejam diferentes dos mais        
   conhecidos. Em particular, sig_reload especifica o sinal que faz o daemon recarregar sua         
   configurac,ao; e SIGHUP por padrao. Outro sinal e enviado para parar o processo do daemon; o     
   padrao e SIGTERM, mas isso pode ser alterado definindo sig_stop apropriadamente.                 
                                                                                                    
     Nota:                                                                                          
                                                                                                    
   Os nomes dos sinais devem ser especificados para o rc.subr(8) sem o prefixo SIG, como e mostrado 
   no exemplo. A versao do FreeBSD do kill(1) pode reconhecer o prefixo SIG, mas as versoes de      
   outros tipos de sistema operacional nao.                                                         
5  Realizar tarefas adicionais antes ou depois dos metodos padrao e facil. Para cada argumento de   
6  comando suportado pelo nosso script, podemos definir o argumento _precmd e _postcmd. Esses       
   comandos no sh (1) sao invocados antes e depois do respectivo metodo, como e evidente em seus    
   nomes.                                                                                           
                                                                                                    
     Nota:                                                                                          
                                                                                                    
   Sobrescrever um metodo padrao com um argumento _cmd personalizado ainda nao nos impede de fazer  
   uso do argumento _precmd ou argumento _postcmd se precisarmos. Em particular, o primeiro e bom   
   para verificar condic,oes personalizadas e sofisticadas que devem ser atendidas antes de         
   executar o comando em si. Usar o argumento _precmd junto com o argumento _cmd nos permite        
   separar logicamente as verificac,oes da ac,ao.                                                   
                                                                                                    
   Nao se esquec,a de que voce pode amontoar qualquer expressao valida do sh(1) nos metodos, pre e  
   pos-comandos definidos por voce. Apenas invocar uma func,ao que faz com que o trabalho real seja 
   um bom estilo na maioria dos casos, mas nunca deixe o estilo limitar sua compreensao do que esta 
   acontecendo por tras da cortina.                                                                 
7  Se quisermos implementar argumentos customizados, que tambem podem ser considerados como         
   comandos para o nosso script, precisamos lista-los em extra_commands e fornecer metodos para     
   manipula-los.                                                                                    
                                                                                                    
     Nota:                                                                                          
                                                                                                    
   O comando reload e especial. Por um lado, tem um metodo predefinido em rc.subr(8). Por outro     
   lado, reload nao e oferecido por padrao. A razao e que nem todos os daemons usam o mesmo         
   mecanismo de recarga e alguns nao tem nada para recarregar. Portanto, precisamos solicitar       
   explicitamente que a funcionalidade incorporada seja fornecida. Podemos fazer isso via           
   extra_commands.                                                                                  
                                                                                                    
   O que obtemos do metodo padrao para reload? Muitas vezes, os daemons recarregam sua              
   configurac,ao na recepc,ao de um sinal - normalmente, SIGHUP. Portanto, o rc.subr(8) tenta       
   recarregar o daemon enviando um sinal para ele. O sinal e predefinido para SIGHUP, mas pode ser  
   personalizado via sig_reload, caso necessario.                                                   
8  Nosso script suporta dois comandos nao padrao, plugh e xyzzy. Nos os vimos listados em           
14 extra_commands, e agora e hora de fornecer metodos para eles. O metodo para xyzzy e apenas       
   embutido, enquanto que para plugh e implementado como a func,ao mumbled_plugh.                   
                                                                                                    
   Comandos nao padrao nao sao chamados durante a inicializac,ao ou o desligamento. Geralmente eles 
   sao para a conveniencia do administrador do sistema. Eles tambem podem ser usados de outros      
   subsistemas, por exemplo, devd(8) se especificado em devd.conf(5).                               
                                                                                                    
   A lista completa de comandos disponiveis pode ser encontrada na linha de uso impressa por        
   rc.subr(8) quando o script e invocado sem argumentos. Por exemplo, aqui esta a linha de uso do   
   script em estudo:                                                                                
                                                                                                    
   # /etc/rc.d/mumbled                                                                              
   Uso: /etc/rc.d/mumbled [fast|force|one](start|stop|restart|rcvar|reload|plugh|xyzzy|status|poll) 
13 Um script pode invocar seus proprios comandos padrao ou nao padrao, se necessario. Isto pode     
   parecer semelhante as func,oes de chamada, mas sabemos que comandos e func,oes de shell nem      
   sempre sao a mesma coisa. Por exemplo, xyzzy nao e implementado como uma func,ao aqui. Alem      
   disso, pode haver um pre-comando e um pos-comando, que devem ser chamados ordenadamente.         
   Portanto, a maneira correta de um script executar seu proprio comando e por meio de rc.subr(8),  
   conforme mostrado no exemplo.                                                                    
9  Uma func,ao util chamada checkyesno e fornecida por rc.subr(8). Ele usa um nome de variavel como 
   argumento e retorna um codigo de saida zero se, e somente se, a variavel estiver configurada     
   como YES, ou TRUE, ou ON, ou 1, sem distinc,ao entre maiusculas e minusculas; um codigo de saida 
   diferente de zero e retornado de outra forma. No ultimo caso, a func,ao testa a variavel como    
   sendo definida como NO,FALSE,OFFou0 insensivel a maiusculas e minusculas; imprime uma mensagem   
   de aviso se a variavel contiver qualquer outra coisa, ou seja, lixo.                             
                                                                                                    
   Tenha em mente que para o sh(1) um codigo de saida zero significa verdadeiro e um codigo de      
   saida diferente de zero significa falso.                                                         
                                                                                                    
     Importante:                                                                                    
                                                                                                    
   A func,ao checkyesno recebe um nome da variavel. Nao passe o valor expandido de uma variavel     
   para ele; nao funcionara como esperado.                                                          
                                                                                                    
   O uso correto de checkyesno e:                                                                   
                                                                                                    
   if checkyesno mumbled_enable; then                                                               
           foo                                                                                      
   fi                                                                                               
                                                                                                    
   Pelo contrario, chamar checkyesno como mostrado abaixo nao funcionara - pelo menos nao como      
   esperado:                                                                                        
                                                                                                    
   if checkyesno "${mumbled_enable}"; then                                                          
           foo                                                                                      
   fi                                                                                               
10 Podemos afetar os sinalizadores a serem passados para $command modificando rc_flags em           
   $start_precmd.                                                                                   
11 Em certos casos, podemos precisar emitir uma mensagem importante que tambem deve ser enviada     
   para o syslog. Isto pode ser feito facilmente com as seguintes func,oes rc.subr(8): debug, info, 
   warn e err. A ultima func,ao, em seguida, sai do script com o codigo especificado.               
12 Os codigos de saida dos metodos e seus pre-comandos nao sao apenas ignorados por padrao. Se o    
   argumento _precmd retornar um codigo de saida diferente de zero, o metodo principal nao sera     
   executado. Por sua vez, o argumento_postcmd nao sera invocado a menos que o metodo principal     
   retorne um codigo de saida zero.                                                                 
                                                                                                    
     Nota:                                                                                          
                                                                                                    
   No entanto, o rc.subr(8) pode ser instruido a partir da linha de comando para ignorar esses      
   codigos de saida e invocar todos os comandos, prefixando um argumento com force, como em         
   forcestart.                                                                                      

7. Conectando um script ao framework rc.d

   Depois que um script foi escrito, ele precisa ser integrado em rc.d>. O
   passo crucial e instalar o script em /etc/rc.d (para o sistema base) ou
   /usr/local/etc/rc.d (para ports). Ambos <bsd.prog.mk > e < bsd.port.mk >
   fornecer ganchos convenientes para isso, e geralmente voce nao precisa se
   preocupar com a propriedade e o modo adequado. Os scripts do sistema devem
   ser instalados a partir do src /etc/rc.d atraves do Makefile encontrado
   la. Os scripts de porta podem ser instalados usando USE_RC_SUBR conforme
   descrito em no Manual do Porter.

   No entanto, devemos considerar antecipadamente o local do nosso script na
   sequencia de inicializac,ao do sistema. O servic,o manipulado pelo nosso
   script provavelmente depende de outros servic,os. Por exemplo, um daemon
   de rede nao pode funcionar sem as interfaces de rede e o roteamento em
   funcionamento. Mesmo que um servic,o parec,a nao exigir nada, dificilmente
   pode ser iniciado antes que os sistemas de arquivos basicos tenham sido
   verificados e montados.

   Nos ja mencionamos o rcorder(8). Agora e hora de dar uma olhada de perto.
   Em poucas palavras, o rcorder(8) pega um conjunto de arquivos, examina seu
   conteudo e imprime uma lista ordenada por dependencia de arquivos do
   conjunto para stdout. O objetivo e manter as informac,oes de dependencia
   dentro dos arquivos para que cada arquivo possa falar por si so. Um
   arquivo pode especificar as seguintes informac,oes:

     * os nomes das "condic,oes" (o que significa servic,os para nos) que ele
       fornece;

     * os nomes das "condic,oes" que ele requer;

     * os nomes das "condic,oes" deste arquivo devem ser executados antes;

     * palavras-chave adicionais que podem ser usadas para selecionar um
       subconjunto de todo o conjunto de arquivos (rcorder(8) podem ser
       instruidos atraves de opc,oes para incluir ou omitir os arquivos com
       determinadas palavras-chave listadas.)

   Nao e surpresa que rcorder(8) possa manipular apenas arquivos de texto com
   uma sintaxe proxima a de sh(1). Ou seja, linhas especiais compreendidas
   por rcorder(8) se parecem com comentarios sh(1). A sintaxe de tais linhas
   especiais e bastante rigida para simplificar seu processamento. Veja
   rcorder(8) para detalhes.

   Alem de usar linhas especiais do rcorder(8), um script pode insistir em
   sua dependencia de outro servic,o apenas iniciando-o forc,adamente. Isso
   pode ser necessario quando o outro servic,o e opcional e nao sera iniciado
   automaticamente porque o administrador do sistema o desativou por engano
   no rc.conf(5).

   Com este conhecimento geral em mente, vamos considerar o simples script
   daemon aprimorado com coisas de dependencia:

 #!/bin/sh

 # PROVIDE: mumbled oldmumble 1
 # REQUIRE: DAEMON cleanvar frotz2
 # BEFORE:  LOGIN3
 # KEYWORD: nojail shutdown4

 . /etc/rc.subr

 name=mumbled
 rcvar=mumbled_enable

 command="/usr/sbin/${name}"
 start_precmd="${name}_prestart"

 mumbled_prestart()
 {
         if ! checkyesno frotz_enable && \
             ! /etc/rc.d/frotz forcestatus 1>/dev/null 2>&1; then
                 force_depend frotz || return 15
         fi
         return 0
 }

 load_rc_config $name
 run_rc_command "$1"

   Como antes, a analise detalhada segue:

   1   Esta linha declara os nomes das "condic,oes" que nosso script fornece. 
       Agora, outros scripts podem registrar uma dependencia em nosso script  
       por estes nomes.                                                       
                                                                              
         Nota:                                                                
                                                                              
       Geralmente, um script especifica uma unica condic,ao fornecida. No     
       entanto, nada nos impede de listar varias condic,oes, por exemplo, por 
       razoes de compatibilidade.                                             
                                                                              
       Em qualquer caso, o nome da condic,ao principal, ou a unica, PROVIDE:  
       deve ser o mesmo que ${name}.                                          
   2 3 Portanto, nosso script indica quais condic,oes "" sao fornecidas por   
       outros scripts dos quais depende. De acordo com as linhas, nosso       
       script pede ao rcorder(8) para coloca-lo apos o(s) script(s)           
       fornecendo DAEMON e cleanvar, mas antes disso prover LOGIN.            
                                                                              
         Nota:                                                                
                                                                              
       A linha BEFORE: nao deve ser abusada para contornar uma lista de       
       dependencias incompleta no outro script. O caso apropriado para usar o 
       BEFORE: e quando o outro script nao se importa com o nosso, mas nosso  
       script pode fazer sua tarefa melhor se for executado antes do outro.   
       Um tipico exemplo da vida real sao as interfaces de rede versus o      
       firewall: embora as interfaces nao dependam do firewall em realizar    
       seu trabalho, a seguranc,a do sistema se beneficiara do firewall estar 
       pronto antes que haja qualquer trafego de rede.                        
                                                                              
       Alem das condic,oes correspondentes a um unico servic,o, existem       
       meta-condic,oes e seus scripts "placeholder" usados para garantir que  
       determinados grupos de operac,oes sejam executados antes dos outros.   
       Estes sao denotados pelos nomes UPPERCASE. Sua lista e finalidades     
       podem ser encontradas em rc(8).                                        
                                                                              
       Tenha em mente que colocar um nome de servic,o na linha REQUIRE: nao   
       garante que o servic,o estara realmente em execuc,ao no momento em que 
       nosso script for iniciado. O servic,o necessario pode falhar ao        
       iniciar ou simplesmente ser desativado em rc.conf(5). Obviamente, o    
       rcorder(8) nao pode controlar tais detalhes, e o rc(8) tambem nao fara 
       isso. Consequentemente, o aplicativo iniciado por nosso script deve    
       ser capaz de lidar com quaisquer servic,os necessarios indisponiveis.  
       Em certos casos, podemos ajuda-lo conforme discutido abaixo.           
   4   Como lembramos do texto acima, as palavras-chave do rcorder(8) podem   
       ser usadas para selecionar ou deixar alguns scripts. Ou seja, qualquer 
       consumidor rcorder(8) pode especificar atraves das opc,oes -k e -s que 
       as palavras-chave estao na "keep list" e na "skip list",               
       respectivamente. De todos os arquivos a serem classificados, o         
       rcorder(8) selecionara apenas aqueles que tiverem uma palavra-chave da 
       lista de manutenc,ao (a menos que vazia) e nao uma palavra-chave da    
       lista de itens ignorados.                                              
                                                                              
       No FreeBSD, o rcorder(8) e usado por /etc/rc e /etc/rc.shutdown. Esses 
       dois scripts definem a lista padrao de palavras-chave do rc.d do       
       FreeBSD e seus significados da seguinte forma:                         
                                                                              
       nojail                                                                 
                                                                              
          O servic,o nao e para o ambiente jail(8). Os procedimentos          
          automaticos de inicializac,ao e encerramento ignoram o script se    
          estiverem executando dentro de uma jail.                            
                                                                              
       nostart                                                                
                                                                              
          O servic,o deve ser iniciado manualmente ou nao iniciado. O         
          procedimento de inicializac,ao automatica ira ignorar o script. Em  
          conjunto com a palavra-chave shutdown, isso pode ser usado para     
          escrever scripts que fazem algo apenas no desligamento do sistema.  
                                                                              
       shutdown                                                               
                                                                              
          Esta palavra-chave deve ser listada explicitamente se o servic,o    
          precisar ser interrompido antes do desligamento do sistema.         
                                                                              
         Nota:                                                                
                                                                              
          Quando o sistema for desligado, o /etc/rc.shutdown sera executado.  
          Ele assume que a maioria dos scripts rc.d nao tem nada a fazer      
          naquele momento. Portanto, /etc/rc.shutdown invoca seletivamente os 
          scripts rc.d com a palavra-chave shutdown, efetivamente ignorando o 
          restante dos scripts. Para um desligamento ainda mais rapido, o     
          /etc/rc.shutdown passa o comando faststop para os scripts que       
          executa, para que eles ignorem as verificac,oes preliminares, por   
          exemplo, a verificac,ao do pidfile. Como os servic,os dependentes   
          devem ser parados antes de seus pre-requisitos, /etc/rc.shutdown    
          executa os scripts na ordem de dependencia inversa.                 
                                                                              
          Se estiver escrevendo um script rc.d real, voce deve considerar se  
          e relevante no momento do desligamento do sistema. Por exemplo, se  
          o seu script funcionar apenas em resposta ao comando start, nao     
          sera necessario incluir essa palavra-chave. No entanto, se o seu    
          script gerenciar um servic,o, provavelmente sera uma boa ideia      
          para-lo antes que o sistema prossiga para o estagio final de sua    
          sequ:encia de desligamento descrita em halt(8). Em particular, um   
          servic,o deve ser interrompido explicitamente se precisar de tempo  
          consideravel ou ac,oes especiais para encerrar de forma limpa. Um   
          exemplo tipico de tal servic,o e um mecanismo de banco de dados.    
   5   Para comec,ar, force_depend deve ser usado com muito cuidado.          
       Geralmente e melhor revisar a hierarquia de variaveis de configurac,ao 
       para seus scripts rc. se eles forem interdependentes.                  
                                                                              
       Se voce ainda nao pode fazer sem force_depend, o exemplo oferece uma   
       expressao de como invoca-lo condicionalmente. No exemplo, nosso daemon 
       mumbled requer que outro, frotz, seja iniciado antecipadamente. No     
       entanto, frotz e opcional tambem; e rcorder(8) nao sabe nada sobre     
       esses detalhes. Felizmente, nosso script tem acesso a todas as         
       variaveis rc.conf(5). Se frotz_enable estiver como true, esperamos     
       pelo melhor e dependemos de rc.d para iniciar frotz. Caso contrario,   
       nos forc,adamente verificaremos o status de frotz. Finalmente, impomos 
       nossa dependencia ao frotz se ele nao estiver sendo executado. Uma     
       mensagem de aviso sera emitida por force_depend porque ele deve ser    
       chamado apenas se um erro de configurac,ao for detectado.              

8. Dando mais flexibilidade a um script rc.d

   Quando chamado durante a inicializac,ao ou desligamento, um script rc.d
   deve agir em todo o subsistema pelo qual e responsavel. Por exemplo,
   /etc/rc.d/netif deve iniciar ou parar todas as interfaces de rede
   descritas por rc.conf(5). Qualquer tarefa pode ser indicada exclusivamente
   por um unico argumento de comando, como start ou stop. Entre a
   inicializac,ao e o desligamento, os scripts rc.d ajudam o administrador a
   controlar o sistema em execuc,ao, e e quando surge a necessidade de mais
   flexibilidade e precisao. Por exemplo, o administrador pode querer
   adicionar as configurac,oes de uma nova interface de rede ao rc.conf(5) e
   entao inicia-lo sem interferir o funcionamento das interfaces existentes.
   Da proxima vez, o administrador pode precisar desligar uma unica interface
   de rede. No espirito da linha de comando, o respectivo script rc.d
   solicita um argumento extra, o nome da interface.

   Felizmente, rc.subr(8) permite passar qualquer numero de argumentos para
   os metodos do script (dentro dos limites do sistema). Devido a isso, as
   alterac,oes no proprio script podem ser minimas.

   Como o rc.subr(8) pode obter acesso aos argumentos de linha de comando
   extra. Deveria pega-los diretamente? Nao por qualquer meio. Primeiro, uma
   func,ao sh(1) nao tem acesso aos parametros posicionais de seu chamador,
   mas o rc.subr(8) e apenas uma despedida de tais func,oes. Em segundo
   lugar, a boa maneira de rc.d determina que e para o script principal
   decidir quais argumentos devem ser passados para seus metodos.

   Portanto, a abordagem adotada por rc.subr(8) e a seguinte: run_rc_command
   transmite todos os seus argumentos, mas o primeiro um para o respectivo
   metodo na integra. O primeiro, omitido, argumento e o nome do proprio
   metodo: start,stop, etc. Ele sera deslocado por run_rc_command, entao o
   que e $2 na linha de comando original sera apresentado como $1 ao metodo,
   e assim por diante.

   Para ilustrar essa oportunidade, vamos modificar o script ficticio
   primitivo para que suas mensagens dependam dos argumentos adicionais
   fornecidos. Aqui vamos nos:

 #!/bin/sh

 . /etc/rc.subr

 name="dummy"
 start_cmd="${name}_start"
 stop_cmd=":"
 kiss_cmd="${name}_kiss"
 extra_commands="kiss"

 dummy_start()
 {
         if [ $# -gt 0 ]; then1
                 echo "Greeting message: $*"
         else
                 echo "Nothing started."
         fi
 }

 dummy_kiss()
 {
         echo -n "A ghost gives you a kiss"
         if [ $# -gt 0 ]; then2
                 echo -n " and whispers: $*"
         fi
         case "$*" in
         *[.!?])
                 echo
                 ;;
         *)
                 echo .
                 ;;
         esac
 }

 load_rc_config $name
 run_rc_command "$@"3

   Quais mudanc,as essenciais podemos notar no script?

   1 Todos os argumentos digitados apos start podem terminar como parametros  
     posicionais para o respectivo metodo. Podemos usa-los de qualquer        
     maneira de acordo com nossa tarefa, habilidades e fantasia. No exemplo   
     atual, apenas passamos todos eles para echo(1) como uma cadeia na linha  
     seguinte - note $* entre aspas duplas. Aqui esta como o script pode ser  
     chamado agora:                                                           
                                                                              
     # /etc/rc.d/dummy start                                                  
     Nothing started.                                                         
     # /etc/rc.d/dummy start Hello world!                                     
     Greeting message: Hello world!                                           
   2 O mesmo se aplica a qualquer metodo que nosso script fornec,a, nao       
     apenas a um metodo padrao. Nos adicionamos um metodo customizado chamado 
     kiss, e ele pode tirar proveito dos argumentos extras da mesma forma que 
     o start tira. Por exemplo:                                               
                                                                              
     # /etc/rc.d/dummy kiss                                                   
     A ghost gives you a kiss.                                                
     # /etc/rc.d/dummy kiss Once I was Etaoin Shrdlu...                       
     A ghost gives you a kiss and whispers: Once I was Etaoin Shrdlu...       
   3 Se quisermos apenas passar todos os argumentos extras para qualquer      
     metodo, podemos simplesmente substituir "$@" por "$ 1" na ultima linha   
     do nosso script, onde invocamos o run_rc_command.                        
                                                                              
       Importante:                                                            
                                                                              
     Um programador sh(1) deve entender a diferenc,a sutil entre $* e $@ como 
     as formas de designar todos os parametros posicionais. Para sua          
     discussao aprofundada, consulte um bom manual sobre programac,ao de      
     scripts sh(1). Nao use estas expressoes ate que voce as compreenda       
     completamente, porque o uso incorreto delas resultara em scripts         
     inseguros e contendo bugs.                                               
                                                                              
       Nota:                                                                  
                                                                              
     Atualmente, o run_rc_command pode ter um bug que o impede de manter os   
     limites originais entre os argumentos. Ou seja, argumentos com espac,os  
     em branco incorporados podem nao ser processados corretamente. O bug     
     deriva do uso inadequado de $*.                                          

9. Leitura adicional

   O artigo original de Luke Mewburn oferece uma visao geral do rc.d e o
   raciocinio detalhado que o levou a suas decisoes de design. Ele fornece
   informac,oes sobre toda o framework do rc.d e o seu lugar em um moderno
   sistema operacional BSD.

   As paginas de manual rc (8), rc.subr(8) e rcorder(8) documentam os
   componentes do rc.d com grande detalhe. Voce nao pode usar totalmente o
   poder do rc.d sem estudar as paginas de manual e se referir a elas
   enquanto escreve seus proprios scripts.

   A sua principal fonte de inspirac,ao sao os exemplos da vida real,
   existentes em no /etc/rc.d de um sistema vivo. Seu conteudo e facil e
   agradavel de ler, porque a maioria dos cantos asperos estao escondidos
   fundo no rc.subr(8). Tenha em mente que os scripts /etc/rc.d nao foram
   escritos por anjos, entao eles podem sofrer de bugs e decisoes sub-otimas
   de design. Agora voce pode melhora-los!
