WvStreams
|
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 }