А теперь немного извращений: демон на PHP.
ЧАСТЬ 1. ПИШЕМ ДЕМОНА.
Описание команд читать в мануалах. По-возможности, я даю на них ссылки.
declare(ticks=1);
$g_signals = array(
'SIGTERM' => 0,
'SIGHUP' => 0,
);
$g_ppid = null;
$g_pid = null;
dmn_sys_init(); // сердце демона!
$g_ppid = posix_getppid(); // pid родителя
$g_pid = posix_getpid(); // pid дочернего (текущего) процесса
// поехали!
while(1){
// делаем что-то полезное.
if($g_signals['SIGTERM'] == 1) // если получен сигнал завершения процесса, то не спеша, не паникуя тихонечко заканчиваем работу и выходим.
break;
if($sleep_time > 0)
sleep(1); // спать одну секунду (на самом деле это идеологически не правильно, но для шаблона подойдет)
}
function dmn_sys_init(){
global $g_pid;
// 1. создаем дочерний процесс. при одном работающем это может быть не нужно, но если планируется запуск одного контрольного и нескольких рабочих процессов, то этот код необходим.
if(-1 == ($g_pid = pcntl_fork()))
exit("Could not fork.\n");
// очень важный момент! Здесь определяем в каком мы процессе после разветвления.
if($g_pid != 0){
// 2. мы в родительском процессе
// тут нам нужно отслеживать завершение дочерних процессов.
// делается это банально просто: при создании процесса накручиваем счетчик дочерних процессов
// при завершении дочернего процесса уменьшаем счетчик. Когда дочерних не осталось - выходим.
// ждать завершения можно командой pcntl_wait.
// написание этого кода я оставлю читателю в качестве домашного задания
// для тех, кому лень думать есть приятные новости - дочерние процессы можно не ждать,
// а делать в лучших традициях мира животных - выкидывать их из гнезда и тихонько помирать.
exit("The end of parent process.\n");
}
// 3. мы в дочернем процессе!
// отцепляем процесс от консоли
if(-1 == posix_setsid())
exit("Could not detach from terminal.\n");
// устанавливаем хуки для обрабатываемых сигналов
if(!pcntl_signal(SIGTERM, "dmn_sys_sig_handler"))
exit("Could not setup SIGTERM handler.\n");
if(!pcntl_signal(SIGHUP, "dmn_sys_sig_handler"))
exit("Could not setup SIGHUP handler.\n");
}
// функция-хук обработки сигналов от операционной системы
function dmn_sys_sig_handler($signo)
{
global $g_signals;
switch ($signo){
case SIGTERM:
$g_signals['SIGTERM'] = 1;
break;
case SIGHUP:
$g_signals['SIGHUP'] = 1;
break;
default:
$g_signals['OTHER'] = $signo;
}
}
ЧАСТЬ 2. ПИШЕМ RC СКРИПТЫ.
Здесь пример примитивнейшего RC скрипта для Gentoo.
RC скрипт и PHP файлы записать в /etc/init.d/my-php-daemon и /usr/lib/my-php-daemon соответственно.
#!/sbin/runscript
depend() {
# выставляем зависимости
need net
}
start() {
# показываем юзеру что мы делаем
ebegin "Starting my PHP daemon"
# ведем лог
echo --------------------- >> /var/log/my-php-daemon/rc.log
date >> /var/log/my-php-daemon/rc.log
# запускаем демоненка
php-cgi -f /usr/lib/my-php-daemon/index.php >> /var/log/my-php-daemon/rc.log
# конец
eend $?
}
stop() {
# показываем юзеру что мы делаем
ebegin "Stopping my php daemon"
# останавливаем демона так - ищем процесс, выполняющий файл /usr/lib/my-php-daemon/index.php и посылаем ему сигнал SIGTERM.
# внимание! если таких процессов несколько, то все они получат сигналы SIGTERM.
pkill -TERM -f /usr/lib/my-php-daemon/index.php
# ведем лог
date >> /var/log/my-php-daemon/rc.log
echo --------------------- >> /var/log/my-php-daemon/rc.log
# конец
eend $?
}
Таким образом все варнинги и ошибки PHP будут сыпаться в /var/log/my-php-daemon/rc.log, но, к сожалению, без даты. Добавить дату - это будет читателю второе домашнее задание.
Конечно, мануал не претендует на полноту и безупречность, но это лучше, чем ничего. А еще лучше для простого изучения.
БОНУС-ТРЕК
Поскольку PHP бывает весьма не предсказуем, то для контроля работы демона можно написать небольшой cron скриптик:
$g_vs_gw = "http://gw1.viasms.ru?"
$g_vs_eid = "12345";
$g_vs_password = "secure_word";
$g_needed = array(
'my-php-daemon' => array(
'regexp'=>'/php-cgi.+my-php-daemon/',
'reset_cmd'=>'/etc/init.d/my-php-daemon zap --nocolor',
'start_cmd'=>'/etc/init.d/my-php-daemon start --nocolor'
),
);
// fill array with system fields
$needed = array();
foreach($g_needed as $key=>$val){
$needed[$key] = $val;
$needed[$key]['running'] = 0;
}
$exec = shell_exec("ps -C php-cgi -o pid= -o command=");
$pss = explode("\n", $exec);
foreach($needed as $key=>$val)
if(!$val['running'])
foreach($pss as $ps)
if(preg_match($val['regexp'], $ps))
$needed[$key]['running'] = 1;
foreach($needed as $key=>$val){
if(!$val['running']){
print date("Ymd-His") . "\n";
print shell_exec($val['reset_cmd']);
$cmdresult = shell_exec($val['start_cmd']);
print $cmdresult;
$msg = $key . ' service is down. ' . $cmdresult;
$pass = md5(md5($g_vs_password) . $msg);
$url = $g_vs_gw . '?' .
'eid=' . urlencode($g_vs_eid) .
'&pass=' . urlencode($pass) .
'&msg=' . urlencode($msg);
print $url . "\n";
readfile($url);
print "\n";
}
}
Этот скрипт во время своего запуска проверяет все демоны, указанные в массиве $g_needed. Если один из них не работает, то скрипт сбрасывает его состояние (zap) и запускает снова. Ориентировано, опять таки, на дистрибутив Gentoo. Для подстройки для вашего дистрибутива подкрутите элементы массива reset_cmd и start_cmd.
О результате выполнения пишет лог и отправляет SMS сообщение с текстом о состоянии запуска.
В качестве SMS шлюза используется сайт viasms.ru. Для этого в переменных $g_vs_eid и $g_vs_password надо указать id и пароль события.
В результате, работы скрипта к вам придет SMS сообщение примерно следующего содержания:
my-php-daemon service is down. Starting my PHP daemon [ ok ].
И вы сможете дальше спокойно пить пиво :О)