WvStreams
wvdaemon.cc
1/* -*- Mode: C++ -*-
2 * Worldvisions Tunnel Vision Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4 *
5 * High-level abstraction for creating daemon processes. Handles
6 * command-line argument processing, forking into the background,
7 * and signal handling.
8 */
9
10#include "wvdaemon.h"
11
12#include "wvlinklist.h"
13#include "wvsyslog.h"
14#ifndef _WIN32
15#include "wvcrash.h"
16#include "wvcrashlog.h"
17#include "wvfile.h"
18#include "wvatomicfile.h"
19
20#include <signal.h>
21#include <sys/types.h>
22#include <sys/stat.h>
23#include <fcntl.h>
24#else
25#include "wvlogrcv.h"
26#endif
27
28#ifndef _WIN32
29# define CAN_SYSLOG true
30# define CAN_DAEMONIZE true
31#else
32# define CAN_SYSLOG false
33# define CAN_DAEMONIZE false
34#endif
35
36#ifdef _MSC_VER
37static const int STDOUT_FILENO = 0;
38#endif
39
40
41WvDaemon *WvDaemon::singleton = NULL;
42
43
44#ifndef _WIN32
45
46static void sighup_handler(int signum)
47{
48 signal(signum, SIG_IGN);
49
50 WvDaemon::me()->log(WvLog::Notice, "Restarting on signal %s.\n", signum);
51 WvDaemon::me()->restart();
52}
53
54
55static void sigterm_handler(int signum)
56{
57 signal(signum, SIG_DFL);
58
59 WvDaemon::me()->log(WvLog::Notice, "Dying on signal %s.\n", signum);
60 WvDaemon::me()->die();
61}
62
63
64static void sigquit_handler(int signum)
65{
66 signal(signum, SIG_IGN);
67
68 exit(1);
69}
70
71#endif // _WIN32
72
73void WvDaemon::init(WvStringParm _name,
74 WvStringParm _version,
75 WvDaemonCallback _start_callback,
76 WvDaemonCallback _run_callback,
77 WvDaemonCallback _stop_callback)
78{
79 name = _name;
80 version = _version;
81 pid_file = WvString("/var/run/%s.pid", _name);
82 daemonize = false;
83 log_level = WvLog::Info;
84 syslog = false;
85 start_callback = _start_callback;
86 run_callback = _run_callback;
87 stop_callback = _stop_callback;
88
89 assert(singleton == NULL);
90 singleton = this;
91
92 args.add_option('q', "quiet",
93 "Decrease log level (can be used multiple times)",
94 wv::bind(&WvDaemon::dec_log_level, this, _1));
95 args.add_option('v', "verbose",
96 "Increase log level (can be used multiple times)",
97 wv::bind(&WvDaemon::inc_log_level, this, _1));
98 if (CAN_DAEMONIZE)
99 args.add_option('d', "daemonize",
100 "Fork into background and return (implies --syslog)",
101 wv::bind(&WvDaemon::set_daemonize, this, _1));
102 if (CAN_SYSLOG)
103 {
104 args.add_set_bool_option('s', "syslog",
105 "Write log entries to syslog", syslog);
106 args.add_reset_bool_option(0, "no-syslog",
107 "Do not write log entries to syslog", syslog);
108 }
109
110 args.set_version(WvString("%s version %s", name, version).cstr());
111}
112
113
114WvDaemon::~WvDaemon()
115{
116}
117
118
119int WvDaemon::run(const char *argv0)
120{
121#ifndef _WIN32
122 if (CAN_DAEMONIZE && daemonize)
123 {
124 pid_t pid = ::fork();
125 if (pid < 0)
126 {
127 wverr->print("Failed to fork daemon: %s\n",
128 strerror(errno));
129 return 3;
130 }
131 else if (pid == 0)
132 {
133 setsid();
134 pid = fork();
135 if (pid < 0)
136 {
137 wverr->print("Failed to double-fork daemon: %s\n",
138 strerror(errno));
139 }
140 else if (pid == 0)
141 {
142 // FIXME: this happens *before* we do the daemon setup!
143 // We should only fork into the background *after* doing
144 // things like opening our listen sockets.
145 ::chdir("/");
146 ::umask(0);
147
148 int null_fd;
149 do
150 {
151 null_fd = ::open("/dev/null", O_RDWR);
152 if (null_fd == -1)
153 {
154 log(WvLog::Error, "Failed to open /dev/null: %s\n",
155 strerror(errno));
156 _exit(1);
157 }
158 } while (null_fd == 0 || null_fd == 1 || null_fd == 2);
159
160 if (::dup2(null_fd, 0) == -1
161 || ::dup2(null_fd, 1) == -1
162 || ::dup2(null_fd, 2) == -1)
163 {
164 log(WvLog::Error, "Failed to dup2(null_fd, (0|1|2)): %s\n",
165 strerror(errno));
166 _exit(1);
167 }
168 ::close(null_fd);
169
170 // Make sure the close-on-exec flag is not set for
171 // the first three descriptors, since many programs
172 // assume that they are open after exec()
173 if (::fcntl(0, F_SETFD, 0) == -1
174 || ::fcntl(1, F_SETFD, 0) == -1
175 || ::fcntl(2, F_SETFD, 0) == -1)
176 {
177 log(WvLog::Warning, "Failed to fcntl((0|1|2), F_SETFD, 0): %s\n",
178 strerror(errno));
179 }
180
181 return _run(argv0); // Make sure destructors are called
182 }
183
184 _exit(0);
185 }
186
187 return 0;
188 }
189 else
190#endif // !_WIN32
191 {
192 WvLogConsole console_log(STDOUT_FILENO, log_level);
193 if (CAN_SYSLOG && syslog)
194 {
195 WvSyslog syslog(name, false);
196 return _run(argv0);
197 }
198 else
199 return _run(argv0);
200 }
201}
202
203
204int WvDaemon::run(int argc, char **argv)
205{
206 if (!args.process(argc, argv, &_extra_args))
207 return 1;
208 return run(argv[0]);
209}
210
211
212int WvDaemon::_run(const char *argv0)
213{
214 WvLogRcv *logr = NULL;
215#ifndef _WIN32
216 WvCrashLog crashlog;
217 wvcrash_setup(argv0, version);
218#endif
219
220 if (CAN_SYSLOG && syslog)
221 logr = new WvSyslog(name, false);
222
223 _want_to_die = false;
224 do_load();
225 while (!want_to_die())
226 {
227 _want_to_restart = false;
228
229 do_start();
230
231 while (should_run())
232 do_run();
233
234 do_stop();
235 }
236 do_unload();
237
238 if (logr)
239 delete logr;
240
241 return _exit_status;
242}
243
244
245void WvDaemon::do_load()
246{
247#ifndef _WIN32
248 if (!!pid_file && daemonize)
249 {
250 // FIXME: this is racy!
251
252 // First, make sure we aren't already running
253 WvFile old_pid_fd(pid_file, O_RDONLY);
254 if (old_pid_fd.isok())
255 {
256 WvString line = old_pid_fd.getline(0);
257 if (!!line)
258 {
259 pid_t old_pid = line.num();
260 if (old_pid > 0 && (kill(old_pid, 0) == 0 || errno == EPERM))
261 {
262 log(WvLog::Error,
263 "%s is already running (pid %s); exiting\n",
264 name, old_pid);
265 die();
266 }
267 }
268 }
269 old_pid_fd.close();
270 if (want_to_die())
271 return;
272
273 // Now write our new PID file
274 WvAtomicFile pid_fd(pid_file, O_WRONLY, 0600);
275 pid_fd.print("%s\n", getpid());
276 if (!pid_fd.isok())
277 log(WvLog::Warning, "Failed to write PID file %s: %s\n",
278 pid_file, pid_fd.errstr());
279 pid_fd.close();
280 }
281#endif
282 log(WvLog::Notice, "Starting %s version %s.\n", name, version);
283
284#ifndef _WIN32
285 if (daemonize)
286 signal(SIGINT, SIG_IGN);
287 else
288 signal(SIGINT, sigterm_handler);
289 signal(SIGTERM, sigterm_handler);
290 signal(SIGQUIT, sigquit_handler);
291 signal(SIGHUP, sighup_handler);
292#endif
293
294 if (load_callback)
296}
297
298
299void WvDaemon::do_start()
300{
301 if (start_callback)
302 start_callback();
303}
304
305
306void WvDaemon::do_run()
307{
308 if (run_callback)
309 run_callback();
310}
311
312
313void WvDaemon::do_stop()
314{
315 if (stop_callback)
316 stop_callback();
317}
318
319
320void WvDaemon::do_unload()
321{
322 if (unload_callback)
323 unload_callback();
324
325#ifndef _WIN32
326 signal(SIGHUP, SIG_DFL);
327 signal(SIGQUIT, SIG_DFL);
328 signal(SIGINT, SIG_DFL);
329 signal(SIGTERM, SIG_DFL);
330#endif
331
332 log(WvLog::Notice, "Exiting with status %s\n", _exit_status);
333
334#ifndef _WIN32
335 if (!!pid_file && daemonize)
336 ::unlink(pid_file);
337#endif
338}
339
340
341bool WvDaemon::set_daemonize(void *)
342{
343 daemonize = true;
344 syslog = true;
345 return true;
346}
bool process(int argc, char **argv, WvStringList *remaining_args=NULL)
Process the command line arguments passed to main() using the options provided through calls to add_o...
Definition wvargs.cc:784
void set_version(WvStringParm version)
Set the –version string.
Definition wvargs.cc:819
void add_option(char short_option, WvStringParm long_option, WvStringParm desc, WvStringParm arg_desc, int &val)
Add a switch that takes an integer argument.
Definition wvargs.cc:888
void add_reset_bool_option(char short_option, WvStringParm long_option, WvStringParm desc, bool &val)
Add a boolean option, which, when spefied, sets the specified boolean variable to false.
Definition wvargs.cc:864
void add_set_bool_option(char short_option, WvStringParm long_option, WvStringParm desc, bool &val)
Add a boolean option, which, when specified, sets the specified boolean variable to true.
Definition wvargs.cc:856
WvAtomicFile implements a simple extension to wvfile to allow for atomic file creation.
WvLogRcv that sticks log messages in the wvcrash_ring_buffer.
Definition wvcrashlog.h:15
WvDaemon - High-level abstraction for creating daemon processes.
Definition wvdaemon.h:86
WvString pid_file
The path to the pid file to use for the daemon; defaults to /var/run/name.pid, where name is above.
Definition wvdaemon.h:97
WvString name
The name and version of the daemon; used for -V and logging.
Definition wvdaemon.h:93
WvDaemonCallback load_callback
See the class description.
Definition wvdaemon.h:113
bool should_run() const
Whether the daemon should continue runnning.
Definition wvdaemon.h:205
int run(const char *argv0)
Run the daemon with no argument processing. Returns exit status.
Definition wvdaemon.cc:119
bool daemonize
Whether the daemon should daemonize by default (it can be changed by the default options); defaults t...
Definition wvdaemon.h:100
WvLog log
The daemon's log mechanism.
Definition wvdaemon.h:106
void die(int status=0)
Force the daemon to exit as soon as the run callback exits.
Definition wvdaemon.h:187
bool want_to_die() const
Whether the daemon will quit when the run callback exits.
Definition wvdaemon.h:199
WvArgs args
The arguments the daemon accepts; the defaults are described above.
Definition wvdaemon.h:104
void restart()
Force the daemon to restart as soon as the run callback exits.
Definition wvdaemon.h:182
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
Definition wvstring.h:94
int num() const
Return a stdc++ string with the contents of this string.
Definition wvstring.h:286
WvFile implements a stream connected to a file or Unix device.
Definition wvfile.h:29
Captures formatted log messages and outputs them to the specified file descriptor.
Definition wvlogrcv.h:108
WvLogRcv adds some intelligence to WvLogRcvBase, to keep track of line-prefix-printing and other form...
Definition wvlogrcv.h:29
WvString is an implementation of a simple and efficient printable-string class.
Definition wvstring.h:330
WvSyslog is a descendant of WvLogRcv that sends messages to the syslogd daemon.
Definition wvsyslog.h:16