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
37 static const int STDOUT_FILENO = 0;
38 #endif
39 
40 
41 WvDaemon *WvDaemon::singleton = NULL;
42 
43 
44 #ifndef _WIN32
45 
46 static 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 
55 static 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 
64 static void sigquit_handler(int signum)
65 {
66  signal(signum, SIG_IGN);
67 
68  exit(1);
69 }
70 
71 #endif // _WIN32
72 
73 void 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 
114 WvDaemon::~WvDaemon()
115 {
116 }
117 
118 
119 int 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 
204 int 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 
212 int 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 
245 void 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)
295  load_callback();
296 }
297 
298 
299 void WvDaemon::do_start()
300 {
301  if (start_callback)
302  start_callback();
303 }
304 
305 
306 void WvDaemon::do_run()
307 {
308  if (run_callback)
309  run_callback();
310 }
311 
312 
313 void WvDaemon::do_stop()
314 {
315  if (stop_callback)
316  stop_callback();
317 }
318 
319 
320 void 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 
341 bool WvDaemon::set_daemonize(void *)
342 {
343  daemonize = true;
344  syslog = true;
345  return true;
346 }
WvLogConsole
Captures formatted log messages and outputs them to the specified file descriptor.
Definition: wvlogrcv.h:107
WvArgs::set_version
void set_version(WvStringParm version)
Set the –version string.
Definition: wvargs.cc:819
WvArgs::add_option
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
WvDaemon::args
WvArgs args
The arguments the daemon accepts; the defaults are described above.
Definition: wvdaemon.h:104
WvArgs::add_set_bool_option
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
WvDaemon::daemonize
bool daemonize
Whether the daemon should daemonize by default (it can be changed by the default options); defaults t...
Definition: wvdaemon.h:100
WvFile
WvFile implements a stream connected to a file or Unix device.
Definition: wvfile.h:28
WvDaemon::pid_file
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
WvArgs::add_reset_bool_option
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
WvDaemon::restart
void restart()
Force the daemon to restart as soon as the run callback exits.
Definition: wvdaemon.h:182
WvString
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:329
WvSyslog
WvSyslog is a descendant of WvLogRcv that sends messages to the syslogd daemon.
Definition: wvsyslog.h:15
WvDaemon::log
WvLog log
The daemon's log mechanism.
Definition: wvdaemon.h:106
WvLogRcv
WvLogRcv adds some intelligence to WvLogRcvBase, to keep track of line-prefix-printing and other form...
Definition: wvlogrcv.h:28
WvDaemon::name
WvString name
The name and version of the daemon; used for -V and logging.
Definition: wvdaemon.h:93
WvDaemon::die
void die(int status=0)
Force the daemon to exit as soon as the run callback exits.
Definition: wvdaemon.h:187
WvAtomicFile
WvAtomicFile implements a simple extension to wvfile to allow for atomic file creation.
Definition: wvatomicfile.h:21
WvDaemon
WvDaemon - High-level abstraction for creating daemon processes.
Definition: wvdaemon.h:85
WvDaemon::run
int run(const char *argv0)
Run the daemon with no argument processing. Returns exit status.
Definition: wvdaemon.cc:119
WvCrashLog
WvLogRcv that sticks log messages in the wvcrash_ring_buffer.
Definition: wvcrashlog.h:14
WvArgs::process
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
WvDaemon::load_callback
WvDaemonCallback load_callback
See the class description.
Definition: wvdaemon.h:113
WvFastString::num
int num() const
Return a stdc++ string with the contents of this string.
Definition: wvstring.h:286
WvDaemon::want_to_die
bool want_to_die() const
Whether the daemon will quit when the run callback exits.
Definition: wvdaemon.h:199
WvDaemon::should_run
bool should_run() const
Whether the daemon should continue runnning.
Definition: wvdaemon.h:205