# HG changeset patch
# User John Levon <john.levon@xxxxxxx>
# Date 1236822270 25200
# Node ID 93d25b0df1be0abc503ff721e291b6a1553f4da2
# Parent 403118d22a30b83b863609ae4e0a7f694e612e42
Fix qemu spawn for Solaris
On Solaris, xend runs in a 'process contract' such that all children are
killed when the service is restarted. Spawn qemu processes in a new
contract to avoid this.
Signed-off-by: John Levon <john.levon@xxxxxxx>
diff --git a/tools/python/setup.py b/tools/python/setup.py
--- a/tools/python/setup.py
+++ b/tools/python/setup.py
@@ -38,6 +38,13 @@ scf = Extension("scf",
libraries = libraries,
sources = [ "xen/lowlevel/scf/scf.c" ])
+process = Extension("process",
+ extra_compile_args = extra_compile_args,
+ include_dirs = include_dirs + [ "xen/lowlevel/process" ],
+ library_dirs = library_dirs,
+ libraries = libraries + [ "contract" ],
+ sources = [ "xen/lowlevel/process/process.c" ])
+
acm = Extension("acm",
extra_compile_args = extra_compile_args,
include_dirs = include_dirs + [ "xen/lowlevel/acm" ],
@@ -63,6 +70,7 @@ modules = [ xc, xs, ptsname, acm, flask
modules = [ xc, xs, ptsname, acm, flask ]
if os.uname()[0] == 'SunOS':
modules.append(scf)
+ modules.append(process)
setup(name = 'xen',
version = '3.0',
diff --git a/tools/python/xen/lowlevel/process/process.c
b/tools/python/xen/lowlevel/process/process.c
new file mode 100644
--- /dev/null
+++ b/tools/python/xen/lowlevel/process/process.c
@@ -0,0 +1,164 @@
+/*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <Python.h>
+
+#include <libcontract.h>
+#include <sys/contract/process.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+/*
+ * On Solaris, xend runs under a contract as an smf(5) service. As a
+ * result, when spawning long-running children such as a domain's
+ * qemu-dm instantiation, we have to make sure it's in a separate
+ * contract. Before we fork, we must activate a separate process
+ * contract template to place the child processes in a new contract.
+ */
+
+static PyObject *
+pyprocess_activate(PyObject *o, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "name", NULL };
+ char *name = NULL;
+ int flags;
+ int cfd;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", kwlist, &name))
+ return (NULL);
+
+ cfd = open64("/system/contract/process/template", O_RDWR);
+
+ if (cfd == -1)
+ goto err;
+
+ if ((flags = fcntl(cfd, F_GETFD, 0)) == -1)
+ goto err;
+
+ if (fcntl(cfd, F_SETFD, flags | FD_CLOEXEC) == -1)
+ goto err;
+
+ if (name != NULL)
+ ct_pr_tmpl_set_svc_aux(cfd, name);
+
+ if (ct_tmpl_activate(cfd))
+ goto err;
+
+ return (PyInt_FromLong((long)cfd));
+
+err:
+ if (cfd != -1)
+ close(cfd);
+ PyErr_SetFromErrno(PyExc_OSError);
+ return (NULL);
+}
+
+static PyObject *
+pyprocess_clear(PyObject *o, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "contract", NULL };
+ int cfd;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &cfd))
+ return (NULL);
+
+ if (ct_tmpl_clear(cfd) != 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return (NULL);
+ }
+
+ close(cfd);
+
+ Py_INCREF(Py_None);
+ return (Py_None);
+}
+
+static PyObject *
+pyprocess_abandon_latest(PyObject *o, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { NULL };
+ static char path[PATH_MAX];
+ ct_stathdl_t st;
+ ctid_t latest;
+ int cfd;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
+ return (NULL);
+
+ cfd = open64("/system/contract/process/latest", O_RDONLY);
+ if (cfd == -1)
+ goto err;
+
+ ct_status_read(cfd, CTD_COMMON, &st);
+ latest = ct_status_get_id(st);
+ ct_status_free(st);
+ close(cfd);
+
+ snprintf(path, PATH_MAX, "/system/contract/process/%ld/ctl",
+ (long)latest);
+
+ if ((cfd = open64(path, O_WRONLY)) < 0)
+ goto err;
+ if (ct_ctl_abandon(cfd))
+ goto err;
+ close(cfd);
+
+ Py_INCREF(Py_None);
+ return (Py_None);
+err:
+ PyErr_SetFromErrno(PyExc_OSError);
+ return (NULL);
+}
+
+PyDoc_STRVAR(pyprocess_activate__doc__,
+ "activate(name)\n"
+ "\n"
+ "Activate a new process contract template. If name is given,\n"
+ "it is used as the template's auxiliary value.\n"
+ "Returns the new contract template.\n");
+
+PyDoc_STRVAR(pyprocess_clear__doc__,
+ "clear(contract)\n"
+ "\n"
+ "Clear and close the given contract template.\n");
+
+PyDoc_STRVAR(pyprocess_abandon_latest__doc__,
+ "abandon_latest()\n"
+ "\n"
+ "Abandon the latest contract created by this thread.\n");
+
+static struct PyMethodDef pyprocess_module_methods[] = {
+ { "activate", (PyCFunction) pyprocess_activate,
+ METH_VARARGS|METH_KEYWORDS, pyprocess_activate__doc__ },
+ { "clear", (PyCFunction) pyprocess_clear,
+ METH_VARARGS|METH_KEYWORDS, pyprocess_clear__doc__ },
+ { "abandon_latest", (PyCFunction) pyprocess_abandon_latest,
+ METH_VARARGS|METH_KEYWORDS, pyprocess_abandon_latest__doc__ },
+ { NULL, NULL, 0, NULL }
+};
+
+PyMODINIT_FUNC
+initprocess(void)
+{
+ Py_InitModule("process", pyprocess_module_methods);
+}
diff --git a/tools/python/xen/xend/image.py b/tools/python/xen/xend/image.py
--- a/tools/python/xen/xend/image.py
+++ b/tools/python/xen/xend/image.py
@@ -40,6 +40,7 @@ from xen.xend import XendOptions
from xen.xend import XendOptions
from xen.util import oshelp
from xen.util import utils
+from xen.xend import osdep
xc = xen.lowlevel.xc.xc()
@@ -407,9 +408,12 @@ class ImageHandler:
logfd = os.open(self.logfile, logfile_mode)
sys.stderr.flush()
+ contract = osdep.prefork("%s:%d" %
+ (self.vm.getName(), self.vm.getDomid()))
pid = os.fork()
if pid == 0: #child
try:
+ osdep.postfork(contract)
os.dup2(null, 0)
os.dup2(logfd, 1)
os.dup2(logfd, 2)
@@ -426,6 +430,7 @@ class ImageHandler:
except:
os._exit(127)
else:
+ osdep.postfork(contract, abandon=True)
self.pid = pid
os.close(null)
os.close(logfd)
diff --git a/tools/python/xen/xend/osdep.py b/tools/python/xen/xend/osdep.py
--- a/tools/python/xen/xend/osdep.py
+++ b/tools/python/xen/xend/osdep.py
@@ -290,6 +290,32 @@ _boot_dev = {
"SunOS": _solaris_boot_dev
}
+def _default_prefork(name):
+ pass
+
+def _default_postfork(ct, abandon=False):
+ pass
+
+# call this for long-running processes that should survive a xend
+# restart
+def _solaris_prefork(name):
+ from xen.lowlevel import process
+ return process.activate(name)
+
+def _solaris_postfork(ct, abandon=False):
+ from xen.lowlevel import process
+ process.clear(ct)
+ if abandon:
+ process.abandon_latest()
+
+_get_prefork = {
+ "SunOS": _solaris_prefork
+}
+
+_get_postfork = {
+ "SunOS": _solaris_postfork
+}
+
def _get(var, default=None):
return var.get(os.uname()[0], default)
@@ -304,3 +330,5 @@ connection_allowed = _get(_connection_al
connection_allowed = _get(_connection_allowed, _default_connection_allowed)
get_boot_dev = _get(_boot_dev, _default_boot_dev)
do_broadcast = _get(_do_broadcast)
+prefork = _get(_get_prefork, _default_prefork)
+postfork = _get(_get_postfork, _default_postfork)
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|