Яндекс.Метрика

    Системное администрирование

    reboot с веб-интерфейсом или trigger: простая и дешевая синхронизация процессов через блокируемый read()

    Часто админские и веб-программерские задачи требуют синхронизации между разными компонентами системы, например, вебморда принимает команду на совершение какого-то действия, это действие желательно выполнить как можно раньше, но сам веб-интерфейс не может это сделать (скажем — не может изменить правила файрвола или таблицу роутинга просто потому что требуются полномочия root'а). Обычно я решал это некрасивым и неэффективным способом — веб-интерфейс писал команду в какой-то специальный файл, а другой шелл-скрипт (работающий от рута) в цикле проверял этот файл раз в несколько секунд, и если есть команды — то обрабатывал их.

    В этом посте я опишу простой способ, который:
    • не требует программирования — только unix-way сборка системы из маленьких кирпичиков
    • не отжирает много ресурсов (не нужно зря поллить файл, а сама программа весит значительно меньше шелла)
    • срабатывает моментально


    Решение подсмотрено в исходниках qmail'а. Стандартная операция read() — блокируется, если нет данных на чтение (кроме случая, когда вы специально задали неблокирующие опции сокету). read()+fifo файлы решают проблему. Программа-обработчик должна читать данные из fifo файла. Пока в нем пусто — она просто уйдет в sleep и не будет создавать нагрузку. А как только данные появятся — read() завершится, и можно выполнять код обработки.

    Для этого «на коленке» была написана простенькая программка trigger. При запуске она слушает указанный ей файл, и при появлении в нем данных — тут же вызывает указанную в параметрах команду (от пользователя, от которого запущен триггер, а не от пользователя, кто «нажал на спусковой крючок»)

    xenon@dot:~/trigger-1.0$ ./trigger /tmp/myfifo ./prog.sh
    fifo: /tmp/myfifo, program: ./prog.sh
    file /tmp/myfifo doesn't exists, create it
    created fifo /tmp/myfifo
    xenon@dot:~/trigger-1.0$ chmod a+w /tmp/myfifo
    xenon@dot:~/trigger-1.0$ pidof trigger
    8552

    Программа запустилась (демоном), создала FIFO файл myfifo и когда в нем появятся данные — запустит программу prog.sh. Второй командой мы разрешили всем писать в FIFO, но, конечно, можно настроить и поаккуратнее (например, разрешить запись для членов группы и присвоить группу www-data).

    Сам скрипт prog.sh очень простой в нашем случае — просто записывает в лог время запуска:
    xenon@dot:~/trigger-1.0$ cat prog.sh
    #!/bin/sh

    echo `date` started, ui: `whoami` >> program.log


    Естественно, в вашем случае надо заменить его на ваш скрипт, который будет делать работу.

    Теперь проверим:
    www-data@dot:/tmp$ id
    uid=33(www-data) gid=33(www-data) группы=33(www-data)
    www-data@dot:/tmp$ echo > /tmp/myfifo
    www-data@dot:/tmp$ echo > /tmp/myfifo
    www-data@dot:/tmp$ echo > /tmp/myfifo
    www-data@dot:/tmp$ exit


    Смотрим, что получилось:
    xenon@dot:~/trigger-1.0$ cat program.log
    Чт. сент. 1 20:47:47 NOVST 2011 started, ui: xenon
    Чт. сент. 1 20:47:48 NOVST 2011 started, ui: xenon
    Чт. сент. 1 20:47:49 NOVST 2011 started, ui: xenon


    Как видим — скрипт запустился в ту же секунду, что и была послана команда. Вуаля!

    Сам триггер весит всего 12Кб, а блокируемый read() особо не нагружает систему и срабатывает моментально.

    Теперь можете легко прилепить веб-интерфейс к системным операциям (перегрузить машину, рестартовать сервис, добавить правила в iptables и т.д.). Естественно, полагаем, что недоверенный код от группы вебсервера не работает, а доступ к веб-интерфейсу будет запаролен, через https и только с локальной сети. Спусковой крючок у вас есть, так что теперь прострелить ступню стало еще удобнее. :-)