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

Generated on Thu Jan 24 16:50:56 2008 for WvStreams by  doxygen 1.5.4