daemon: Start monitor process, not daemon process, in new session.
[cascardo/ovs.git] / python / ovs / daemon.py
1 # Copyright (c) 2010, 2011, 2012 Nicira, Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import errno
16 import fcntl
17 import os
18 import resource
19 import signal
20 import sys
21 import time
22
23 import ovs.dirs
24 import ovs.fatal_signal
25 #import ovs.lockfile
26 import ovs.process
27 import ovs.socket_util
28 import ovs.timeval
29 import ovs.util
30 import ovs.vlog
31
32 vlog = ovs.vlog.Vlog("daemon")
33
34 # --detach: Should we run in the background?
35 _detach = False
36
37 # --pidfile: Name of pidfile (null if none).
38 _pidfile = None
39
40 # Our pidfile's inode and device, if we have created one.
41 _pidfile_dev = None
42 _pidfile_ino = None
43
44 # --overwrite-pidfile: Create pidfile even if one already exists and is locked?
45 _overwrite_pidfile = False
46
47 # --no-chdir: Should we chdir to "/"?
48 _chdir = True
49
50 # --monitor: Should a supervisory process monitor the daemon and restart it if
51 # it dies due to an error signal?
52 _monitor = False
53
54 # File descriptor used by daemonize_start() and daemonize_complete().
55 _daemonize_fd = None
56
57 RESTART_EXIT_CODE = 5
58
59
60 def make_pidfile_name(name):
61     """Returns the file name that would be used for a pidfile if 'name' were
62     provided to set_pidfile()."""
63     if name is None or name == "":
64         return "%s/%s.pid" % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME)
65     else:
66         return ovs.util.abs_file_name(ovs.dirs.RUNDIR, name)
67
68
69 def set_pidfile(name):
70     """Sets up a following call to daemonize() to create a pidfile named
71     'name'.  If 'name' begins with '/', then it is treated as an absolute path.
72     Otherwise, it is taken relative to ovs.util.RUNDIR, which is
73     $(prefix)/var/run by default.
74
75     If 'name' is null, then ovs.util.PROGRAM_NAME followed by ".pid" is
76     used."""
77     global _pidfile
78     _pidfile = make_pidfile_name(name)
79
80
81 def get_pidfile():
82     """Returns an absolute path to the configured pidfile, or None if no
83     pidfile is configured."""
84     return _pidfile
85
86
87 def set_no_chdir():
88     """Sets that we do not chdir to "/"."""
89     global _chdir
90     _chdir = False
91
92
93 def is_chdir_enabled():
94     """Will we chdir to "/" as part of daemonizing?"""
95     return _chdir
96
97
98 def ignore_existing_pidfile():
99     """Normally, daemonize() or daemonize_start() will terminate the program
100     with a message if a locked pidfile already exists.  If this function is
101     called, an existing pidfile will be replaced, with a warning."""
102     global _overwrite_pidfile
103     _overwrite_pidfile = True
104
105
106 def set_detach():
107     """Sets up a following call to daemonize() to detach from the foreground
108     session, running this process in the background."""
109     global _detach
110     _detach = True
111
112
113 def get_detach():
114     """Will daemonize() really detach?"""
115     return _detach
116
117
118 def set_monitor():
119     """Sets up a following call to daemonize() to fork a supervisory process to
120     monitor the daemon and restart it if it dies due to an error signal."""
121     global _monitor
122     _monitor = True
123
124
125 def _fatal(msg):
126     vlog.err(msg)
127     sys.stderr.write("%s\n" % msg)
128     sys.exit(1)
129
130
131 def _make_pidfile():
132     """If a pidfile has been configured, creates it and stores the running
133     process's pid in it.  Ensures that the pidfile will be deleted when the
134     process exits."""
135     pid = os.getpid()
136
137     # Create a temporary pidfile.
138     tmpfile = "%s.tmp%d" % (_pidfile, pid)
139     ovs.fatal_signal.add_file_to_unlink(tmpfile)
140     try:
141         # This is global to keep Python from garbage-collecting and
142         # therefore closing our file after this function exits.  That would
143         # unlock the lock for us, and we don't want that.
144         global file_handle
145
146         file_handle = open(tmpfile, "w")
147     except IOError, e:
148         _fatal("%s: create failed (%s)" % (tmpfile, e.strerror))
149
150     try:
151         s = os.fstat(file_handle.fileno())
152     except IOError, e:
153         _fatal("%s: fstat failed (%s)" % (tmpfile, e.strerror))
154
155     try:
156         file_handle.write("%s\n" % pid)
157         file_handle.flush()
158     except OSError, e:
159         _fatal("%s: write failed: %s" % (tmpfile, e.strerror))
160
161     try:
162         fcntl.lockf(file_handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
163     except IOError, e:
164         _fatal("%s: fcntl failed: %s" % (tmpfile, e.strerror))
165
166     # Rename or link it to the correct name.
167     if _overwrite_pidfile:
168         try:
169             os.rename(tmpfile, _pidfile)
170         except OSError, e:
171             _fatal("failed to rename \"%s\" to \"%s\" (%s)"
172                    % (tmpfile, _pidfile, e.strerror))
173     else:
174         while True:
175             try:
176                 os.link(tmpfile, _pidfile)
177                 error = 0
178             except OSError, e:
179                 error = e.errno
180             if error == errno.EEXIST:
181                 _check_already_running()
182             elif error != errno.EINTR:
183                 break
184         if error:
185             _fatal("failed to link \"%s\" as \"%s\" (%s)"
186                    % (tmpfile, _pidfile, os.strerror(error)))
187
188     # Ensure that the pidfile will get deleted on exit.
189     ovs.fatal_signal.add_file_to_unlink(_pidfile)
190
191     # Delete the temporary pidfile if it still exists.
192     if not _overwrite_pidfile:
193         error = ovs.fatal_signal.unlink_file_now(tmpfile)
194         if error:
195             _fatal("%s: unlink failed (%s)" % (tmpfile, os.strerror(error)))
196
197     global _pidfile_dev
198     global _pidfile_ino
199     _pidfile_dev = s.st_dev
200     _pidfile_ino = s.st_ino
201
202
203 def daemonize():
204     """If configured with set_pidfile() or set_detach(), creates the pid file
205     and detaches from the foreground session."""
206     daemonize_start()
207     daemonize_complete()
208
209
210 def _waitpid(pid, options):
211     while True:
212         try:
213             return os.waitpid(pid, options)
214         except OSError, e:
215             if e.errno == errno.EINTR:
216                 pass
217             return -e.errno, 0
218
219
220 def _fork_and_wait_for_startup():
221     try:
222         rfd, wfd = os.pipe()
223     except OSError, e:
224         sys.stderr.write("pipe failed: %s\n" % os.strerror(e.errno))
225         sys.exit(1)
226
227     try:
228         pid = os.fork()
229     except OSError, e:
230         sys.stderr.write("could not fork: %s\n" % os.strerror(e.errno))
231         sys.exit(1)
232
233     if pid > 0:
234         # Running in parent process.
235         os.close(wfd)
236         ovs.fatal_signal.fork()
237         while True:
238             try:
239                 s = os.read(rfd, 1)
240                 error = 0
241             except OSError, e:
242                 s = ""
243                 error = e.errno
244             if error != errno.EINTR:
245                 break
246         if len(s) != 1:
247             retval, status = _waitpid(pid, 0)
248             if retval == pid:
249                 if os.WIFEXITED(status) and os.WEXITSTATUS(status):
250                     # Child exited with an error.  Convey the same error to
251                     # our parent process as a courtesy.
252                     sys.exit(os.WEXITSTATUS(status))
253                 else:
254                     sys.stderr.write("fork child failed to signal "
255                                      "startup (%s)\n"
256                                      % ovs.process.status_msg(status))
257             else:
258                 assert retval < 0
259                 sys.stderr.write("waitpid failed (%s)\n"
260                                  % os.strerror(-retval))
261                 sys.exit(1)
262
263         os.close(rfd)
264     else:
265         # Running in parent process.
266         os.close(rfd)
267         ovs.timeval.postfork()
268         #ovs.lockfile.postfork()
269
270         global _daemonize_fd
271         _daemonize_fd = wfd
272     return pid
273
274
275 def _fork_notify_startup(fd):
276     if fd is not None:
277         error, bytes_written = ovs.socket_util.write_fully(fd, "0")
278         if error:
279             sys.stderr.write("could not write to pipe\n")
280             sys.exit(1)
281         os.close(fd)
282
283
284 def _should_restart(status):
285     global RESTART_EXIT_CODE
286
287     if os.WIFEXITED(status) and os.WEXITSTATUS(status) == RESTART_EXIT_CODE:
288         return True
289
290     if os.WIFSIGNALED(status):
291         for signame in ("SIGABRT", "SIGALRM", "SIGBUS", "SIGFPE", "SIGILL",
292                         "SIGPIPE", "SIGSEGV", "SIGXCPU", "SIGXFSZ"):
293             if os.WTERMSIG(status) == getattr(signal, signame, None):
294                 return True
295     return False
296
297
298 def _monitor_daemon(daemon_pid):
299     # XXX should log daemon's stderr output at startup time
300     # XXX should use setproctitle module if available
301     last_restart = None
302     while True:
303         retval, status = _waitpid(daemon_pid, 0)
304         if retval < 0:
305             sys.stderr.write("waitpid failed\n")
306             sys.exit(1)
307         elif retval == daemon_pid:
308             status_msg = ("pid %d died, %s"
309                           % (daemon_pid, ovs.process.status_msg(status)))
310
311             if _should_restart(status):
312                 if os.WCOREDUMP(status):
313                     # Disable further core dumps to save disk space.
314                     try:
315                         resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
316                     except resource.error:
317                         vlog.warn("failed to disable core dumps")
318
319                 # Throttle restarts to no more than once every 10 seconds.
320                 if (last_restart is not None and
321                     ovs.timeval.msec() < last_restart + 10000):
322                     vlog.warn("%s, waiting until 10 seconds since last "
323                               "restart" % status_msg)
324                     while True:
325                         now = ovs.timeval.msec()
326                         wakeup = last_restart + 10000
327                         if now > wakeup:
328                             break
329                         print "sleep %f" % ((wakeup - now) / 1000.0)
330                         time.sleep((wakeup - now) / 1000.0)
331                 last_restart = ovs.timeval.msec()
332
333                 vlog.err("%s, restarting" % status_msg)
334                 daemon_pid = _fork_and_wait_for_startup()
335                 if not daemon_pid:
336                     break
337             else:
338                 vlog.info("%s, exiting" % status_msg)
339                 sys.exit(0)
340
341    # Running in new daemon process.
342
343
344 def _close_standard_fds():
345     """Close stdin, stdout, stderr.  If we're started from e.g. an SSH session,
346     then this keeps us from holding that session open artificially."""
347     null_fd = ovs.socket_util.get_null_fd()
348     if null_fd >= 0:
349         os.dup2(null_fd, 0)
350         os.dup2(null_fd, 1)
351         os.dup2(null_fd, 2)
352
353
354 def daemonize_start():
355     """If daemonization is configured, then starts daemonization, by forking
356     and returning in the child process.  The parent process hangs around until
357     the child lets it know either that it completed startup successfully (by
358     calling daemon_complete()) or that it failed to start up (by exiting with a
359     nonzero exit code)."""
360
361     if _detach:
362         if _fork_and_wait_for_startup() > 0:
363             # Running in parent process.
364             sys.exit(0)
365
366         # Running in daemon or monitor process.
367         os.setsid()
368
369     if _monitor:
370         saved_daemonize_fd = _daemonize_fd
371         daemon_pid = _fork_and_wait_for_startup()
372         if daemon_pid > 0:
373             # Running in monitor process.
374             _fork_notify_startup(saved_daemonize_fd)
375             _close_standard_fds()
376             _monitor_daemon(daemon_pid)
377         # Running in daemon process
378
379     if _pidfile:
380         _make_pidfile()
381
382
383 def daemonize_complete():
384     """If daemonization is configured, then this function notifies the parent
385     process that the child process has completed startup successfully."""
386     _fork_notify_startup(_daemonize_fd)
387
388     if _detach:
389         if _chdir:
390             os.chdir("/")
391         _close_standard_fds()
392
393
394 def usage():
395     sys.stdout.write("""
396 Daemon options:
397    --detach                run in background as daemon
398    --no-chdir              do not chdir to '/'
399    --pidfile[=FILE]        create pidfile (default: %s/%s.pid)
400    --overwrite-pidfile     with --pidfile, start even if already running
401 """ % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME))
402
403
404 def __read_pidfile(pidfile, delete_if_stale):
405     if _pidfile_dev is not None:
406         try:
407             s = os.stat(pidfile)
408             if s.st_ino == _pidfile_ino and s.st_dev == _pidfile_dev:
409                 # It's our own pidfile.  We can't afford to open it,
410                 # because closing *any* fd for a file that a process
411                 # has locked also releases all the locks on that file.
412                 #
413                 # Fortunately, we know the associated pid anyhow.
414                 return os.getpid()
415         except OSError:
416             pass
417
418     try:
419         file_handle = open(pidfile, "r+")
420     except IOError, e:
421         if e.errno == errno.ENOENT and delete_if_stale:
422             return 0
423         vlog.warn("%s: open: %s" % (pidfile, e.strerror))
424         return -e.errno
425
426     # Python fcntl doesn't directly support F_GETLK so we have to just try
427     # to lock it.
428     try:
429         fcntl.lockf(file_handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
430
431         # pidfile exists but wasn't locked by anyone.  Now we have the lock.
432         if not delete_if_stale:
433             file_handle.close()
434             vlog.warn("%s: pid file is stale" % pidfile)
435             return -errno.ESRCH
436
437         # Is the file we have locked still named 'pidfile'?
438         try:
439             raced = False
440             s = os.stat(pidfile)
441             s2 = os.fstat(file_handle.fileno())
442             if s.st_ino != s2.st_ino or s.st_dev != s2.st_dev:
443                 raced = True
444         except IOError:
445             raced = True
446         if raced:
447             vlog.warn("%s: lost race to delete pidfile" % pidfile)
448             return -errno.EALREADY
449
450         # We won the right to delete the stale pidfile.
451         try:
452             os.unlink(pidfile)
453         except IOError, e:
454             vlog.warn("%s: failed to delete stale pidfile (%s)"
455                             % (pidfile, e.strerror))
456             return -e.errno
457         else:
458             vlog.dbg("%s: deleted stale pidfile" % pidfile)
459             file_handle.close()
460             return 0
461     except IOError, e:
462         if e.errno not in [errno.EACCES, errno.EAGAIN]:
463             vlog.warn("%s: fcntl: %s" % (pidfile, e.strerror))
464             return -e.errno
465
466     # Someone else has the pidfile locked.
467     try:
468         try:
469             error = int(file_handle.readline())
470         except IOError, e:
471             vlog.warn("%s: read: %s" % (pidfile, e.strerror))
472             error = -e.errno
473         except ValueError:
474             vlog.warn("%s does not contain a pid" % pidfile)
475             error = -errno.EINVAL
476
477         return error
478     finally:
479         try:
480             file_handle.close()
481         except IOError:
482             pass
483
484
485 def read_pidfile(pidfile):
486     """Opens and reads a PID from 'pidfile'.  Returns the positive PID if
487     successful, otherwise a negative errno value."""
488     return __read_pidfile(pidfile, False)
489
490
491 def _check_already_running():
492     pid = __read_pidfile(_pidfile, True)
493     if pid > 0:
494         _fatal("%s: already running as pid %d, aborting" % (_pidfile, pid))
495     elif pid < 0:
496         _fatal("%s: pidfile check failed (%s), aborting"
497                % (_pidfile, os.strerror(pid)))
498
499
500 def add_args(parser):
501     """Populates 'parser', an ArgumentParser allocated using the argparse
502     module, with the command line arguments required by the daemon module."""
503
504     pidfile = make_pidfile_name(None)
505
506     group = parser.add_argument_group(title="Daemon Options")
507     group.add_argument("--detach", action="store_true",
508             help="Run in background as a daemon.")
509     group.add_argument("--no-chdir", action="store_true",
510             help="Do not chdir to '/'.")
511     group.add_argument("--monitor", action="store_true",
512             help="Monitor %s process." % ovs.util.PROGRAM_NAME)
513     group.add_argument("--pidfile", nargs="?", const=pidfile,
514             help="Create pidfile (default %s)." % pidfile)
515     group.add_argument("--overwrite-pidfile", action="store_true",
516             help="With --pidfile, start even if already running.")
517
518
519 def handle_args(args):
520     """Handles daemon module settings in 'args'.  'args' is an object
521     containing values parsed by the parse_args() method of ArgumentParser.  The
522     parent ArgumentParser should have been prepared by add_args() before
523     calling parse_args()."""
524
525     if args.detach:
526         set_detach()
527
528     if args.no_chdir:
529         set_no_chdir()
530
531     if args.pidfile:
532         set_pidfile(args.pidfile)
533
534     if args.overwrite_pidfile:
535         ignore_existing_pidfile()
536
537     if args.monitor:
538         set_monitor()