incron в Linux: запуск скриптов по событиям файловой системы

incron в Linux: запуск скриптов по событиям файловой системы

incron это «cron для файловой системы»: команды запускаются не по расписанию, а когда с файлом или каталогом что-то происходит. Файл создали, изменили, удалили или переместили, и incron сразу запускает нужный скрипт. Работает он на механизме ядра inotify, так что не нужно каждую минуту опрашивать каталог из cron.

Сразу о состоянии проекта: последний выпуск incron 0.5.12 вышел ещё в 2015 году, с тех пор в репозитории только редкие правки. Программа простая и стабильная, пакеты есть в Debian, Ubuntu, Arch и Fedora, но новых функций ждать не стоит. Для новых задач на systemd-системах стоит присмотреться к path-юнитам, о них ниже.

Где incron пригодится

  • обработать файл сразу после загрузки в каталог: сконвертировать, сжать, переложить;
  • получить уведомление, если кто-то поменял важный конфиг вроде nginx.conf;
  • запустить бэкап или синхронизацию, когда в каталоге появились новые данные;
  • простой триггер сборки на отдельном сервере, когда туда прилетел артефакт.

Установка

# Debian, Ubuntu
sudo apt install incron
sudo systemctl enable --now incron

# Arch
sudo pacman -S incron
sudo systemctl enable --now incrond

# Fedora (на RHEL, Alma и Rocky пакет в EPEL)
sudo dnf install incron
sudo systemctl enable --now incrond

В Debian и Ubuntu служба называется incron, в Fedora и Arch incrond. В Debian пакет есть во всех актуальных ветках, от Bookworm до Sid, в Arch в репозитории extra, в Fedora и EPEL 8, 9 и 10.

Кому можно пользоваться incrontab

Как и у cron, доступ задаётся двумя файлами, это описано в man incrond. Если есть /etc/incron.allow, пользоваться incron могут только те, кто в нём записан. Если его нет, но есть /etc/incron.deny, то все, кроме перечисленных. Если нет ни того ни другого, ограничений нет. Системные таблицы с правами root лежат в /etc/incron.d/, пользовательские редактируются командой incrontab и выполняются от имени владельца таблицы.

incrontab -e   # редактировать свои правила
incrontab -l   # показать правила

Формат правил

Каждая строка выглядит так:

<путь> <маска событий> <команда>

Основные события: IN_CREATE, IN_MODIFY, IN_DELETE, IN_MOVED_TO, IN_MOVED_FROM, IN_ATTRIB, IN_CLOSE_WRITE. Несколько событий пишутся через запятую. Есть и сокращения: IN_ALL_EVENTS для всех событий, IN_MOVE для обоих перемещений, IN_CLOSE для закрытия файла. Один и тот же путь в таблице может встречаться только один раз, остальные строки с ним игнорируются.

В команде можно подставлять:

  • $@ путь из правила;
  • $# имя файла, с которым случилось событие;
  • $% событие текстом, например IN_CLOSE_WRITE;
  • $& событие числом;
  • $$ сам знак доллара.

Ещё есть три собственные опции incron, их пишут в маске через запятую. loopable=true не реагирует на новые события, пока не закончилась команда от предыдущего. recursive=false следит только за самим каталогом, без подкаталогов: значит, по умолчанию эта версия incron следит и за подкаталогами. dotdirs=true включает в наблюдение скрытые каталоги, имя которых начинается с точки.

Примеры

Обработка загруженных файлов. Лучше ловить IN_CLOSE_WRITE, а не IN_CREATE: событие придёт, когда файл дописан, а не когда появился пустой файл.

/var/www/uploads IN_CLOSE_WRITE /usr/local/bin/process_new_file.sh $@/$#

Уведомление о правке конфига. Отправку в почту или мессенджер удобнее держать в отдельном скрипте, а не городить длинную команду прямо в таблице.

/etc/nginx/nginx.conf IN_MODIFY /usr/local/bin/notify_nginx_change.sh

Пример скрипта, который аккуратно берёт путь из аргумента:

#!/bin/bash
# /usr/local/bin/process_new_file.sh
f="$1"
[ -f "$f" ] || exit 0
logger -t incron "новый файл: $f"
# здесь обработка, например:
# convert "$f" -resize 1600x "$f"

Скрипт должен спокойно переживать повторный запуск на том же файле: события иногда приходят дважды. А если скрипт сам пишет в каталог, за которым следит incron, без loopable=true легко получить бесконечный цикл.

Ограничения и частые ошибки

  • Служба не запущена: пакет поставили, а systemctl enable --now не сделали.
  • Пользователь не попал в incron.allow, и incrontab ему отказывает.
  • Повесили IN_CREATE и получили недописанный файл.
  • Пробелы и спецсимволы в именах ломают команду: передавайте путь аргументом и берите его в кавычки уже в скрипте.
  • Шумный каталог вроде всего /tmp даёт шторм событий. Следите за узким каталогом приложения.
  • Кончились inotify-вотчи: на больших деревьях упирается в fs.inotify.max_user_watches.
  • Логи ищите в journalctl -u incron (или -u incrond) и в syslog.
sysctl fs.inotify.max_user_watches
journalctl -u incron -n 50

Альтернатива: systemd.path

Если задача звучит как «появился файл, запусти сервис», на системе с systemd это делается без отдельного демона. Path-юнит следит за путями через PathExists, PathChanged, PathModified или DirectoryNotEmpty и запускает одноимённый сервис. Для аудита безопасности, кто и что поменял, правильнее auditd, а для задач по расписанию обычный cron или таймеры systemd.

# /etc/systemd/system/uploads.path
[Path]
DirectoryNotEmpty=/var/www/uploads

[Install]
WantedBy=multi-user.target

# рядом uploads.service с ExecStart=/usr/local/bin/process_uploads.sh
# sudo systemctl enable --now uploads.path

В итоге incron до сих пор хорошо закрывает нишу «файл изменился, сразу скрипт»: обработка загрузок, уведомления о правке конфигов, простая автоматизация. Ставьте пакет, включайте службу, ограничивайте доступ через incron.allow и пишите короткие скрипты, которые не боятся повторного запуска. А для новых задач на systemd-серверах сначала посмотрите на path-юниты.

Источники и ссылки


Комментарии загружаются…