python: Resolve some indentation warnings.
[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, e:
136         _fatal("%s: create failed (%s)" % (tmpfile, e.strerror))
137
138     try:
139         s = os.fstat(file_handle.fileno())
140     except IOError, 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, 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, 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, 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, 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, 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, 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, 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, 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                         print "sleep %f" % ((wakeup - now) / 1000.0)
317                         time.sleep((wakeup - now) / 1000.0)
318                 last_restart = ovs.timeval.msec()
319
320                 vlog.err("%s, restarting" % status_msg)
321                 daemon_pid = _fork_and_wait_for_startup()
322                 if not daemon_pid:
323                     break
324             else:
325                 vlog.info("%s, exiting" % status_msg)
326                 sys.exit(0)
327
328     # Running in new daemon process.
329
330
331 def _close_standard_fds():
332     """Close stdin, stdout, stderr.  If we're started from e.g. an SSH session,
333     then this keeps us from holding that session open artificially."""
334     null_fd = ovs.socket_util.get_null_fd()
335     if null_fd >= 0:
336         os.dup2(null_fd, 0)
337         os.dup2(null_fd, 1)
338         os.dup2(null_fd, 2)
339
340
341 def daemonize_start():
342     """If daemonization is configured, then starts daemonization, by forking
343     and returning in the child process.  The parent process hangs around until
344     the child lets it know either that it completed startup successfully (by
345     calling daemon_complete()) or that it failed to start up (by exiting with a
346     nonzero exit code)."""
347
348     if _detach:
349         if _fork_and_wait_for_startup() > 0:
350             # Running in parent process.
351             sys.exit(0)
352
353         # Running in daemon or monitor process.
354         os.setsid()
355
356     if _monitor:
357         saved_daemonize_fd = _daemonize_fd
358         daemon_pid = _fork_and_wait_for_startup()
359         if daemon_pid > 0:
360             # Running in monitor process.
361             _fork_notify_startup(saved_daemonize_fd)
362             _close_standard_fds()
363             _monitor_daemon(daemon_pid)
364         # Running in daemon process
365
366     if _pidfile:
367         _make_pidfile()
368
369
370 def daemonize_complete():
371     """If daemonization is configured, then this function notifies the parent
372     process that the child process has completed startup successfully."""
373     _fork_notify_startup(_daemonize_fd)
374
375     if _detach:
376         if _chdir:
377             os.chdir("/")
378         _close_standard_fds()
379
380
381 def usage():
382     sys.stdout.write("""
383 Daemon options:
384    --detach                run in background as daemon
385    --no-chdir              do not chdir to '/'
386    --pidfile[=FILE]        create pidfile (default: %s/%s.pid)
387    --overwrite-pidfile     with --pidfile, start even if already running
388 """ % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME))
389
390
391 def __read_pidfile(pidfile, delete_if_stale):
392     if _pidfile_dev is not None:
393         try:
394             s = os.stat(pidfile)
395             if s.st_ino == _pidfile_ino and s.st_dev == _pidfile_dev:
396                 # It's our own pidfile.  We can't afford to open it,
397                 # because closing *any* fd for a file that a process
398                 # has locked also releases all the locks on that file.
399                 #
400                 # Fortunately, we know the associated pid anyhow.
401                 return os.getpid()
402         except OSError:
403             pass
404
405     try:
406         file_handle = open(pidfile, "r+")
407     except IOError, e:
408         if e.errno == errno.ENOENT and delete_if_stale:
409             return 0
410         vlog.warn("%s: open: %s" % (pidfile, e.strerror))
411         return -e.errno
412
413     # Python fcntl doesn't directly support F_GETLK so we have to just try
414     # to lock it.
415     try:
416         fcntl.lockf(file_handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
417
418         # pidfile exists but wasn't locked by anyone.  Now we have the lock.
419         if not delete_if_stale:
420             file_handle.close()
421             vlog.warn("%s: pid file is stale" % pidfile)
422             return -errno.ESRCH
423
424         # Is the file we have locked still named 'pidfile'?
425         try:
426             raced = False
427             s = os.stat(pidfile)
428             s2 = os.fstat(file_handle.fileno())
429             if s.st_ino != s2.st_ino or s.st_dev != s2.st_dev:
430                 raced = True
431         except IOError:
432             raced = True
433         if raced:
434             vlog.warn("%s: lost race to delete pidfile" % pidfile)
435             return -errno.EALREADY
436
437         # We won the right to delete the stale pidfile.
438         try:
439             os.unlink(pidfile)
440         except IOError, e:
441             vlog.warn("%s: failed to delete stale pidfile (%s)"
442                             % (pidfile, e.strerror))
443             return -e.errno
444         else:
445             vlog.dbg("%s: deleted stale pidfile" % pidfile)
446             file_handle.close()
447             return 0
448     except IOError, e:
449         if e.errno not in [errno.EACCES, errno.EAGAIN]:
450             vlog.warn("%s: fcntl: %s" % (pidfile, e.strerror))
451             return -e.errno
452
453     # Someone else has the pidfile locked.
454     try:
455         try:
456             error = int(file_handle.readline())
457         except IOError, e:
458             vlog.warn("%s: read: %s" % (pidfile, e.strerror))
459             error = -e.errno
460         except ValueError:
461             vlog.warn("%s does not contain a pid" % pidfile)
462             error = -errno.EINVAL
463
464         return error
465     finally:
466         try:
467             file_handle.close()
468         except IOError:
469             pass
470
471
472 def read_pidfile(pidfile):
473     """Opens and reads a PID from 'pidfile'.  Returns the positive PID if
474     successful, otherwise a negative errno value."""
475     return __read_pidfile(pidfile, False)
476
477
478 def _check_already_running():
479     pid = __read_pidfile(_pidfile, True)
480     if pid > 0:
481         _fatal("%s: already running as pid %d, aborting" % (_pidfile, pid))
482     elif pid < 0:
483         _fatal("%s: pidfile check failed (%s), aborting"
484                % (_pidfile, os.strerror(pid)))
485
486
487 def add_args(parser):
488     """Populates 'parser', an ArgumentParser allocated using the argparse
489     module, with the command line arguments required by the daemon module."""
490
491     pidfile = make_pidfile_name(None)
492
493     group = parser.add_argument_group(title="Daemon Options")
494     group.add_argument("--detach", action="store_true",
495             help="Run in background as a daemon.")
496     group.add_argument("--no-chdir", action="store_true",
497             help="Do not chdir to '/'.")
498     group.add_argument("--monitor", action="store_true",
499             help="Monitor %s process." % ovs.util.PROGRAM_NAME)
500     group.add_argument("--pidfile", nargs="?", const=pidfile,
501             help="Create pidfile (default %s)." % pidfile)
502     group.add_argument("--overwrite-pidfile", action="store_true",
503             help="With --pidfile, start even if already running.")
504
505
506 def handle_args(args):
507     """Handles daemon module settings in 'args'.  'args' is an object
508     containing values parsed by the parse_args() method of ArgumentParser.  The
509     parent ArgumentParser should have been prepared by add_args() before
510     calling parse_args()."""
511
512     if args.detach:
513         set_detach()
514
515     if args.no_chdir:
516         set_no_chdir()
517
518     if args.pidfile:
519         set_pidfile(args.pidfile)
520
521     if args.overwrite_pidfile:
522         ignore_existing_pidfile()
523
524     if args.monitor:
525         set_monitor()