Don't like ads? PRO users don't see any ads ;-)
Guest

Untitled

By: Bkmz on Sep 27th, 2011  |  syntax: C  |  size: 12.85 KB  |  hits: 73  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
This paste has a previous version, view the difference. Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  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.