daemon: Avoid races on pidfile creation.
[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.  The caller must not modify or free the returned
79     string."""
80     return _pidfile
81
82 def set_no_chdir():
83     """Sets that we do not chdir to "/"."""
84     global _chdir
85     _chdir = False
86
87 def is_chdir_enabled():
88     """Will we chdir to "/" as part of daemonizing?"""
89     return _chdir
90
91 def ignore_existing_pidfile():
92     """Normally, daemonize() or daemonize_start() will terminate the program
93     with a message if a locked pidfile already exists.  If this function is
94     called, an existing pidfile will be replaced, with a warning."""
95     global _overwrite_pidfile
96     _overwrite_pidfile = True
97
98 def set_detach():
99     """Sets up a following call to daemonize() to detach from the foreground
100     session, running this process in the background."""
101     global _detach
102     _detach = True
103
104 def get_detach():
105     """Will daemonize() really detach?"""
106     return _detach
107
108 def set_monitor():
109     """Sets up a following call to daemonize() to fork a supervisory process to
110     monitor the daemon and restart it if it dies due to an error signal."""
111     global _monitor
112     _monitor = True
113
114 def _fatal(msg):
115     logging.error(msg)
116     sys.stderr.write("%s\n" % msg)
117     sys.exit(1)
118
119 def _make_pidfile():
120     """If a pidfile has been configured, creates it and stores the running
121     process's pid in it.  Ensures that the pidfile will be deleted when the
122     process exits."""
123     pid = os.getpid()
124
125     # Create a temporary pidfile.
126     tmpfile = "%s.tmp%d" % (_pidfile, pid)
127     ovs.fatal_signal.add_file_to_unlink(tmpfile)
128     try:
129         # This is global to keep Python from garbage-collecting and
130         # therefore closing our file after this function exits.  That would
131         # unlock the lock for us, and we don't want that.
132         global file
133
134         file = open(tmpfile, "w")
135     except IOError, e:
136         _fatal("%s: create failed (%s)" % (tmpfile, e.strerror))
137
138     try:
139         s = os.fstat(file.fileno())
140     except IOError, e:
141         _fatal("%s: fstat failed (%s)" % (tmpfile, e.strerror))
142
143     try:
144         file.write("%s\n" % pid)
145         file.flush()
146     except OSError, e:
147         _fatal("%s: write failed: %s" % (tmpfile, e.strerror))
148
149     try:
150         fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
151     except IOError, e:
152         _fatal("%s: fcntl failed: %s" % (tmpfile, e.strerror))
153
154     # Rename or link it to the correct name.
155     if _overwrite_pidfile:
156         try:
157             os.rename(tmpfile, _pidfile)
158         except OSError, e:
159             _fatal("failed to rename \"%s\" to \"%s\" (%s)"
160                    % (tmpfile, _pidfile, e.strerror))
161     else:
162         while True:
163             try:
164                 os.link(tmpfile, _pidfile)
165                 error = 0
166             except OSError, e:
167                 error = e.errno
168             if error == errno.EEXIST:
169                 _check_already_running()
170             elif error != errno.EINTR:
171                 break
172         if error:
173             _fatal("failed to link \"%s\" as \"%s\" (%s)"
174                    % (tmpfile, _pidfile, os.strerror(error)))
175
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     _pidfile_dev = s.st_dev
187     _pidfile_ino = s.st_ino
188
189 def daemonize():
190     """If configured with set_pidfile() or set_detach(), creates the pid file
191     and detaches from the foreground session."""
192     daemonize_start()
193     daemonize_complete()
194
195 def _waitpid(pid, options):
196     while True:
197         try:
198             return os.waitpid(pid, options)
199         except OSError, e:
200             if e.errno == errno.EINTR:
201                 pass
202             return -e.errno, 0
203
204 def _fork_and_wait_for_startup():
205     try:
206         rfd, wfd = os.pipe()
207     except OSError, e:
208         sys.stderr.write("pipe failed: %s\n" % os.strerror(e.errno))
209         sys.exit(1)
210
211     try:
212         pid = os.fork()
213     except OSError, e:
214         sys.stderr.write("could not fork: %s\n" % os.strerror(e.errno))
215         sys.exit(1)
216
217     if pid > 0:
218         # Running in parent process.
219         os.close(wfd)
220         ovs.fatal_signal.fork()
221         while True:
222             try:
223                 s = os.read(rfd, 1)
224                 error = 0
225             except OSError, e:
226                 s = ""
227                 error = e.errno
228             if error != errno.EINTR:
229                 break
230         if len(s) != 1:
231             retval, status = _waitpid(pid, 0)
232             if (retval == pid and
233                 os.WIFEXITED(status) and os.WEXITSTATUS(status)):
234                 # Child exited with an error.  Convey the same error to
235                 # our parent process as a courtesy.
236                 sys.exit(os.WEXITSTATUS(status))
237             else:
238                 sys.stderr.write("fork child failed to signal startup\n")
239                 sys.exit(1)
240
241         os.close(rfd)
242     else:
243         # Running in parent process.
244         os.close(rfd)
245         ovs.timeval.postfork()
246         #ovs.lockfile.postfork()
247
248         global _daemonize_fd
249         _daemonize_fd = wfd
250     return pid
251
252 def _fork_notify_startup(fd):
253     if fd is not None:
254         error, bytes_written = ovs.socket_util.write_fully(fd, "0")
255         if error:
256             sys.stderr.write("could not write to pipe\n")
257             sys.exit(1)
258         os.close(fd)
259
260 def _should_restart(status):
261     global RESTART_EXIT_CODE
262
263     if os.WIFEXITED(status) and os.WEXITSTATUS(status) == RESTART_EXIT_CODE:
264         return True
265
266     if os.WIFSIGNALED(status):
267         for signame in ("SIGABRT", "SIGALRM", "SIGBUS", "SIGFPE", "SIGILL",
268                         "SIGPIPE", "SIGSEGV", "SIGXCPU", "SIGXFSZ"):
269             if (signame in signal.__dict__ and
270                 os.WTERMSIG(status) == signal.__dict__[signame]):
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
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