7.17. Работа с cron

[+]7.17.1. Системный crontab
[+]7.17.2. Каталоги с периодически выполняемыми заданиями во FreeBSD
[+]7.17.3. Особенности OpenBSD и NetBSD
[+]7.17.4. Пользовательский crontab

Описание:  Кандидат должен понимать разницу между системным crontab и пользовательским. В добавок, он должен владеть редактором crontab, разбираться в его полях и понимать важность предварительного тестирования скрипта перед тем как записать его в crontab. Кандидат так же должен знать про то, что он может создать файлы /etc/cron/allow и /etc/cron/deny и для чего они нужны.

Практика: crontab(1), cron(8), crontab(5)

Комментарий

Система cron предназначена для запуска заданий по расписанию. Написан cron программистом Паулем Викси (Paul Vixie). Как и многие другие популярные программы, cron имеет множество инкарнаций. Больше того, существует множество разных vixie-cron'ов. В основном авторы клонов cron'а занимались тем, что добавляли те или иные удобства в синтаксис файла crontab(5).

Итак, демон cron раз в минуту перечитывает все crontab-файлы, системный и пользовательские.

7.17.1. Системный crontab

Системный файл crontab расположен в каталоге /etc. рассмотрим пример:

Пример 7.1. Системный crontab файл

# /etc/crontab - root's crontab for FreeBSD
#
# $FreeBSD: src/etc/crontab,v 1.32 2002/11/22 16:13:39 tom Exp $
#
SHELL=/bin/sh
PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin
HOME=/var/log
#
#minute hour    mday    month   wday    who     command
#
*/5     *       *       *       *       root    /usr/libexec/atrun
#
# Save some entropy so that /dev/random can re-seed on boot.
*/11    *       *       *       *       operator /usr/libexec/save-entropy
#
# Rotate log files every hour, if necessary.
0       *       *       *       *       root    newsyslog
#
# Perform daily/weekly/monthly maintenance.
1       3       *       *       *       root    periodic daily
15      4       *       *       6       root    periodic weekly
30      5       1       *       *       root    periodic monthly
#
# Adjust the time zone if the CMOS clock keeps local time, as opposed to
# UTC time.  See adjkerntz(8) for details.
1,31    0-5     *       *       *       root    adjkerntz -a
        

Как видно, в начале этого файла задаются некоторые переменные окружения. Стоит обратить внимание на некоторые из них:

SHELL
Эта переменная ответственна за то, в какой оболочке будут выполняться команды данного файла crontab. Теоретически сюда можно подставить что-нибудь более экзотическое, например интерпретаторы perl или python.
MAILTO
В этой переменной должен упоминаться пользователь (или email) которому в виде почты будет отправляться стандартный вывод команд crontab'а. Если переменная определена, но пустая (MAILTO="") то письмо не будет отправлено никому. Если же она просто неопределена, письмо будет отправлено хозяину файла crontab или root'у если это системный crontab.
PATH
Стоит заметить, что изначально эта переменная неопределена. Поэтому команды лучше указывать с полными путями. Это хорошая практика так же и с точки зрения безопасности. Но имейте ввиду, что если вы вызываете shell-сценарий, то и в нём все команды должны отдаваться с абсолютными путями. И накаких фокусов со стартовой строкой вроде: #!/usr/bin/env python явно или неявно расчитывающих на наличие переменной PATH там быть недолжно.

Далее следуют пять полей отвечающих за время, когда будет выполнено задание: минуты, часы, дни (месяца), месяца и дни недели. Касательно дней недели следует заметить, что их не 7, как кто-то мог подумать, а 8: 0 и 7 соответствуют воскресенью. Таким образом, те кто считают, что первый день недели это понедельник, могут отсчитывать дни с единицы, а кто думает, что первый день недели это воскресенье — с нуля.

Формат каждого поля: числа должны перечисляться через запятую. * — то же, что перечисленные через запятую все числа. Диапазон можно указывать через-. Таким образом, следующие записи эквивалентны:

0       *        *       *       *       root    cmd
0       0-23     *       *       *       root    cmd
0       0,1,2-23 *       *       *       root    cmd
        

Можно задавать через/ условия типа «каждый второй». Например */2 в поле час эквивалентно заданию каждый второй час. Обратите внимане: каждый второй это не тоже самое, что каждый чётный. например:

# следующие два задания выполняются каждый чётный час
0       */2     *       *       *       root    cmd
0       0-23/2  *       *       *       root    cmd
# а следующее задание выполняется каждый  НЕчётный час
0       1-23/2  *       *       *       root    cmd
        

BSD варианты файла crontab допускают использование ключевых слов вместо пяти полей отвечающих за дату:

Таблица 7.16. Короткие имена используемые в crontab(5) для описания времени выполнения заданий

Ключевое словоЭквивалент
@rebootЗапускается при старте системы
@yearly0 0 1 1 *
@annually@yearly
@monthly0 0 1 * *
@weekly0 0 * * 0
@daily0 0 * * *
@midnight@daily
@hourly0 * * * *

Шестое поле системного файла crontab указывает на то, от чьего имени будет выполнено задание. И, наконец, седьмое поле — сама задача.

[Важно]Важно
Будьте осторожны используя знак % в команде в crontab. В файле crontab % используется для обозначения конца строки и для использования его в аргументах команд, его необходимо защищать:
# Поздравляем пользователя root с новым годом
# и высылаем ему содержимое файла /etc/motd
@yearly         root    echo "Счастливого нового года!"%/bin/cat /etc/motd 1
# Эквивалентная форма записи:
@yearly         root    echo "Счастливого нового года!"; /bin/cat /etc/motd 2

# каждую минуту посылаем пользователю root письмо в котором написано 3
# сколько времени
*       *       *       *       *       root    /bin/date '+\%x \%c'
        
1 Здесь cron получил задание из двух строк
2 Здесь cron получил задание из одной строки и /bin/sh справится с ним, однако есть языки в которых важно писать команды с новой строки и там придётся употреблять % как в примере выше
3 Здесь cron получил задание из одной строки с аргументром содержащим два символа %.

7.17.2. Каталоги с периодически выполняемыми заданиями во FreeBSD

Вернёмся к системному файлу crontab. Обратите внимание на строки приведённые ниже между отточиями:

# /etc/crontab - root's crontab for FreeBSD
#
# $FreeBSD: src/etc/crontab,v 1.32 2002/11/22 16:13:39 tom Exp $
#
SHELL=/bin/sh
PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin
HOME=/var/log
#
#minute hour    mday    month   wday    who     command
#
........................................................................
# Perform daily/weekly/monthly maintenance.
1       3       *       *       *       root    periodic daily
15      4       *       *       6       root    periodic weekly
30      5       1       *       *       root    periodic monthly
........................................................................
        

В других (не BSD) системах вместо отточенных строк было бы что-то вроде:

# Perform daily/weekly/monthly maintenance.
1       3       *       *       *       root    run-parts /etc/cron.daily
15      4       *       *       6       root    run-parts /etc/cron.weekly
30      5       1       *       *       root    run-parts /etc/cron.monthly
        

Скрипт run-parts заходит внутрь указанных каталогов и выполняет все найденные в них сценарии.

В FreeBSD принят иной механизм, более сложный и гибкий: имеется sh-сценарий /usr/sbin/periodic, который получает аргумент daily, или weekly, или monthly, или security. Этот скрипт изучает переменные заданные в файлах /etc/defaults/periodic.conf и /etc/periodic.conf (как всегда в подобных случаях файл /etc/defaults/periodic.conf исправлять не следует, вместо него надо писать пользовательские переопределения в /etc/periodic.conf). В этих конфигурационных файлах записано какие из периодических скриптов надо выполнять и с какими аргументами (если они нужны), какие им надо задать переменные окружения, куда направлять вывод (в почту или в файл). Синтаксис описан в man странице periodic.conf(5)

Сами периодически выполняемые скрипты могут находиться в каталогах /etc/periodic/daily, /etc/periodic/weekly, /etc/periodic/monthly или в аналогичных подкаталогах в иерархии /usr/local/etc/periodic или /usr/X11R6/etc/periodic.

Недокументированной особенностью скрипта periodic является его относительная всеядность к аргументам. Например, вы можете создать собственный каталог с заданиями расчитанными на ваш период /usr/local/etc/periodic/myperiod, положить в него задание mytask и выполнять его командой periodic myperiod. При этом у вас будут все возможности влиять на работу вашего скрипта через файл /etc/periodic.conf. Останется только добавить в системный (или пользовательский) crontab вызов задания periodic myperiod.

$ mkdir /usr/local/etc/periodic/myperiod
$ vi /usr/local/etc/periodic/myperiod/mytask
....
$ cat /usr/local/etc/periodic/myperiod/mytask
#!/bin/sh
echo Hello world | tee /tmp/hello
$ periodic myperiod
Hello world

-- End of myperiod output --
        

7.17.3. Особенности OpenBSD и NetBSD

В OpenBSD и NetBSD системный crontab отсутствует (что не означает, что его не может быть в принципе). Все действия осуществляются путём работы с пользовательскими crontab'ами (в том числе crontab пользователя root).

Каталогов с периодически выполняемыми заданиями тоже нет. Вместо них имеются shell сценарии /etc/daily, /etc/weekly и /etc/monthly запускаемые из crontab пользователя root и настраиваемые при помощи файлов /etc/daily.conf, /etc/weekly.conf и /etc/monthly.conf соответственно.

В OpenBSD соответствующую справку можно получить из страниц man daily.conf(5), weekly.conf(5), monthly.conf(5). В NetBSD daily(8), weekly(8), monthly(8).

7.17.4. Пользовательский crontab

Пользовательский crontab файл отличается от системного тем, что в нём нет шестого поля с указанием от чьего имени выполняется команда. Команда может выполняться только от имени владельца файла crontab.

Расположены пользовательские crontab файлы в каталоге /var/cron/tabs и названы по именам пользователей. Владельцем всех crontab файлов является root. Таким образом, если пользователь окажется переименован, но его UID останется прежним это не спасёт его от потери своего файла crontab. Это редкий случай когда в UNIX нечто завязано не на UID, а именно на имя пользователя.

Пользовательские файлы crontab создаются при помощи утилиты crontab(1). Будучи запущена пользователем user с аргументом -e эта утилита вызывает редактор заданный в переменной окружения EDITOR (если эта переменная не задана, вызывается vi(1)) и в этом редакторе пользователь может редактировать свой файл crontab. Суперпользователь может редактировать чужие файлы crontab, задавая пользователя в аргументе -u.

Можно рекомендовать администратору не править системный crontab с целью внедрения в него своих скриптов, дабы облегчить себе работу с mergemaster(8) при обновлении системы. Вместо этого удобнее править crontab пользователя root

 $ sudo crontab
            -u root -e
        
или другого подходящего пользователя (того, который должен был быть указан в шестом поле системного crontab).

Имеется способ ограничить пользователей в написании файлов crontab: файлы /etc/cron/allow и /etc/cron/deny (это не ошибка, в Linux файлы называются по-другому). В них можно перечислить пользователей которым разрешено или запрещено запускать программу crontab(1)