Como eu adiciono / removo tarefas cron por script?

4

Vou fazer um script bash que é executado na inicialização e é executado periodicamente.

Eu quero que seja configurável pelo usuário, para que um usuário possa adicionar uma tarefa agendada 0 * * * * my_script executando my_script add 0 * * * * , listar tarefas por my_script list e remover por my_script remove job_number onde o número da tarefa está listado na saída do comando my_script list .

Se eu pudesse gerenciar arquivos crontab separadamente, isso seria facilmente alcançado. No entanto, parece crontab é apenas um arquivo por usuário (se não, por favor me avise). Diretamente lidar com esse arquivo crontab é uma solução ruim, é claro.

Então, qual é a maneira correta de lidar com os trabalhos agendados? Ou existe uma maneira melhor de lidar periodicamente com scripts?

Condições:

  1. Qualquer usuário deve ser capaz de executá-lo, seja privilegiado ou não.
  2. Sem dependências.

Pergunta adicional:

Como não consegui encontrar uma maneira adequada de gerenciar scripts em execução periodicamente, pensei no que poderia estar fazendo de errado. No sentido de design de software, não é prático implementar a interface para gerenciar as tarefas agendadas do software? Devo deixar todos os gerenciamentos de agendamento para os usuários?

    
por queued 06.05.2017 / 15:06

4 respostas

6

O uso do cron é a maneira correta de agendar a execução periódica de tarefas na maioria dos sistemas Unix. Usar um crontab pessoal é a maneira mais conveniente para um usuário agendar suas próprias tarefas. As tarefas do sistema podem ser agendadas por root ( não usando o script abaixo! ) no crontab do sistema, que geralmente tem um formato um pouco diferente (um campo de usuário extra).

Aqui está um script simples para você. Qualquer usuário pode usar isso para gerenciar seu próprio crontab.

  • Ele não faz nenhum tipo de validação de sua entrada, exceto que ele irá reclamar se você fornecer poucos argumentos. Portanto, é completamente possível adicionar entradas crontab formatadas incorretamente.

  • O subcomando remove usa um número de linha e remove o que está nessa linha no crontab, independentemente do que seja. O número é passado, não-analizado, diretamente para sed .

  • A entrada crontab, quando você adiciona uma, deve ser citada. Isso afeta como você deve manipular aspas dentro da própria entrada do crontab.

  • Ele usa crontab.tmp como nome de arquivo para armazenar um arquivo temporário no diretório atual, independentemente de o arquivo já existir ou não (ele será removido).

A maioria dessas coisas deve ser relativamente fácil para você corrigir.

#!/bin/sh

usage () {
    cat <<USAGE_END
Usage:
    $0 add "job-spec"
    $0 list
    $0 remove "job-spec-lineno"
USAGE_END
}

if [ -z "$1" ]; then
    usage >&2
    exit 1
fi

case "$1" in
    add)
        if [ -z "$2" ]; then
            usage >&2
            exit 1
        fi

        crontab -l >crontab.tmp
        printf '%s\n' "$2" >>crontab.tmp
        crontab crontab.tmp && rm -f crontab.tmp
        ;;
    list)
        crontab -l | cat -n
        ;;
    remove)
        if [ -z "$2" ]; then
            usage >&2
            exit 1
        fi

        crontab -l | sed -e "$2d" >crontab.tmp
        crontab crontab.tmp && rm -f crontab.tmp
        ;;
    *)
        usage >&2
        exit 1
        ;;
esac

Para usar:

$ ./script
Usage:
    ./script add "job-spec"
    ./script list
    ./script remove "job-spec-lineno"

$ ./script list
     1  */15 * * * * /bin/date >>"$HOME"/.fetchmail.log
     2  @hourly /usr/bin/newsyslog -r -f "$HOME/.newsyslog.conf"
     3  @reboot /usr/local/bin/fetchmail

$ ./script add "0 15 * * * echo 'hello world!'"

$ ./script list
     1  */15 * * * * /bin/date >>"$HOME"/.fetchmail.log
     2  @hourly /usr/bin/newsyslog -r -f "$HOME/.newsyslog.conf"
     3  @reboot /usr/local/bin/fetchmail
     4  0 15 * * * echo 'hello world!'

$ ./script remove 4

$ ./script list
     1  */15 * * * * /bin/date >>"$HOME"/.fetchmail.log
     2  @hourly /usr/bin/newsyslog -r -f "$HOME/.newsyslog.conf"
     3  @reboot /usr/local/bin/fetchmail
    
por 06.05.2017 / 17:03
1

Sua implementação atual do cron provavelmente suporta /etc/cron.d, onde as tarefas têm um usuário adicional "run as" especificado após os campos de horário regulares e antes do comando. Portanto, basta ajustar sua interface para criar arquivos nesse diretório, como entradas regulares do cron com o nome de usuário (talvez extraído do loginuid) anexado após o quinto campo. Então você pode fazer um trabalho por arquivo. :)

Veja man 5 crontab link

Observe que o principal problema com isso é que um usuário que pode editar arquivos nesse diretório pode criar um arquivo que é executado como raiz. Então, você provavelmente quer um script wrapper executado via sudo que irá validar a entrada e forçar o nome de usuário calculado no arquivo gerado. E então precisamos ter certeza de que você está fazendo as coisas com segurança dentro do script, como não confiar em $ PATH, etc.

PS, em um script de shell: getent passwd $(</proc/self/loginuid)

Honestamente, se você está informando os usuários sobre o formato de horário do cron, alguns segundos extras para ensiná-los a usar crontab -e (e definir $EDITOR se vi é assustador) não são muito difíceis.

    
por 07.05.2017 / 00:50
0

Se você quiser uma entrada cron:

0 * * * * my_script

então eu recomendo que você pense em um nome separado para a função de gerenciamento do cron, como "cron_mgmt", cujo primeiro argumento é um SWITCH em uma instrução CASE:

cron_mgmt () {

    case $1 in
    add ) ... ;;
    list ) ...;;
    remove ) ...
    help ) ...
    * )  echo "You need help. Here it is"
         cron_mgmt help
         ;;
    esac
}   
    
por 06.05.2017 / 16:58
0

Em vez de criar sua própria interface, mantenha-a simples. Para instalar um cron job com um determinado agendamento, coloque-o em um determinado diretório. Para removê-lo, remova-o do diretório. Você não precisa fornecer aos usuários quaisquer ferramentas para fazer isso, basta dizer-lhes em qual diretório colocar o trabalho.

É assim que a maioria das distribuições Linux gerencia tarefas agendadas do sistema que podem ser instaladas por pacotes individuais. O Debian, o Fedora, o Arch Linux e outros disponibilizam uma ferramenta chamada run-parts cujo trabalho é executar os scripts em um diretório particular, um por um. As diferentes implementações de run-parts possuem regras diferentes sobre quais nomes de arquivo são aceitáveis para scripts. Se você não quer depender disso, você pode fazer o seu próprio:

for x in /path/to/daily-jobs/*; do
  if [ -x "$x" ]; then
    case "${x##*/}" in
      *[!-0-9A-Z_a-z]*) :;; # skip file names containing "weird" characters
      *) "$x";;
    esac
  fi
done

Se você quiser permitir que os usuários especifiquem o próprio agendamento, essa abordagem não funcionará. Você precisa reconstruir o arquivo crontab sempre que um novo trabalho é enviado. Isso pode ser feito, mas é complicado configurar de forma confiável. No entanto, se os usuários puderem especificar sua própria programação, haverá poucas razões para adicionar outra interface em cima de crontab -e ou uma GUI que chame crontab -e em segundo plano.

    
por 07.05.2017 / 00:30