00001
00002
00003
00004
00005
00006
00007
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
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
00153
00154
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);
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
00237
00238
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
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 }