wvdaemon.cc

00001 /* -*- Mode: C++ -*-
00002  * Worldvisions Tunnel Vision Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  *
00005  * High-level abstraction for creating daemon processes.  Handles
00006  * command-line argument processing, forking into the background,
00007  * and signal handling.
00008  */
00009 
00010 #include "wvdaemon.h"
00011 
00012 #include "wvlinklist.h"
00013 #ifndef _WIN32
00014 #include "wvsyslog.h"
00015 #include "wvcrash.h"
00016 #include "wvfile.h"
00017 #include "wvatomicfile.h"
00018 
00019 #include <signal.h>
00020 #include <sys/types.h>
00021 #include <sys/stat.h>
00022 #include <fcntl.h>
00023 #else
00024 #include "wvlogrcv.h"
00025 #endif
00026 
00027 DeclareWvList(WvDaemon);
00028 static WvDaemonList daemons;
00029 
00030 #ifndef _WIN32
00031 
00032 static void sighup_handler(int signum)
00033 {
00034     signal(signum, SIG_IGN);
00035 
00036     WvDaemonList::Iter i(daemons);
00037     for (i.rewind(); i.next(); )
00038     {
00039         i->log(WvLog::Notice, "Restarting on signal %s.\n", signum);
00040         i->restart();
00041     }
00042 }
00043 
00044 static void sigterm_handler(int signum)
00045 {
00046     signal(signum, SIG_DFL);
00047 
00048     WvDaemonList::Iter i(daemons);
00049     for (i.rewind(); i.next(); )
00050     {
00051         i->log(WvLog::Notice, "Dying on signal %s.\n", signum);
00052         i->die();
00053     }
00054 }
00055 
00056 static void sigquit_handler(int signum)
00057 {
00058     signal(signum, SIG_IGN);
00059 
00060     exit(1);
00061 }
00062 
00063 #endif // _WIN32
00064 
00065 WvDaemon::WvDaemon(WvStringParm _name, WvStringParm _version,
00066         WvDaemonCallback _start_callback,
00067         WvDaemonCallback _run_callback,
00068         WvDaemonCallback _stop_callback,
00069         void *_ud)
00070     : name(_name), version(_version),
00071 #ifndef _WIN32
00072             pid_file("/var/run/%s.pid", _name),
00073 #endif
00074             daemonize(false),
00075             log(_name, WvLog::Debug),
00076             log_level(WvLog::Info),
00077             syslog(false),
00078             start_callback(_start_callback),
00079             run_callback(_run_callback),
00080             stop_callback(_stop_callback),
00081             ud(_ud)
00082 {
00083     args.add_option('q', "quiet",
00084             "Decrease log level (can be used multiple times)",
00085             WvArgs::NoArgCallback(this, &WvDaemon::dec_log_level));
00086     args.add_option('v', "verbose",
00087             "Increase log level (can be used multiple times)",
00088             WvArgs::NoArgCallback(this, &WvDaemon::inc_log_level));
00089 #ifndef _WIN32
00090     args.add_option('d', "daemonize",
00091             "Fork into background and return (implies --syslog)",
00092             WvArgs::NoArgCallback(this, &WvDaemon::set_daemonize));
00093     args.add_set_bool_option('s', "syslog",
00094             "Write log entries to syslog", syslog);
00095     args.add_reset_bool_option(0, "no-syslog",
00096             "Do not write log entries to syslog", syslog);
00097 #endif
00098     args.set_version(WvString("%s version %s", name, version).cstr());
00099 }
00100 
00101 int WvDaemon::run(const char *argv0)
00102 {
00103 #ifndef _WIN32
00104     if (daemonize)
00105     {
00106         pid_t pid = ::fork();
00107         if (pid < 0)
00108         {
00109             wverr->print("Failed to fork daemon: %s\n",
00110                     strerror(errno));
00111             return 3;
00112         }
00113         else if (pid == 0)
00114         {
00115             setsid();
00116             pid = fork();
00117             if (pid < 0)
00118             {
00119                 wverr->print("Failed to double-fork daemon: %s\n",
00120                         strerror(errno));
00121             }
00122             else if (pid == 0)
00123             {
00124                 ::chdir("/");
00125                 
00126                 ::umask(0);
00127                 
00128                 int null_fd;
00129                 do
00130                 {
00131                     null_fd = ::open("/dev/null", O_RDWR);
00132                     if (null_fd == -1)
00133                     {
00134                         log(WvLog::Error, "Failed to open /dev/null: %s\n",
00135                                 strerror(errno));
00136                         _exit(1);
00137                     }
00138                 } while (null_fd == 0 || null_fd == 1 || null_fd == 2);
00139                 
00140                 if (::dup2(null_fd, 0) == -1
00141                         || ::dup2(null_fd, 1) == -1
00142                         || ::dup2(null_fd, 2) == -1)
00143                 {
00144                     // Can no longer write to syslog...
00145                     log(WvLog::Error, "Failed to dup2(null_fd, (0|1|2)): %s\n",
00146                             strerror(errno));
00147                     _exit(1);
00148                 }
00149                 ::close(null_fd);
00150 
00151                 // Make sure the close-on-exec flag is not set for
00152                 // the first three descriptors, since many programs
00153                 // assume that they are open after exec()
00154                 if (::fcntl(0, F_SETFD, 0) == -1
00155                         || ::fcntl(1, F_SETFD, 0) == -1
00156                         || ::fcntl(2, F_SETFD, 0) == -1)
00157                 {
00158                     log(WvLog::Warning, "Failed to fcntl((0|1|2), F_SETFD, 0): %s\n",
00159                             strerror(errno));
00160                 }
00161                 
00162                 return _run(argv0); // Make sure destructors are called
00163             }
00164 
00165             _exit(0);
00166         }
00167 
00168         return 0;
00169     }
00170     else
00171 #else
00172     if (1)
00173 #endif
00174     {
00175         WvLogConsole console_log(STDOUT_FILENO, log_level);
00176         return _run(argv0);
00177     }
00178 }
00179 
00180 int WvDaemon::run(int argc, char **argv)
00181 {
00182     if (!args.process(argc, argv, &_extra_args))
00183         return 1;
00184 
00185     if (syslog)
00186     {
00187         WvSyslog syslog(name, false);
00188         return run(argv[0]);
00189     }
00190     else
00191         return run(argv[0]);
00192 }
00193 
00194 int WvDaemon::_run(const char *argv0)
00195 {
00196 #ifndef _WIN32
00197     wvcrash_setup(argv0, version);
00198 
00199     if (!!pid_file && daemonize)
00200     {
00201         // FIXME: this is racy!
00202         
00203         // First, make sure we aren't already running
00204         WvFile old_pid_fd(pid_file, O_RDONLY);
00205         if (old_pid_fd.isok())
00206         {
00207             WvString line = old_pid_fd.getline(0);
00208             if (!!line)
00209             {
00210                 pid_t old_pid = line.num();
00211                 if (old_pid > 0 && (kill(old_pid, 0) == 0 || errno == EPERM))
00212                 {
00213                     log(WvLog::Error,
00214                             "%s is already running (pid %s); exiting\n",
00215                             name, old_pid);
00216                     return 1;
00217                 }
00218             }
00219         }
00220         old_pid_fd.close();
00221 
00222         // Now write our new PID file
00223         WvAtomicFile pid_fd(pid_file, O_WRONLY, 0600);
00224         pid_fd.print("%s\n", getpid());
00225         if (!pid_fd.isok())
00226             log(WvLog::Warning, "Failed to write PID file %s: %s\n",
00227                     pid_file, pid_fd.errstr());
00228         pid_fd.close();
00229     }
00230 #endif
00231     log(WvLog::Notice, "Starting %s version %s.\n", name, version);
00232 
00233     daemons.append(this, false);
00234 
00235 #ifndef _WIN32    
00236     if (daemonize)
00237         signal(SIGINT, SIG_IGN);
00238     else
00239         signal(SIGINT, sigterm_handler);
00240     signal(SIGTERM, sigterm_handler);
00241     signal(SIGQUIT, sigquit_handler);
00242     signal(SIGHUP, sighup_handler);
00243 #endif
00244 
00245     _want_to_die = false;
00246     while (!want_to_die())
00247     {
00248         _want_to_restart = false;
00249 
00250         if (!!start_callback)
00251             start_callback(*this, ud);
00252 
00253         run_callback(*this, ud);
00254 
00255         if (!!stop_callback)
00256             stop_callback(*this, ud);
00257     }
00258 
00259     daemons.unlink(this);
00260 #ifndef _WIN32
00261     if (daemons.count() == 0)
00262     {
00263         signal(SIGHUP, SIG_DFL);
00264         signal(SIGQUIT, SIG_DFL);
00265         signal(SIGINT, SIG_DFL);
00266         signal(SIGTERM, SIG_DFL);
00267     }
00268 #endif
00269     log(WvLog::Notice, "Exiting with status %s\n", _exit_status);
00270 
00271 #ifndef _WIN32    
00272     if (!!pid_file)
00273         ::unlink(pid_file);
00274 #endif
00275 
00276     return _exit_status;
00277 }
00278 
00279 bool WvDaemon::set_daemonize(void *)
00280 {
00281     daemonize = true;
00282     syslog = true;
00283     return true;
00284 }

Generated on Thu May 25 21:51:03 2006 for WvStreams by  doxygen 1.4.6