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