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