Управление демоном C из другой программы - PullRequest
0 голосов
/ 25 июня 2018

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

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

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

#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 ]

0 голосов
/ 26 июня 2018

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

0 голосов
/ 26 июня 2018

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

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

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

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

В 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 и т. Д. Для лучшего понимания того, как они реализуют это более подробно.

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