00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "wvdaemon.h"
00011
00012 #include "wvlinklist.h"
00013 #ifndef _WIN32
00014 #include "wvsyslog.h"
00015 #include "wvcrash.h"
00016 #include "wvfile.h"
00017 #include "wvatomicfile.h"
00018
00019 #include <signal.h>
00020 #include <sys/types.h>
00021 #include <sys/stat.h>
00022 #include <fcntl.h>
00023 #else
00024 #include "wvlogrcv.h"
00025 #endif
00026
00027 DeclareWvList(WvDaemon);
00028 static WvDaemonList daemons;
00029
00030 #ifndef _WIN32
00031
00032 static void sighup_handler(int signum)
00033 {
00034 signal(signum, SIG_IGN);
00035
00036 WvDaemonList::Iter i(daemons);
00037 for (i.rewind(); i.next(); )
00038 {
00039 i->log(WvLog::Notice, "Restarting on signal %s.\n", signum);
00040 i->restart();
00041 }
00042 }
00043
00044 static void sigterm_handler(int signum)
00045 {
00046 signal(signum, SIG_DFL);
00047
00048 WvDaemonList::Iter i(daemons);
00049 for (i.rewind(); i.next(); )
00050 {
00051 i->log(WvLog::Notice, "Dying on signal %s.\n", signum);
00052 i->die();
00053 }
00054 }
00055
00056 static void sigquit_handler(int signum)
00057 {
00058 signal(signum, SIG_IGN);
00059
00060 exit(1);
00061 }
00062
00063 #endif // _WIN32
00064
00065 WvDaemon::WvDaemon(WvStringParm _name, WvStringParm _version,
00066 WvDaemonCallback _start_callback,
00067 WvDaemonCallback _run_callback,
00068 WvDaemonCallback _stop_callback,
00069 void *_ud)
00070 : name(_name), version(_version),
00071 #ifndef _WIN32
00072 pid_file("/var/run/%s.pid", _name),
00073 #endif
00074 daemonize(false),
00075 log(_name, WvLog::Debug),
00076 log_level(WvLog::Info),
00077 syslog(false),
00078 start_callback(_start_callback),
00079 run_callback(_run_callback),
00080 stop_callback(_stop_callback),
00081 ud(_ud)
00082 {
00083 args.add_option('q', "quiet",
00084 "Decrease log level (can be used multiple times)",
00085 WvArgs::NoArgCallback(this, &WvDaemon::dec_log_level));
00086 args.add_option('v', "verbose",
00087 "Increase log level (can be used multiple times)",
00088 WvArgs::NoArgCallback(this, &WvDaemon::inc_log_level));
00089 #ifndef _WIN32
00090 args.add_option('d', "daemonize",
00091 "Fork into background and return (implies --syslog)",
00092 WvArgs::NoArgCallback(this, &WvDaemon::set_daemonize));
00093 args.add_set_bool_option('s', "syslog",
00094 "Write log entries to syslog", syslog);
00095 args.add_reset_bool_option(0, "no-syslog",
00096 "Do not write log entries to syslog", syslog);
00097 #endif
00098 args.set_version(WvString("%s version %s", name, version).cstr());
00099 }
00100
00101 int WvDaemon::run(const char *argv0)
00102 {
00103 #ifndef _WIN32
00104 if (daemonize)
00105 {
00106 pid_t pid = ::fork();
00107 if (pid < 0)
00108 {
00109 wverr->print("Failed to fork daemon: %s\n",
00110 strerror(errno));
00111 return 3;
00112 }
00113 else if (pid == 0)
00114 {
00115 setsid();
00116 pid = fork();
00117 if (pid < 0)
00118 {
00119 wverr->print("Failed to double-fork daemon: %s\n",
00120 strerror(errno));
00121 }
00122 else if (pid == 0)
00123 {
00124 ::chdir("/");
00125
00126 ::umask(0);
00127
00128 int null_fd;
00129 do
00130 {
00131 null_fd = ::open("/dev/null", O_RDWR);
00132 if (null_fd == -1)
00133 {
00134 log(WvLog::Error, "Failed to open /dev/null: %s\n",
00135 strerror(errno));
00136 _exit(1);
00137 }
00138 } while (null_fd == 0 || null_fd == 1 || null_fd == 2);
00139
00140 if (::dup2(null_fd, 0) == -1
00141 || ::dup2(null_fd, 1) == -1
00142 || ::dup2(null_fd, 2) == -1)
00143 {
00144
00145 log(WvLog::Error, "Failed to dup2(null_fd, (0|1|2)): %s\n",
00146 strerror(errno));
00147 _exit(1);
00148 }
00149 ::close(null_fd);
00150
00151
00152
00153
00154 if (::fcntl(0, F_SETFD, 0) == -1
00155 || ::fcntl(1, F_SETFD, 0) == -1
00156 || ::fcntl(2, F_SETFD, 0) == -1)
00157 {
00158 log(WvLog::Warning, "Failed to fcntl((0|1|2), F_SETFD, 0): %s\n",
00159 strerror(errno));
00160 }
00161
00162 return _run(argv0);
00163 }
00164
00165 _exit(0);
00166 }
00167
00168 return 0;
00169 }
00170 else
00171 #else
00172 if (1)
00173 #endif
00174 {
00175 WvLogConsole console_log(STDOUT_FILENO, log_level);
00176 return _run(argv0);
00177 }
00178 }
00179
00180 int WvDaemon::run(int argc, char **argv)
00181 {
00182 if (!args.process(argc, argv, &_extra_args))
00183 return 1;
00184
00185 if (syslog)
00186 {
00187 WvSyslog syslog(name, false);
00188 return run(argv[0]);
00189 }
00190 else
00191 return run(argv[0]);
00192 }
00193
00194 int WvDaemon::_run(const char *argv0)
00195 {
00196 #ifndef _WIN32
00197 wvcrash_setup(argv0, version);
00198
00199 if (!!pid_file && daemonize)
00200 {
00201
00202
00203
00204 WvFile old_pid_fd(pid_file, O_RDONLY);
00205 if (old_pid_fd.isok())
00206 {
00207 WvString line = old_pid_fd.getline(0);
00208 if (!!line)
00209 {
00210 pid_t old_pid = line.num();
00211 if (old_pid > 0 && (kill(old_pid, 0) == 0 || errno == EPERM))
00212 {
00213 log(WvLog::Error,
00214 "%s is already running (pid %s); exiting\n",
00215 name, old_pid);
00216 return 1;
00217 }
00218 }
00219 }
00220 old_pid_fd.close();
00221
00222
00223 WvAtomicFile pid_fd(pid_file, O_WRONLY, 0600);
00224 pid_fd.print("%s\n", getpid());
00225 if (!pid_fd.isok())
00226 log(WvLog::Warning, "Failed to write PID file %s: %s\n",
00227 pid_file, pid_fd.errstr());
00228 pid_fd.close();
00229 }
00230 #endif
00231 log(WvLog::Notice, "Starting %s version %s.\n", name, version);
00232
00233 daemons.append(this, false);
00234
00235 #ifndef _WIN32
00236 if (daemonize)
00237 signal(SIGINT, SIG_IGN);
00238 else
00239 signal(SIGINT, sigterm_handler);
00240 signal(SIGTERM, sigterm_handler);
00241 signal(SIGQUIT, sigquit_handler);
00242 signal(SIGHUP, sighup_handler);
00243 #endif
00244
00245 _want_to_die = false;
00246 while (!want_to_die())
00247 {
00248 _want_to_restart = false;
00249
00250 if (!!start_callback)
00251 start_callback(*this, ud);
00252
00253 run_callback(*this, ud);
00254
00255 if (!!stop_callback)
00256 stop_callback(*this, ud);
00257 }
00258
00259 daemons.unlink(this);
00260 #ifndef _WIN32
00261 if (daemons.count() == 0)
00262 {
00263 signal(SIGHUP, SIG_DFL);
00264 signal(SIGQUIT, SIG_DFL);
00265 signal(SIGINT, SIG_DFL);
00266 signal(SIGTERM, SIG_DFL);
00267 }
00268 #endif
00269 log(WvLog::Notice, "Exiting with status %s\n", _exit_status);
00270
00271 #ifndef _WIN32
00272 if (!!pid_file)
00273 ::unlink(pid_file);
00274 #endif
00275
00276 return _exit_status;
00277 }
00278
00279 bool WvDaemon::set_daemonize(void *)
00280 {
00281 daemonize = true;
00282 syslog = true;
00283 return true;
00284 }