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