Como continuar um script depois que ele reinicia a máquina?

18

Estou escrevendo um script de shell no bash. Em algum momento do script, ele detecta que a máquina precisa ser reinicializada antes de continuar. Emite:

sudo reboot

Quando a máquina volta a funcionar, há mais trabalho que esse script precisa executar. Como faço para configurar algo para continuar fazendo o trabalho neste script?

Estou assumindo que existe algum lugar em que posso escrever um shell script para que seja executado na próxima reinicialização. Onde é esse lugar? Vejo que o cron possui uma diretiva @reboot. Eu também sei que serviços como o Apache são iniciados na inicialização pelo iniciante. Qualquer um desses mecanismos seria apropriado? Se sim, como isso seria acionado?

Este script precisa ser executado apenas uma vez, nem toda reinicialização. Portanto, ele terá que ir para algum lugar que seja executado na próxima reinicialização ou poderá se remover depois que for executado.

Esta pergunta pergunta sobre como salvar o estado do seu aplicativo após a reinicialização. Meu script não tem muito estado, então eu posso gerenciar isso. Eu só preciso saber como esse script aciona algo para executar após a próxima reinicialização.

Minha versão específica é o Ubuntu Linux 14.04. O script original é iniciado na linha de comando por um administrador do sistema (em vez de executar a partir do cron).

Stephen Ostermiller
fonte
Uma reinicialização dentro de um script deve ser usada apenas se você NÃO puder evitá-la. Por exemplo, uma nova instalação do kernel. Tenho certeza que você pode fazer seu trabalho sem reiniciar. Você pode especificar por que precisa da reinicialização?
caos
11
Este script instala um novo kernel (ou melhor, chama apt upgrade que pode fazer isso). Ele também verifica se uma reinicialização é realmente necessária antes de fazer a reinicialização.
Stephen Ostermiller
"Uma reinicialização dentro de um script deve ser usada apenas se você NÃO puder evitá-la." - @chaos Por quê?
John Red

Respostas:

16

Em um sistema, a única coisa realmente persistente é um arquivo. Isso é basicamente o que você deve usar. Aqui está uma solução usando um script init.d.

Vamos considerar o seguinte script (simples) /etc/init.d/myupdate:

#! /bin/sh

### BEGIN INIT INFO
# Provides:          myupdate
### END INIT INFO

PATH=/sbin:/bin:/usr/sbin:/usr/bin

case "$1" in
    start)
        /path/to/update/script
        ;;
    stop|restart|reload)
        ;;
esac

Se você ativá-lo com update-rc.d myupdate defaults, a startação será executada na inicialização. Agora, quando o script de atualização solicitar uma reinicialização:

touch /var/run/rebooting-for-updates
sudo reboot

Com esta solução, você pode dividir seu script de atualização em duas partes:

before_reboot(){
    # Do stuff
}

after_reboot(){
    # Do stuff
}

if [ -f /var/run/rebooting-for-updates ]; then
    after_reboot
    rm /var/run/rebooting-for-updates
    update-rc.d myupdate remove
else
    before_reboot
    touch /var/run/rebooting-for-updates
    update-rc.d myupdate defaults
    sudo reboot
fi

Ele executará a before_rebootseção de código, criará um arquivo /var/rune reinicializará. Na inicialização, o script será chamado novamente, mas, como o arquivo existe, after_rebootserá chamado em vez de before_reboot.

Observe que update-rc.drequer privilégios de root.

Sem usar um arquivo (do comentário de Stephen Ostermiller ):

Se você estiver familiarizado com o getoptsutilitário, convém usar opções em vez de arquivos. No script init, chame o script com:

/path/to/update/script -r

E no seu script, verifique as opções em vez de arquivos. Chame seu script uma vez sem a opção, e o init.d o chamará novamente na inicialização, desta vez com -r.

# Set AFTER_REBOOT according to options (-r).

if [ "x$AFTER_REBOOT" = "xyes" ]; then
    # After reboot
else
    # Before reboot
fi

Você encontrará mais informações sobre o manuseio de opções aqui (apenas para opções curtas) . Também editei meu script com chamadas para update-rc.dmanter esse trabalho único (de outro comentário).

John WH Smith
fonte
2
Pode ser mais simples ligar /path/to/update/script --after-rebootdo /etc/init.d/myupdateque confiar na presença de /var/run/rebooting-for-updates. Então ele teria argumentos diferentes quando executado diretamente versus chamado na inicialização.
Stephen Ostermiller
Agradável, não pensou em opções. Permita-me editar;)
John WH Smith
11
Além disso, como é um trabalho único, eu provavelmente gostaria de adicionar o update-rc.d myupdate defaultse update-rc.d myupdate removeao próprio script, além da gravação e remoção, /etc/init.d/myupdatepara que não deixe os arquivos por aí.
Stephen Ostermiller
Eu implementei isso e funciona bem. A única outra coisa que vou acrescentar é que o meu sistema baseado em Debian requer itens adicionais na INIT INFOsecção: wiki.debian.org/LSBInitScripts
Stephen Ostermiller
Eu realmente queria mantê-lo simples;) Escrever o init.d adequado leva um pouco de tempo, mas estou feliz que você tenha buscado mais informações: esses scripts podem ser bastante úteis.
John WH Smith