ovs-test: A new tool that allows to diagnose connectivity and performance issues
[sliver-openvswitch.git] / python / ovs / daemon.py
1 # Copyright (c) 2010, 2011 Nicira Networks
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 and
249                 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 startup\n")
255                 sys.exit(1)
256
257         os.close(rfd)
258     else:
259         # Running in parent process.
260         os.close(rfd)
261         ovs.timeval.postfork()
262         #ovs.lockfile.postfork()
263
264         global _daemonize_fd
265         _daemonize_fd = wfd
266     return pid
267
268
269 def _fork_notify_startup(fd):
270     if fd is not None:
271         error, bytes_written = ovs.socket_util.write_fully(fd, "0")
272         if error:
273             sys.stderr.write("could not write to pipe\n")
274             sys.exit(1)
275         os.close(fd)
276
277
278 def _should_restart(status):
279     global RESTART_EXIT_CODE
280
281     if os.WIFEXITED(status) and os.WEXITSTATUS(status) == RESTART_EXIT_CODE:
282         return True
283
284     if os.WIFSIGNALED(status):
285         for signame in ("SIGABRT", "SIGALRM", "SIGBUS", "SIGFPE", "SIGILL",
286                         "SIGPIPE", "SIGSEGV", "SIGXCPU", "SIGXFSZ"):
287             if os.WTERMSIG(status) == getattr(signal, signame, None):
288                 return True
289     return False
290
291
292 def _monitor_daemon(daemon_pid):
293     # XXX should log daemon's stderr output at startup time
294     # XXX should use setproctitle module if available
295     last_restart = None
296     while True:
297         retval, status = _waitpid(daemon_pid, 0)
298         if retval < 0:
299             sys.stderr.write("waitpid failed\n")
300             sys.exit(1)
301         elif retval == daemon_pid:
302             status_msg = ("pid %d died, %s"
303                           % (daemon_pid, ovs.process.status_msg(status)))
304
305             if _should_restart(status):
306                 if os.WCOREDUMP(status):
307                     # Disable further core dumps to save disk space.
308                     try:
309                         resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
310                     except resource.error:
311                         vlog.warn("failed to disable core dumps")
312
313                 # Throttle restarts to no more than once every 10 seconds.
314                 if (last_restart is not None and
315                     ovs.timeval.msec() < last_restart + 10000):
316                     vlog.warn("%s, waiting until 10 seconds since last "
317                               "restart" % status_msg)
318                     while True:
319                         now = ovs.timeval.msec()
320                         wakeup = last_restart + 10000
321                         if now > wakeup:
322                             break
323                         print "sleep %f" % ((wakeup - now) / 1000.0)
324                         time.sleep((wakeup - now) / 1000.0)
325                 last_restart = ovs.timeval.msec()
326
327                 vlog.err("%s, restarting" % status_msg)
328                 daemon_pid = _fork_and_wait_for_startup()
329                 if not daemon_pid:
330                     break
331             else:
332                 vlog.info("%s, exiting" % status_msg)
333                 sys.exit(0)
334
335    # Running in new daemon process.
336
337
338 def _close_standard_fds():
339     """Close stdin, stdout, stderr.  If we're started from e.g. an SSH session,
340     then this keeps us from holding that session open artificially."""
341     null_fd = ovs.socket_util.get_null_fd()
342     if null_fd >= 0:
343         os.dup2(null_fd, 0)
344         os.dup2(null_fd, 1)
345         os.dup2(null_fd, 2)
346
347
348 def daemonize_start():
349     """If daemonization is configured, then starts daemonization, by forking
350     and returning in the child process.  The parent process hangs around until
351     the child lets it know either that it completed startup successfully (by
352     calling daemon_complete()) or that it failed to start up (by exiting with a
353     nonzero exit code)."""
354
355     if _detach:
356         if _fork_and_wait_for_startup() > 0:
357             # Running in parent process.
358             sys.exit(0)
359         # Running in daemon or monitor process.
360
361     if _monitor:
362         saved_daemonize_fd = _daemonize_fd
363         daemon_pid = _fork_and_wait_for_startup()
364         if daemon_pid > 0:
365             # Running in monitor process.
366             _fork_notify_startup(saved_daemonize_fd)
367             _close_standard_fds()
368             _monitor_daemon(daemon_pid)
369         # Running in daemon process
370
371     if _pidfile:
372         _make_pidfile()
373
374
375 def daemonize_complete():
376     """If daemonization is configured, then this function notifies the parent
377     process that the child process has completed startup successfully."""
378     _fork_notify_startup(_daemonize_fd)
379
380     if _detach:
381         os.setsid()
382         if _chdir:
383             os.chdir("/")
384         _close_standard_fds()
385
386
387 def usage():
388     sys.stdout.write("""
389 Daemon options:
390    --detach                run in background as daemon
391    --no-chdir              do not chdir to '/'
392    --pidfile[=FILE]        create pidfile (default: %s/%s.pid)
393    --overwrite-pidfile     with --pidfile, start even if already running
394 """ % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME))
395
396
397 def __read_pidfile(pidfile, delete_if_stale):
398     if _pidfile_dev is not None:
399         try:
400             s = os.stat(pidfile)
401             if s.st_ino == _pidfile_ino and s.st_dev == _pidfile_dev:
402                 # It's our own pidfile.  We can't afford to open it,
403                 # because closing *any* fd for a file that a process
404                 # has locked also releases all the locks on that file.
405                 #
406                 # Fortunately, we know the associated pid anyhow.
407                 return os.getpid()
408         except OSError:
409             pass
410
411     try:
412         file_handle = open(pidfile, "r+")
413     except IOError, e:
414         if e.errno == errno.ENOENT and delete_if_stale:
415             return 0
416         vlog.warn("%s: open: %s" % (pidfile, e.strerror))
417         return -e.errno
418
419     # Python fcntl doesn't directly support F_GETLK so we have to just try
420     # to lock it.
421     try:
422         fcntl.lockf(file_handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
423
424         # pidfile exists but wasn't locked by anyone.  Now we have the lock.
425         if not delete_if_stale:
426             file_handle.close()
427             vlog.warn("%s: pid file is stale" % pidfile)
428             return -errno.ESRCH
429
430         # Is the file we have locked still named 'pidfile'?
431         try:
432             raced = False
433             s = os.stat(pidfile)
434             s2 = os.fstat(file_handle.fileno())
435             if s.st_ino != s2.st_ino or s.st_dev != s2.st_dev:
436                 raced = True
437         except IOError:
438             raced = True
439         if raced:
440             vlog.warn("%s: lost race to delete pidfile" % pidfile)
441             return -errno.EALREADY
442
443         # We won the right to delete the stale pidfile.
444         try:
445             os.unlink(pidfile)
446         except IOError, e:
447             vlog.warn("%s: failed to delete stale pidfile (%s)"
448                             % (pidfile, e.strerror))
449             return -e.errno
450         else:
451             vlog.dbg("%s: deleted stale pidfile" % pidfile)
452             file_handle.close()
453             return 0
454     except IOError, e:
455         if e.errno not in [errno.EACCES, errno.EAGAIN]:
456             vlog.warn("%s: fcntl: %s" % (pidfile, e.strerror))
457             return -e.errno
458
459     # Someone else has the pidfile locked.
460     try:
461         try:
462             error = int(file_handle.readline())
463         except IOError, e:
464             vlog.warn("%s: read: %s" % (pidfile, e.strerror))
465             error = -e.errno
466         except ValueError:
467             vlog.warn("%s does not contain a pid" % pidfile)
468             error = -errno.EINVAL
469
470         return error
471     finally:
472         try:
473             file_handle.close()
474         except IOError:
475             pass
476
477
478 def read_pidfile(pidfile):
479     """Opens and reads a PID from 'pidfile'.  Returns the positive PID if
480     successful, otherwise a negative errno value."""
481     return __read_pidfile(pidfile, False)
482
483
484 def _check_already_running():
485     pid = __read_pidfile(_pidfile, True)
486     if pid > 0:
487         _fatal("%s: already running as pid %d, aborting" % (_pidfile, pid))
488     elif pid < 0:
489         _fatal("%s: pidfile check failed (%s), aborting"
490                % (_pidfile, os.strerror(pid)))
491
492
493 def add_args(parser):
494     """Populates 'parser', an ArgumentParser allocated using the argparse
495     module, with the command line arguments required by the daemon module."""
496
497     pidfile = make_pidfile_name(None)
498
499     group = parser.add_argument_group(title="Daemon Options")
500     group.add_argument("--detach", action="store_true",
501             help="Run in background as a daemon.")
502     group.add_argument("--no-chdir", action="store_true",
503             help="Do not chdir to '/'.")
504     group.add_argument("--monitor", action="store_true",
505             help="Monitor %s process." % ovs.util.PROGRAM_NAME)
506     group.add_argument("--pidfile", nargs="?", const=pidfile,
507             help="Create pidfile (default %s)." % pidfile)
508     group.add_argument("--overwrite-pidfile", action="store_true",
509             help="With --pidfile, start even if already running.")
510
511
512 def handle_args(args):
513     """Handles daemon module settings in 'args'.  'args' is an object
514     containing values parsed by the parse_args() method of ArgumentParser.  The
515     parent ArgumentParser should have been prepared by add_args() before
516     calling parse_args()."""
517
518     if args.detach:
519         set_detach()
520
521     if args.no_chdir:
522         set_no_chdir()
523
524     if args.pidfile:
525         set_pidfile(args.pidfile)
526
527     if args.overwrite_pidfile:
528         ignore_existing_pidfile()
529
530     if args.monitor:
531         set_monitor()