Global replace of Nicira Networks.
[sliver-openvswitch.git] / python / ovs / daemon.py
1 # Copyright (c) 2010, 2011 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         # Running in daemon or monitor process.
366
367     if _monitor:
368         saved_daemonize_fd = _daemonize_fd
369         daemon_pid = _fork_and_wait_for_startup()
370         if daemon_pid > 0:
371             # Running in monitor process.
372             _fork_notify_startup(saved_daemonize_fd)
373             _close_standard_fds()
374             _monitor_daemon(daemon_pid)
375         # Running in daemon process
376
377     if _pidfile:
378         _make_pidfile()
379
380
381 def daemonize_complete():
382     """If daemonization is configured, then this function notifies the parent
383     process that the child process has completed startup successfully."""
384     _fork_notify_startup(_daemonize_fd)
385
386     if _detach:
387         os.setsid()
388         if _chdir:
389             os.chdir("/")
390         _close_standard_fds()
391
392
393 def usage():
394     sys.stdout.write("""
395 Daemon options:
396    --detach                run in background as daemon
397    --no-chdir              do not chdir to '/'
398    --pidfile[=FILE]        create pidfile (default: %s/%s.pid)
399    --overwrite-pidfile     with --pidfile, start even if already running
400 """ % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME))
401
402
403 def __read_pidfile(pidfile, delete_if_stale):
404     if _pidfile_dev is not None:
405         try:
406             s = os.stat(pidfile)
407             if s.st_ino == _pidfile_ino and s.st_dev == _pidfile_dev:
408                 # It's our own pidfile.  We can't afford to open it,
409                 # because closing *any* fd for a file that a process
410                 # has locked also releases all the locks on that file.
411                 #
412                 # Fortunately, we know the associated pid anyhow.
413                 return os.getpid()
414         except OSError:
415             pass
416
417     try:
418         file_handle = open(pidfile, "r+")
419     except IOError, e:
420         if e.errno == errno.ENOENT and delete_if_stale:
421             return 0
422         vlog.warn("%s: open: %s" % (pidfile, e.strerror))
423         return -e.errno
424
425     # Python fcntl doesn't directly support F_GETLK so we have to just try
426     # to lock it.
427     try:
428         fcntl.lockf(file_handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
429
430         # pidfile exists but wasn't locked by anyone.  Now we have the lock.
431         if not delete_if_stale:
432             file_handle.close()
433             vlog.warn("%s: pid file is stale" % pidfile)
434             return -errno.ESRCH
435
436         # Is the file we have locked still named 'pidfile'?
437         try:
438             raced = False
439             s = os.stat(pidfile)
440             s2 = os.fstat(file_handle.fileno())
441             if s.st_ino != s2.st_ino or s.st_dev != s2.st_dev:
442                 raced = True
443         except IOError:
444             raced = True
445         if raced:
446             vlog.warn("%s: lost race to delete pidfile" % pidfile)
447             return -errno.EALREADY
448
449         # We won the right to delete the stale pidfile.
450         try:
451             os.unlink(pidfile)
452         except IOError, e:
453             vlog.warn("%s: failed to delete stale pidfile (%s)"
454                             % (pidfile, e.strerror))
455             return -e.errno
456         else:
457             vlog.dbg("%s: deleted stale pidfile" % pidfile)
458             file_handle.close()
459             return 0
460     except IOError, e:
461         if e.errno not in [errno.EACCES, errno.EAGAIN]:
462             vlog.warn("%s: fcntl: %s" % (pidfile, e.strerror))
463             return -e.errno
464
465     # Someone else has the pidfile locked.
466     try:
467         try:
468             error = int(file_handle.readline())
469         except IOError, e:
470             vlog.warn("%s: read: %s" % (pidfile, e.strerror))
471             error = -e.errno
472         except ValueError:
473             vlog.warn("%s does not contain a pid" % pidfile)
474             error = -errno.EINVAL
475
476         return error
477     finally:
478         try:
479             file_handle.close()
480         except IOError:
481             pass
482
483
484 def read_pidfile(pidfile):
485     """Opens and reads a PID from 'pidfile'.  Returns the positive PID if
486     successful, otherwise a negative errno value."""
487     return __read_pidfile(pidfile, False)
488
489
490 def _check_already_running():
491     pid = __read_pidfile(_pidfile, True)
492     if pid > 0:
493         _fatal("%s: already running as pid %d, aborting" % (_pidfile, pid))
494     elif pid < 0:
495         _fatal("%s: pidfile check failed (%s), aborting"
496                % (_pidfile, os.strerror(pid)))
497
498
499 def add_args(parser):
500     """Populates 'parser', an ArgumentParser allocated using the argparse
501     module, with the command line arguments required by the daemon module."""
502
503     pidfile = make_pidfile_name(None)
504
505     group = parser.add_argument_group(title="Daemon Options")
506     group.add_argument("--detach", action="store_true",
507             help="Run in background as a daemon.")
508     group.add_argument("--no-chdir", action="store_true",
509             help="Do not chdir to '/'.")
510     group.add_argument("--monitor", action="store_true",
511             help="Monitor %s process." % ovs.util.PROGRAM_NAME)
512     group.add_argument("--pidfile", nargs="?", const=pidfile,
513             help="Create pidfile (default %s)." % pidfile)
514     group.add_argument("--overwrite-pidfile", action="store_true",
515             help="With --pidfile, start even if already running.")
516
517
518 def handle_args(args):
519     """Handles daemon module settings in 'args'.  'args' is an object
520     containing values parsed by the parse_args() method of ArgumentParser.  The
521     parent ArgumentParser should have been prepared by add_args() before
522     calling parse_args()."""
523
524     if args.detach:
525         set_detach()
526
527     if args.no_chdir:
528         set_no_chdir()
529
530     if args.pidfile:
531         set_pidfile(args.pidfile)
532
533     if args.overwrite_pidfile:
534         ignore_existing_pidfile()
535
536     if args.monitor:
537         set_monitor()