Управление демоном C из другой программы

Я пытаюсь управлять программой C daemon из другой пользовательской программы.

- Простой C-демон

Этот демон - просто программа на C, которая демонизирует себя и регистрирует сообщение каждую секунду через системный журнал.

#include <stdlib.h>
#include <stdio.h>
#include <syslog.h>
#include <unistd.h>
#include <signal.h>

void bye();

int main()
{
  printf("Daemon starting ...\n");
  openlog("daemon-test", LOG_PID, LOG_DAEMON);
  signal(SIGTERM, bye);

  if(0 != daemon(0, 0))
  {
    syslog(LOG_ERR, "Can't daemonize\n");
    return EXIT_FAILURE;
  }

  syslog(LOG_INFO, "Daemon started !\n");

  while(1)
  {
    syslog(LOG_INFO, "Daemon alive\n");
    sleep(1);
  }

  return EXIT_SUCCESS;
}

void bye()
{
  syslog(LOG_INFO, "Daemon killed !\n");
  exit(EXIT_SUCCESS);
}

- Запуск и уничтожение демона из программы C test

Для целей тестирования я разработал минимальный пример. я использую popen чтобы запустить демон, потому что я хочу, чтобы моя программа продолжила его выполнение.

Через 5 секунд тестовая программа должна убить демона.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#define DAEMON_NAME       "daemon-test"

int main()
{
    FILE* pipe = NULL;
    int i = 0;

    printf("Launching '%s' program\n", DAEMON_NAME);
    if(NULL == (pipe = popen(DAEMON_NAME, "re")))
    {
        printf("An error occured launching '%s': %m\n", DAEMON_NAME);
        return EXIT_FAILURE;
    }
    printf("Program '%s' launched\n", DAEMON_NAME);

    while(i<5)
    {
        printf("Program alive !\n");
        sleep(1);
        i++;
    }

    if(NULL == (pipe = popen("killall " DAEMON_NAME, "re")))
    {
        printf("An error occured killing '%s' program: %m\n", DAEMON_NAME);
        return EXIT_FAILURE;
    }
    printf("Program '%s' killed\n", DAEMON_NAME);

    return EXIT_SUCCESS;
}

Журнал тестовой программы:

$ ./popenTest 
Launching 'daemon-test' program
Program 'daemon-test' launched
Program alive !
Program alive !
Program alive !
Program alive !
Program alive !
Program 'daemon-test' killed

Syslog:

Jun 25 13:58:15 PC325 daemon-test[4445]: Daemon started !
Jun 25 13:58:15 PC325 daemon-test[4445]: Daemon alive
Jun 25 13:58:16 PC325 daemon-test[4445]: Daemon alive
Jun 25 13:58:17 PC325 daemon-test[4445]: Daemon alive
Jun 25 13:58:18 PC325 daemon-test[4445]: Daemon alive
Jun 25 13:58:19 PC325 daemon-test[4445]: Daemon alive
Jun 25 13:58:20 PC325 daemon-test[4445]: Daemon alive
Jun 25 13:58:20 PC325 daemon-test[4445]: Daemon killed !

Таким образом, я могу запустить и убить демона из моей программы на C, однако я хотел бы улучшить поведение в некоторых конкретных случаях.

- Обработка аварии демона

В какой-то момент демон может выйти из строя, и в этом случае управляющая программа должна быть уведомлена, чтобы ее можно было перезапустить. Моя проблема состоит в том, чтобы обнаружить, что демон был остановлен.

Я имею хотя о запуске потока, ожидающего завершения демона при вызове pcloseОднако это не будет работать, так как демонизация уже закрыла файловые дескрипторы и отключила процесс.

Поэтому я ищу лучший способ, чтобы программа была уведомлена о выходе демона.

Я мог бы опросить с помощью вызовов Linux с exec семья (например, pgrep daemon-test или же ps aux | grep daemon-test) но я думаю, что есть более эффективный способ достичь этого.

- Обработка ошибок тестовой программы

Если тестовая программа убита или дает сбой до того, как она убьет демона, при следующем выполнении два экземпляра демона будут запущены одновременно.

Журнал тестовой программы:

$ ./popenTest 
Launching 'daemon-test' program
Program 'daemon-test' launched
Program alive !
Program alive !
Program alive !
^C
$ ./popenTest 
Launching 'daemon-test' program
Program 'daemon-test' launched
Program alive !
Program alive !
Program alive !
Program alive !
Program alive !
Program 'daemon-test' killed

Syslog:

Jun 25 14:17:25 PC325 daemon-test[4543]: Daemon started !
Jun 25 14:17:25 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:26 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:27 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:28 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:29 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:29 PC325 daemon-test[4547]: Daemon started !
Jun 25 14:17:29 PC325 daemon-test[4547]: Daemon alive
Jun 25 14:17:30 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:30 PC325 daemon-test[4547]: Daemon alive
Jun 25 14:17:31 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:31 PC325 daemon-test[4547]: Daemon alive
Jun 25 14:17:32 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:32 PC325 daemon-test[4547]: Daemon alive
Jun 25 14:17:33 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:33 PC325 daemon-test[4547]: Daemon alive
Jun 25 14:17:34 PC325 daemon-test[4543]: Daemon alive
Jun 25 14:17:34 PC325 daemon-test[4547]: Daemon alive
Jun 25 14:17:34 PC325 daemon-test[4543]: Daemon killed !
Jun 25 14:17:34 PC325 daemon-test[4547]: Daemon killed !

Я хочу избежать этой ситуации, проверив, запущены ли уже экземпляры демонов. Если нет, я могу запустить демон из управляющей программы.

В противном случае, если запущен один или несколько экземпляров демона, я убью их перед запуском нового.

Это может быть достигнуто путем вызова killall daemon-test но вызов этой команды при каждом выполнении меня не удовлетворяет, потому что большую часть времени она бесполезна.

Более того, я хотел бы явно регистрировать ситуацию при каждом выполнении, и хотя я хочу точно знать, сколько экземпляров было запущено в этом случае.

Еще раз это можно легко решить с помощью командных вызовов linux, но я ищу наиболее эффективный способ сделать это.

Кто-нибудь знает, как я мог бы реализовать управление процессом демона, не полагаясь на вызовы команд linux?


РЕДАКТИРОВАТЬ: 26 июня 2018

Я должен был уточнить это с самого начала, но моя цель - иметь возможность контролировать процесс демона без необходимости изменять его код.

Таким образом, демон не записывает свой pid в файл и всегда отсоединяется от своего вызывающего.

2 ответа

Решение

Вместо запуска программы через popen почему бы не использовать старый добрый POSIX fork + exec? Это дает вам больше гибкости.

Теперь, чтобы ответить на ваш вопрос:

Моя проблема состоит в том, чтобы обнаружить, что демон был остановлен.

Для этого нужно слушать SIGCHLD сигнал в вашем родительском / контролирующем процессе. Это достаточно хорошо, так как вы напрямую вызвали процесс. Но если бы вы вызвали сценарий оболочки, который затем разветвлял вашего демона, это было бы сложно. Вот почему большинство демонов пишут что-то pid file - файл, написанный демоном на ранней стадии с его PID в качестве единственного содержимого в этом файле. Обычно люди говорят /tmp/mydaemon.pid или что-то типа того.

В Linux ваш управляющий процесс может читать PID из этого файла, а затем каждую секунду вы можете проверить, /proc/<pid>/exe Файл существует. Если нет, то вы знаете, что демон умер. Например, если PID вашей детской программы равен 1234, то /proc/1234/exe будет мягкой ссылкой на фактическое местоположение исполняемого файла дочерней программы.

Что-то вроде этого:

FILE *f;
pid_t pid_child;
char proc_path[256];

f = fopen("/tmp/mydaemon.pid", "r");
fscanf(f, "%d", &pid_child);
fclose(f);
sprintf(proc_path, "/proc/%d/exe", pid_child);

while(1) {
    if (access(proc_path, F_OK) == 0) {
        printf("Program alive !\n");
        sleep(1);
    } else {
        printf("Program dead!\n");
        break;
    }
}

На самом деле это примерно столько же систем инициализации. Посмотрите rc, systemd, upstart и т. Д. Для лучшего понимания того, как они реализуют это более подробно.

Вы можете запустить сервер сокетов в демоне, а затем использовать управляющий клиент как обычный CLI. CLI отправляет тестовые сообщения или управляющие команды демону, а демон дает ответ. Основываясь на ответах от демона, CLI может наблюдать за состоянием демона и контролировать его.

Другие вопросы по тегам