From 458dbeddf3a3ca20ebe5c1404b79dd6101c9a67d Mon Sep 17 00:00:00 2001 From: "emellor@leeni.uk.xensource.com" Date: Thu, 8 Dec 2005 15:54:35 +0000 Subject: [PATCH 1/7] Fix ip_of to cope with interfaces with multiple addresses, courtesy of Adam Heath . Signed-off-by: Ewan Mellor --- tools/examples/vif-common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/examples/vif-common.sh b/tools/examples/vif-common.sh index 18a0b6c88e..105a843900 100644 --- a/tools/examples/vif-common.sh +++ b/tools/examples/vif-common.sh @@ -113,7 +113,7 @@ function handle_iptable() # function ip_of() { - ip addr show "$1" | sed -n 's/^.*inet \([0-9.]*\).*$/\1/p' + ip addr show "$1" | awk "/^.*inet.*$1\$/{print \$2}" | sed 's,/.*,,' } From 72bb6de0e016c567ff17504ac3ca792dc0792471 Mon Sep 17 00:00:00 2001 From: "emellor@leeni.uk.xensource.com" Date: Thu, 8 Dec 2005 16:11:48 +0000 Subject: [PATCH 2/7] Flush the addresses from the new eth0 before trying to add the new address to it, when using ip on the kernel command line and not using the installed ifup / ifdown. This stops the script bailing out if there is already an address attached to that device, which was leaving the device without routing (since this has already been removed). Fix the stop action under the same circumstances. No attempt was made to restore the kernel command line details to the interfaces if the ifdown failed in that case. Signed-off-by: Ewan Mellor --- tools/examples/network-bridge | 61 +++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/tools/examples/network-bridge b/tools/examples/network-bridge index 2534b928c4..e915ea95e3 100755 --- a/tools/examples/network-bridge +++ b/tools/examples/network-bridge @@ -89,6 +89,31 @@ legacy_mask_to_prefix() { exit 1 } +parse_kernel_ip() { + if egrep 'ip=[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:' /proc/cmdline; then + kip=`sed -e 's!.*ip=\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\):.*!\1!' /proc/cmdline` + kmask=`sed -e 's!.*ip=[^:]*:[^:]*:[^:]*:\([^:]*\):.*!\1!' /proc/cmdline` + kgate=`sed -e 's!.*ip=[^:]*:[^:]*:\([^:]*\):.*!\1!' /proc/cmdline` + fi +} + +do_ifup() { + if ! ifup $1 ; then + if [ ${kip} ] ; then + # use the addresses we grocked from /proc/cmdline + if [ -z "${kmask}" ]; then + PREFIX=32 + else + legacy_mask_to_prefix ${kmask} + fi + ip addr flush $1 + ip addr add ${kip}/${PREFIX} dev $1 + ip link set dev $1 up + [ ${kgate} ] && ip route add default via ${kgate} + fi + fi +} + # Usage: transfer_addrs src dst # Copy all IP addresses (including aliases) from device $src to device $dst. transfer_addrs () { @@ -234,14 +259,11 @@ using loopback.nloopbacks= on the domain 0 kernel command line. preiftransfer ${netdev} transfer_addrs ${netdev} ${vdev} if ! ifdown ${netdev}; then - # if ifup didn't work, see if we have an ip= on cmd line - if egrep 'ip=[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:' /proc/cmdline; then - kip=`sed -e 's!.*ip=\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\):.*!\1!' /proc/cmdline` - kmask=`sed -e 's!.*ip=[^:]*:[^:]*:[^:]*:\([^:]*\):.*!\1!' /proc/cmdline` - kgate=`sed -e 's!.*ip=[^:]*:[^:]*:\([^:]*\):.*!\1!' /proc/cmdline` - ip link set ${netdev} down - ip addr flush ${netdev} - fi + # If ifdown fails, take the IP details from the kernel command + # line. + parse_kernel_ip + ip link set ${netdev} down + ip addr flush ${netdev} fi ip link set ${netdev} name ${pdev} ip link set ${vdev} name ${netdev} @@ -253,20 +275,8 @@ using loopback.nloopbacks= on the domain 0 kernel command line. add_to_bridge ${bridge} ${pdev} ip link set ${bridge} up ip link set ${vif0} up - ip link set ${pdev} up - if ! ifup ${netdev} ; then - if [ ${kip} ] ; then - # use the addresses we grocked from /proc/cmdline - if [ -z "${kmask}" ]; then - PREFIX=32; - else - legacy_mask_to_prefix ${kmask}; - fi - ip addr add ${kip}/${PREFIX} dev ${netdev} - ip link set dev ${netdev} up - [ ${kgate} ] && ip route add default via ${kgate} - fi - fi + ip link set ${pdev} up + do_ifup ${netdev} else # old style without ${vdev} transfer_addrs ${netdev} ${bridge} @@ -290,7 +300,9 @@ op_stop () { ip link set dev ${vif0} down mac=`ip link show ${netdev} | grep 'link\/ether' | sed -e 's/.*ether \(..:..:..:..:..:..\).*/\1/'` transfer_addrs ${netdev} ${pdev} - ifdown ${netdev} + if ! ifdown ${netdev}; then + parse_kernel_ip + fi ip link set ${netdev} down arp off ip link set ${netdev} addr fe:ff:ff:ff:ff:ff ip link set ${pdev} down @@ -303,8 +315,7 @@ op_stop () { ip link set ${netdev} name ${vdev} ip link set ${pdev} name ${netdev} - ifup ${netdev} - + do_ifup ${netdev} else transfer_routes ${bridge} ${netdev} ip link set ${bridge} down From d2b3b7424b1a35756e8cd2d49866eb1fa9dcab9b Mon Sep 17 00:00:00 2001 From: "emellor@leeni.uk.xensource.com" Date: Thu, 8 Dec 2005 16:17:53 +0000 Subject: [PATCH 3/7] Detach Xend from terminal, courtesy of Horms . * For setsid to effectively detach a process from the terminal, the following needs to occur: fork () Return control to the shell setsid () New session with no controlling terminal fork () The session leader (parent) exits and thus the resulting child process can never regain the terminal This patch adds the second fork * The call to self.daemonize(), which now forks, is moved to run before self.tracing(), as now that it actually disconnects from the terminal, and thus the prevailing process, the trace loses the processes created in self.run(). Signed-off-by: Ewan Mellor --- tools/python/xen/xend/server/SrvDaemon.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tools/python/xen/xend/server/SrvDaemon.py b/tools/python/xen/xend/server/SrvDaemon.py index 499da92f22..ff0a4ca9be 100644 --- a/tools/python/xen/xend/server/SrvDaemon.py +++ b/tools/python/xen/xend/server/SrvDaemon.py @@ -123,9 +123,18 @@ class Daemon: def daemonize(self): if not XEND_DAEMONIZE: return + # Detach from TTY. + + # Become the group leader (already a child process) os.setsid() + # Fork, this allows the group leader to exit, + # which means the child can never again regain control of the + # terminal + if self.fork_pid(XEND_PID_FILE): + self.exit() + # Detach from standard file descriptors, and redirect them to # /dev/null or the log as appropriate. os.close(0) @@ -164,7 +173,7 @@ class Daemon: # we can avoid a race condition during startup r,w = os.pipe() - if self.fork_pid(XEND_PID_FILE): + if os.fork(): os.close(w) r = os.fdopen(r, 'r') try: @@ -178,6 +187,7 @@ class Daemon: else: os.close(r) # Child + self.daemonize() self.tracing(trace) self.run(os.fdopen(w, 'w')) @@ -274,7 +284,6 @@ class Daemon: relocate.listenRelocation() servers = SrvServer.create() - self.daemonize() servers.start(status) except Exception, ex: print >>sys.stderr, 'Exception starting xend:', ex From 9ab74b250b00062ca051a03d4df0cd7b549e6585 Mon Sep 17 00:00:00 2001 From: "emellor@leeni.uk.xensource.com" Date: Fri, 9 Dec 2005 10:49:29 +0000 Subject: [PATCH 4/7] Add one more fork to the Xend start-up process, this time with the parent staying alive to watch the child. If the child crashes, then the parent starts another one in its place. This provides robustness in the face of crashes like one currently tracked as bug #411. Tidy up chunks of SrvDaemon.py on the way past. Signed-off-by: Ewan Mellor --- tools/python/xen/xend/server/SrvDaemon.py | 165 +++++++++++++--------- tools/python/xen/xend/server/SrvServer.py | 8 +- 2 files changed, 102 insertions(+), 71 deletions(-) diff --git a/tools/python/xen/xend/server/SrvDaemon.py b/tools/python/xen/xend/server/SrvDaemon.py index ff0a4ca9be..29e4c21ef0 100644 --- a/tools/python/xen/xend/server/SrvDaemon.py +++ b/tools/python/xen/xend/server/SrvDaemon.py @@ -23,73 +23,37 @@ import relocate from params import * +XEND_PROCESS_NAME = 'xend' + + class Daemon: """The xend daemon. """ def __init__(self): - self.shutdown = 0 - self.traceon = 0 + self.traceon = False self.tracefile = None self.traceindent = 0 self.child = 0 - - def read_pid(self, pidfile): - """Read process id from a file. - @param pidfile: file to read - @return pid or 0 - """ - if os.path.isfile(pidfile) and os.path.getsize(pidfile): - try: - f = open(pidfile, 'r') - try: - return int(f.read()) - finally: - f.close() - except: - return 0 - else: - return 0 - def find_process(self, pid, name): - """Search for a process. - - @param pid: process id - @param name: process name - @return: pid if found, 0 otherwise - """ - running = 0 - if pid: - lines = os.popen('ps %d 2>/dev/null' % pid).readlines() - exp = '^ *%d.+%s' % (pid, name) - for line in lines: - if re.search(exp, line): - running = pid - break - return running - - def cleanup_process(self, pidfile, name, kill): - """Clean up the pidfile for a process. + def cleanup_xend(self, kill): + """Clean up the Xend pidfile. If a running process is found, kills it if 'kill' is true. - @param pidfile: pid file - @param name: process name @param kill: whether to kill the process @return running process id or 0 """ running = 0 - pid = self.read_pid(pidfile) - if self.find_process(pid, name): + pid = read_pid(XEND_PID_FILE) + if find_process(pid, XEND_PROCESS_NAME): if kill: - os.kill(pid, 1) + os.kill(pid, signal.SIGTERM) else: running = pid - if running == 0 and os.path.isfile(pidfile): - os.remove(pidfile) + if running == 0 and os.path.isfile(XEND_PID_FILE): + os.remove(XEND_PID_FILE) return running - def cleanup_xend(self, kill): - return self.cleanup_process(XEND_PID_FILE, "xend", kill) def status(self): """Returns the status of the xend daemon. @@ -97,15 +61,15 @@ class Daemon: 0 Running 3 Not running """ - if self.cleanup_process(XEND_PID_FILE, "xend", False) == 0: + if self.cleanup_xend(False) == 0: return 3 else: return 0 - def fork_pid(self, pidfile): - """Fork and write the pid of the child to 'pidfile'. - @param pidfile: pid file + def fork_pid(self): + """Fork and write the pid of the child to XEND_PID_FILE. + @return: pid of child in parent, 0 in child """ @@ -113,7 +77,7 @@ class Daemon: if self.child: # Parent - pidfile = open(pidfile, 'w') + pidfile = open(XEND_PID_FILE, 'w') try: pidfile.write(str(self.child)) finally: @@ -121,6 +85,7 @@ class Daemon: return self.child + def daemonize(self): if not XEND_DAEMONIZE: return @@ -132,9 +97,9 @@ class Daemon: # Fork, this allows the group leader to exit, # which means the child can never again regain control of the # terminal - if self.fork_pid(XEND_PID_FILE): - self.exit() - + if os.fork(): + os._exit(0) + # Detach from standard file descriptors, and redirect them to # /dev/null or the log as appropriate. os.close(0) @@ -189,7 +154,41 @@ class Daemon: # Child self.daemonize() self.tracing(trace) - self.run(os.fdopen(w, 'w')) + + # If Xend proper segfaults, then we want to restart it. Thus, + # we fork a child for running Xend itself, and if it segfaults + # (or exits any way other than cleanly) then we run it again. + # The first time through we want the server to write to the (r,w) + # pipe created above, so that we do not exit until the server is + # ready to receive requests. All subsequent restarts we don't + # want this behaviour, or the pipe will eventually fill up, so + # we just pass None into run in subsequent cases (by clearing w + # in the parent of the first fork). + while True: + pid = self.fork_pid() + if pid: + os.close(w) + w = False + + (_, status) = os.waitpid(pid, 0) + + if os.WIFEXITED(status): + code = os.WEXITSTATUS(status) + log.info('Xend exited with status %d.', code) + sys.exit(code) + + if os.WIFSIGNALED(status): + sig = os.WTERMSIG(status) + + if sig in (signal.SIGINT, signal.SIGTERM): + log.info('Xend stopped due to signal %d.', sig) + sys.exit(0) + else: + log.fatal( + 'Xend died due to signal %d! Restarting it.', + sig) + else: + self.run(w and os.fdopen(w, 'w') or None) return ret @@ -290,18 +289,11 @@ class Daemon: if XEND_DEBUG: traceback.print_exc() log.exception("Exception starting xend (%s)" % ex) - status.write('1') - status.close() - self.exit(1) + if status: + status.write('1') + status.close() + sys.exit(1) - def exit(self, rc=0): - # Calling sys.exit() raises a SystemExit exception, which only - # kills the current thread. Calling os._exit() makes the whole - # Python process exit immediately. There doesn't seem to be another - # way to exit a Python with running threads. - #sys.exit(rc) - os._exit(rc) - def instance(): global inst try: @@ -311,10 +303,47 @@ def instance(): return inst +def read_pid(pidfile): + """Read process id from a file. + + @param pidfile: file to read + @return pid or 0 + """ + if os.path.isfile(pidfile) and os.path.getsize(pidfile): + try: + f = open(pidfile, 'r') + try: + return int(f.read()) + finally: + f.close() + except: + return 0 + else: + return 0 + + +def find_process(pid, name): + """Search for a process. + + @param pid: process id + @param name: process name + @return: pid if found, 0 otherwise + """ + running = 0 + if pid: + lines = os.popen('ps %d 2>/dev/null' % pid).readlines() + exp = '^ *%d.+%s' % (pid, name) + for line in lines: + if re.search(exp, line): + running = pid + break + return running + + def main(argv = None): global XEND_DAEMONIZE - XEND_DAEMONIZE = 0 + XEND_DAEMONIZE = False if argv is None: argv = sys.argv diff --git a/tools/python/xen/xend/server/SrvServer.py b/tools/python/xen/xend/server/SrvServer.py index 576de6be16..38a06be9e3 100644 --- a/tools/python/xen/xend/server/SrvServer.py +++ b/tools/python/xen/xend/server/SrvServer.py @@ -68,7 +68,8 @@ class XendServers: # Running the network script will spawn another process, which takes # the status fd with it unless we set FD_CLOEXEC. Failing to do this # causes the read in SrvDaemon to hang even when we have written here. - fcntl.fcntl(status, fcntl.F_SETFD, fcntl.FD_CLOEXEC) + if status: + fcntl.fcntl(status, fcntl.F_SETFD, fcntl.FD_CLOEXEC) Vifctl.network('start') threads = [] @@ -93,8 +94,9 @@ class XendServers: if threads_left: time.sleep(.5) - status.write('0') - status.close() + if status: + status.write('0') + status.close() for t in threads: t.join() From c542b41febc017715cb47368a644d3eef30061e7 Mon Sep 17 00:00:00 2001 From: "emellor@leeni.uk.xensource.com" Date: Fri, 9 Dec 2005 10:50:26 +0000 Subject: [PATCH 5/7] Added do_without_error function, and squelch the error from mkdir when trying to create the lock directory. Signed-off-by: Ewan Mellor --- tools/examples/xen-hotplug-common.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/examples/xen-hotplug-common.sh b/tools/examples/xen-hotplug-common.sh index 4c2c836953..ce9a9f6e1a 100644 --- a/tools/examples/xen-hotplug-common.sh +++ b/tools/examples/xen-hotplug-common.sh @@ -46,6 +46,10 @@ do_or_die() { "$@" || fatal "$@ failed" } +do_without_error() { + "$@" 2>/dev/null || log debug "$@ failed" +} + sigerr() { fatal "$0 failed; error detected." } @@ -121,7 +125,7 @@ _claim_lock() while [ $retries -lt $LOCK_RETRIES ] do - mkdir "$lockdir" && trap "release_lock $1; sigerr" ERR && + mkdir "$lockdir" 2>/dev/null && trap "release_lock $1; sigerr" ERR && _update_lock_info "$lockdir" && return local new_owner=$(_lock_owner "$lockdir") From c9392b836da218d0d32f4133415c0a34604b4352 Mon Sep 17 00:00:00 2001 From: "emellor@leeni.uk.xensource.com" Date: Fri, 9 Dec 2005 10:50:49 +0000 Subject: [PATCH 6/7] Use new do_without_error function to squelch the errors when closing down an interface. Signed-off-by: Ewan Mellor --- tools/examples/vif-bridge | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/examples/vif-bridge b/tools/examples/vif-bridge index e816c8146a..27e2b23a3a 100755 --- a/tools/examples/vif-bridge +++ b/tools/examples/vif-bridge @@ -61,9 +61,8 @@ case "$command" in ;; offline) - brctl delif "$bridge" "$vif" || - log debug "brctl delif $bridge $vif failed" - ifconfig "$vif" down || log debug "ifconfig $vif down failed" + do_without_error brctl delif "$bridge" "$vif" + do_without_error ifconfig "$vif" down ;; esac From 3732520993e8e5e24e3d55f3a6486c5c872611fc Mon Sep 17 00:00:00 2001 From: "emellor@leeni.uk.xensource.com" Date: Fri, 9 Dec 2005 10:51:20 +0000 Subject: [PATCH 7/7] Use new do_without_error function to squelch the error when closing down the interface. Signed-off-by: Ewan Mellor --- tools/examples/vif-nat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/examples/vif-nat b/tools/examples/vif-nat index 896f5961d1..9ce79f0434 100644 --- a/tools/examples/vif-nat +++ b/tools/examples/vif-nat @@ -155,7 +155,7 @@ case "$command" in ;; offline) [ "$dhcp" != 'no' ] && dhcp_down - ifconfig "$vif" down || true + do_without_error ifconfig "$vif" down ;; esac