home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1995 August / NEBULA.bin / SourceCode / MiscKit1.2.6 / Source / Miscdaemon.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-04-16  |  9.6 KB  |  374 lines

  1. /*  Functions to turn a program into a daemon, handle signals, manage
  2.  *  a log file facility, and do memory allocation checking.
  3.  *
  4.  *  Copyright (c) 1994 Christopher J. Kane.
  5.  *
  6.  *  This software is subject to the terms of the MiscKit license
  7.  *  agreement. Refer to the license document included with the
  8.  *  MiscKit distribution for these terms.
  9.  *
  10.  *  Version 1.0.1 (16 April 1994)
  11.  */
  12.  
  13. #include <misckit/Miscdaemon.h> /* for MiscKit use */
  14. /* #include "Miscdaemon.h" /* outside of MiscKit */
  15. #include <errno.h>
  16. #include <libc.h>
  17. #include <varargs.h>
  18.  
  19. #if !defined(SIGRET_T)
  20.     #if defined(SUN3) || defined(HPUX7) || defined(SUN5) || defined(NeXT) || defined(__NetBSD__) || defined(sparc) || defined(_AIX)
  21.         #define SIGRET_T void
  22.     #else
  23.         #define SIGRET_T int
  24.     #endif
  25. #endif
  26.  
  27. #if !defined(WAIT_T)
  28.     #if defined(SUN3) || defined(SUN4) || defined(NeXT)
  29.         #define WAIT_T union wait
  30.     #else
  31.         #define WAIT_T int
  32.     #endif
  33. #endif
  34.  
  35. #if !defined(SIGCHLD) && defined(SIGCLD)
  36.     #define SIGCHLD SIGCLD
  37. #endif
  38.  
  39. void *_daemon_alloc(unsigned int, char *, int);
  40. void *_daemon_free(void *, char *, int);
  41.  
  42.  
  43. /* Exported variable definitions */
  44.  
  45. int daemon_pid = 0;
  46. char *daemon_name = "";
  47. char *daemon_host = "";
  48.  
  49.  
  50. /* Private variable definitions */
  51.  
  52. static char *syslog_prionames[] =
  53.     {"EMERG", "ALERT", "CRIT", "ERR", "WARN", "NOTICE", "INFO", "DEBUG"};
  54.  
  55. static int default_ignsigs[] = {SIGINT, SIGPIPE, SIGALRM, SIGTSTP, SIGTTIN,
  56.     SIGTTOU, SIGVTALRM, SIGPROF, SIGUSR1, SIGUSR2, 0};
  57.  
  58. static char *daemon_lockfn = NULL;
  59. static char *daemon_logfn = NULL;
  60. static FILE *daemon_logfile = NULL;
  61. static int daemon_logfac = 0;
  62.  
  63.  
  64. /* Private function definitions */
  65.  
  66. /* Handles log-hangup signals received by the daemon, returning nothing.
  67.  * The signal that causes this function to be called is specified by a
  68.  * parameter to daemonize().  The log file is closed and re-opened, so
  69.  * that it may be trimmed. */
  70. static void daemon_hangup_log()
  71. {
  72.     FILE *new_file;
  73.     int olderrno;
  74.  
  75.     olderrno = errno;
  76.     if (daemon_logfn != NULL && strcmp(daemon_logfn, "syslog") &&
  77.         strcmp(daemon_logfn, "stderr")) {
  78.         daemon_log(LOG_INFO, "closing log file %s", daemon_logfn);
  79.         new_file = fopen(daemon_logfn, "a");
  80.         if (new_file == NULL) {
  81.             daemon_log(LOG_ERR, "could not reopen log file; retaining old handle");
  82.         } else {
  83.             fclose(daemon_logfile);
  84.             daemon_logfile = new_file;
  85.             daemon_log(LOG_INFO, "log file reopened");
  86.         }
  87.     }
  88.     errno = olderrno;
  89. }
  90.  
  91. /* Handles SIGTERMs received by the daemon.  SIGTERM cleans up nicely
  92.  * and shuts the daemon down. */
  93. static void daemon_sigterm()
  94. {
  95.     daemon_log(LOG_WARNING, "daemon shutdown on SIGTERM");
  96.     daemon_exit(0);
  97. }
  98.  
  99. /* Handles SIGCHLDs received by the daemon, reaping terminated children.
  100.  * This is a default version, used only if no SIGCHLD handler has been
  101.  * set when daemonize() is called. */
  102. static void daemon_sigchld()
  103. {
  104.     WAIT_T status;
  105.     int olderrno;
  106.  
  107.     olderrno = errno;
  108.     while (wait3(&status, WNOHANG | WUNTRACED, NULL) > 0);
  109.     errno = olderrno;
  110. }
  111.  
  112. /* Setup file descriptors (mostly, close them) and log file variables.
  113.  * Bits set in the ignfds fd_set indicate that those file descriptors
  114.  * should be ignored--open file descriptors should remain open, closed
  115.  * file descriptors should remain closed. */
  116. static void setup_fds(fd_set *ignfds, char *log_fn, int log_fac)
  117. {
  118.     int i, ignerr, nullfd, ttyfd;
  119.  
  120.     ignerr = FD_ISSET(fileno(stderr), ignfds);
  121.     FD_SET(fileno(stderr), ignfds);
  122.     for (i = getdtablesize(); i--;) {
  123.         if (!FD_ISSET(i, ignfds)) {
  124.             close(i);
  125.         }
  126.     }
  127.     FD_CLR(fileno(stderr), ignfds);
  128.     nullfd = open("/dev/null", O_RDWR, 0666);
  129.     if (nullfd != fileno(stdin) && !FD_ISSET(fileno(stdin), ignfds)) {
  130.         dup2(nullfd, fileno(stdin));
  131.     }
  132.     if (nullfd != fileno(stdout) && !FD_ISSET(fileno(stdout), ignfds)) {
  133.         dup2(nullfd, fileno(stdout));
  134.     }
  135.     if (log_fn == NULL) {
  136.         if (debug) {
  137.             log_fn = "stderr";
  138.         } else {
  139.             if (!ignerr && nullfd != fileno(stderr)) {
  140.                 dup2(nullfd, fileno(stderr));
  141.             }
  142.             if (nullfd != fileno(stdin) && nullfd != fileno(stdout)
  143.                 && nullfd != fileno(stderr)) {
  144.                 close(nullfd);
  145.             }
  146.             if (ignerr && nullfd == fileno(stderr)) {
  147.                 /* if stderr was closed */
  148.                 close(nullfd);
  149.             }
  150.             return;
  151.         }
  152.     }
  153.     daemon_logfac = log_fac & LOG_FACMASK;
  154.     if (daemon_logfac == 0) {
  155.         daemon_logfac = LOG_DAEMON;
  156.     }
  157.     if (!strcmp(log_fn, "stderr")) {
  158.         if (fcntl(fileno(stderr), F_GETFL, 0) != -1 || errno != EBADF) {
  159.             daemon_logfn = daemon_alloc(7);
  160.             strcpy(daemon_logfn, "stderr");
  161.             daemon_logfile = stderr;
  162.             return;
  163.         } else {
  164.             log_fn = "syslog";
  165.             daemon_log(LOG_ERR, "daemonize(): attempt to log to "
  166.                "stderr, but stderr is closed. logging to syslog.");
  167.         }
  168.     }
  169.     if (!ignerr && nullfd != fileno(stderr)) {
  170.         dup2(nullfd, fileno(stderr));
  171.     }
  172.     if (nullfd != fileno(stdin) && nullfd != fileno(stdout) &&
  173.         nullfd != fileno(stderr)) {
  174.         close(nullfd);
  175.     }
  176.     if (ignerr && nullfd == fileno(stderr)) {
  177.         /* if stderr was closed */
  178.         close(nullfd);
  179.     }
  180. #if defined(TIOCNOTTY)
  181.     setpgrp(0, daemon_pid);
  182.     ttyfd = open("/dev/tty", O_RDWR, 0);
  183.     if (ttyfd >= 0) {
  184.         ioctl(ttyfd, TIOCNOTTY, 0);
  185.         close(ttyfd);
  186.     }
  187. #else
  188.     setsid();
  189. #endif
  190.     if (strcmp(log_fn, "syslog") != 0) {
  191.         daemon_logfile = fopen(log_fn, "a");
  192.         if (daemon_logfile == NULL) {
  193.             log_fn = "syslog";
  194.             daemon_log(LOG_ERR, "daemonize(): attempt to log to "
  195.                 "%s failed: %s. logging to syslog.", log_fn,
  196.                 sys_errlist[errno]);
  197.         } else {
  198.             daemon_logfn = daemon_alloc(strlen(log_fn) + 1);
  199.             strcpy(daemon_logfn, log_fn);
  200.             return;
  201.         }
  202.     }
  203.     daemon_logfn = daemon_alloc(7);
  204.     strcpy(daemon_logfn, "syslog");
  205.     openlog(daemon_name, LOG_PID | LOG_CONS, daemon_logfac);
  206. }
  207.  
  208.  
  209. /* Exported function definitions */
  210.  
  211. void daemonize(char *prog_name, char *log_fn, int log_facility, char *safe_dir,
  212.         char *lockf_dir, fd_set *ignfds, int loghup_sig, int *ignsigs)
  213. {
  214.     static int daemonized = 0;
  215.     fd_set defaultfds;
  216.     char lockfn[MAXPATHLEN + 1];
  217.     int lockfd, i;
  218.     SIGRET_T (*osigh)(int);
  219.  
  220.     if (daemonized) {
  221.         daemon_log(LOG_ERR, "daemonize(): called multiple times");
  222.         return;
  223.     }
  224.     daemonized = 1;
  225.     daemon_pid = getpid();
  226.     if (prog_name == NULL) {
  227.         prog_name = "daemon";
  228.         daemon_log(LOG_ERR, "daemonize(): NULL prog_name");
  229.     }
  230.     if (safe_dir != NULL) {
  231.         if (chdir(safe_dir) < 0) {
  232.             daemon_log(LOG_ERR, "daemonize(): chdir() to %s "
  233.                 "failed: %s", safe_dir, sys_errlist[errno]);
  234.         }
  235.     }
  236.     daemon_name = daemon_alloc(strlen(prog_name) + 1);
  237.     strcpy(daemon_name, prog_name);
  238.     daemon_host = daemon_alloc(MAXHOSTNAMELEN + 1);
  239.     gethostname(daemon_host, MAXHOSTNAMELEN);
  240.     umask(0033);
  241.     FD_ZERO(&defaultfds);
  242.     setup_fds((ignfds == NULL ? &defaultfds : ignfds), log_fn, log_facility);
  243.     if (debug) {
  244.         daemon_log(LOG_NOTICE, "daemonize(): not forking for debug");
  245.     } else {
  246.         switch (fork()) {
  247.         case -1:
  248.             daemon_log(LOG_ERR, "daemonize(): fork() failed: %s",
  249.                     sys_errlist[errno]);
  250.             daemon_exit(1);
  251.         case 0:
  252.             break;
  253.         default:
  254.             exit(0);
  255.         }
  256.         daemon_pid = getpid();
  257.     }
  258.     if (debug) {
  259.         daemon_log(LOG_NOTICE, "daemonize(): not creating lock file for debug");
  260.     } else if (lockf_dir != NULL) {
  261.         sprintf(lockfn, "%s/%s.pid", lockf_dir, daemon_name);
  262.         lockfd = open(lockfn, O_RDWR | O_CREAT | O_EXCL, 0666);
  263.         if (lockfd < 0) {
  264.             daemon_log(LOG_WARNING, "daemonize(): could "
  265.                     "not obtain lock file %s", lockfn);
  266.             daemon_exit(1);
  267.         }
  268.         daemon_lockfn = daemon_alloc(strlen(lockfn) + 1);
  269.         strcpy(daemon_lockfn, lockfn);
  270.         sprintf(lockfn, "%d\n", daemon_pid);
  271.         write(lockfd, lockfn, strlen(lockfn));
  272.     }
  273.     if (0 < loghup_sig || loghup_sig < NSIG) {
  274.         signal(loghup_sig, daemon_hangup_log);
  275.     }
  276.     signal(SIGTERM, daemon_sigterm);
  277.     if ((osigh = signal(SIGCHLD, daemon_sigchld)) != SIG_DFL) {
  278.         signal(SIGCHLD, osigh);
  279.     }
  280.     if (ignsigs == NULL && debug) {
  281.         daemon_log(LOG_NOTICE, "daemonize(): not masking signals for debug");
  282.     } else {
  283.         if (ignsigs == NULL) {
  284.             ignsigs = default_ignsigs;
  285.         }
  286.         for (i = 0; ignsigs[i] != 0; i++) {
  287.             osigh = signal(ignsigs[i], SIG_IGN);
  288.             if (osigh != SIG_DFL) {
  289.                 signal(ignsigs[i], osigh);
  290.             }
  291.         }
  292.     }
  293.     daemon_log(LOG_INFO, "daemon started");
  294. }
  295.  
  296. void *_daemon_alloc(unsigned int size, char *file, int line)
  297. {
  298.     void *block;
  299.  
  300.     block = calloc(1, size);
  301.     if (block == NULL) {
  302.         daemon_log(LOG_ERR, "%s:%d: aborting on memory allocation"
  303.                 " failure", file, line);
  304.         abort();
  305.     }
  306.     daemon_log(LOG_DEBUG, "%s:%d: daemon_alloc(): allocating %d bytes"
  307.             " (0x%x)", file, line, size, block);
  308.     return block;
  309. }
  310.  
  311. void *_daemon_free(void *block, char *file, int line)
  312. {
  313.     daemon_log(LOG_DEBUG, "%s:%d: daemon_free(): freeing block (0x%x)",
  314.             file, line, block);
  315.     free(block);
  316.     return NULL;
  317. }
  318.  
  319. void daemon_log(va_alist)
  320.     va_dcl
  321. {
  322.     va_list va_args;
  323.     int prio;
  324.     char *msg;
  325.  
  326.     va_start(va_args);
  327.     prio = va_arg(va_args, int);
  328.     msg = va_arg(va_args, char *);
  329.     if (msg == NULL) {
  330.         daemon_log(LOG_ERR, "daemon_log(): NULL message");
  331.         return;
  332.     }
  333.     if ((!debug && prio == LOG_DEBUG) || (daemon_logfn == NULL) ||
  334.         (prio < 0) || (abs(LOG_DEBUG-LOG_EMERG) < prio)) {
  335.         return;
  336.     }
  337.     if (!strcmp(daemon_logfn, "syslog")) {
  338.         char buf[4100];
  339.  
  340.         if (vsprintf(buf, msg, va_args) > 4100) {
  341.             daemon_log(LOG_ERR, "daemon_log(): message buffer overflow");
  342.             daemon_exit(1);
  343.         }
  344.         syslog(prio | daemon_logfac, buf);
  345.     } else if (daemon_logfile != NULL) {
  346.         struct timeval tv_tm;
  347.         char *time;
  348.  
  349.         gettimeofday(&tv_tm, NULL);
  350.         time = ctime(&tv_tm.tv_sec) + 4;
  351.         time[15] = '\0';
  352.         fprintf(daemon_logfile, "%s %s %s[%d]: %s ", time, daemon_host,
  353.             daemon_name, daemon_pid, syslog_prionames[prio]);
  354.         vfprintf(daemon_logfile, msg, va_args);
  355.         fprintf(daemon_logfile, "\n");
  356.         fflush(daemon_logfile);
  357.     }
  358.     va_end(va_args);
  359. }
  360.  
  361. void daemon_exit(int exit_val)
  362. {
  363.     if (exit_val != 0) {
  364.         daemon_log(LOG_INFO, "daemon exiting on error (%d)", exit_val);
  365.     }
  366.     if (daemon_logfn != NULL && !strcmp(daemon_logfn, "syslog")) {
  367.         closelog();
  368.     }
  369.     if (daemon_lockfn != NULL) {
  370.         unlink(daemon_lockfn);
  371.     }
  372.     exit(exit_val);
  373. }
  374.