xenserver: monitor-external-ids should run with --monitor
[sliver-openvswitch.git] / python / ovs / daemon.py
1 # Copyright (c) 2010 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, die_if_already_running() will terminate the program with a
93     message if a locked pidfile already exists.  If this function is called,
94     die_if_already_running() will merely log 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 _already_running():
115     """If a pidfile has been configured and that pidfile already exists and is
116     locked by a running process, returns True.  Otherwise, returns False."""
117     if _pidfile is not None:
118         try:
119             file = open(_pidfile, "r+")
120             try:
121                 try:
122                     fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
123                 except IOError, e:
124                     if e.errno in [errno.EACCES, errno.EAGAIN]:
125                         return True
126                     logging.error("error locking %s (%s)"
127                                   % (_pidfile, os.strerror(e.errno)))
128                     return False
129             finally:
130                 # This releases the lock, which we don't really want.
131                 file.close()
132         except IOError, e:
133             if e.errno == errno.ENOENT:
134                 return False
135             logging.error("error opening %s (%s)"
136                           % (_pidfile, os.strerror(e.errno)))
137     return False
138
139 def die_if_already_running():
140     """If a locked pidfile exists, issue a warning message and, unless
141     ignore_existing_pidfile() has been called, terminate the program."""
142     if _already_running():
143         if not _overwrite_pidfile:
144             sys.stderr.write("%s: already running\n" % get_pidfile())
145             sys.exit(1)
146         else:
147             logging.warn("%s: %s already running"
148                          % (get_pidfile(), ovs.util.PROGRAM_NAME))
149
150 def _make_pidfile():
151     """If a pidfile has been configured, creates it and stores the running
152     process's pid in it.  Ensures that the pidfile will be deleted when the
153     process exits."""
154     if _pidfile is not None:
155         # Create pidfile via temporary file, so that observers never see an
156         # empty pidfile or an unlocked pidfile.
157         pid = os.getpid()
158         tmpfile = "%s.tmp%d" % (_pidfile, pid)
159         ovs.fatal_signal.add_file_to_unlink(tmpfile)
160
161         try:
162             # This is global to keep Python from garbage-collecting and
163             # therefore closing our file after this function exits.  That would
164             # unlock the lock for us, and we don't want that.
165             global file
166
167             file = open(tmpfile, "w")
168         except IOError, e:
169             logging.error("%s: create failed: %s"
170                           % (tmpfile, os.strerror(e.errno)))
171             return
172
173         try:
174             fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
175         except IOError, e:
176             logging.error("%s: fcntl failed: %s"
177                           % (tmpfile, os.strerror(e.errno)))
178             file.close()
179             return
180
181         try:
182             file.write("%s\n" % pid)
183             file.flush()
184             ovs.fatal_signal.add_file_to_unlink(_pidfile)
185         except OSError, e:
186             logging.error("%s: write failed: %s"
187                           % (tmpfile, os.strerror(e.errno)))
188             file.close()
189             return
190             
191         try:
192             os.rename(tmpfile, _pidfile)
193         except OSError, e:
194             ovs.fatal_signal.remove_file_to_unlink(_pidfile)
195             logging.error("failed to rename \"%s\" to \"%s\": %s"
196                           % (tmpfile, _pidfile, os.strerror(e.errno)))
197             file.close()
198             return
199
200         s = os.fstat(file.fileno())
201         _pidfile_dev = s.st_dev
202         _pidfile_ino = s.st_ino
203
204 def daemonize():
205     """If configured with set_pidfile() or set_detach(), creates the pid file
206     and detaches from the foreground session."""
207     daemonize_start()
208     daemonize_complete()
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 def _fork_and_wait_for_startup():
220     try:
221         rfd, wfd = os.pipe()
222     except OSError, e:
223         sys.stderr.write("pipe failed: %s\n" % os.strerror(e.errno))
224         sys.exit(1)
225
226     try:
227         pid = os.fork()
228     except OSError, e:
229         sys.stderr.write("could not fork: %s\n" % os.strerror(e.errno))
230         sys.exit(1)
231
232     if pid > 0:
233         # Running in parent process.
234         os.close(wfd)
235         ovs.fatal_signal.fork()
236         try:
237             s = os.read(rfd, 1)
238         except OSError, e:
239             s = ""
240         if len(s) != 1:
241             retval, status = _waitpid(pid, 0)
242             if (retval == pid and
243                 os.WIFEXITED(status) and os.WEXITSTATUS(status)):
244                 # Child exited with an error.  Convey the same error to
245                 # our parent process as a courtesy.
246                 sys.exit(os.WEXITSTATUS(status))
247             else:
248                 sys.stderr.write("fork child failed to signal startup\n")
249                 sys.exit(1)
250
251         os.close(rfd)
252     else:
253         # Running in parent process.
254         os.close(rfd)
255         ovs.timeval.postfork()
256         #ovs.lockfile.postfork()
257
258         global _daemonize_fd
259         _daemonize_fd = wfd
260     return pid
261
262 def _fork_notify_startup(fd):
263     if fd is not None:
264         error, bytes_written = ovs.socket_util.write_fully(fd, "0")
265         if error:
266             sys.stderr.write("could not write to pipe\n")
267             sys.exit(1)
268         os.close(fd)
269
270 def _should_restart(status):
271     global RESTART_EXIT_CODE
272
273     if os.WIFEXITED(status) and os.WEXITSTATUS(status) == RESTART_EXIT_CODE:
274         return True
275
276     if os.WIFSIGNALED(status):
277         for signame in ("SIGABRT", "SIGALRM", "SIGBUS", "SIGFPE", "SIGILL",
278                         "SIGPIPE", "SIGSEGV", "SIGXCPU", "SIGXFSZ"):
279             if (signame in signal.__dict__ and
280                 os.WTERMSIG(status) == signal.__dict__[signame]):
281                 return True
282     return False
283
284 def _monitor_daemon(daemon_pid):
285     # XXX should log daemon's stderr output at startup time
286     # XXX should use setproctitle module if available
287     last_restart = None
288     while True:
289         retval, status = _waitpid(daemon_pid, 0)
290         if retval < 0:
291             sys.stderr.write("waitpid failed\n")
292             sys.exit(1)
293         elif retval == daemon_pid:
294             status_msg = ("pid %d died, %s"
295                           % (daemon_pid, ovs.process.status_msg(status)))
296             
297             if _should_restart(status):
298                 if os.WCOREDUMP(status):
299                     # Disable further core dumps to save disk space.
300                     try:
301                         resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
302                     except resource.error:
303                         logging.warning("failed to disable core dumps")
304
305                 # Throttle restarts to no more than once every 10 seconds.
306                 if (last_restart is not None and
307                     ovs.timeval.msec() < last_restart + 10000):
308                     logging.warning("%s, waiting until 10 seconds since last "
309                                     "restart" % status_msg)
310                     while True:
311                         now = ovs.timeval.msec()
312                         wakeup = last_restart + 10000
313                         if now > wakeup:
314                             break
315                         print "sleep %f" % ((wakeup - now) / 1000.0)
316                         time.sleep((wakeup - now) / 1000.0)
317                 last_restart = ovs.timeval.msec()
318
319                 logging.error("%s, restarting" % status_msg)
320                 daemon_pid = _fork_and_wait_for_startup()
321                 if not daemon_pid:
322                     break
323             else:
324                 logging.info("%s, exiting" % status_msg)
325                 sys.exit(0)
326
327    # Running in new daemon process.
328
329 def _close_standard_fds():
330     """Close stdin, stdout, stderr.  If we're started from e.g. an SSH session,
331     then this keeps us from holding that session open artificially."""
332     null_fd = ovs.socket_util.get_null_fd()
333     if null_fd >= 0:
334         os.dup2(null_fd, 0)
335         os.dup2(null_fd, 1)
336         os.dup2(null_fd, 2)
337
338 def daemonize_start():
339     """If daemonization is configured, then starts daemonization, by forking
340     and returning in the child process.  The parent process hangs around until
341     the child lets it know either that it completed startup successfully (by
342     calling daemon_complete()) or that it failed to start up (by exiting with a
343     nonzero exit code)."""
344     
345     if _detach:
346         if _fork_and_wait_for_startup() > 0:
347             # Running in parent process.
348             sys.exit(0)
349         # Running in daemon or monitor process.
350
351     if _monitor:
352         saved_daemonize_fd = _daemonize_fd
353         daemon_pid = _fork_and_wait_for_startup()
354         if daemon_pid > 0:
355             # Running in monitor process.
356             _fork_notify_startup(saved_daemonize_fd)
357             _close_standard_fds()
358             _monitor_daemon(daemon_pid)
359         # Running in daemon process
360     
361     _make_pidfile()
362
363 def daemonize_complete():
364     """If daemonization is configured, then this function notifies the parent
365     process that the child process has completed startup successfully."""
366     _fork_notify_startup(_daemonize_fd)
367
368     if _detach:
369         os.setsid()
370         if _chdir:
371             os.chdir("/")
372         _close_standard_fds()
373
374 def usage():
375     sys.stdout.write("""
376 Daemon options:
377    --detach                run in background as daemon
378    --no-chdir              do not chdir to '/'
379    --pidfile[=FILE]        create pidfile (default: %s/%s.pid)
380    --overwrite-pidfile     with --pidfile, start even if already running
381 """ % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME))
382
383 def read_pidfile(pidfile):
384     """Opens and reads a PID from 'pidfile'.  Returns the nonnegative PID if
385     successful, otherwise a negative errno value."""
386     if _pidfile_dev is not None:
387         try:
388             s = os.stat(pidfile)
389             if s.st_ino == _pidfile_ino and s.st_dev == _pidfile_dev:
390                 # It's our own pidfile.  We can't afford to open it,
391                 # because closing *any* fd for a file that a process
392                 # has locked also releases all the locks on that file.
393                 #
394                 # Fortunately, we know the associated pid anyhow.
395                 return os.getpid()
396         except OSError:
397             pass
398
399     try:
400         file = open(pidfile, "r")
401     except IOError, e:
402         logging.warning("%s: open: %s" % (pidfile, os.strerror(e.errno)))
403         return -e.errno
404
405     # Python fcntl doesn't directly support F_GETLK so we have to just try
406     # to lock it.  If we get a conflicting lock that's "success"; otherwise
407     # the file is not locked.
408     try:
409         fcntl.lockf(file, fcntl.LOCK_EX | fcntl.LOCK_NB)
410         # File isn't locked if we get here, so treat that as an error.
411         logging.warning("%s: pid file is not locked" % pidfile)
412         try:
413             # As a side effect, this drops the lock.
414             file.close()
415         except IOError:
416             pass
417         return -errno.ESRCH
418     except IOError, e:
419         if e.errno not in [errno.EACCES, errno.EAGAIN]:
420             logging.warn("%s: fcntl: %s" % (pidfile, os.strerror(e.errno)))
421             return -e.errno
422
423     try:
424         try:
425             return int(file.readline())
426         except IOError, e:
427             logging.warning("%s: read: %s" % (pidfile, e.strerror))
428             return -e.errno
429         except ValueError:
430             logging.warning("%s does not contain a pid" % pidfile)
431             return -errno.EINVAL
432     finally:
433         try:
434             file.close()
435         except IOError:
436             pass
437
438 # XXX Python's getopt does not support options with optional arguments, so we
439 # have to separate --pidfile (with no argument) from --pidfile-name (with an
440 # argument).  Need to write our own getopt I guess.
441 LONG_OPTIONS = ["detach", "no-chdir", "pidfile", "pidfile-name=",
442                 "overwrite-pidfile", "monitor"]
443
444 def parse_opt(option, arg):
445     if option == '--detach':
446         set_detach()
447     elif option == '--no-chdir':
448         set_no_chdir()
449     elif option == '--pidfile':
450         set_pidfile(None)
451     elif option == '--pidfile-name':
452         set_pidfile(arg)
453     elif option == '--overwrite-pidfile':
454         ignore_existing_pidfile()
455     elif option == '--monitor':
456         set_monitor()
457     else:
458         return False
459     return True