home *** CD-ROM | disk | FTP | other *** search
- /* Functions to turn a program into a daemon, handle signals, manage
- * a log file facility, and do memory allocation checking.
- *
- * Copyright (c) 1994 Christopher J. Kane.
- *
- * This software is subject to the terms of the MiscKit license
- * agreement. Refer to the license document included with the
- * MiscKit distribution for these terms.
- *
- * Version 1.0.1 (16 April 1994)
- */
-
- #include <misckit/Miscdaemon.h> /* for MiscKit use */
- /* #include "Miscdaemon.h" /* outside of MiscKit */
- #include <errno.h>
- #include <libc.h>
- #include <varargs.h>
-
- #if !defined(SIGRET_T)
- #if defined(SUN3) || defined(HPUX7) || defined(SUN5) || defined(NeXT) || defined(__NetBSD__) || defined(sparc) || defined(_AIX)
- #define SIGRET_T void
- #else
- #define SIGRET_T int
- #endif
- #endif
-
- #if !defined(WAIT_T)
- #if defined(SUN3) || defined(SUN4) || defined(NeXT)
- #define WAIT_T union wait
- #else
- #define WAIT_T int
- #endif
- #endif
-
- #if !defined(SIGCHLD) && defined(SIGCLD)
- #define SIGCHLD SIGCLD
- #endif
-
- void *_daemon_alloc(unsigned int, char *, int);
- void *_daemon_free(void *, char *, int);
-
-
- /* Exported variable definitions */
-
- int daemon_pid = 0;
- char *daemon_name = "";
- char *daemon_host = "";
-
-
- /* Private variable definitions */
-
- static char *syslog_prionames[] =
- {"EMERG", "ALERT", "CRIT", "ERR", "WARN", "NOTICE", "INFO", "DEBUG"};
-
- static int default_ignsigs[] = {SIGINT, SIGPIPE, SIGALRM, SIGTSTP, SIGTTIN,
- SIGTTOU, SIGVTALRM, SIGPROF, SIGUSR1, SIGUSR2, 0};
-
- static char *daemon_lockfn = NULL;
- static char *daemon_logfn = NULL;
- static FILE *daemon_logfile = NULL;
- static int daemon_logfac = 0;
-
-
- /* Private function definitions */
-
- /* Handles log-hangup signals received by the daemon, returning nothing.
- * The signal that causes this function to be called is specified by a
- * parameter to daemonize(). The log file is closed and re-opened, so
- * that it may be trimmed. */
- static void daemon_hangup_log()
- {
- FILE *new_file;
- int olderrno;
-
- olderrno = errno;
- if (daemon_logfn != NULL && strcmp(daemon_logfn, "syslog") &&
- strcmp(daemon_logfn, "stderr")) {
- daemon_log(LOG_INFO, "closing log file %s", daemon_logfn);
- new_file = fopen(daemon_logfn, "a");
- if (new_file == NULL) {
- daemon_log(LOG_ERR, "could not reopen log file; retaining old handle");
- } else {
- fclose(daemon_logfile);
- daemon_logfile = new_file;
- daemon_log(LOG_INFO, "log file reopened");
- }
- }
- errno = olderrno;
- }
-
- /* Handles SIGTERMs received by the daemon. SIGTERM cleans up nicely
- * and shuts the daemon down. */
- static void daemon_sigterm()
- {
- daemon_log(LOG_WARNING, "daemon shutdown on SIGTERM");
- daemon_exit(0);
- }
-
- /* Handles SIGCHLDs received by the daemon, reaping terminated children.
- * This is a default version, used only if no SIGCHLD handler has been
- * set when daemonize() is called. */
- static void daemon_sigchld()
- {
- WAIT_T status;
- int olderrno;
-
- olderrno = errno;
- while (wait3(&status, WNOHANG | WUNTRACED, NULL) > 0);
- errno = olderrno;
- }
-
- /* Setup file descriptors (mostly, close them) and log file variables.
- * Bits set in the ignfds fd_set indicate that those file descriptors
- * should be ignored--open file descriptors should remain open, closed
- * file descriptors should remain closed. */
- static void setup_fds(fd_set *ignfds, char *log_fn, int log_fac)
- {
- int i, ignerr, nullfd, ttyfd;
-
- ignerr = FD_ISSET(fileno(stderr), ignfds);
- FD_SET(fileno(stderr), ignfds);
- for (i = getdtablesize(); i--;) {
- if (!FD_ISSET(i, ignfds)) {
- close(i);
- }
- }
- FD_CLR(fileno(stderr), ignfds);
- nullfd = open("/dev/null", O_RDWR, 0666);
- if (nullfd != fileno(stdin) && !FD_ISSET(fileno(stdin), ignfds)) {
- dup2(nullfd, fileno(stdin));
- }
- if (nullfd != fileno(stdout) && !FD_ISSET(fileno(stdout), ignfds)) {
- dup2(nullfd, fileno(stdout));
- }
- if (log_fn == NULL) {
- if (debug) {
- log_fn = "stderr";
- } else {
- if (!ignerr && nullfd != fileno(stderr)) {
- dup2(nullfd, fileno(stderr));
- }
- if (nullfd != fileno(stdin) && nullfd != fileno(stdout)
- && nullfd != fileno(stderr)) {
- close(nullfd);
- }
- if (ignerr && nullfd == fileno(stderr)) {
- /* if stderr was closed */
- close(nullfd);
- }
- return;
- }
- }
- daemon_logfac = log_fac & LOG_FACMASK;
- if (daemon_logfac == 0) {
- daemon_logfac = LOG_DAEMON;
- }
- if (!strcmp(log_fn, "stderr")) {
- if (fcntl(fileno(stderr), F_GETFL, 0) != -1 || errno != EBADF) {
- daemon_logfn = daemon_alloc(7);
- strcpy(daemon_logfn, "stderr");
- daemon_logfile = stderr;
- return;
- } else {
- log_fn = "syslog";
- daemon_log(LOG_ERR, "daemonize(): attempt to log to "
- "stderr, but stderr is closed. logging to syslog.");
- }
- }
- if (!ignerr && nullfd != fileno(stderr)) {
- dup2(nullfd, fileno(stderr));
- }
- if (nullfd != fileno(stdin) && nullfd != fileno(stdout) &&
- nullfd != fileno(stderr)) {
- close(nullfd);
- }
- if (ignerr && nullfd == fileno(stderr)) {
- /* if stderr was closed */
- close(nullfd);
- }
- #if defined(TIOCNOTTY)
- setpgrp(0, daemon_pid);
- ttyfd = open("/dev/tty", O_RDWR, 0);
- if (ttyfd >= 0) {
- ioctl(ttyfd, TIOCNOTTY, 0);
- close(ttyfd);
- }
- #else
- setsid();
- #endif
- if (strcmp(log_fn, "syslog") != 0) {
- daemon_logfile = fopen(log_fn, "a");
- if (daemon_logfile == NULL) {
- log_fn = "syslog";
- daemon_log(LOG_ERR, "daemonize(): attempt to log to "
- "%s failed: %s. logging to syslog.", log_fn,
- sys_errlist[errno]);
- } else {
- daemon_logfn = daemon_alloc(strlen(log_fn) + 1);
- strcpy(daemon_logfn, log_fn);
- return;
- }
- }
- daemon_logfn = daemon_alloc(7);
- strcpy(daemon_logfn, "syslog");
- openlog(daemon_name, LOG_PID | LOG_CONS, daemon_logfac);
- }
-
-
- /* Exported function definitions */
-
- void daemonize(char *prog_name, char *log_fn, int log_facility, char *safe_dir,
- char *lockf_dir, fd_set *ignfds, int loghup_sig, int *ignsigs)
- {
- static int daemonized = 0;
- fd_set defaultfds;
- char lockfn[MAXPATHLEN + 1];
- int lockfd, i;
- SIGRET_T (*osigh)(int);
-
- if (daemonized) {
- daemon_log(LOG_ERR, "daemonize(): called multiple times");
- return;
- }
- daemonized = 1;
- daemon_pid = getpid();
- if (prog_name == NULL) {
- prog_name = "daemon";
- daemon_log(LOG_ERR, "daemonize(): NULL prog_name");
- }
- if (safe_dir != NULL) {
- if (chdir(safe_dir) < 0) {
- daemon_log(LOG_ERR, "daemonize(): chdir() to %s "
- "failed: %s", safe_dir, sys_errlist[errno]);
- }
- }
- daemon_name = daemon_alloc(strlen(prog_name) + 1);
- strcpy(daemon_name, prog_name);
- daemon_host = daemon_alloc(MAXHOSTNAMELEN + 1);
- gethostname(daemon_host, MAXHOSTNAMELEN);
- umask(0033);
- FD_ZERO(&defaultfds);
- setup_fds((ignfds == NULL ? &defaultfds : ignfds), log_fn, log_facility);
- if (debug) {
- daemon_log(LOG_NOTICE, "daemonize(): not forking for debug");
- } else {
- switch (fork()) {
- case -1:
- daemon_log(LOG_ERR, "daemonize(): fork() failed: %s",
- sys_errlist[errno]);
- daemon_exit(1);
- case 0:
- break;
- default:
- exit(0);
- }
- daemon_pid = getpid();
- }
- if (debug) {
- daemon_log(LOG_NOTICE, "daemonize(): not creating lock file for debug");
- } else if (lockf_dir != NULL) {
- sprintf(lockfn, "%s/%s.pid", lockf_dir, daemon_name);
- lockfd = open(lockfn, O_RDWR | O_CREAT | O_EXCL, 0666);
- if (lockfd < 0) {
- daemon_log(LOG_WARNING, "daemonize(): could "
- "not obtain lock file %s", lockfn);
- daemon_exit(1);
- }
- daemon_lockfn = daemon_alloc(strlen(lockfn) + 1);
- strcpy(daemon_lockfn, lockfn);
- sprintf(lockfn, "%d\n", daemon_pid);
- write(lockfd, lockfn, strlen(lockfn));
- }
- if (0 < loghup_sig || loghup_sig < NSIG) {
- signal(loghup_sig, daemon_hangup_log);
- }
- signal(SIGTERM, daemon_sigterm);
- if ((osigh = signal(SIGCHLD, daemon_sigchld)) != SIG_DFL) {
- signal(SIGCHLD, osigh);
- }
- if (ignsigs == NULL && debug) {
- daemon_log(LOG_NOTICE, "daemonize(): not masking signals for debug");
- } else {
- if (ignsigs == NULL) {
- ignsigs = default_ignsigs;
- }
- for (i = 0; ignsigs[i] != 0; i++) {
- osigh = signal(ignsigs[i], SIG_IGN);
- if (osigh != SIG_DFL) {
- signal(ignsigs[i], osigh);
- }
- }
- }
- daemon_log(LOG_INFO, "daemon started");
- }
-
- void *_daemon_alloc(unsigned int size, char *file, int line)
- {
- void *block;
-
- block = calloc(1, size);
- if (block == NULL) {
- daemon_log(LOG_ERR, "%s:%d: aborting on memory allocation"
- " failure", file, line);
- abort();
- }
- daemon_log(LOG_DEBUG, "%s:%d: daemon_alloc(): allocating %d bytes"
- " (0x%x)", file, line, size, block);
- return block;
- }
-
- void *_daemon_free(void *block, char *file, int line)
- {
- daemon_log(LOG_DEBUG, "%s:%d: daemon_free(): freeing block (0x%x)",
- file, line, block);
- free(block);
- return NULL;
- }
-
- void daemon_log(va_alist)
- va_dcl
- {
- va_list va_args;
- int prio;
- char *msg;
-
- va_start(va_args);
- prio = va_arg(va_args, int);
- msg = va_arg(va_args, char *);
- if (msg == NULL) {
- daemon_log(LOG_ERR, "daemon_log(): NULL message");
- return;
- }
- if ((!debug && prio == LOG_DEBUG) || (daemon_logfn == NULL) ||
- (prio < 0) || (abs(LOG_DEBUG-LOG_EMERG) < prio)) {
- return;
- }
- if (!strcmp(daemon_logfn, "syslog")) {
- char buf[4100];
-
- if (vsprintf(buf, msg, va_args) > 4100) {
- daemon_log(LOG_ERR, "daemon_log(): message buffer overflow");
- daemon_exit(1);
- }
- syslog(prio | daemon_logfac, buf);
- } else if (daemon_logfile != NULL) {
- struct timeval tv_tm;
- char *time;
-
- gettimeofday(&tv_tm, NULL);
- time = ctime(&tv_tm.tv_sec) + 4;
- time[15] = '\0';
- fprintf(daemon_logfile, "%s %s %s[%d]: %s ", time, daemon_host,
- daemon_name, daemon_pid, syslog_prionames[prio]);
- vfprintf(daemon_logfile, msg, va_args);
- fprintf(daemon_logfile, "\n");
- fflush(daemon_logfile);
- }
- va_end(va_args);
- }
-
- void daemon_exit(int exit_val)
- {
- if (exit_val != 0) {
- daemon_log(LOG_INFO, "daemon exiting on error (%d)", exit_val);
- }
- if (daemon_logfn != NULL && !strcmp(daemon_logfn, "syslog")) {
- closelog();
- }
- if (daemon_lockfn != NULL) {
- unlink(daemon_lockfn);
- }
- exit(exit_val);
- }
-