Оригинал написан для http://www.permlug.org/.
Что такое демон, человеку, знакомому с UNIX средой, думаю объяснять не нужно. Сегодня я расскажу, как быстро и просто запускать процессы в SysV-style системе в качестве демонов с pid, lock файлами и т.п., в общем, по-феншую. Думаю, новичкам пригодится. А может быть и не только новичкам.
Всё ниже описанное было реализовано и проверено на моём домашнем сервере, работающем под Debian Lenny 5.0.3.
Итак, я против изобретения велосипеда, если этот самый велосипед имеется в наличии, не требует топлива из обеднённого урана экзотических зависимостей и не натирает попу при езде (читай — довольно удобен в использовании). Встречайте — daemonize! Эту небольшую, простую в использовании и удобную утилиту написал некто Брайан Клаппер (Brian Clapper), за что ему моё большое нечеловеческое спасибо.
Подбираем тут:
Оффсайт: http://www.clapper.org/software/daemonize/
Буквально из одной строки мана сразу становится понятно, как использовать утилитку. В качестве опций задаются все необходимые пути/имена.
1 2 3
| SYNOPSIS
daemonize [-a] [-c directory] [-e stderr] [-o stdout] [-p pidfile] [-l lockfile] [-u user] [-v] path [arg] ... |
Специально для танкистов ниже в мане подробно описано значение каждого параметра. То, что неочевидно, приведу ниже, дабы облегчить Вам жизнь:
1
| -a Append to the output files, rather than overwriting them (which is the default). Only applicable if -e and/or -o are specified. |
Иными словами, при использовании с ключом -a вместо перезаписи лог файлов будет происходить дописывание в уже существующие.
1
| -v Cause daemonize to write verbose messages to standard error, telling what it’s doing as it daemonizes the program. |
Обычный «вербоуз» — чуть больше информации в еррорлоге о работе программы.
path — это [b]полный путь[/b] к запускаемой программе. Например, если я хочу запустить своего джаббер бота, я должен в качестве path указать /opt/bot/bot, а не просто bot, даже если я использовал параметр -c /opt/bot.
arg — в данном случае — аргументы запускаемой программы.
Рекомендации по сборке велосипеда поражают своей подробностью, дотошностью и вниманием к деталям:
1 2 3 4 5 6 7 8 9 10 11 12 13
| INSTALLATION
Normal installation:
$ sh configure
$ make
$ su
# make install
For a detailed report of the available "configure" options:
$ sh configure --help |
Что радует, вышеописанного вполне достаточно. На упомянутой lenny 5.0.3 велосипед самособрался в мгновение ока, я даже не успел сказать «вотнифигасебебрайанклаппермолодецкакой»! Чего и Вам желаю.
Итак, с устройством велосипеда разобрались, теперь учимся на нём ездить. :) Что необходимо для феншуистой SysV системы? Правильно! Скрипт запуска/остановки в /etc/init.d/ и соответствующие симлинки в rc*.d. Не буду особо мучать вас теорией, а лучше сразу перейду к практике. Все, кто ещё не знают принципов, по которым должен работать скрипт из /etc/init.d/, могут пообщаться с гуглом на предмет «SysV-style».
Рассмотрим создание подобного файла на примере моей задачи. Это всего лишь пример, который я набросал на коленке. Он всем меня устраивает и даже (о, чудо!) работает.
Бот состоит из двух частей. :)
"Внутре у ей неонка и думатель" © Стругацкие
Внутре у него действительно «думатель» и транспорт для общения думателя с джаббером (есть ещё транспорт для работы с ICQ, реализованный на licq, но код совершенно идентичен, так что не за чем перегружать блог). Как они общаются между собой в данном контексте не важно. Нам необходимо, чтобы в качестве демонов были запущены два процесса (/opt/bot/bot и /opt/bot/js) от имени некоего пользователя (опять таки bot), при этом в отдельной директории были созданы для каждого из процессоров лог, еррор лог, лок файл и пид файл. А при останове — пид и лок файлы удалились. Ну что-ж, у нас теперь есть велосипед, поэтому можно с места в карьер:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
| #!/bin/bash
# Переменная содержит путь к рабочей директории процесса
BPATH="/opt/bot"
# А здесь хранится путь к некоей директории var, в которой будут содержаться наши логи, пид и лок файлы
VP="/opt/bot/var"
# Имя пользователя, от которого должны запускаться процессы. Помни! Сильно кастрированный в правах пользователь - залог твоей безопасности!
USER="bot"
# Функция проверяет, запущен ли процесс с именем, переданным параметром. Если запущен, возвращает 0, в противном случае 1. Не логично? Конечно! Мы же используем stderr!
# Usage: chk <short name>
function chk() {
if [ -f "$VP/$1.pid" ]; then # Смотрим, существует ли пид файл
TPID="$( ps -A | grep "^ *$( cat $VP/$1.pid) " )" # Если существует, проверяем, запущен ли процесс с пидом, хранящемся в этом файле
fi
if [ -n "$TPID" ]; then # Если в этой переменной что-то есть, т.е. процесс запущен, то возвращаем 0
return 0
else # А если не запущен, то 1.
return 1
fi
}
# Функция для запуска. 1-й параметр - имя процесса, второй - его описание для красоты в stdout :)
# Usage: fstop <short name> [long name]
function fstart() {
if [ -z "$2" ]; then # Если описания нет, то берём в качестве описания имя процесса
LNAME="$1"
else
LNAME="$2"
fi
if [ -n "$1" ]; then # Нам нужен хотя бы один параметр - имя процесса
SNAME="$1"
echo "Starting $LNAME module..."
if chk $SNAME; then # Если процесс уже запущен, грязно ругаемся на пользователя
echo Another $LNAME process is already running...
else # Если нет, то запускаем по-феншую, как и обещали
/usr/local/sbin/daemonize -c "$BPATH" -e "$VP/err_$SNAME.log" -o "$VP/$SNAME.log" -p "$VP/$SNAME.pid" -l "$VP/$SNAME.lock" -u "$USER" -v "$BPATH"/$SNAME
if chk $SNAME; then # Проверяем снова. Процесс таки запустился?
echo $LNAME started successfully... # Всё хорошо, юзер, спи спокойно. :)
else
echo $LNAME start failed! See $VP/err_$SNAME.log for more information. # Шеф, всё пропало! Надо послать юзера [s]на[/s] в лог.
exit 1 # Выходим тоже по-феншую, указывая, что завершились фейлом
fi
fi
fi
}
# Тут аналогичная функция для останова. Хорошая, пушистая. Умеет подтирать локи и пиды за собой.
# Usage: fstop <short name> [long name]
function fstop() {
if [ -z "$2" ]; then
LNAME="$1"
else
LNAME="$2"
fi
if [ -n "$1" ]; then
SNAME="$1"
echo "Stopping "$LNAME" process..."
if chk $SNAME; then
kill "$( cat "$VP/$SNAME.pid" )"
if chk $SNAME; then
echo "$LNAME stopping failed!"
else
echo Removing lock file...
rm -f $VP/$SNAME.lock
echo Removing pid file...
rm -f $VP/$SNAME.pid
echo Main bot stopped successfully...
fi
else
echo The "$LNAME" process not running!
fi
else
echo Error stopping some process. Not enough parameters.
exit 1
fi
}
# Функция, проверяющая статус запущенных процессов. С функцией chk это очень просто.
# Usage: fstatus <short name> [long name]
function fstatus() {
if [ -z "$2" ]; then
LNAME="$1"
else
LNAME="$2"
fi
if [ -n "$1" ]; then
SNAME="$1"
if chk $SNAME; then
echo "$LNAME" is working...
else
echo "$LNAME" is done...
fi
else
echo Not enough parameters!
exit 1
fi
}
# С вот этого места начинаем наслаждаться результатом. Научились ездить на велосипеде и теперь это нам даётся легко и просто.
#Если мне надо добавить ещё транспорт аськи например, то для этого вполне достаточно двух строчек в start и stop. Да и переправить скрипт для другого приложения тоже довольно просто.
case "$1" in
start)
fstart bot "Main bot"
fstart jb "Jabber transport"
;;
stop)
fstop bot "Main bot"
fstop jb "Jabber transport"
;;
stat*)
fstatus bot "Main bot"
fstatus jb "Jabber transport"
;;
restart)
$0 stop && $0 start
;;
*)
echo "Usage: /etc/init.d/$NAME {start|stop|status|restart}" >&2
exit 1
;;
esac
exit 0 |
Таким образом, мы при помощи одного скрипта можем ездить сразу на двух велосипедах, а можем и на трёх и на четырёх и т.п.. На мой взгляд, получилось довольно удобно. Только если запуск программы требует кучи параметров, то для использования с данным скриптом лучше создать ещё один небольшой однострочный вспомогательный скриптик, который будет включать в себя строку запуска процесса со всеми необходимыми параметрами.
Возможно также вам захочется запускать из одного срипта различные процессы от имени различных пользователей, тогда необходимо будет допилить функцию fstart, чтобы передавать туда имя пользователя ещё одним параметром. На данный момент синтаксис вызова всех функций сделан одинаковым для всеобщего вселенского блага. А можно например ещё сделать force-reload через kill -9, если вам это принесёт счастье. У меня просто сегодня не дошли до этого руки. Скрипт писался минут 20–30, а статья 2–3 часа. Но чего только не сделаешь, когда задалбывают по телефону и в аське однотипными вопросами хочется помочь начинающим собратьям по разуму! :)
Осталось только поместить полученный скрипт в /etc/init.d и наделать кучу нужных симлинков. Но это совсем просто, поэтому я подло оставляю Вас с этим один на один и вероломно иду спать, ибо за бортом -3 и половина третьего ночи, а за спиной рабочая неделя. До новых встреч!
*Мну открыл пасть, засунул в неё лапу и упал в спячку*
Хрррррр.... Хррррр..... Хрррррр....
Полезное
bash, linux, демоны
Последние комментарии