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