Системное администрирование →
reboot с веб-интерфейсом или trigger: простая и дешевая синхронизация процессов через блокируемый read()
Часто админские и веб-программерские задачи требуют синхронизации между разными компонентами системы, например, вебморда принимает команду на совершение какого-то действия, это действие желательно выполнить как можно раньше, но сам веб-интерфейс не может это сделать (скажем — не может изменить правила файрвола или таблицу роутинга просто потому что требуются полномочия root'а). Обычно я решал это некрасивым и неэффективным способом — веб-интерфейс писал команду в какой-то специальный файл, а другой шелл-скрипт (работающий от рута) в цикле проверял этот файл раз в несколько секунд, и если есть команды — то обрабатывал их.
В этом посте я опишу простой способ, который:
Решение подсмотрено в исходниках qmail'а. Стандартная операция read() — блокируется, если нет данных на чтение (кроме случая, когда вы специально задали неблокирующие опции сокету). read()+fifo файлы решают проблему. Программа-обработчик должна читать данные из fifo файла. Пока в нем пусто — она просто уйдет в sleep и не будет создавать нагрузку. А как только данные появятся — read() завершится, и можно выполнять код обработки.
Для этого «на коленке» была написана простенькая программка trigger. При запуске она слушает указанный ей файл, и при появлении в нем данных — тут же вызывает указанную в параметрах команду (от пользователя, от которого запущен триггер, а не от пользователя, кто «нажал на спусковой крючок»)
Программа запустилась (демоном), создала FIFO файл myfifo и когда в нем появятся данные — запустит программу prog.sh. Второй командой мы разрешили всем писать в FIFO, но, конечно, можно настроить и поаккуратнее (например, разрешить запись для членов группы и присвоить группу www-data).
Сам скрипт prog.sh очень простой в нашем случае — просто записывает в лог время запуска:
Естественно, в вашем случае надо заменить его на ваш скрипт, который будет делать работу.
Теперь проверим:
Смотрим, что получилось:
Как видим — скрипт запустился в ту же секунду, что и была послана команда. Вуаля!
Сам триггер весит всего 12Кб, а блокируемый read() особо не нагружает систему и срабатывает моментально.
Теперь можете легко прилепить веб-интерфейс к системным операциям (перегрузить машину, рестартовать сервис, добавить правила в iptables и т.д.). Естественно, полагаем, что недоверенный код от группы вебсервера не работает, а доступ к веб-интерфейсу будет запаролен, через https и только с локальной сети. Спусковой крючок у вас есть, так что теперь прострелить ступню стало еще удобнее. :-)
В этом посте я опишу простой способ, который:
- не требует программирования — только 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 и только с локальной сети. Спусковой крючок у вас есть, так что теперь прострелить ступню стало еще удобнее. :-)
01.09.2011 17:59+0400