7bd7370ee253bb120f28cd33d333c0cb5f30468a
[openvswitch] / 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     global _pidfile_dev
186     global _pidfile_ino
187     _pidfile_dev = s.st_dev
188     _pidfile_ino = s.st_ino
189
190 def daemonize():
191     """If configured with set_pidfile() or set_detach(), creates the pid file
192     and detaches from the foreground session."""
193     daemonize_start()
194     daemonize_complete()
195
196 def _waitpid(pid, options):
197     while True:
198         try:
199             return os.waitpid(pid, options)
200         except OSError, e:
201             if e.errno == errno.EINTR:
202                 pass
203             return -e.errno, 0
204
205 def _fork_and_wait_for_startup():
206     try:
207         rfd, wfd = os.pipe()
208     except OSError, e:
209         sys.stderr.write("pipe failed: %s\n" % os.strerror(e.errno))
210         sys.exit(1)
211
212     try:
213         pid = os.fork()
214     except OSError, e:
215         sys.stderr.write("could not fork: %s\n" % os.strerror(e.errno))
216         sys.exit(1)
217
218     if pid > 0:
219         # Running in parent process.
220         os.close(wfd)
221         ovs.fatal_signal.fork()
222         while True:
223             try:
224                 s = os.read(rfd, 1)
225                 error = 0
226             except OSError, e:
227                 s = ""
228                 error = e.errno
229             if error != errno.EINTR:
230                 break
231         if len(s) != 1:
232             retval, status = _waitpid(pid, 0)
233             if (retval == pid and
234                 os.WIFEXITED(status) and os.WEXITSTATUS(status)):
235                 # Child exited with an error.  Convey the same error to
236                 # our parent process as a courtesy.
237                 sys.exit(os.WEXITSTATUS(status))
238             else:
239                 sys.stderr.write("fork child failed to signal startup\n")
240                 sys.exit(1)
241
242         os.close(rfd)
243     else:
244         # Running in parent process.
245         os.close(rfd)
246         ovs.timeval.postfork()
247         #ovs.lockfile.postfork()
248
249         global _daemonize_fd
250         _daemonize_fd = wfd
251     return pid
252
253 def _fork_notify_startup(fd):
254     if fd is not None:
255         error, bytes_written = ovs.socket_util.write_fully(fd, "0")
256         if error:
257             sys.stderr.write("could not write to pipe\n")
258             sys.exit(1)
259         os.close(fd)
260
261 def _should_restart(status):
262     global RESTART_EXIT_CODE
263
264     if os.WIFEXITED(status) and os.WEXITSTATUS(status) == RESTART_EXIT_CODE:
265         return True
266
267     if os.WIFSIGNALED(status):
268         for signame in ("SIGABRT", "SIGALRM", "SIGBUS", "SIGFPE", "SIGILL",
269                         "SIGPIPE", "SIGSEGV", "SIGXCPU", "SIGXFSZ"):
270             if os.WTERMSIG(status) == getattr(signal, signame, None):
271                 return True
272     return False
273
274 def _monitor_daemon(daemon_pid):
275     # XXX should log daemon's stderr output at startup time
276     # XXX should use setproctitle module if available
277     last_restart = None
278     while True:
279         retval, status = _waitpid(daemon_pid, 0)
280         if retval < 0:
281             sys.stderr.write("waitpid failed\n")
282             sys.exit(1)
283         elif retval == daemon_pid:
284             status_msg = ("pid %d died, %s"
285                           % (daemon_pid, ovs.process.status_msg(status)))
286             
287             if _should_restart(status):
288                 if os.WCOREDUMP(status):
289                     # Disable further core dumps to save disk space.
290                     try:
291                         resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
292                     except resource.error:
293                         logging.warning("failed to disable core dumps")
294
295                 # Throttle restarts to no more than once every 10 seconds.
296                 if (last_restart is not None and
297                     ovs.timeval.msec() < last_restart + 10000):
298                     logging.warning("%s, waiting until 10 seconds since last "
299                                     "restart" % status_msg)
300                     while True:
301                         now = ovs.timeval.msec()
302                         wakeup = last_restart + 10000
303                         if now > wakeup:
304                             break
305                         print "sleep %f" % ((wakeup - now) / 1000.0)
306                         time.sleep((wakeup - now) / 1000.0)
307                 last_restart = ovs.timeval.msec()
308
309                 logging.error("%s, restarting" % status_msg)
310                 daemon_pid = _fork_and_wait_for_startup()
311                 if not daemon_pid:
312                     break
313             else:
314                 logging.info("%s, exiting" % status_msg)
315                 sys.exit(0)
316
317    # Running in new daemon process.
318
319 def _close_standard_fds():
320     """Close stdin, stdout, stderr.  If we're started from e.g. an SSH session,
321     then this keeps us from holding that session open artificially."""
322     null_fd = ovs.socket_util.get_null_fd()
323     if null_fd >= 0:
324         os.dup2(null_fd, 0)
325         os.dup2(null_fd, 1)
326         os.dup2(null_fd, 2)
327
328 def daemonize_start():
329     """If daemonization is configured, then starts daemonization, by forking
330     and returning in the child process.  The parent process hangs around until
331     the child lets it know either that it completed startup successfully (by
332     calling daemon_complete()) or that it failed to start up (by exiting with a
333     nonzero exit code)."""
334     
335     if _detach:
336         if _fork_and_wait_for_startup() > 0:
337             # Running in parent process.
338             sys.exit(0)
339         # Running in daemon or monitor process.
340
341     if _monitor:
342         saved_daemonize_fd = _daemonize_fd
343         daemon_pid = _fork_and_wait_for_startup()
344         if daemon_pid > 0:
345             # Running in monitor process.
346             _fork_notify_startup(saved_daemonize_fd)
347             _close_standard_fds()
348             _monitor_daemon(daemon_pid)
349         # Running in daemon process
350     
351     if _pidfile:
352         _make_pidfile()
353
354 def daemonize_complete():
355     """If daemonization is configured, then this function notifies the parent
356     process that the child process has completed startup successfully."""
357     _fork_notify_startup(_daemonize_fd)
358
359     if _detach:
360         os.setsid()
361         if _chdir:
362             os.chdir("/")
363         _close_standard_fds()
364
365 def usage():
366     sys.stdout.write("""
367 Daemon options:
368    --detach                run in background as daemon
369    --no-chdir              do not chdir to '/'
370    --pidfile[=FILE]        create pidfile (default: %s/%s.pid)
371    --overwrite-pidfile     with --pidfile, start even if already running
372 """ % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME))
373
374 def __read_pidfile(pidfile, delete_if_stale):
375     if _pidfile_dev is not None:
376         try:
377             s = os.stat(pidfile)
378             if s.st_ino == _pidfile_ino and s.st_dev == _pidfile_dev:
379                 # It's our own pidfile.  We can't afford to open it,
380                 # because closing *any* fd for a file that a process
381                 # has locked also releases all the locks on that file.
382                 #
383                 # Fortunately, we know the associated pid anyhow.
384                 return os.getpid()
385         except OSError:
386             pass
387
388     try:
389         file = open(pidfile, "r+")
390     except IOError, e:
391         if e.errno == errno.ENOENT and delete_if_stale:
392             return 0
393         logging.warning("%s: open: %s" % (pidfile, e.strerror))
394         return -e.errno
395
396     # Python fcntl doesn't directly support F_GETLK so we have to just try
397     # to lock it.
398     try:
399         fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
400
401         # pidfile exists but wasn't locked by anyone.  Now we have the lock.
402         if not delete_if_stale:
403             file.close()
404             logging.warning("%s: pid file is stale" % pidfile)
405             return -errno.ESRCH
406
407         # Is the file we have locked still named 'pidfile'?
408         try:
409             raced = False
410             s = os.stat(pidfile)
411             s2 = os.fstat(file.fileno())
412             if s.st_ino != s2.st_ino or s.st_dev != s2.st_dev:
413                 raced = True
414         except IOError:
415             raced = True
416         if raced:
417             logging.warning("%s: lost race to delete pidfile" % pidfile)
418             return -errno.ALREADY
419
420         # We won the right to delete the stale pidfile.
421         try:
422             os.unlink(pidfile)
423         except IOError, e:
424             logging.warning("%s: failed to delete stale pidfile"
425                             % (pidfile, e.strerror))
426             return -e.errno
427         else:
428             logging.debug("%s: deleted stale pidfile" % pidfile)
429             file.close()
430             return 0
431     except IOError, e:
432         if e.errno not in [errno.EACCES, errno.EAGAIN]:
433             logging.warn("%s: fcntl: %s" % (pidfile, e.strerror))
434             return -e.errno
435
436     # Someone else has the pidfile locked.
437     try:
438         try:
439             return int(file.readline())
440         except IOError, e:
441             logging.warning("%s: read: %s" % (pidfile, e.strerror))
442             return -e.errno
443         except ValueError:
444             logging.warning("%s does not contain a pid" % pidfile)
445             return -errno.EINVAL
446     finally:
447         try:
448             file.close()
449         except IOError:
450             pass
451
452 def read_pidfile(pidfile):
453     """Opens and reads a PID from 'pidfile'.  Returns the positive PID if
454     successful, otherwise a negative errno value."""
455     return __read_pidfile(pidfile, False)
456
457 def _check_already_running():
458     pid = __read_pidfile(_pidfile, True)
459     if pid > 0:
460         _fatal("%s: already running as pid %d, aborting" % (_pidfile, pid))
461     elif pid < 0:
462         _fatal("%s: pidfile check failed (%s), aborting"
463                % (_pidfile, os.strerror(pid)))
464
465 # XXX Python's getopt does not support options with optional arguments, so we
466 # have to separate --pidfile (with no argument) from --pidfile-name (with an
467 # argument).  Need to write our own getopt I guess.
468 LONG_OPTIONS = ["detach", "no-chdir", "pidfile", "pidfile-name=",
469                 "overwrite-pidfile", "monitor"]
470
471 def parse_opt(option, arg):
472     if option == '--detach':
473         set_detach()
474     elif option == '--no-chdir':
475         set_no_chdir()
476     elif option == '--pidfile':
477         set_pidfile(None)
478     elif option == '--pidfile-name':
479         set_pidfile(arg)
480     elif option == '--overwrite-pidfile':
481         ignore_existing_pidfile()
482     elif option == '--monitor':
483         set_monitor()
484     else:
485         return False
486     return True