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 # 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
00143
00144
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
00171
00172
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);
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
00251
00252
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
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 }