Advertisement
Bkmz

Untitled

Sep 27th, 2011
156
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 12.85 KB | None | 0 0
  1. #if !defined(_GNU_SOURCE)
  2.     #define _GNU_SOURCE
  3. #endif
  4.  
  5.  
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <sys/file.h>
  10. #include <execinfo.h>
  11. #include <unistd.h>
  12. #include <errno.h>
  13. #include <wait.h>
  14.  
  15. // лимит для установки максимально кол-во открытых дискрипторов
  16. #define FD_LIMIT            1024*10
  17.  
  18. // константы для кодов завершения процесса
  19. #define CHILD_NEED_WORK         1
  20. #define CHILD_NEED_TERMINATE    2
  21.  
  22. #define PID_FILE "/var/run/my_daemon.pid"
  23.  
  24. // функция записи лога
  25. void WriteLog(char* Msg, ...)
  26. {
  27.     // тут должен быть код который пишет данные в лог
  28. }
  29.  
  30. // функция загрузки конфига
  31. int LoadConfig(char* FileName)
  32. {
  33.     // тут должен быть код для загрузки конфига
  34.     return 1;
  35. }
  36.  
  37. // функция которая загрузит конфиг заново
  38. // и внесет нужные поправки в работу
  39. int ReloadConfig()
  40. {
  41.     // код функции
  42.     return 1;
  43. }
  44.  
  45. // функция для остановки потоков и освобождения ресурсов
  46. void DestroyWorkThread()
  47. {
  48.     // тут должен быть код который остановит все потоки и
  49.     // корректно освободит ресурсы
  50. }
  51.  
  52. // функция которая инициализирует рабочие потоки
  53. int InitWorkThread()
  54. {
  55.     // код функции
  56.     return 1;
  57. }
  58.  
  59. // функция обработки сигналов
  60. static void signal_error(int sig, siginfo_t *si, void *ptr)
  61. {
  62.     void*  ErrorAddr;
  63.     void*  Trace[16];
  64.     int    x;
  65.     int    TraceSize;
  66.     char** Messages;
  67.  
  68.     // запишем в лог что за сигнал пришел
  69.     WriteLog("[DAEMON] Signal: %s, Addr: 0x%0.16X\n", strsignal(sig), si->si_addr);
  70.  
  71.  
  72.     #if __WORDSIZE == 64 // если дело имеем с 64 битной ОС
  73.         // получим адрес инструкции которая вызвала ошибку
  74.         ErrorAddr = (void*)((ucontext_t*)ptr)->uc_mcontext.gregs[REG_RIP];
  75.     #else
  76.         // получим адрес инструкции которая вызвала ошибку
  77.         ErrorAddr = (void*)((ucontext_t*)ptr)->uc_mcontext.gregs[REG_EIP];
  78.     #endif
  79.  
  80.     // произведем backtrace чтобы получить весь стек вызовов
  81.     TraceSize = backtrace(Trace, 16);
  82.     Trace[1] = ErrorAddr;
  83.  
  84.     // получим расшифровку трасировки
  85.     Messages = backtrace_symbols(Trace, TraceSize);
  86.     if (Messages)
  87.     {
  88.         WriteLog("== Backtrace ==\n");
  89.  
  90.         // запишем в лог
  91.         for (x = 1; x < TraceSize; x++)
  92.         {
  93.             WriteLog("%s\n", Messages[x]);
  94.         }
  95.  
  96.         WriteLog("== End Backtrace ==\n");
  97.         free(Messages);
  98.     }
  99.  
  100.     WriteLog("[DAEMON] Stopped\n");
  101.  
  102.     // остановим все рабочие потоки и корректно закроем всё что надо
  103.     DestroyWorkThread();
  104.  
  105.     // завершим процесс с кодом требующим перезапуска
  106.     exit(CHILD_NEED_WORK);
  107. }
  108.  
  109.  
  110. // функция установки максимального кол-во дескрипторов которое может быть открыто
  111. int SetFdLimit(int MaxFd)
  112. {
  113.     struct rlimit lim;
  114.     int           status;
  115.  
  116.     // зададим текущий лимит на кол-во открытых дискриптеров
  117.     lim.rlim_cur = MaxFd;
  118.     // зададим максимальный лимит на кол-во открытых дискриптеров
  119.     lim.rlim_max = MaxFd;
  120.  
  121.     // установим указанное кол-во
  122.     status = setrlimit(RLIMIT_NOFILE, &lim);
  123.  
  124.     return status;
  125. }
  126.  
  127.  
  128. int WorkProc()
  129. {
  130.     struct sigaction sigact;
  131.     sigset_t         sigset;
  132.     int              signo;
  133.     int              status;
  134.  
  135.     // сигналы об ошибках в программе будут обрататывать более тщательно
  136.     // указываем что хотим получать расширенную информацию об ошибках
  137.     sigact.sa_flags = SA_SIGINFO;
  138.     // задаем функцию обработчик сигналов
  139.     sigact.sa_sigaction = signal_error;
  140.  
  141.     sigemptyset(&sigact.sa_mask);
  142.  
  143.     // установим наш обработчик на сигналы
  144.  
  145.     sigaction(SIGFPE, &sigact, 0); // ошибка FPU
  146.     sigaction(SIGILL, &sigact, 0); // ошибочная инструкция
  147.     sigaction(SIGSEGV, &sigact, 0); // ошибка доступа к памяти
  148.     sigaction(SIGBUS, &sigact, 0); // ошибка шины, при обращении к физической памяти
  149.  
  150.     sigemptyset(&sigset);
  151.  
  152.     // блокируем сигналы которые будем ожидать
  153.     // сигнал остановки процесса пользователем
  154.     sigaddset(&sigset, SIGQUIT);
  155.  
  156.     // сигнал для остановки процесса пользователем с терминала
  157.     sigaddset(&sigset, SIGINT);
  158.  
  159.     // сигнал запроса завершения процесса
  160.     sigaddset(&sigset, SIGTERM);
  161.  
  162.     // пользовательский сигнал который мы будем использовать для обновления конфига
  163.     sigaddset(&sigset, SIGUSR1);
  164.     sigprocmask(SIG_BLOCK, &sigset, NULL);
  165.  
  166.     // Установим максимальное кол-во дискрипторов которое можно открыть
  167.     SetFdLimit(FD_LIMIT);
  168.  
  169.     // запишем в лог, что наш демон стартовал
  170.     WriteLog("[DAEMON] Started\n");
  171.  
  172.     // запускаем все рабочие потоки
  173.     status = InitWorkThread();
  174.     if (!status)
  175.     {
  176.         // цикл ожижания сообщений
  177.         for (;;)
  178.         {
  179.             // ждем указанных сообщений
  180.             sigwait(&sigset, &signo);
  181.  
  182.             // если то сообщение обновления конфига
  183.             if (signo == SIGUSR1)
  184.             {
  185.                 // обновим конфиг
  186.                 status = ReloadConfig();
  187.                 if (status == 0)
  188.                 {
  189.                     WriteLog("[DAEMON] Reload config failed\n");
  190.                 }
  191.                 else
  192.                 {
  193.                     WriteLog("[DAEMON] Reload config OK\n");
  194.                 }
  195.             }
  196.             else // если какой-либо другой сигнал, то выйдим из цикла
  197.             {
  198.                 break;
  199.             }
  200.         }
  201.  
  202.         // остановим все рабочеи потоки и корректно закроем всё что надо
  203.         DestroyWorkThread();
  204.     }
  205.     else
  206.     {
  207.         WriteLog("[DAEMON] Create work thread failed\n");
  208.     }
  209.  
  210.     WriteLog("[DAEMON] Stopped\n");
  211.  
  212.     // вернем код не требующим перезапуска
  213.     return CHILD_NEED_TERMINATE;
  214. }
  215.  
  216.  
  217. void SetPidFile(char* Filename)
  218. {
  219.     FILE* f;
  220.  
  221.     f = fopen(Filename, "w+");
  222.     if (f)
  223.     {
  224.         fprintf(f, "%u", getpid());
  225.         fclose(f);
  226.     }
  227. }
  228.  
  229.  
  230.  
  231. int MonitorProc()
  232. {
  233.     int       pid;
  234.     int       status;
  235.     int       need_start = 1;
  236.     sigset_t  sigset;
  237.     siginfo_t siginfo;
  238.  
  239.     // настраиваем сигналы которые будем обрабатывать
  240.     sigemptyset(&sigset);
  241.  
  242.     // сигнал остановки процесса пользователем
  243.     sigaddset(&sigset, SIGQUIT);
  244.  
  245.     // сигнал для остановки процесса пользователем с терминала
  246.     sigaddset(&sigset, SIGINT);
  247.  
  248.     // сигнал запроса завершения процесса
  249.     sigaddset(&sigset, SIGTERM);
  250.  
  251.     // сигнал посылаемый при изменении статуса дочернего процесс
  252.     sigaddset(&sigset, SIGCHLD);
  253.  
  254.     // сигнал посылаемый при изменении статуса дочернего процесс
  255.     sigaddset(&sigset, SIGCHLD);
  256.  
  257.     // пользовательский сигнал который мы будем использовать для обновления конфига
  258.     sigaddset(&sigset, SIGUSR1);
  259.     sigprocmask(SIG_BLOCK, &sigset, NULL);
  260.  
  261.     // данная функция создат файл с нашим PID'ом
  262.     SetPidFile(PID_FILE);
  263.  
  264.     // бесконечный цикл работы
  265.     for (;;)
  266.     {
  267.         // если необходимо создать потомка
  268.         if (need_start)
  269.         {
  270.             // создаём потомка
  271.             pid = fork();
  272.         }
  273.  
  274.         need_start = 1;
  275.  
  276.         if (pid == -1) // если произошла ошибка
  277.         {
  278.             // запишем в лог сообщение об этом
  279.             WriteLog("[MONITOR] Fork failed (%s)\n", strerror(errno));
  280.         }
  281.         else if (!pid) // если мы потомок
  282.         {
  283.             // данный код выполняется в потомке
  284.  
  285.             // запустим функцию отвечающую за работу демона
  286.             status = WorkProc();
  287.  
  288.             // завершим процесс
  289.             exit(status);
  290.         }
  291.         else // если мы родитель
  292.         {
  293.             // данный код выполняется в родителе
  294.  
  295.             // ожидаем поступление сигнала
  296.             sigwaitinfo(&sigset, &siginfo);
  297.  
  298.             // если пришел сигнал от потомка
  299.             if (siginfo.si_signo == SIGCHLD)
  300.             {
  301.                 // получаем статус завершение
  302.                 wait(&status);
  303.  
  304.                 // преобразуем статус в нормальный вид
  305.                 status = WEXITSTATUS(status);
  306.  
  307.                  // если потомок завершил работу с кодом говорящем о том, что нет нужны дальше работать
  308.                 if (status == CHILD_NEED_TERMINATE)
  309.                 {
  310.                     // запишем в лог сообщени об этом
  311.                     WriteLog("[MONITOR] Childer stopped\n");
  312.  
  313.                     // прервем цикл
  314.                     break;
  315.                 }
  316.                 else if (status == CHILD_NEED_WORK) // если требуется перезапустить потомка
  317.                 {
  318.                     // запишем в лог данное событие
  319.                     WriteLog("[MONITOR] Childer restart\n");
  320.                 }
  321.             }
  322.             else if (siginfo.si_signo == SIGUSR1) // если пришел сигнал что необходимо перезагрузить конфиг
  323.             {
  324.                 kill(pid, SIGUSR1); // перешлем его потомку
  325.                 need_start = 0; // установим флаг что нам не надо запускать потомка заново
  326.             }
  327.             else // если пришел какой-либо другой ожидаемый сигнал
  328.             {
  329.                 // запишем в лог информацию о пришедшем сигнале
  330.                 WriteLog("[MONITOR] Signal %s\n", strsignal(siginfo.si_signo));
  331.  
  332.                 // убьем потомка
  333.                 kill(pid, SIGTERM);
  334.                 status = 0;
  335.                 break;
  336.             }
  337.         }
  338.     }
  339.  
  340.     // запишем в лог, что мы остановились
  341.     WriteLog("[MONITOR] Stopped\n");
  342.  
  343.     // удалим файл с PID'ом
  344.     unlink(PID_FILE);
  345.  
  346.     return status;
  347. }
  348.  
  349.  
  350. int main(int argc, char** argv)
  351. {
  352.     int status;
  353.     int pid;
  354.  
  355.     // если параметров командной строки меньше двух, то покажем как использовать демана
  356.     if (argc != 2)
  357.     {
  358.         printf("Usage: ./my_daemon filename.cfg\n");
  359.         return -1;
  360.     }
  361.  
  362.     // загружаем файл конфигурации
  363.     status = LoadConfig(argv[1]);
  364.  
  365.     if (!status) // если произошла ошибка загрузки конфига
  366.     {
  367.         printf("Error: Load config failed\n");
  368.         return -1;
  369.     }
  370.  
  371.     // создаем потомка
  372.     pid = fork();
  373.  
  374.     if (pid == -1) // если не удалось запустить потомка
  375.     {
  376.         // выведем на экран ошибку и её описание
  377.         printf("Start Daemon Error: %s\n", strerror(errno));
  378.  
  379.         return -1;
  380.     }
  381.     else if (!pid) // если это потомок
  382.     {
  383.         // данный код уже выполняется в процессе потомка
  384.         // разрешаем выставлять все биты прав на создаваемые файлы,
  385.         // иначе у нас могут быть проблемы с правами доступа
  386.         umask(0);
  387.  
  388.         // создаём новый сеанс, чтобы не зависеть от родителя
  389.         setsid();
  390.  
  391.         // переходим в корень диска, если мы этого не сделаем, то могут быть проблемы.
  392.         // к примеру с размантированием дисков
  393.         chdir("/");
  394.  
  395.         // закрываем дискрипторы ввода/вывода/ошибок, так как нам они больше не понадобятся
  396.         close(STDIN_FILENO);
  397.         close(STDOUT_FILENO);
  398.         close(STDERR_FILENO);
  399.  
  400.         // Данная функция будет осуществлять слежение за процессом
  401.         status = MonitorProc();
  402.  
  403.         return status;
  404.     }
  405.     else // если это родитель
  406.     {
  407.         // завершим процес, т.к. основную свою задачу (запуск демона) мы выполнили
  408.         return 0;
  409.     }
  410. }
  411.  
  412.  
  413.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement