Schedulazione task ricorrenti con cron e crontab

Cron e anacron permettono la schedulazione di un task utente con una specifica ricorrenza, ad esempio giornaliera o mensile. Per la scrittura dei task da eseguire, ci si avvale di due programmi: crontab ed anacrontab.

Cron presume che il sistema sia attivo 24 ore su 24, quindi se in un certo periodo il computer è spento, i task schedulati in quel periodo non saranno eseguiti, e si dovrà attendere successiva ricorrenza schedulata affinché siano eseguiti. Ad esempio, se un task è schedulato per il primo giorno di ogni mese, ma il primo giorno di Giugno il computer è spento e sarà acceso solo il terzo giorno, allora i task non saranno eseguiti a Giugno, la successiva esecuzione avverrà il primo giorno di Luglio.

Gli utenti comuni non possono usare cron, però possono gestire i propri task attraverso crontab, se l’amministratore da loro i permessi di esecuzione del programma.

Anche gli amministratori possono utilizzare crontab, ed il corrispondente anacrontab, per gestire i task ricorrenti.

Configurazione di cron per l’amministratore

Per verificare lo stato del servizio cron, eseguire come utente root il comando:

systemctl status cron.service

Sia l’amministratore, sia le applicazioni di sistema possono inserire i propri task nelle directory:

/etc/cron.d/
/var/spool/cron

Gli amministratori possono comunque utilizzare crontab per gestire i task ricorrenti. I task vengono raccolti nel file:

/etc/crontab

Crontab

Per autorizzare gli utenti all’uso di crontab, creare (o modificare) il file /etc/cron.allow (utente proprietario root e gruppo proprietario crontab) ed inserire una riga per ogni utente autorizzato.

Crontab si occupa di gestire il file in cui saranno indicati i task utente. Ogni file ha il nome dell’utente che ha schedulato i task.

Ogni utente abilitato ha il file dei task posizionato nella cartella:

/var/spool/cron/crontabs

Questo file deve essere modificato esclusivamente attraverso il comando crontab -e, poiché questo comando verifica la sintassi del file e riavvia il demone cron.d.

Ogni task eseguito prevede l’invio dell’output tramite una mail destinata all’utente. Le email sono memorizzate nella cartella:

/var/spool/mail/

Configurazione primo job con crontab

Per schedulare un nuovo task, sono necessari i seguenti step:

  1. Selezionare l’editor per editare i task, aggiungendo al file .profile una riga in cui si specifica la variabile:

    export EDITOR="/usr/bin/micro"
    
  2. Eseguire il comando:

    crontab -e
    
  3. Indicare le variabili SHELL, PATH ed eventualmente MAILTO:

    SHELL=/bin/bash
    PATH=/usr/sbin:/usr/bin:/sbin:/bin:/root/bin:/bin
    MAILTO=root@example.com
    
  4. Inserire l’espressione che indica l’intervallo di ripetizione (da https://crontab.guru/) seguito dal comando (percorso completo) da eseguire:

    @reboot /bin/date "+\%F-\%T - Cron started" >> /home/io/login.txt 2>&1
    

Riavviando il sistema, l’utente dovrebbe trovare nella propria cartella /home/io il file login.txt con la data e l’ora di esecuzione del primo job schedulato.

Rilevare gli errori

Nel caso un task non sia eseguito, si può cercare una traccia di eventuali errori riferiti a cron nei file seguenti:

grep 'cron' /var/log/syslog
grep 'cron' /var/log/messages

I commenti non sono ammessi sulla stella linea di un comando o di una variabile, poiché vengono interpretati come parte del comando o della variabile.

Il carattere % è interpretato come new line, per poterlo utilizzare come carattere deve essere preceduto da un “escape” \.

Dato che l’output di ogni task eseguito è salvato in una mail destinata all’utente, eventuali messaggi di errore possono venire salvati nelle mail. Per una ricerca di questi errori, visualizzare i file presenti nella cartella:

/var/spool/mail/

Sintassi di schedulazione di cron

La sintassi standard di una regola di cron è:

minuto ora giornoMese mese giornoSettimana  job
minuto da 0 a 59 oppure * per indicare ogni minuto
ora da 0 a 23 oppure * per indicare ogni ora
giornoMese da 1 a 31 oppure * per indicare ogni giorno
mese da 1 a 12 oppure * per indicare ogni mese
giornoSettimana da 0 (Domenica) a 6 (Sabato) oppure * per indicare ogni giorno della settimana
job il comando da eseguire

Il campo giornoSettimana va in OR logico con il campo giornoMese, per cui il task viene eseguito sia nei giorni indicati dal campo giornoMese, sia nei giorni indicati nel campo giornoSettimana.

Se il task deve essere eseguito il giorno 30 di ogni mese, allora il task non sarà eseguito a Febbraio (non ha 30 giorni). Se l’orario di esecuzione è alle 2:30 di notte, allora il task non sarà eseguito nel giorno del passaggio dall’ora solare a quella legale (gli orologi avanzano di un’ora, dalle 2:00 alle 3:00).

In ogni campo si possono usare i seguenti modelli:

* qualsiasi valore per il campo
A,B esecuzione del task quando ha valore A oppure B (non aggiungere il carattere spazio)
A-B esecuzione del task quando ha valore tra A e B (estremi compresi, senza spazio)
A/B esecuzione del task quando la divisione ha resto zero

Invece di specificare i singoli campi, si possono utilizzare le seguenti espressioni non standard:

@yearly esecuzione del task annuale (non-standard)
@annually esecuzione del task annuale (non-standard)
@monthly esecuzione del task mensile (non-standard)
@weekly esecuzione del task settimanale (non-standard)
@daily esecuzione del task giornaliera (non-standard)
@hourly esecuzione del task oraria (non-standard)
@reboot esecuzione del task ad ogni riavvio (non-standard)

Esempi di schedulazione

Per lanciare il comando ogni giorno:

@daily comando

Per lanciare il comando ogni giorno alle 7.30:

30 7 * * * comando

Per lanciare il comando il giorno 1 ed il giorno 16 dei mesi che vanno da aprile a ottobre:

26 * 1,16 4-10 * comando

Per lanciare il comando nei mesi che vanno da aprile a ottobre, il giorno 1 ed il giorno 16 ma anche il lunedì dei mesi predetti:

26 * 1,16 4-10 1 comando

Per lanciare il comando alla fine di ogni mese, dato che non c’è l’opzione, si può usare il giorno successivo, quindi il primo del mese, magari alle due di notte:

0 2 1 * * comando

Si vuole lanciare il comando ogni 5 minuti delle ore lavorative (9-18), ma solo un’ora si ed un’ora no. Ricordando che la regola A/B viene eseguito solo quando il resto della divisione è zero, la si può sfruttare per specificare l’esecuzione nelle ore pari, dato che solo le ore pari hanno resto zero, per cui l’espressione 9-18/2 realizza quanto desiderato:

*/5 9-18/2 * * * comando

Redirezione output

Aggiungendo una redirezione, l’output del task finisce nel file di log, come con la regola:

@reboot /bin/date "+\%F-\%T - Cron started" >> /home/io/login.txt

E’ possibile redirezionare il log ed evitare che la mail venga inviata, con la regola:

@reboot /bin/date "+\%F-\%T - Cron started" >> /home/io/login.txt 2>&1

E’ possibile verificare il valore delle variabili con le redirezioni:

/bin/echo $PATH > /root/path.txt
/usr/bin/env > /root/allEvnVars.txt

Errori tipici

Tutti i comandi devono terminare con una nuova linea.

I caratteri speciali devono essere con escape (ad esempio il segno % deve essere scritto \% );

Esegui gli script tramite la shell corretta indicandola con:

#!/bin/bash

oppure con

/bin/bash -c "script.sh"

Se si hanno problemi di accesso o scrittura quando si esegue il comando crontab -e, allora controllare i permessi:

Il comando crontab deve appartenere all’utente root:

# set owner, group and permission on the command "crontab"
chown root:crontab /usr/bin/crontab
chmod 2755 /usr/bin/crontab          # set the permission to -rwxr-sr-x

La cartella /var/spool/cron/crontabs deve appartenere a root (permessi rwx), al gruppo crontab (permessi ws) e deve avere lo sticky bit (il permesso di esecuzione) impostato per tutti gli altri utenti. Per comprendere i permessi, ricordare che T=sticky bit senza permesso esecuzione, t=sticky bit con permesso esecuzione.

# set owner, group and permission on the spool folders
chown root:crontab /var/spool/cron/crontabs
chmod 1730 /var/spool/cron/crontabs  # set the permission to drwx-wx--T

Se si hanno problemi a rimuovere lo sticky bit, usare la sintassi simbolica “user meno S” o “others meno T”:

chmod u-s /var/spool/cron/crontabs

I file interni alla cartella /var/spool/cron/crontabs devono appartenere ai rispettivi proprietari ed al gruppo crontab e devono avere il permesso di lettura e scrittura rw solo per il proprietario -rw-------, nessun altro permesso (al gruppo o altri) deve essere impostato, altrimenti i task vengono eseguiti in modalità insicura (messaggio “INSECURE MODE (mode 0600 expected)”).


Riferimenti

How to use cron and anacron in Linux: Scheduling tasks with cron, crontab and anacron

Use systemd timers instead of cronjobs: Use systemd timers instead of cronjobs

Analyzing systemd calendar and timespans: Analyzing systemd calendar and timespans