company_banner

Кунг-фу стиля Linux: устранение неполадок в работе incron

Автор оригинала: Al Williams
  • Перевод
Возможно, вам знакома утилита cron, которая позволяет планировать запуск различных задач в заданное время. Мы, кроме того, уже говорили о программе incron, которая реагирует не на события, связанные со временем, а на изменения в файловой системе. Возможно, вам когда-нибудь надо было написать программу, которая, например, обнаруживает изменения в файле и автоматически прошивает какой-нибудь контроллер, или делает резервную копию файла, или отправляет файл по электронной почте. Для решения подобных задач вполне можно воспользоваться incron. Мы обсуждали основы incron, но надо сказать, что в работе этой утилиты есть некоторые особенности, которые сильно усложняют поиск и устранение неполадок, возникающих при её применении. Здесь я хочу рассказать о некоторых приёмах, которыми я пользовался для того чтобы привести в рабочее состояние проекты, основанные на incron.

В моём случае речь идёт о разработке простой системы для работы с документами, хранящимися в директории, находящейся под контролем git. А именно, при изменении Markdown-файла с расширением .md, находящегося в этой директории, генерируются эквивалентные ему .docx- и .pdf-документы. Аналогично — при измерении .docx-документа воссоздаются .md- и .pdf-файлы.



Работать с документами можно с помощью pandoc. Эта программа поддерживает множество форматов документов. Главная сложность тут — запуск процедуры сразу после изменения файлов и обработка только тех файлов, которые были изменены. Это — не такая уж и сложная, хотя и нетривиальная задача. Поэтому у меня ушло некоторое время на то, чтобы заставить мою систему правильно работать.

Настройка incron


При настройке incron можно столкнуться с некоторыми сложностями. Тут я исхожу из предположения о том, что у вас есть возможность установить incron с помощью менеджера пакетов вроде apt, и о том, что в вашей системе для запуска и остановки сервисов используется systemd.

Но, правда, одной только установки для обеспечения нормальной работы incron недостаточно. Так, ваше имя пользователя должно присутствовать в файле /etc/incron.allow (и его не должно быть в файле /etc/incron.deny). После того, как всё готово, пользоваться incron очень просто. По крайней мере — до тех пор, пока не возникнут какие-нибудь проблемы. Справочные сведения по программе можно узнать, воспользовавшись командой man 5 incrontab.


Вывод сведений о таблице событий, за которыми наблюдает incron

У каждого пользователя есть своя таблица событий, за которыми наблюдает incron. Для редактирования таблицы можно воспользоваться такой командой:

incrontab -e

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

В поле, описывающем команду, можно использовать специальные символы. Так, например, последовательность символов $@ даёт имя директории, $# — имя файла, $% — тип события в виде строки, $& — числовой код события. Если в поле с командой требуется обычный знак $ — нужно воспользоваться конструкцией $$.

События — это то, что описывается строками вроде IN_CREATE, IN_DELETE, IN_MODIFY. В записях таблиц можно использовать любое количество событий, записывая их через запятую. Подробнее об этом я расскажу ниже. В таблице, кроме того, применяются опции наподобие IN_DONT_FOLLOW, которая предотвращает разыменовывание символических ссылок. Ещё можно воспользоваться опцией recursive=false, отключив тем самым мониторинг поддиректорий. Ещё одна опция, loopable=true, предназначена для решения одной распространённой проблемы (отключение мониторинга событий до обработки текущего события), но решает она эту проблему не всегда.

В интернете иногда попадается устаревшая документация по incrontab. В настоящее время работа над проектом ведётся на GitHub, но главный разработчик оставил проект в 2012 году, а работа над ним продолжилась лишь через 2 года, когда некто взялся за исправление ошибок. Иногда полезно бывает почитать исходный код используемой версии incron для того чтобы разобраться в том, что именно происходит при работе программы.

Главная проблема incron


Главная проблема incron — это кризис идентичности. И функционал программы, и её название недвусмысленно намекают на то, что она должна быть очень похожа на cron. На первый взгляд — так оно и есть. Но дьявол кроется в деталях. Например, старые версии incron не поддерживают использование комментариев в таблицах. Поэтому можно подумать, что нечто закомментировано, а на самом деле это будет не так. Кроме того, используя incron очень сложно получить выходные данные от выполняемых команд. Сложно даже просто узнать о том, успешно или нет отработала программа. Нельзя сказать, что это невозможно, но узнать это нелегко. Современные версии программы позволяют использовать комментарии, но на включение в incron этой возможности ушло много времени, поэтому у вас вполне может быть устаревшая версия incron.

Ещё одна часто встречающаяся проблема заключается в том, что любое действие в программе, которое вызывает изменения в файловой системе, может привести к бесконечному циклу. Можно подумать, что incron распознает такую ситуацию и что-то предпримет. Но вместо этого одна строка в вашем incrontab-файле может привести к нарушению работы всего демона.

Ещё сильнее всё усложняет то, что некоторые программы делают то, чего от них не ожидают, а это вносит нарушения в обработку некоторых событий. Например, может показаться, что если нужно узнать о том, что файл изменился, надо мониторить событие IN_MODIFY. Такое ощущение, что это — вполне здравая мысль. Но большинство редакторов файлов работают иначе. Так, если файл редактируют, работа, на самом деле, ведётся над временным файлом. А когда файл в программе сохраняют, выполняется операция перемещения файла. Иногда программы, обладающие одинаковым функционалом, инициируют разные последовательности событий файловой системы. Например, утилиты scp и rsync обрабатывают файлы по-разному. Поэтому, чтобы обнаружить появление нового файла, понадобится, в зависимости от конкретной используемой утилиты, по-разному настраивать incron.

Совет: работая над командами, применяемыми в incrontab-файле, тщательно исследуйте их, используя логирование


Вышеописанная ситуация ведёт нас к первому совету по работе с incron. Он заключается в том, что, прежде чем выйти на готовое incrontab-правило, стоит создать временное правило, используя ключевое слово IN_ALL_EVENTS, и применить простой скрипт для логирования сведений о том, что происходит, когда вы выполняете с файлом некоторые действия. Так вы можете проверить себя, поняв, работает ли всё так, как вы того ждёте. То, что получится, может вас удивить. Поэтому и рекомендуется, перед написанием реальных скриптов, понять то, какую именно последовательность событий файловой системы вызывает та или иная операция.

Пусть у нас имеется следующий скрипт, который мы назовём echoarg.sh:

#!/bin/bash
fn="$1"
shift
echo "$@" >>"$fn"

Скрипт это до крайности простой, но нас он вполне устраивает. Вот запись incrontab-таблицы, в которой он используется:

/home/user/tmp/itest IN_ALL_EVENTS /bin/bash echoarg.sh /home/user/tmp/echoarg.log $% - $@/$#

Конечно, в именах файлов нужны кавычки, но так как мы всего лишь их выводим, особого значения это не имеет. Тут надо обратить внимание на одну вещь: в некоторых системах incron запрещено выполнять операции записи в папках наподобие /tmp, а, возможно, даже запрещено мониторить файлы в подобных папках. Лучше всего экспериментировать с директориями, владельцем которых точно являетесь вы (в данном случае — это /home/user/tmp). Вот результат выполнения команды touch foo в директории ~/tmp/itest:

IN_ATTRIB - /home/user/tmp/itest/foo
IN_CREATE - /home/user/tmp/itest/foo
IN_OPEN - /home/user/tmp/itest/foo
IN_CLOSE_WRITE - /home/user/tmp/itest/foo

Ещё некоторые проблемы


В каждом дистрибутиве всё может быть устроено не так, как в других. Поэтому вам, возможно, придётся почитать документацию. Например, в системах, основанных на Debian, то немногое, что логирует incron, попадает в system.log. А в каких-то других распространённых дистрибутивах данные могут попадать в файл cron.log.

Программа весьма привередливо относится к формату incrontab-таблиц. Так, лишний пробел между вторым и третьим полями нарушит работу системы. То же произойдёт и в том случае, если после имени программы будет идти символ табуляции. Оболочка воспримет этот знак и следующую за ним последовательность символов как часть имени программы.

Если говорить о командной оболочке, то можно отметить, что утилита incron весьма разборчива в плане поиска оболочки и в плане настройки переменных окружения. Ваша версия incron может вести себя не так, как моя, но я полагаю, что лучше всего будет для всего, что применяется в incrontab, указывать полные пути. Стоит и явным образом указывать используемую командную оболочку. Если вам нужно что-то особенное в PATH или в других переменных среды, выполните соответствующие настройки в скрипте. Если даже вы запускаете бинарный файл — имеет смысл написать небольшую обёртку, позволяющую настроить всё именно так, как нужно. Как минимум, проводя испытания, залогируйте переменные среды во временный файл. Благодаря этому не случится так, что вы неожиданно столкнётесь с отсутствием множества нужных вам переменных.

Использование команд вроде $(date) обречено на неудачу. Дело в том, что incron «съест» знак $. Возможно, если такая команда вам нужна, вам повезёт с конструкцией $$(date).

Наш грандиозный проект


После того, как вы разобрались с тем, какие именно события вам нужно обрабатывать, вам нужно написать скрипт и как можно тщательнее его протестировать без использования incron. В моём случае это был скрипт autopandoc.sh, .pdf-функционал которого я решил реализовать позже:

#!/bin/bash
if [ -z "$1" ]
then
     exit 1
fi
if [ ! -f "$1" ]
then
     exit 2
fi
dir=$(dirname "$1")
ffilename=$(basename -- "$1")
ext="${ffilename##*.}"
filename="${ffilename%.*}"
 
case "$ext" in
doc*) newext="md"
;;
md) newext="docx"
;;
 
*) exit 3
esac
 
if [ ! -f "$dir/generated" ]
then
    mkdir "$dir/generated"
fi
exec pandoc "$1" -o "$dir/generated/$filename.$newext"

Этот скрипт довольно просто вызвать из командной строки, передав ему любые аргументы, представляющие директорию, файл и событие. Так можно удостовериться в том, что работает он именно так, как ожидается. Прошу поверить мне в том, что в таком виде скрипты отлаживать гораздо легче, чем при обработке incron-событий.

Первый вариант такого скрипта у меня не заработал так, как надо. При этом у меня было очень мало подсказок, которые позволили бы справиться с проблемой. Наблюдая за логами, я видел, что происходили события с файлами, но у меня не было свидетельств того, что запускался мой скрипт. При этом то, насколько он был простым, роли не играло. Оказалось, что для решения проблемы нужно было самостоятельно включить в таблицу /bin/bash.

Далее, я столкнулся с проблемой посерьёзнее. Она заключалась в многократном срабатывании incron при записи файлов в ту директорию, за которой ведётся наблюдение. Я создавал поддиректорию, это событие вызывало команду, которая создавала ещё одну поддиректорию, а на это событие программа реагировала снова. В итоге утилита incron останавливалась. При этом речь идёт не о потоке, связанном с моим пользователем. Программа переставала работать для всех пользователей. Я так думаю, можно настроить systemd на перезапуск incron, но это, на самом деле, не решает проблему.

Есть несколько опций, позволяющих предотвратить реагирование incron на изменения в одном и том же файле, но создание нового файла, всё равно, приводит к возникновению событий. И, честно говоря, если бы это было не так, то у нас имелась бы ещё одна проблема, связанная с поддержкой работы нескольких пользователей. Я решил мою задачу, но, прежде чем об этом рассказать, хочу поговорить о том, что происходит в ходе работы incron.

Анализ файла журнала incron


Как я уже говорил, incron может логировать данные в различные файлы. В деле анализа логов тем, кто пользуется KDE, пригодится приложение для просмотра системных журналов KSystemLog. Эта утилита позволяет фильтровать и выводить записи о событиях по мере их появления. Журналы, конечно, можно анализировать и с помощью команды tail -f. Но чтобы избавиться от информационного шума вам, при таком подходе, может понадобиться прибегнуть к grep.

Если вы пользуетесь systemd, вы можете попробовать такую команду:

journalctl -f -u incron.service

Это — нечто вроде tail -f для лог-файла incron. Наблюдение за тем, как incron постоянно вызывает события для /my_dir/subdir/subdir/subdir… многое расскажет вам о том, что происходит в вашем скрипте.

Дополнительные советы: запускайте демон сами, используйте strace, увеличьте max_user_watches


Сначала остановите демон incron. Сделать это можно любым удобным для вас способом (например — с помощью команды systemctl stop incron). После этого выполните команду incrond с использованием опции -n. Это позволит вам наблюдать за действиями программы. Учитывайте то, что программу надо запускать с root-правами.

Ещё один вариант исследования работы incron заключается в использовании strace для запуска программы. Это позволит узнать обо всех системных вызовах, выполняемых incron. Поэтому, если вас интересует то, какие именно файлы открывает incron, и то, к чему это приводит, вам стоит попробовать такую команду:

sudo strace incrond -n

Опция -n позволяет программе работать на переднем плане. Не забудьте, после того, как завершите эксперименты, остановить incron и запустить снова в виде сервиса. Конечно, если вы работаете на компьютере, которым пользуются и другие люди, то это — далеко не самая правильная идея.

Если вы приступили к реальной работе с incron, вы можете обнаружить, что не укладываетесь в системный лимит файлов, которые может отслеживать один пользователь. Лимит задаётся с помощью max_user_watches. Если это произошло — попробуйте такую команду:

sysctl fs.inotify.max_user_watches

Временно изменить лимит можно так:

sysctl -w fs.inotify.max_user_watches=1000000

Изменить его насовсем можно, отредактировав /etc/sysctl.conf или добавив файл в /etc/sysctl.d.

Итоги


После того, как решены все проблемы incron, эта программа будет работать и решать возлагаемые на неё задачи. Если говорить об использовании incron в продакшне, то меня беспокоит то, что один некорректно составленный скрипт способен остановить работу всего сервиса. Если вы пользуетесь systemd, то тут имеются механизмы работы с юнитами path, предназначенными для отслеживания изменений путей в файловой системе. Проекты, реализующие похожий функционал, имеются и на GitHub, но они, правда, выглядят заброшенными.

Иногда incron — это именно то, что нужно, а иногда лучше поискать альтернативу. Эта ситуация характерна для большинства Linux-инструментов. Но знание особенностей работы различных инструментов обычно себя оправдывает.

Сталкивались ли вы с проблемами incron, подобными тем, о которых рассказывает автор материала?



RUVDS.com
VDS/VPS-хостинг. Скидка 10% по коду HABR

Комментарии 3

    +2

    Главная проблема incron — решение надуманных проблем неподходящими средствами.
    У вас явный CI процесс, даже файлы уже в git — так используйте Jenkins/Github/Gitlab…

      0
      Да хотя бы git-хуки. Pre-commit будет всяко адекватнее, чем компиляция на отслеживании.
      0
      ОЧЕНЬ много воды и рассуждений что все может быть неочевидно, не совсем так, не на поверхности, не сразу выясняется что немного иначе, не всегда по умолчанию. Но из конкретики — банальная работа с tmp файлом редактора… Статья при этом ужимается в 5-10 раз.

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое