WvStreams
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 #include "wvsyslog.h"
00014 #ifndef _WIN32
00015 #include "wvcrash.h"
00016 #include "wvcrashlog.h"
00017 #include "wvfile.h"
00018 #include "wvatomicfile.h"
00019 
00020 #include <signal.h>
00021 #include <sys/types.h>
00022 #include <sys/stat.h>
00023 #include <fcntl.h>
00024 #else
00025 #include "wvlogrcv.h"
00026 #endif
00027 
00028 #ifndef _WIN32
00029 # define CAN_SYSLOG true
00030 # define CAN_DAEMONIZE true
00031 #else
00032 # define CAN_SYSLOG false
00033 # define CAN_DAEMONIZE false
00034 #endif
00035 
00036 #ifdef _MSC_VER
00037 static const int STDOUT_FILENO = 0;
00038 #endif
00039 
00040 
00041 WvDaemon *WvDaemon::singleton = NULL;
00042 
00043 
00044 #ifndef _WIN32
00045 
00046 static void sighup_handler(int signum)
00047 {
00048     signal(signum, SIG_IGN);
00049 
00050     WvDaemon::me()->log(WvLog::Notice, "Restarting on signal %s.\n", signum);
00051     WvDaemon::me()->restart();
00052 }
00053 
00054 
00055 static void sigterm_handler(int signum)
00056 {
00057     signal(signum, SIG_DFL);
00058 
00059     WvDaemon::me()->log(WvLog::Notice, "Dying on signal %s.\n", signum);
00060     WvDaemon::me()->die();
00061 }
00062 
00063 
00064 static void sigquit_handler(int signum)
00065 {
00066     signal(signum, SIG_IGN);
00067 
00068     exit(1);
00069 }
00070 
00071 #endif // _WIN32
00072 
00073 void WvDaemon::init(WvStringParm _name,
00074                     WvStringParm _version,
00075                     WvDaemonCallback _start_callback,
00076                     WvDaemonCallback _run_callback,
00077                     WvDaemonCallback _stop_callback)
00078 {
00079     name = _name;
00080     version = _version;
00081     pid_file = WvString("/var/run/%s.pid", _name);
00082     daemonize = false;
00083     log_level = WvLog::Info;
00084     syslog = false;
00085     start_callback = _start_callback;
00086     run_callback = _run_callback;
00087     stop_callback = _stop_callback;
00088 
00089     assert(singleton == NULL);
00090     singleton = this;
00091     
00092     args.add_option('q', "quiet",
00093             "Decrease log level (can be used multiple times)",
00094                     wv::bind(&WvDaemon::dec_log_level, this, _1));
00095     args.add_option('v', "verbose",
00096                     "Increase log level (can be used multiple times)",
00097                     wv::bind(&WvDaemon::inc_log_level, this, _1));
00098     if (CAN_DAEMONIZE)
00099         args.add_option('d', "daemonize",
00100                 "Fork into background and return (implies --syslog)",
00101                 wv::bind(&WvDaemon::set_daemonize, this, _1));
00102     if (CAN_SYSLOG)
00103     {
00104         args.add_set_bool_option('s', "syslog",
00105                  "Write log entries to syslog", syslog);
00106         args.add_reset_bool_option(0, "no-syslog",
00107                    "Do not write log entries to syslog", syslog);
00108     }
00109     
00110     args.set_version(WvString("%s version %s", name, version).cstr());
00111 }
00112 
00113 
00114 WvDaemon::~WvDaemon()
00115 {
00116 }
00117 
00118 
00119 int WvDaemon::run(const char *argv0)
00120 {
00121 #ifndef _WIN32
00122     if (CAN_DAEMONIZE && daemonize)
00123     {
00124         pid_t pid = ::fork();
00125         if (pid < 0)
00126         {
00127             wverr->print("Failed to fork daemon: %s\n",
00128                     strerror(errno));
00129             return 3;
00130         }
00131         else if (pid == 0)
00132         {
00133             setsid();
00134             pid = fork();
00135             if (pid < 0)
00136             {
00137                 wverr->print("Failed to double-fork daemon: %s\n",
00138                         strerror(errno));
00139             }
00140             else if (pid == 0)
00141             {
00142                 // FIXME: this happens *before* we do the daemon setup!
00143                 // We should only fork into the background *after* doing
00144                 // things like opening our listen sockets.
00145                 ::chdir("/");
00146                 ::umask(0);
00147                 
00148                 int null_fd;
00149                 do
00150                 {
00151                     null_fd = ::open("/dev/null", O_RDWR);
00152                     if (null_fd == -1)
00153                     {
00154                         log(WvLog::Error, "Failed to open /dev/null: %s\n",
00155                                 strerror(errno));
00156                         _exit(1);
00157                     }
00158                 } while (null_fd == 0 || null_fd == 1 || null_fd == 2);
00159                 
00160                 if (::dup2(null_fd, 0) == -1
00161                         || ::dup2(null_fd, 1) == -1
00162                         || ::dup2(null_fd, 2) == -1)
00163                 {
00164                     log(WvLog::Error, "Failed to dup2(null_fd, (0|1|2)): %s\n",
00165                             strerror(errno));
00166                     _exit(1);
00167                 }
00168                 ::close(null_fd);
00169 
00170                 // Make sure the close-on-exec flag is not set for
00171                 // the first three descriptors, since many programs
00172                 // assume that they are open after exec()
00173                 if (::fcntl(0, F_SETFD, 0) == -1
00174                         || ::fcntl(1, F_SETFD, 0) == -1
00175                         || ::fcntl(2, F_SETFD, 0) == -1)
00176                 {
00177                     log(WvLog::Warning, "Failed to fcntl((0|1|2), F_SETFD, 0): %s\n",
00178                             strerror(errno));
00179                 }
00180                 
00181                 return _run(argv0); // Make sure destructors are called
00182             }
00183 
00184             _exit(0);
00185         }
00186 
00187         return 0;
00188     }
00189     else
00190 #endif // !_WIN32
00191     {
00192         WvLogConsole console_log(STDOUT_FILENO, log_level);
00193         if (CAN_SYSLOG && syslog)
00194         {
00195             WvSyslog syslog(name, false);
00196             return _run(argv0);
00197         }
00198         else 
00199             return _run(argv0);
00200     }
00201 }
00202 
00203 
00204 int WvDaemon::run(int argc, char **argv)
00205 {
00206     if (!args.process(argc, argv, &_extra_args))
00207         return 1;
00208     return run(argv[0]);
00209 }
00210 
00211 
00212 int WvDaemon::_run(const char *argv0)
00213 {
00214     WvLogRcv *logr = NULL;
00215 #ifndef _WIN32
00216     WvCrashLog crashlog;
00217     wvcrash_setup(argv0, version);
00218 #endif
00219     
00220     if (CAN_SYSLOG && syslog)
00221         logr = new WvSyslog(name, false);
00222 
00223     _want_to_die = false;
00224     do_load();
00225     while (!want_to_die())
00226     {
00227         _want_to_restart = false;
00228 
00229         do_start();
00230         
00231         while (should_run())
00232             do_run();
00233         
00234         do_stop();
00235     }
00236     do_unload();
00237     
00238     if (logr)
00239         delete logr;
00240 
00241     return _exit_status;
00242 }
00243 
00244 
00245 void WvDaemon::do_load()
00246 {
00247 #ifndef _WIN32
00248     if (!!pid_file && daemonize)
00249     {
00250         // FIXME: this is racy!
00251         
00252         // First, make sure we aren't already running
00253         WvFile old_pid_fd(pid_file, O_RDONLY);
00254         if (old_pid_fd.isok())
00255         {
00256             WvString line = old_pid_fd.getline(0);
00257             if (!!line)
00258             {
00259                 pid_t old_pid = line.num();
00260                 if (old_pid > 0 && (kill(old_pid, 0) == 0 || errno == EPERM))
00261                 {
00262                     log(WvLog::Error,
00263                             "%s is already running (pid %s); exiting\n",
00264                             name, old_pid);
00265                     die();
00266                 }
00267             }
00268         }
00269         old_pid_fd.close();
00270         if (want_to_die())
00271             return;
00272 
00273         // Now write our new PID file
00274         WvAtomicFile pid_fd(pid_file, O_WRONLY, 0600);
00275         pid_fd.print("%s\n", getpid());
00276         if (!pid_fd.isok())
00277             log(WvLog::Warning, "Failed to write PID file %s: %s\n",
00278                     pid_file, pid_fd.errstr());
00279         pid_fd.close();
00280     }
00281 #endif
00282     log(WvLog::Notice, "Starting %s version %s.\n", name, version);
00283 
00284 #ifndef _WIN32    
00285     if (daemonize)
00286         signal(SIGINT, SIG_IGN);
00287     else
00288         signal(SIGINT, sigterm_handler);
00289     signal(SIGTERM, sigterm_handler);
00290     signal(SIGQUIT, sigquit_handler);
00291     signal(SIGHUP, sighup_handler);
00292 #endif
00293 
00294     if (load_callback)
00295         load_callback();
00296 }
00297 
00298 
00299 void WvDaemon::do_start()
00300 {
00301     if (start_callback)
00302         start_callback();
00303 }
00304 
00305 
00306 void WvDaemon::do_run()
00307 {
00308     if (run_callback)
00309         run_callback();
00310 }
00311 
00312 
00313 void WvDaemon::do_stop()
00314 {
00315     if (stop_callback)
00316         stop_callback();
00317 }
00318 
00319 
00320 void WvDaemon::do_unload()
00321 {
00322     if (unload_callback)
00323         unload_callback();
00324 
00325 #ifndef _WIN32
00326     signal(SIGHUP, SIG_DFL);
00327     signal(SIGQUIT, SIG_DFL);
00328     signal(SIGINT, SIG_DFL);
00329     signal(SIGTERM, SIG_DFL);
00330 #endif
00331 
00332     log(WvLog::Notice, "Exiting with status %s\n", _exit_status);
00333 
00334 #ifndef _WIN32    
00335     if (!!pid_file && daemonize)
00336         ::unlink(pid_file);
00337 #endif
00338 }
00339 
00340 
00341 bool WvDaemon::set_daemonize(void *)
00342 {
00343     daemonize = true;
00344     syslog = true;
00345     return true;
00346 }