13.4. Функция daemon_init
13.4. Функция daemon_init
В листинге 13.1[1] показана функция, называемая daemon_init, которую мы можем вызвать (обычно с сервера), чтобы придать процессу свойства демона.
Листинг 13.1. Функция daemon_init: придание процессу свойств демона
//daemon _init.с
1 #include "unp.h"
2 #include <syslog.h>
3 #define MAXFD 64
4 extern int daemon_proc; /* определен в error.с */
5 int
6 daemon_init(const char *pname, int facility)
7 {
8 int i;
9 pid_t pid;
10 if ((pid = Fork()) < 0)
11 return (-1);
12 else if (pid)
13 _exit(0); /* родитель завершается */
14 /* 1-й дочерний процесс продолжает работу... */
15 if (setsid() < 0) /* становится главным процессом сеанса */
16 return (-1);
17 Signal(SIGHUP, SIG_IGN);
18 if ((pid = Fork()) < 0)
19 return (-1);
20 else if (pid)
21 _exit(0); /* 1-й дочерний процесс завершается */
22 /* 2-й дочерний процесс продолжает работу */
23 daemon_proc = 1; /* для функций err_XXX() */
24 chdir("/"); /* смена текущего каталога */
25 /* закрытие дескрипторов файлов*/
26 for (i = 0; i < MAXFD; i++)
27 close(i);
28 /* перенаправление stdin, stdout и stderr в /dev/null */
29 open("/dev/null", O_RDONLY);
30 open("/dev/null", O_RDWR);
31 open("/dev/null", O_RDWR);
32 openlog(pname, LOG_PID, facility);
33 return (0); /* успешное завершение */
34 }
Вызов функции fork
10-13 Сначала мы вызываем функцию fork, после чего родительский процесс завершается, а дочерний продолжается. Если процесс был запущен из интерпретатора команд в фоновом режиме, то, когда родительский процесс завершается, оболочка считает, что команда выполнена. Это автоматически запускает дочерний процесс в фоновом режиме. Дочерний процесс наследует идентификатор группы процессов от родительского процесса, но получает свой собственный идентификатор процесса. Это гарантирует, что дочерний процесс не является главным в группе процессов, что требуется для следующего вызова функции setsid.
Вызов функции setsid
15-16 Функция setsid — это функция POSIX, создающая новый сеанс. (В главе 9 [110] подробно рассказывается о взаимоотношениях процессов.) Процесс становится главным в новом сеансе, становится главным в новой группе процессов и не имеет управляющего терминала.
Игнорирование сигнала SIGHUP и новый вызов функции fork
17-21 Мы игнорируем сигнал SIGHUP и снова вызываем функцию fork. Когда эта функция завершается, родительский процесс на самом деле является первым дочерним процессом, и он завершается, оставляя выполняться второй дочерний процесс. Назначение второй функции fork — гарантировать, что демон не сможет автоматически получить управляющий терминал, если потом он откроет устройство терминала. В SVR4, когда главный процесс сеанса без управляющего терминала открывает устройство терминала (которое в этот момент не является управляющим терминалом для другого сеанса), терминал становится управляющим терминалом главного процесса сеанса. Но вызывая второй раз функцию fork, мы гарантируем, что второй дочерний процесс больше не является главным в сеансе, поэтому он не может получить управляющий терминал. Сигнал SIGHUP приходится игнорировать, поскольку, когда главный процесс сеанса завершает работу (первый дочерний процесс), всем процессам в сеансе (нашему второму дочернему процессу) посылается сигнал SIGHUP.
Установка флага для функций ошибок
23 Мы присваиваем глобальной переменной daemon_proc ненулевое значение. Эта внешняя переменная задается нашими функциями err_XXX (см. раздел Г.4), и ее ненулевое значение сообщает этим функциям, что нужно вызвать функцию syslog вместо функции fprintf (которая выводит сообщение об ошибке в стандартный поток сообщений об ошибках). Это спасает нас от необходимости проходить через весь наш код и вызывать одну из наших функций ошибок, если сервер не работает как демон (то есть когда мы проверяем сервер), а при работе в режиме демона заменять все вызовы на вызовы syslog.
Изменение рабочего каталога и сброс всех битов в маске режима создания файла
24 Мы изменяем рабочий каталог на корневой каталог, хотя у некоторых демонов могут быть причины изменить рабочий каталог на какой-либо другой. Например, демон печати может изменить его на каталог, в котором накапливается содержимое заданий для принтера и происходит вся работа по выводу данных на печать. Если демоном сбрасывается дамп (файл core), он появляется в текущем рабочем каталоге. Другой причиной для изменения рабочего каталога является то, что демон мог быть запущен в любой файловой системе, и если он там останется, эту систему нельзя будет размонтировать, во всяком случае, без жестких мер.
Закрытие всех открытых дескрипторов
25-27 Мы закрываем все открытые дескрипторы, которые наследуются от процесса, запустившего демон (обычно этим процессом бывает интерпретатор команд). Проблема состоит в определении наибольшего используемого дескриптора: в Unix нет ни одной функции, предоставляющей это значение. Есть способы определения максимального числа дескрипторов, которое может открыть процесс, но даже это достаточно сложно [110, с. 43], поскольку предел может быть бесконечным. Наше решение — закрыть первые 64 дескриптора, даже если большинство из них, возможно, не было открыто.
ПРИМЕЧАНИЕ
Solaris предоставляет функцию closefrom, позволяющую демонам решать эту проблему.
Перенаправление stdin, stdout и stderr в /dev/null
29-31 Некоторые демоны открывают /dev/null для чтения и записи и подключают к нему дескрипторы стандартных потоков ввода, вывода и сообщений об ошибках. Это гарантирует, что наиболее типичные дескрипторы открыты и операция чтения из любого из них возвращает 0 (конец файла), а ядро игнорирует все, что записано в любой из этих трех дескрипторов. Причина, по которой требуется открыть эти дескрипторы, заключается в том, что любая библиотечная функция, вызываемая демоном и считающая, что она может читать из стандартного потока ввода или записывать либо в стандартный поток вывода, либо в стандартный поток сообщений об ошибках, не должна завершиться с ошибкой. Отказ был бы потенциально опасен: если демон открывает сокет для связи с клиентом, дескриптор сокета воспринимается как стандартный поток вывода, поэтому ошибочный вызов какой-нибудь функции типа perror может привести к отправке клиенту нежелательных данных.
Использование демона syslogd для вывода сообщений об ошибках
32 Вызывается функция openlog. Первый ее аргумент берется из вызывающего процесса и обычно является именем программы (например, argv[0]). Мы указываем, что идентификатор процесса должен добавляться к каждому сообщению. Аргумент facility также задается вызывающим процессом, и его значением может быть константа из табл. 13.2 либо, если приемлемо значение по умолчанию LOG_USER, нулевое значение.
Отметим, что поскольку демон выполняется без управляющего терминала, он никогда не должен получать сигнал SIGHUP от ядра. Следовательно, многие демоны используют этот сигнал в качестве уведомления от администратора, что файл конфигурации демона изменился и демон должен еще раз считать файл. Два других сигнала, которые демон никогда не должен получать, — это сигналы SIGINT и SIGWINCH, и они также могут использоваться для уведомления демона о некоторых изменениях.
Данный текст является ознакомительным фрагментом.