Этот вопрос является продолжением этого вопроса: Управление демоном C из другой программы
Моя цель - контролировать выполнение процесса демона из другой программы.
Код демона действительно прост.
int main()
{
printf("Daemon starting ...\n");
openlog("daemon-test", LOG_PID, LOG_DAEMON);
syslog(LOG_INFO, "Daemon started !\n");
while(1)
{
syslog(LOG_INFO, "Daemon alive - pid=%d, pgid=%d\n", getpid(), getpgrp());
sleep(1);
}
return EXIT_SUCCESS;
}
Я реализовал сценарий инициализации SystemV для этого демона следующим образом
#!/bin/sh
NAME=daemon-test
DAEMON=/usr/bin/${NAME}
SCRIPTNAME=/etc/init.d/${NAME}
USER=root
RUN_LEVEL=99
PID_FILE=/var/run/${NAME}.pid
RETRY=3
start_daemon()
{
start-stop-daemon --start --background --name ${NAME} --chuid ${USER} --nicelevel ${RUN_LEVEL} --make-pidfile --pidfile ${PID_FILE} --exec ${DAEMON}
ret=$?
if [ "$ret" -eq 0 ]; then
echo "'${NAME}' started"
elif [ "$ret" -eq 1 ]; then
echo "'${NAME}' is already running"
else
echo "An error occured starting '${NAME}'"
fi
return ${ret}
}
stop_daemon()
{
start-stop-daemon --stop --retry ${RETRY} --remove-pidfile --pidfile ${PID_FILE} --name ${NAME} --signal 9
ret=$?
if [ "$ret" -eq 0 ]; then
echo "'${NAME}' stopped"
elif [ "$ret" -eq 1 ]; then
echo "'${NAME}' is already stopped"
elif [ "$ret" -eq 2 ]; then
echo "'${NAME}' not stopped after ${RETRY} tries"
else
echo "An error occured stopping '${NAME}'"
fi
return ${ret}
}
status_daemon()
{
start-stop-daemon --status --pidfile ${PID_FILE} --name ${NAME}
ret=$?
if [ "$ret" -eq 0 ]; then
echo "'${NAME}' is running"
elif [ "$ret" -eq 1 ]; then
echo "'${NAME}' stopped but pid file exits"
elif [ "$ret" -eq 3 ]; then
echo "'${NAME}' stopped"
elif [ "$ret" -eq 4 ]; then
echo "Unable to get '${NAME}' status"
else
echo "Unknown status : ${ret}"
fi
return ${ret}
}
case "$1" in
start)
echo "Starting '${NAME}' deamon :"
start_daemon
;;
stop)
echo "Stopping '${NAME}' deamon :"
stop_daemon
;;
status)
echo "Getting '${NAME}' deamon status :"
status_daemon
;;
restart|reload)
"$0" stop
"$0" start
;;
*)
echo "Usage: $0 {start|stop|status|restart}"
exit 1
esac
exit $?
Использование этого скрипта из командной строки для управления выполнением демона работает хорошо.
Таким образом, сейчас цель состоит в том, чтобы использовать этот скрипт из другой программы на c для запуска демона и управления его выполнением из этой программы.
Я реализовал простую программу на C, которая:
- Запустить скрипт с аргументом start
- Ожидание создания файла pid
- Чтение pid демона из файла pid
- Периодически проверять, жив ли демон, проверяя наличие файла
/proc/<daemon_pid>/exec
- Если демон убит, перезапустите его
И вот проблема, с которой я сталкиваюсь. Программа работает хорошо, только если я не звоню pclose
.
Вот код программы
#define DAEMON_NAME "daemon-test"
#define DAEMON_START_CMD "/etc/init.d/" DAEMON_NAME " start"
#define DAEMON_STOP_CMD "/etc/init.d/" DAEMON_NAME " stop"
#define DAEMON_PID_FILE "/var/run/" DAEMON_NAME ".pid"
int main()
{
char daemon_proc_path[256];
FILE* daemon_pipe = NULL;
int daemon_pid = 0;
FILE* fp = NULL;
int ret = 0;
int i = 0;
printf("Launching '%s' program\n", DAEMON_NAME);
if(NULL == (daemon_pipe = popen(DAEMON_START_CMD, "r")))
{
printf("An error occured launching '%s': %m\n", DAEMON_START_CMD);
return EXIT_FAILURE;
}
#ifdef USE_PCLOSE
else if(-1 == (ret = pclose(daemon_pipe)))
{
printf("An error occured waiting for '%s': %m\n", DAEMON_START_CMD);
return EXIT_FAILURE;
}
#endif
else
{
printf("Script exit status : %d\n", ret);
while(0 != access(DAEMON_PID_FILE, F_OK))
{
printf("Waiting for pid file creation\n");
sleep(1);
}
if(NULL == (fp = fopen(DAEMON_PID_FILE, "r")))
{
printf("Unable to open '%s'\n", DAEMON_PID_FILE);
return EXIT_FAILURE;
}
fscanf(fp, "%d", &daemon_pid);
fclose(fp);
printf("Daemon has pid=%d\n", daemon_pid);
sprintf(daemon_proc_path, "/proc/%d/exe", daemon_pid);
}
while(1)
{
if(0 != access(daemon_proc_path, F_OK))
{
printf("\n--- Daemon (pid=%d) has been killed ---\n", daemon_pid);
printf("Relaunching new daemon instance...\n");
if(NULL == (daemon_pipe = popen(DAEMON_START_CMD, "r")))
{
printf("An error occured launching '%s': %m\n", DAEMON_START_CMD);
return EXIT_FAILURE;
}
#ifdef USE_PCLOSE
else if(-1 == (ret = pclose(daemon_pipe)))
{
printf("An error occured waiting for '%s': %m\n", DAEMON_START_CMD);
return EXIT_FAILURE;
}
#endif
else
{
printf("Script exit status : %d\n", ret);
while(0 != access(DAEMON_PID_FILE, F_OK))
{
printf("Waiting for pid file creation\n");
sleep(1);
}
if(NULL == (fp = fopen(DAEMON_PID_FILE, "r")))
{
printf("Unable to open '%s'\n", DAEMON_PID_FILE);
return EXIT_FAILURE;
}
fscanf(fp, "%d", &daemon_pid);
fclose(fp);
printf("Daemon has pid=%d\n", daemon_pid);
sprintf(daemon_proc_path, "/proc/%d/exe", daemon_pid);
}
}
else
{
printf("Daemon alive (pid=%d)\n", daemon_pid);
}
sleep(1);
}
return EXIT_SUCCESS;
}
Из того, что я понял, pclose
должен ожидать завершения дочернего процесса, и только когда дочерний процесс вернулся, он закрывает канал.
Так что я не понимаю, почему моя реализация с pclose
не работает, когда работает без вызова.
Вот журналы с и без блока pclose
с комментариями
Без pclose
звонки:
# ./popenTest
Launching 'daemon-test' program
Script exit status : 0
Waiting for pid file creation
Daemon has pid=435
Daemon alive (pid=435)
Daemon alive (pid=435)
Daemon alive (pid=435)
Daemon alive (pid=435)
С pclose
вызов:
# ./popenTest
Launching 'daemon-test' program
Script exit status : 36096
Waiting for pid file creation
Waiting for pid file creation
Waiting for pid file creation
Waiting for pid file creation
Как видите, демон никогда не запускается, и файл pid никогда не создается.
Даже если моя программа работает без pclose
Я бы хотел понять основную проблему с вызовом pclose
.
Почему использование pclose
приводит к сбою программы, если поведение не вызывает ее?
EDIT:
Вот еще немного информации для случая ошибки
errno is Success
Макрос WIFEXITED возвращает true
Макрос WEXITSTATUS возвращает 141
Идя дальше к отладке, я заметил, что изменение сценария инициализации для записи вывода в файл делает его работоспособным ... почему?