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