summaryrefslogtreecommitdiff
path: root/python/ovs
diff options
context:
space:
mode:
Diffstat (limited to 'python/ovs')
-rw-r--r--python/ovs/daemon.py194
-rw-r--r--python/ovs/fatal_signal.py13
-rw-r--r--python/ovs/vlog.py26
3 files changed, 196 insertions, 37 deletions
diff --git a/python/ovs/daemon.py b/python/ovs/daemon.py
index bd0619532..06ef92b78 100644
--- a/python/ovs/daemon.py
+++ b/python/ovs/daemon.py
@@ -13,9 +13,7 @@
# limitations under the License.
import errno
-import fcntl
import os
-import resource
import signal
import sys
import time
@@ -28,11 +26,24 @@ import ovs.timeval
import ovs.util
import ovs.vlog
+if sys.platform != 'win32':
+ import fcntl
+ import resource
+else:
+ import ovs.winutils as winutils
+ import ovs.fcntl_win as fcntl
+ import pywintypes
+ import subprocess
+ import win32process
+
vlog = ovs.vlog.Vlog("daemon")
# --detach: Should we run in the background?
_detach = False
+# Running as the child process - Windows only.
+_detached = False
+
# --pidfile: Name of pidfile (null if none).
_pidfile = None
@@ -98,6 +109,16 @@ def set_detach():
_detach = True
+def set_detached(wp):
+ """Sets up a following call to daemonize() to fork a supervisory
+ process to monitor the daemon and restart it if it dies due to
+ an error signal. Used on Windows only."""
+ global _detached
+ global _daemonize_fd
+ _detached = True
+ _daemonize_fd = int(wp)
+
+
def get_detach():
"""Will daemonize() really detach?"""
return _detach
@@ -123,8 +144,12 @@ def _make_pidfile():
pid = os.getpid()
# Create a temporary pidfile.
- tmpfile = "%s.tmp%d" % (_pidfile, pid)
- ovs.fatal_signal.add_file_to_unlink(tmpfile)
+ if sys.platform != 'win32':
+ tmpfile = "%s.tmp%d" % (_pidfile, pid)
+ ovs.fatal_signal.add_file_to_unlink(tmpfile)
+ else:
+ tmpfile = "%s" % _pidfile
+
try:
# This is global to keep Python from garbage-collecting and
# therefore closing our file after this function exits. That would
@@ -147,40 +172,48 @@ def _make_pidfile():
_fatal("%s: write failed: %s" % (tmpfile, e.strerror))
try:
- fcntl.lockf(file_handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
+ if sys.platform != 'win32':
+ fcntl.lockf(file_handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
+ else:
+ fcntl.lockf(file_handle, fcntl.LOCK_SH | fcntl.LOCK_NB)
except IOError as e:
_fatal("%s: fcntl failed: %s" % (tmpfile, e.strerror))
- # Rename or link it to the correct name.
- if _overwrite_pidfile:
- try:
- os.rename(tmpfile, _pidfile)
- except OSError as e:
- _fatal("failed to rename \"%s\" to \"%s\" (%s)"
- % (tmpfile, _pidfile, e.strerror))
+ if sys.platform == 'win32':
+ # Ensure that the pidfile will gets closed and deleted on exit.
+ ovs.fatal_signal.add_file_to_close_and_unlink(_pidfile, file_handle)
else:
- while True:
+ # Rename or link it to the correct name.
+ if _overwrite_pidfile:
try:
- os.link(tmpfile, _pidfile)
- error = 0
+ os.rename(tmpfile, _pidfile)
except OSError as e:
- error = e.errno
- if error == errno.EEXIST:
- _check_already_running()
- elif error != errno.EINTR:
- break
- if error:
- _fatal("failed to link \"%s\" as \"%s\" (%s)"
- % (tmpfile, _pidfile, os.strerror(error)))
+ _fatal("failed to rename \"%s\" to \"%s\" (%s)"
+ % (tmpfile, _pidfile, e.strerror))
+ else:
+ while True:
+ try:
+ os.link(tmpfile, _pidfile)
+ error = 0
+ except OSError as e:
+ error = e.errno
+ if error == errno.EEXIST:
+ _check_already_running()
+ elif error != errno.EINTR:
+ break
+ if error:
+ _fatal("failed to link \"%s\" as \"%s\" (%s)"
+ % (tmpfile, _pidfile, os.strerror(error)))
- # Ensure that the pidfile will get deleted on exit.
- ovs.fatal_signal.add_file_to_unlink(_pidfile)
+ # Ensure that the pidfile will get deleted on exit.
+ ovs.fatal_signal.add_file_to_unlink(_pidfile)
- # Delete the temporary pidfile if it still exists.
- if not _overwrite_pidfile:
- error = ovs.fatal_signal.unlink_file_now(tmpfile)
- if error:
- _fatal("%s: unlink failed (%s)" % (tmpfile, os.strerror(error)))
+ # Delete the temporary pidfile if it still exists.
+ if not _overwrite_pidfile:
+ error = ovs.fatal_signal.unlink_file_now(tmpfile)
+ if error:
+ _fatal("%s: unlink failed (%s)" % (
+ tmpfile, os.strerror(error)))
global _pidfile_dev
global _pidfile_ino
@@ -206,6 +239,9 @@ def _waitpid(pid, options):
def _fork_and_wait_for_startup():
+ if sys.platform == 'win32':
+ return _fork_and_wait_for_startup_windows()
+
try:
rfd, wfd = os.pipe()
except OSError as e:
@@ -259,7 +295,65 @@ def _fork_and_wait_for_startup():
return pid
+def _fork_and_wait_for_startup_windows():
+ global _detached
+ if _detached:
+ # Running in child process
+ ovs.timeval.postfork()
+ return 0
+
+ """ close the log file, on Windows cannot be moved while the parent has
+ a reference on it."""
+ vlog.close_log_file()
+
+ try:
+ (rfd, wfd) = winutils.windows_create_pipe()
+ except pywintypes.error as e:
+ sys.stderr.write("pipe failed to create: %s\n" % e.strerror)
+ sys.exit(1)
+
+ try:
+ creationFlags = win32process.DETACHED_PROCESS
+ args = ("%s %s --pipe-handle=%ld" % (
+ sys.executable, " ".join(sys.argv), int(wfd)))
+ proc = subprocess.Popen(
+ args=args,
+ close_fds=False,
+ shell=False,
+ creationflags=creationFlags,
+ stdout=sys.stdout,
+ stderr=sys.stderr)
+ pid = proc.pid
+ except OSError as e:
+ sys.stderr.write("CreateProcess failed (%s)\n" % os.strerror(e.errno))
+ sys.exit(1)
+
+ # Running in parent process.
+ winutils.win32file.CloseHandle(wfd)
+ ovs.fatal_signal.fork()
+
+ error, s = winutils.windows_read_pipe(rfd, 1)
+ if error:
+ s = ""
+
+ if len(s) != 1:
+ retval = proc.wait()
+ if retval == 0:
+ sys.stderr.write("fork child failed to signal startup\n")
+ else:
+ # Child exited with an error. Convey the same error to
+ # our parent process as a courtesy.
+ sys.exit(retval)
+ winutils.win32file.CloseHandle(rfd)
+
+ return pid
+
+
def _fork_notify_startup(fd):
+ if sys.platform == 'win32':
+ _fork_notify_startup_windows(fd)
+ return
+
if fd is not None:
error, bytes_written = ovs.socket_util.write_fully(fd, "0")
if error:
@@ -268,9 +362,31 @@ def _fork_notify_startup(fd):
os.close(fd)
+def _fork_notify_startup_windows(fd):
+ if fd is not None:
+ try:
+ # Python 2 requires a string as second parameter, while
+ # Python 3 requires a bytes-like object. b"0" fits for both
+ # python versions.
+ winutils.win32file.WriteFile(fd, b"0", None)
+ except winutils.pywintypes.error as e:
+ sys.stderr.write("could not write to pipe: %s\n" %
+ os.strerror(e.winerror))
+ sys.exit(1)
+
+
def _should_restart(status):
global RESTART_EXIT_CODE
+ if sys.platform == 'win32':
+ # The exit status is encoded in the high byte of the
+ # 16-bit number 'status'.
+ exit_status = status >> 8
+
+ if exit_status == RESTART_EXIT_CODE:
+ return True
+ return False
+
if os.WIFEXITED(status) and os.WEXITSTATUS(status) == RESTART_EXIT_CODE:
return True
@@ -296,7 +412,7 @@ def _monitor_daemon(daemon_pid):
% (daemon_pid, ovs.process.status_msg(status)))
if _should_restart(status):
- if os.WCOREDUMP(status):
+ if sys.platform != 'win32' and os.WCOREDUMP(status):
# Disable further core dumps to save disk space.
try:
resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
@@ -351,8 +467,9 @@ def daemonize_start():
# Running in parent process.
sys.exit(0)
- # Running in daemon or monitor process.
- os.setsid()
+ if sys.platform != 'win32':
+ # Running in daemon or monitor process.
+ os.setsid()
if _monitor:
saved_daemonize_fd = _daemonize_fd
@@ -360,7 +477,8 @@ def daemonize_start():
if daemon_pid > 0:
# Running in monitor process.
_fork_notify_startup(saved_daemonize_fd)
- _close_standard_fds()
+ if sys.platform != 'win32':
+ _close_standard_fds()
_monitor_daemon(daemon_pid)
# Running in daemon process
@@ -502,6 +620,10 @@ def add_args(parser):
help="Create pidfile (default %s)." % pidfile)
group.add_argument("--overwrite-pidfile", action="store_true",
help="With --pidfile, start even if already running.")
+ if sys.platform == 'win32':
+ group.add_argument("--pipe-handle",
+ help=("With --pidfile, start even if "
+ "already running."))
def handle_args(args):
@@ -510,6 +632,10 @@ def handle_args(args):
parent ArgumentParser should have been prepared by add_args() before
calling parse_args()."""
+ if sys.platform == 'win32':
+ if args.pipe_handle:
+ set_detached(args.pipe_handle)
+
if args.detach:
set_detach()
diff --git a/python/ovs/fatal_signal.py b/python/ovs/fatal_signal.py
index a143e24be..eb4b663f4 100644
--- a/python/ovs/fatal_signal.py
+++ b/python/ovs/fatal_signal.py
@@ -59,6 +59,17 @@ def add_file_to_unlink(file):
_files[file] = None
+def add_file_to_close_and_unlink(file, fd=None):
+ """Registers 'file' to be unlinked when the program terminates via
+ sys.exit() or a fatal signal and the 'fd' to be closed. On Windows a file
+ cannot be removed while it is open for writing."""
+ global _added_hook
+ if not _added_hook:
+ _added_hook = True
+ add_hook(_unlink_files, _cancel_files, True)
+ _files[file] = fd
+
+
def remove_file_to_unlink(file):
"""Unregisters 'file' from being unlinked when the program terminates via
sys.exit() or a fatal signal."""
@@ -78,6 +89,8 @@ def unlink_file_now(file):
def _unlink_files():
for file_ in _files:
+ if sys.platform == "win32" and _files[file_]:
+ _files[file_].close()
_unlink(file_)
diff --git a/python/ovs/vlog.py b/python/ovs/vlog.py
index 48d52ade7..99bd36b04 100644
--- a/python/ovs/vlog.py
+++ b/python/ovs/vlog.py
@@ -243,7 +243,14 @@ class Vlog(object):
Vlog._unixctl_vlog_reopen, None)
ovs.unixctl.command_register("vlog/close", "", 0, 0,
Vlog._unixctl_vlog_close, None)
- ovs.unixctl.command_register("vlog/set", "spec", 1, sys.maxsize,
+ try:
+ # Windows limitation on Python 2, sys.maxsize is a long number
+ # on 64 bits environments, while sys.maxint is the maximum int
+ # number. Python 3 works as expected.
+ maxsize_int = sys.maxint
+ except AttributeError:
+ maxsize_int = sys.maxsize
+ ovs.unixctl.command_register("vlog/set", "spec", 1, maxsize_int,
Vlog._unixctl_vlog_set, None)
ovs.unixctl.command_register("vlog/list", "", 0, 0,
Vlog._unixctl_vlog_list, None)
@@ -384,6 +391,16 @@ class Vlog(object):
logger.addHandler(Vlog.__file_handler)
@staticmethod
+ def close_log_file():
+ """Closes the current log file. (This is useful on Windows, to ensure
+ that a reference to the file is not kept by the daemon in case of
+ detach.)"""
+ if Vlog.__log_file:
+ logger = logging.getLogger("file")
+ logger.removeHandler(Vlog.__file_handler)
+ Vlog.__file_handler.close()
+
+ @staticmethod
def _unixctl_vlog_reopen(conn, unused_argv, unused_aux):
if Vlog.__log_file:
Vlog.reopen_log_file()
@@ -394,8 +411,11 @@ class Vlog(object):
@staticmethod
def _unixctl_vlog_close(conn, unused_argv, unused_aux):
if Vlog.__log_file:
- logger = logging.getLogger("file")
- logger.removeHandler(Vlog.__file_handler)
+ if sys.platform != 'win32':
+ logger = logging.getLogger("file")
+ logger.removeHandler(Vlog.__file_handler)
+ else:
+ Vlog.close_log_file()
conn.reply(None)
@staticmethod