0

这个问题是这个问题的后续:Controlling a C daemon from another program

我的目标是控制另一个程序的守护进程执行。
守护进程的代码非常简单。

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 程序,它:

  1. 使用“开始”参数启动脚本
  2. 等待 pid 文件创建
  3. 从 pid 文件中读取守护进程的 pid
  4. 通过检查文件的存在来定期检查守护进程是否处于活动状态/proc/<daemon_pid>/exec
  5. 如果守护进程被杀死,请重新启动它

这是我面临的问题。该程序只有在我不调用时才能正常运行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.

当行为良好而不调用它时,为什么 usingpclose会使程序失败?


编辑:

以下是有关错误案例的更多信息

errno 是Success
WIFEXITED 宏返回 true
WEXITSTATUS 宏返回 141

通过进一步调试,我指出修改 init 脚本以将输出记录到文件使其工作......为什么?

4

1 回答 1

1

你用popen(DAEMON_START_CMD, "r"). 这意味着您的“守护程序观察者”正在读取您的“守护程序启动器”脚本的标准输出。如果您pclose()使用该管道,则脚本将写入标准输出并获得 SIGPIPE,因为管道的读取端已关闭。这是否发生在实际的守护进程启动之前,还有待商榷——还有时间问题。

pclose()在您知道守护程序启动程序已通过某种方式退出之前,请不要使用管道。就个人而言,我会使用pipe(), fork()and execv()(或直接使用函数系列的其他变体exec。我认为这不是popen()适合这项工作的工具。但如果你要使用popen(),请阅读输入,直到你没有更多(EOF),然后pclose()安全使用。您不必打印您阅读的内容,尽管这样做是传统且明智的 - 'daemon starter' 脚本会告诉您有用的信息。

检查进程 ID 是否仍在运行的经典方法是使用kill(daemon_pid, 0). 如果执行的进程具有适当的特权(与进程相同的 UID 或root特权),则此方法有效。如果您无法向 PID 发送活动信号,这将无济于事。

(我假设start-stop-daemon是一个程序,可能是 C 程序而不是 shell 脚本,它启动另一个程序作为守护程序。我有一个类似的程序,我称之为daemonize- 它也用于将不是专门设计为守护程序的程序转换为程序作为守护进程运行。许多程序不能很好地作为守护进程运行 - 考虑 daemonizing lsgreppssort的含义。其他程序可以更明智地作为守护进程运行。)

于 2018-07-03T17:15:06.220 回答