[libvirt] [PATCH 0/9] Add ability to connect to LXC namespaces

This series introduces an LXC specific library libvirt-lxc.so which adds ability for a process to connect to the namespaces used by an LXC container from outside. It uses FD passing magic to allow the caller to connect, even if it is not root.

From: "Daniel P. Berrange" <berrange@redhat.com> The QEMU specific APIs all operate on domains, not the host, so should be in the virsh-domain.c file / group Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- tools/virsh-domain.c | 219 +++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh-host.c | 218 -------------------------------------------------- 2 files changed, 219 insertions(+), 218 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index f3da1d5..e91939c 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -45,6 +45,7 @@ #include "viralloc.h" #include "virutil.h" #include "virfile.h" +#include "virjson.h" #include "virkeycode.h" #include "virmacaddr.h" #include "virstring.h" @@ -6557,6 +6558,219 @@ cmdNumatune(vshControl * ctl, const vshCmd * cmd) } /* + * "qemu-monitor-command" command + */ +static const vshCmdInfo info_qemu_monitor_command[] = { + {"help", N_("QEMU Monitor Command")}, + {"desc", N_("QEMU Monitor Command")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_qemu_monitor_command[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"hmp", VSH_OT_BOOL, 0, N_("command is in human monitor protocol")}, + {"pretty", VSH_OT_BOOL, 0, + N_("pretty-print any qemu monitor protocol output")}, + {"cmd", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("command")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdQemuMonitorCommand(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + bool ret = false; + char *monitor_cmd = NULL; + char *result = NULL; + unsigned int flags = 0; + const vshCmdOpt *opt = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; + bool pad = false; + virJSONValuePtr pretty = NULL; + + dom = vshCommandOptDomain(ctl, cmd, NULL); + if (dom == NULL) + goto cleanup; + + while ((opt = vshCommandOptArgv(cmd, opt))) { + if (pad) + virBufferAddChar(&buf, ' '); + pad = true; + virBufferAdd(&buf, opt->data, -1); + } + if (virBufferError(&buf)) { + vshPrint(ctl, "%s", _("Failed to collect command")); + goto cleanup; + } + monitor_cmd = virBufferContentAndReset(&buf); + + if (vshCommandOptBool(cmd, "hmp")) { + if (vshCommandOptBool(cmd, "pretty")) { + vshError(ctl, _("--hmp and --pretty are not compatible")); + goto cleanup; + } + flags |= VIR_DOMAIN_QEMU_MONITOR_COMMAND_HMP; + } + + if (virDomainQemuMonitorCommand(dom, monitor_cmd, &result, flags) < 0) + goto cleanup; + + if (vshCommandOptBool(cmd, "pretty")) { + char *tmp; + pretty = virJSONValueFromString(result); + if (pretty && (tmp = virJSONValueToString(pretty, true))) { + VIR_FREE(result); + result = tmp; + } else { + vshResetLibvirtError(); + } + } + vshPrint(ctl, "%s\n", result); + + ret = true; + +cleanup: + VIR_FREE(result); + VIR_FREE(monitor_cmd); + virJSONValueFree(pretty); + if (dom) + virDomainFree(dom); + + return ret; +} + +/* + * "qemu-attach" command + */ +static const vshCmdInfo info_qemu_attach[] = { + {"help", N_("QEMU Attach")}, + {"desc", N_("QEMU Attach")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_qemu_attach[] = { + {"pid", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pid")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdQemuAttach(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + bool ret = false; + unsigned int flags = 0; + unsigned int pid_value; /* API uses unsigned int, not pid_t */ + + if (vshCommandOptUInt(cmd, "pid", &pid_value) <= 0) { + vshError(ctl, "%s", _("missing pid value")); + goto cleanup; + } + + if (!(dom = virDomainQemuAttach(ctl->conn, pid_value, flags))) + goto cleanup; + + if (dom != NULL) { + vshPrint(ctl, _("Domain %s attached to pid %u\n"), + virDomainGetName(dom), pid_value); + virDomainFree(dom); + ret = true; + } else { + vshError(ctl, _("Failed to attach to pid %u"), pid_value); + } + +cleanup: + return ret; +} + +/* + * "qemu-agent-command" command + */ +static const vshCmdInfo info_qemu_agent_command[] = { + {"help", N_("QEMU Guest Agent Command")}, + {"desc", N_("Run an arbitrary qemu guest agent command; use at your own risk")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_qemu_agent_command[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"timeout", VSH_OT_INT, VSH_OFLAG_REQ_OPT, N_("timeout seconds. must be positive.")}, + {"async", VSH_OT_BOOL, 0, N_("execute command without waiting for timeout")}, + {"block", VSH_OT_BOOL, 0, N_("execute command without timeout")}, + {"cmd", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("command")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdQemuAgentCommand(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + bool ret = false; + char *guest_agent_cmd = NULL; + char *result = NULL; + int timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT; + int judge = 0; + unsigned int flags = 0; + const vshCmdOpt *opt = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; + bool pad = false; + + dom = vshCommandOptDomain(ctl, cmd, NULL); + if (dom == NULL) + goto cleanup; + + while ((opt = vshCommandOptArgv(cmd, opt))) { + if (pad) + virBufferAddChar(&buf, ' '); + pad = true; + virBufferAdd(&buf, opt->data, -1); + } + if (virBufferError(&buf)) { + vshPrint(ctl, "%s", _("Failed to collect command")); + goto cleanup; + } + guest_agent_cmd = virBufferContentAndReset(&buf); + + judge = vshCommandOptInt(cmd, "timeout", &timeout); + if (judge < 0) { + vshError(ctl, "%s", _("timeout number has to be a number")); + goto cleanup; + } else if (judge > 0) { + judge = 1; + } + if (judge && timeout < 1) { + vshError(ctl, "%s", _("timeout must be positive")); + goto cleanup; + } + + if (vshCommandOptBool(cmd, "async")) { + timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT; + judge++; + } + if (vshCommandOptBool(cmd, "block")) { + timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK; + judge++; + } + + if (judge > 1) { + vshError(ctl, "%s", _("timeout, async and block options are exclusive")); + goto cleanup; + } + result = virDomainQemuAgentCommand(dom, guest_agent_cmd, timeout, flags); + + if (result) printf("%s\n", result); + + ret = true; + +cleanup: + VIR_FREE(result); + VIR_FREE(guest_agent_cmd); + if (dom) + virDomainFree(dom); + + return ret; +} + +/* * "dumpxml" command */ static const vshCmdInfo info_dumpxml[] = { @@ -8559,6 +8773,11 @@ const vshCmdDef domManagementCmds[] = { {"migrate-getspeed", cmdMigrateGetMaxSpeed, opts_migrate_getspeed, info_migrate_getspeed, 0}, {"numatune", cmdNumatune, opts_numatune, info_numatune, 0}, + {"qemu-attach", cmdQemuAttach, opts_qemu_attach, info_qemu_attach, 0}, + {"qemu-monitor-command", cmdQemuMonitorCommand, opts_qemu_monitor_command, + info_qemu_monitor_command, 0}, + {"qemu-agent-command", cmdQemuAgentCommand, opts_qemu_agent_command, + info_qemu_agent_command, 0}, {"reboot", cmdReboot, opts_reboot, info_reboot, 0}, {"reset", cmdReset, opts_reset, info_reset, 0}, {"restore", cmdRestore, opts_restore, info_restore, 0}, diff --git a/tools/virsh-host.c b/tools/virsh-host.c index 45b89a8..352c49a 100644 --- a/tools/virsh-host.c +++ b/tools/virsh-host.c @@ -38,7 +38,6 @@ #include "virsh-domain.h" #include "virxml.h" #include "virtypedparam.h" -#include "virjson.h" /* * "capabilities" command @@ -550,218 +549,6 @@ cmdNodeSuspend(vshControl *ctl, const vshCmd *cmd) } /* - * "qemu-monitor-command" command - */ -static const vshCmdInfo info_qemu_monitor_command[] = { - {"help", N_("QEMU Monitor Command")}, - {"desc", N_("QEMU Monitor Command")}, - {NULL, NULL} -}; - -static const vshCmdOptDef opts_qemu_monitor_command[] = { - {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, - {"hmp", VSH_OT_BOOL, 0, N_("command is in human monitor protocol")}, - {"pretty", VSH_OT_BOOL, 0, - N_("pretty-print any qemu monitor protocol output")}, - {"cmd", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("command")}, - {NULL, 0, 0, NULL} -}; - -static bool -cmdQemuMonitorCommand(vshControl *ctl, const vshCmd *cmd) -{ - virDomainPtr dom = NULL; - bool ret = false; - char *monitor_cmd = NULL; - char *result = NULL; - unsigned int flags = 0; - const vshCmdOpt *opt = NULL; - virBuffer buf = VIR_BUFFER_INITIALIZER; - bool pad = false; - virJSONValuePtr pretty = NULL; - - dom = vshCommandOptDomain(ctl, cmd, NULL); - if (dom == NULL) - goto cleanup; - - while ((opt = vshCommandOptArgv(cmd, opt))) { - if (pad) - virBufferAddChar(&buf, ' '); - pad = true; - virBufferAdd(&buf, opt->data, -1); - } - if (virBufferError(&buf)) { - vshPrint(ctl, "%s", _("Failed to collect command")); - goto cleanup; - } - monitor_cmd = virBufferContentAndReset(&buf); - - if (vshCommandOptBool(cmd, "hmp")) { - if (vshCommandOptBool(cmd, "pretty")) { - vshError(ctl, _("--hmp and --pretty are not compatible")); - goto cleanup; - } - flags |= VIR_DOMAIN_QEMU_MONITOR_COMMAND_HMP; - } - - if (virDomainQemuMonitorCommand(dom, monitor_cmd, &result, flags) < 0) - goto cleanup; - - if (vshCommandOptBool(cmd, "pretty")) { - char *tmp; - pretty = virJSONValueFromString(result); - if (pretty && (tmp = virJSONValueToString(pretty, true))) { - VIR_FREE(result); - result = tmp; - } else { - vshResetLibvirtError(); - } - } - vshPrint(ctl, "%s\n", result); - - ret = true; - -cleanup: - VIR_FREE(result); - VIR_FREE(monitor_cmd); - virJSONValueFree(pretty); - if (dom) - virDomainFree(dom); - - return ret; -} - -/* - * "qemu-attach" command - */ -static const vshCmdInfo info_qemu_attach[] = { - {"help", N_("QEMU Attach")}, - {"desc", N_("QEMU Attach")}, - {NULL, NULL} -}; - -static const vshCmdOptDef opts_qemu_attach[] = { - {"pid", VSH_OT_DATA, VSH_OFLAG_REQ, N_("pid")}, - {NULL, 0, 0, NULL} -}; - -static bool -cmdQemuAttach(vshControl *ctl, const vshCmd *cmd) -{ - virDomainPtr dom = NULL; - bool ret = false; - unsigned int flags = 0; - unsigned int pid_value; /* API uses unsigned int, not pid_t */ - - if (vshCommandOptUInt(cmd, "pid", &pid_value) <= 0) { - vshError(ctl, "%s", _("missing pid value")); - goto cleanup; - } - - if (!(dom = virDomainQemuAttach(ctl->conn, pid_value, flags))) - goto cleanup; - - if (dom != NULL) { - vshPrint(ctl, _("Domain %s attached to pid %u\n"), - virDomainGetName(dom), pid_value); - virDomainFree(dom); - ret = true; - } else { - vshError(ctl, _("Failed to attach to pid %u"), pid_value); - } - -cleanup: - return ret; -} - -/* - * "qemu-agent-command" command - */ -static const vshCmdInfo info_qemu_agent_command[] = { - {"help", N_("QEMU Guest Agent Command")}, - {"desc", N_("Run an arbitrary qemu guest agent command; use at your own risk")}, - {NULL, NULL} -}; - -static const vshCmdOptDef opts_qemu_agent_command[] = { - {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, - {"timeout", VSH_OT_INT, VSH_OFLAG_REQ_OPT, N_("timeout seconds. must be positive.")}, - {"async", VSH_OT_BOOL, 0, N_("execute command without waiting for timeout")}, - {"block", VSH_OT_BOOL, 0, N_("execute command without timeout")}, - {"cmd", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("command")}, - {NULL, 0, 0, NULL} -}; - -static bool -cmdQemuAgentCommand(vshControl *ctl, const vshCmd *cmd) -{ - virDomainPtr dom = NULL; - bool ret = false; - char *guest_agent_cmd = NULL; - char *result = NULL; - int timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT; - int judge = 0; - unsigned int flags = 0; - const vshCmdOpt *opt = NULL; - virBuffer buf = VIR_BUFFER_INITIALIZER; - bool pad = false; - - dom = vshCommandOptDomain(ctl, cmd, NULL); - if (dom == NULL) - goto cleanup; - - while ((opt = vshCommandOptArgv(cmd, opt))) { - if (pad) - virBufferAddChar(&buf, ' '); - pad = true; - virBufferAdd(&buf, opt->data, -1); - } - if (virBufferError(&buf)) { - vshPrint(ctl, "%s", _("Failed to collect command")); - goto cleanup; - } - guest_agent_cmd = virBufferContentAndReset(&buf); - - judge = vshCommandOptInt(cmd, "timeout", &timeout); - if (judge < 0) { - vshError(ctl, "%s", _("timeout number has to be a number")); - goto cleanup; - } else if (judge > 0) { - judge = 1; - } - if (judge && timeout < 1) { - vshError(ctl, "%s", _("timeout must be positive")); - goto cleanup; - } - - if (vshCommandOptBool(cmd, "async")) { - timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT; - judge++; - } - if (vshCommandOptBool(cmd, "block")) { - timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK; - judge++; - } - - if (judge > 1) { - vshError(ctl, "%s", _("timeout, async and block options are exclusive")); - goto cleanup; - } - result = virDomainQemuAgentCommand(dom, guest_agent_cmd, timeout, flags); - - if (result) printf("%s\n", result); - - ret = true; - -cleanup: - VIR_FREE(result); - VIR_FREE(guest_agent_cmd); - if (dom) - virDomainFree(dom); - - return ret; -} -/* * "sysinfo" command */ static const vshCmdInfo info_sysinfo[] = { @@ -1086,11 +873,6 @@ const vshCmdDef hostAndHypervisorCmds[] = { {"nodeinfo", cmdNodeinfo, NULL, info_nodeinfo, 0}, {"nodememstats", cmdNodeMemStats, opts_node_memstats, info_nodememstats, 0}, {"nodesuspend", cmdNodeSuspend, opts_node_suspend, info_nodesuspend, 0}, - {"qemu-attach", cmdQemuAttach, opts_qemu_attach, info_qemu_attach, 0}, - {"qemu-monitor-command", cmdQemuMonitorCommand, opts_qemu_monitor_command, - info_qemu_monitor_command, 0}, - {"qemu-agent-command", cmdQemuAgentCommand, opts_qemu_agent_command, - info_qemu_agent_command, 0}, {"sysinfo", cmdSysinfo, NULL, info_sysinfo, 0}, {"uri", cmdURI, NULL, info_uri, 0}, {"version", cmdVersion, opts_version, info_version, 0}, -- 1.7.11.7

On 12/21/2012 10:08 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
The QEMU specific APIs all operate on domains, not the host, so should be in the virsh-domain.c file / group
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- tools/virsh-domain.c | 219 +++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh-host.c | 218 -------------------------------------------------- 2 files changed, 219 insertions(+), 218 deletions(-)
ACK. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> A number of bugs handling file descriptors received from the server caused the FDs to be lost and leaked. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/rpc/virnetclient.c | 12 +++++++++--- src/rpc/virnetclientprogram.c | 10 +++++----- src/rpc/virnetmessage.c | 6 ++++++ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/rpc/virnetclient.c b/src/rpc/virnetclient.c index 208e2e9..bdceccf 100644 --- a/src/rpc/virnetclient.c +++ b/src/rpc/virnetclient.c @@ -997,6 +997,11 @@ virNetClientCallDispatchReply(virNetClientPtr client) thecall->msg->bufferLength = client->msg.bufferLength; thecall->msg->bufferOffset = client->msg.bufferOffset; + thecall->msg->nfds = client->msg.nfds; + thecall->msg->fds = client->msg.fds; + client->msg.nfds = 0; + client->msg.fds = NULL; + thecall->mode = VIR_NET_CLIENT_MODE_COMPLETE; return 0; @@ -1290,7 +1295,9 @@ virNetClientIOHandleInput(virNetClientPtr client) if (client->msg.header.type == VIR_NET_REPLY_WITH_FDS) { size_t i; - if (virNetMessageDecodeNumFDs(&client->msg) < 0) + + if (client->msg.nfds == 0 && + virNetMessageDecodeNumFDs(&client->msg) < 0) return -1; for (i = client->msg.donefds ; i < client->msg.nfds ; i++) { @@ -1313,8 +1320,7 @@ virNetClientIOHandleInput(virNetClientPtr client) } ret = virNetClientCallDispatch(client); - client->msg.bufferOffset = client->msg.bufferLength = 0; - VIR_FREE(client->msg.buffer); + virNetMessageClear(&client->msg); /* * We've completed one call, but we don't want to * spin around the loop forever if there are many diff --git a/src/rpc/virnetclientprogram.c b/src/rpc/virnetclientprogram.c index a179b8d..9410cff 100644 --- a/src/rpc/virnetclientprogram.c +++ b/src/rpc/virnetclientprogram.c @@ -362,18 +362,18 @@ int virNetClientProgramCall(virNetClientProgramPtr prog, goto error; } for (i = 0 ; i < *ninfds ; i++) - *infds[i] = -1; + (*infds)[i] = -1; for (i = 0 ; i < *ninfds ; i++) { - if ((*infds[i] = dup(msg->fds[i])) < 0) { + if (((*infds)[i] = dup(msg->fds[i])) < 0) { virReportSystemError(errno, _("Cannot duplicate FD %d"), msg->fds[i]); goto error; } - if (virSetInherit(*infds[i], false) < 0) { + if (virSetInherit((*infds)[i], false) < 0) { virReportSystemError(errno, _("Cannot set close-on-exec %d"), - *infds[i]); + (*infds)[i]); goto error; } } @@ -401,7 +401,7 @@ error: virNetMessageFree(msg); if (infds && ninfds) { for (i = 0 ; i < *ninfds ; i++) - VIR_FORCE_CLOSE(*infds[i]); + VIR_FORCE_CLOSE((*infds)[i]); } return -1; } diff --git a/src/rpc/virnetmessage.c b/src/rpc/virnetmessage.c index b7330de..647fef7 100644 --- a/src/rpc/virnetmessage.c +++ b/src/rpc/virnetmessage.c @@ -175,6 +175,12 @@ int virNetMessageDecodeHeader(virNetMessagePtr msg) XDR xdr; int ret = -1; + if (msg->bufferLength < VIR_NET_MESSAGE_LEN_MAX) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to decode header until len is received")); + return -1; + } + msg->bufferOffset = VIR_NET_MESSAGE_LEN_MAX; /* Parse the header. */ -- 1.7.11.7

On 12/21/2012 10:08 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
A number of bugs handling file descriptors received from the server caused the FDs to be lost and leaked.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/rpc/virnetclient.c | 12 +++++++++--- src/rpc/virnetclientprogram.c | 10 +++++----- src/rpc/virnetmessage.c | 6 ++++++ 3 files changed, 20 insertions(+), 8 deletions(-)
ACK. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> Currently the libvirt client can pass FDs to the server, but the dispatch mechanism provides no way to return FDs back from the server to the client. Tweak the dispatch code, such that if a dispatcher returns '1', this indicates that it populated the virNetMessagePtr with FDs to return Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/rpc/virnetserverprogram.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/rpc/virnetserverprogram.c b/src/rpc/virnetserverprogram.c index 3ac3809..06b6325 100644 --- a/src/rpc/virnetserverprogram.c +++ b/src/rpc/virnetserverprogram.c @@ -431,17 +431,20 @@ virNetServerProgramDispatchCall(virNetServerProgramPtr prog, rv = (dispatcher->func)(server, client, msg, &rerr, arg, ret); /* - * Clear out the FDs we got from the client, we don't - * want to send them back ! + * If rv == 1, this indicates the dispatch func has + * populated 'msg' with a list of FDs to return to + * the caller. + * + * Otherwise we must clear out the FDs we got from + * the client originally. * - * XXX we don't have a way to let dispatcher->func - * return any FDs. Fortunately we don't need this - * capability just yet */ - for (i = 0 ; i < msg->nfds ; i++) - VIR_FORCE_CLOSE(msg->fds[i]); - VIR_FREE(msg->fds); - msg->nfds = 0; + if (rv != 1) { + for (i = 0 ; i < msg->nfds ; i++) + VIR_FORCE_CLOSE(msg->fds[i]); + VIR_FREE(msg->fds); + msg->nfds = 0; + } xdr_free(dispatcher->arg_filter, arg); -- 1.7.11.7

On 12/21/2012 10:08 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Currently the libvirt client can pass FDs to the server, but the dispatch mechanism provides no way to return FDs back from the server to the client. Tweak the dispatch code, such that if a dispatcher returns '1', this indicates that it populated the virNetMessagePtr with FDs to return
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/rpc/virnetserverprogram.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-)
ACK. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> Add some APIs for acquiring namespace file descriptors and switching namespaces Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/libvirt_private.syms | 2 + src/util/virprocess.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++ src/util/virprocess.h | 7 +++ 3 files changed, 127 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 497d5d3..a422839 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1790,9 +1790,11 @@ virPidFileWritePath; # virprocess.h virProcessAbort; virProcessGetAffinity; +virProcessGetNamespaces; virProcessKill; virProcessKillPainfully; virProcessSetAffinity; +virProcessSetNamespaces; virProcessTranslateStatus; virProcessWait; diff --git a/src/util/virprocess.c b/src/util/virprocess.c index 0858553..b6da47a 100644 --- a/src/util/virprocess.c +++ b/src/util/virprocess.c @@ -22,6 +22,8 @@ #include <config.h> +#include <dirent.h> +#include <fcntl.h> #include <signal.h> #include <errno.h> #include <sys/wait.h> @@ -30,6 +32,7 @@ #include "virprocess.h" #include "virerror.h" #include "viralloc.h" +#include "virfile.h" #include "virlog.h" #include "virutil.h" @@ -489,3 +492,118 @@ int virProcessGetAffinity(pid_t pid ATTRIBUTE_UNUSED, return -1; } #endif /* HAVE_SCHED_GETAFFINITY */ + + +#if HAVE_SETNS +int virProcessGetNamespaces(pid_t pid, + size_t *nfdlist, + int **fdlist) +{ + int ret = -1; + DIR *dh = NULL; + struct dirent *de; + char *nsdir = NULL; + char *nsfile = NULL; + size_t i; + + *nfdlist = 0; + *fdlist = NULL; + + if (virAsprintf(&nsdir, "/proc/%llu/ns", + (unsigned long long)pid) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (!(dh = opendir(nsdir))) { + virReportSystemError(errno, + _("Cannot read directory %s"), + nsdir); + goto cleanup; + } + + while ((de = readdir(dh))) { + int fd; + if (de->d_name[0] == '.') + continue; + + if (VIR_EXPAND_N(*fdlist, *nfdlist, 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (virAsprintf(&nsfile, "%s/%s", nsdir, de->d_name) < 0) { + virReportOOMError(); + goto cleanup; + } + + if ((fd = open(nsfile, O_RDWR)) < 0) { + virReportSystemError(errno, + _("Unable to open %s"), + nsfile); + goto cleanup; + } + + (*fdlist)[(*nfdlist)-1] = fd; + + VIR_FREE(nsfile); + } + + ret = 0; + +cleanup: + if (dh) + closedir(dh); + VIR_FREE(nsdir); + VIR_FREE(nsfile); + if (ret < 0) { + for (i = 0 ; i < *nfdlist ; i++) { + VIR_FORCE_CLOSE((*fdlist)[i]); + } + VIR_FREE(*fdlist); + } + return ret; +} + + +int virProcessSetNamespaces(size_t nfdlist, + int *fdlist) +{ + size_t i; + + if (nfdlist == 0) { + virReportInvalidArg(nfdlist, "%s", + _("Expected at least one file descriptor")); + return -1; + } + for (i = 0 ; i < nfdlist ; i++) { + if (setns(fdlist[i], 0) < 0) { + virReportSystemError(errno, "%s", + _("Unable to join domain namespace")); + return -1; + } + } + return 0; +} +#else /* ! HAVE_SETNS */ +int virProcessGetNamespaces(pid_t pid, + size_t *nfdlist ATTRIBUTE_UNUSED, + int **fdlist ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, + _("Cannot get namespaces for %llu"), + (unsigned long long)pid); + return -1; +} + + +int virProcessSetNamespaces(pid_t pid, + size_t *nfdlist ATTRIBUTE_UNUSED, + int **fdlist ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, + _("Cannot set namespaces for %llu"), + (unsigned long long)pid); + return -1; +} +#endif /* ! HAVE_SETNS */ diff --git a/src/util/virprocess.h b/src/util/virprocess.h index 8724f64..53475d3 100644 --- a/src/util/virprocess.h +++ b/src/util/virprocess.h @@ -47,4 +47,11 @@ int virProcessGetAffinity(pid_t pid, virBitmapPtr *map, int maxcpu); +int virProcessGetNamespaces(pid_t pid, + size_t *nfdlist, + int **fdlist); + +int virProcessSetNamespaces(size_t nfdlist, + int *fdlist); + #endif /* __VIR_PROCESS_H__ */ -- 1.7.11.7

On 12/21/2012 10:08 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Add some APIs for acquiring namespace file descriptors and switching namespaces
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/libvirt_private.syms | 2 + src/util/virprocess.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++ src/util/virprocess.h | 7 +++ 3 files changed, 127 insertions(+)
ACK. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> To avoid confusion between the LXC driver <-> controller monitor RPC protocol and the libvirt-lxc.so <-> libvirtd public RPC protocol, rename the former to lxc_monitor_protocol.x Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- .gitignore | 4 ++-- src/Makefile.am | 22 +++++++++++----------- src/lxc/lxc_controller.c | 2 +- src/lxc/lxc_monitor.c | 1 - src/lxc/lxc_monitor.h | 2 +- src/lxc/{lxc_protocol.x => lxc_monitor_protocol.x} | 0 6 files changed, 15 insertions(+), 16 deletions(-) rename src/lxc/{lxc_protocol.x => lxc_monitor_protocol.x} (100%) diff --git a/.gitignore b/.gitignore index 0557d59..882ae4c 100644 --- a/.gitignore +++ b/.gitignore @@ -115,8 +115,8 @@ /src/locking/test_libvirt_sanlock.aug /src/lxc/lxc_controller_dispatch.h /src/lxc/lxc_monitor_dispatch.h -/src/lxc/lxc_protocol.c -/src/lxc/lxc_protocol.h +/src/lxc/lxc_monitor_protocol.c +/src/lxc/lxc_monitor_protocol.h /src/lxc/test_libvirtd_lxc.aug /src/qemu/test_libvirtd_qemu.aug /src/remote/*_client_bodies.h diff --git a/src/Makefile.am b/src/Makefile.am index f7a9b91..494c184 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -413,9 +413,9 @@ if WITH_XEN_INOTIFY XEN_DRIVER_SOURCES += xen/xen_inotify.c xen/xen_inotify.h endif -LXC_PROTOCOL_GENERATED = \ - $(srcdir)/lxc/lxc_protocol.h \ - $(srcdir)/lxc/lxc_protocol.c \ +LXC_MONITOR_PROTOCOL_GENERATED = \ + $(srcdir)/lxc/lxc_monitor_protocol.h \ + $(srcdir)/lxc/lxc_monitor_protocol.c \ $(NULL) LXC_MONITOR_GENERATED = \ @@ -427,32 +427,32 @@ LXC_CONTROLLER_GENERATED = \ $(NULL) LXC_GENERATED = \ - $(LXC_PROTOCOL_GENERATED) \ + $(LXC_MONITOR_PROTOCOL_GENERATED) \ $(LXC_MONITOR_GENERATED) \ $(LXC_CONTROLLER_GENERATED) \ $(NULL) -LXC_PROTOCOL = $(srcdir)/lxc/lxc_protocol.x +LXC_MONITOR_PROTOCOL = $(srcdir)/lxc/lxc_monitor_protocol.x $(srcdir)/lxc/lxc_monitor_dispatch.h: $(srcdir)/rpc/gendispatch.pl \ - $(LXC_PROTOCOL) + $(LXC_MONITOR_PROTOCOL) $(AM_V_GEN)$(PERL) -w $(srcdir)/rpc/gendispatch.pl \ - -k virLXCProtocol VIR_LXC_PROTOCOL $(LXC_PROTOCOL) > $@ + -k virLXCProtocol VIR_LXC_MONITOR_PROTOCOL $(LXC_MONITOR_PROTOCOL) > $@ $(srcdir)/lxc/lxc_controller_dispatch.h: $(srcdir)/rpc/gendispatch.pl \ $(REMOTE_PROTOCOL) $(AM_V_GEN)$(PERL) -w $(srcdir)/rpc/gendispatch.pl \ - -b virLXCProtocol VIR_LXC_PROTOCOL $(LXC_PROTOCOL) > $@ + -b virLXCProtocol VIR_LXC_MONITOR_PROTOCOL $(LXC_MONITOR_PROTOCOL) > $@ EXTRA_DIST += \ - $(LXC_PROTOCOL) \ + $(LXC_MONITOR_PROTOCOL) \ $(LXC_GENERATED) \ $(NULL) BUILT_SOURCES += $(LXC_GENERATED) LXC_DRIVER_SOURCES = \ - $(LXC_PROTOCOL_GENERATED) \ + $(LXC_MONITOR_PROTOCOL_GENERATED) \ $(LXC_MONITOR_GENERATED) \ lxc/lxc_conf.c lxc/lxc_conf.h \ lxc/lxc_container.c lxc/lxc_container.h \ @@ -465,7 +465,7 @@ LXC_DRIVER_SOURCES = \ lxc/lxc_driver.c lxc/lxc_driver.h LXC_CONTROLLER_SOURCES = \ - $(LXC_PROTOCOL_GENERATED) \ + $(LXC_MONITOR_PROTOCOL_GENERATED) \ $(LXC_CONTROLLER_GENERATED) \ lxc/lxc_conf.c lxc/lxc_conf.h \ lxc/lxc_container.c lxc/lxc_container.h \ diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c index c9d96b3..5617a54 100644 --- a/src/lxc/lxc_controller.c +++ b/src/lxc/lxc_controller.c @@ -58,7 +58,7 @@ #include "lxc_conf.h" #include "lxc_container.h" #include "lxc_cgroup.h" -#include "lxc_protocol.h" +#include "lxc_monitor_protocol.h" #include "lxc_fuse.h" #include "virnetdev.h" #include "virnetdevveth.h" diff --git a/src/lxc/lxc_monitor.c b/src/lxc/lxc_monitor.c index 65194a5..6971bcb 100644 --- a/src/lxc/lxc_monitor.c +++ b/src/lxc/lxc_monitor.c @@ -22,7 +22,6 @@ #include "lxc_monitor.h" #include "lxc_conf.h" -#include "lxc_protocol.h" #include "lxc_monitor_dispatch.h" #include "viralloc.h" diff --git a/src/lxc/lxc_monitor.h b/src/lxc/lxc_monitor.h index 9b06a37..cc31a9c 100644 --- a/src/lxc/lxc_monitor.h +++ b/src/lxc/lxc_monitor.h @@ -23,7 +23,7 @@ # include "virobject.h" # include "domain_conf.h" -# include "lxc_protocol.h" +# include "lxc_monitor_protocol.h" typedef struct _virLXCMonitor virLXCMonitor; typedef virLXCMonitor *virLXCMonitorPtr; diff --git a/src/lxc/lxc_protocol.x b/src/lxc/lxc_monitor_protocol.x similarity index 100% rename from src/lxc/lxc_protocol.x rename to src/lxc/lxc_monitor_protocol.x -- 1.7.11.7

On 12/21/2012 10:08 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
To avoid confusion between the LXC driver <-> controller monitor RPC protocol and the libvirt-lxc.so <-> libvirtd public RPC protocol, rename the former to lxc_monitor_protocol.x
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- .gitignore | 4 ++-- src/Makefile.am | 22 +++++++++++----------- src/lxc/lxc_controller.c | 2 +- src/lxc/lxc_monitor.c | 1 - src/lxc/lxc_monitor.h | 2 +- src/lxc/{lxc_protocol.x => lxc_monitor_protocol.x} | 0 6 files changed, 15 insertions(+), 16 deletions(-) rename src/lxc/{lxc_protocol.x => lxc_monitor_protocol.x} (100%)
Gotta love git rename detection :) [Hmm, we ought to mention that setting in HACKING, as it is not a default git setting but one that we ask people to use. But that's unrelated to this patch.] ACK. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> This patch introduces support for LXC specific public APIs. In common with what was done for QEMU, this creates a libvirt_lxc.so library and libvirt/libvirt-lxc.h header file. The actual APIs are int virDomainLxcOpenNamespace(virDomainPtr domain, int **fdlist, unsigned int flags); int virDomainLxcEnterNamespace(virDomainPtr domain, unsigned int flags); which provide a way to use the setns() system call to move the calling process into the container's namespace. This is not practical to write in a generically applicable manner. The nearest that we could get to such an API would be an API which allows to pass a command + argv to be executed inside a container. Even if we had such a generic API, this LXC specific API is still useful, because it allows the caller to maintain the current process context, in particular any I/O streams they have open. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- .gitignore | 4 + cfg.mk | 3 +- configure.ac | 5 +- daemon/remote.c | 1 + docs/Makefile.am | 21 ++++- docs/apibuild.py | 10 ++- docs/hvsupport.pl | 40 +++++++++ include/libvirt/Makefile.am | 1 + include/libvirt/libvirt-lxc.h | 50 +++++++++++ libvirt.spec.in | 1 + mingw-libvirt.spec.in | 10 +++ python/Makefile.am | 35 ++++++-- python/generator.py | 172 +++++++++++++++++++++++++++++++++++- python/libvirt-lxc-override-api.xml | 19 ++++ python/libvirt-lxc-override.c | 141 +++++++++++++++++++++++++++++ src/Makefile.am | 18 +++- src/driver.h | 6 ++ src/internal.h | 1 + src/libvirt-lxc.c | 165 ++++++++++++++++++++++++++++++++++ src/libvirt_lxc.syms | 17 ++++ src/lxc/lxc_driver.c | 1 + tools/virsh.c | 1 + 22 files changed, 708 insertions(+), 14 deletions(-) create mode 100644 include/libvirt/libvirt-lxc.h create mode 100644 python/libvirt-lxc-override-api.xml create mode 100644 python/libvirt-lxc-override.c create mode 100644 src/libvirt-lxc.c create mode 100644 src/libvirt_lxc.syms diff --git a/.gitignore b/.gitignore index 882ae4c..579b324 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,7 @@ /docs/hvsupport.html.in /docs/libvirt-api.xml /docs/libvirt-qemu-*.xml +/docs/libvirt-lxc-*.xml /docs/libvirt-refs.xml /docs/search.php /docs/todo.html.in @@ -92,10 +93,13 @@ /python/generated.stamp /python/generator.py.stamp /python/libvirt-export.c +/python/libvirt-lxc-export.c +/python/libvirt-lxc.[ch] /python/libvirt-qemu-export.c /python/libvirt-qemu.[ch] /python/libvirt.[ch] /python/libvirt.py +/python/libvirt_lxc.py /python/libvirt_qemu.py /run /sc_* diff --git a/cfg.mk b/cfg.mk index f9270b0..a98b3f1 100644 --- a/cfg.mk +++ b/cfg.mk @@ -302,6 +302,7 @@ sc_flags_usage: @test "$$(cat $(srcdir)/include/libvirt/libvirt.h.in \ $(srcdir)/include/libvirt/virterror.h \ $(srcdir)/include/libvirt/libvirt-qemu.h \ + $(srcdir)/include/libvirt/libvirt-lxc.h \ | grep -c '\(long\|unsigned\) flags')" != 4 && \ { echo '$(ME): new API should use "unsigned int flags"' 1>&2; \ exit 1; } || : @@ -767,7 +768,7 @@ exclude_file_name_regexp--sc_prohibit_VIR_ERR_NO_MEMORY = \ exclude_file_name_regexp--sc_prohibit_access_xok = ^src/util/virutil\.c$$ exclude_file_name_regexp--sc_prohibit_always_true_header_tests = \ - ^python/(libvirt-(qemu-)?override|typewrappers)\.c$$ + ^python/(libvirt-(lxc-|qemu-)?override|typewrappers)\.c$$ exclude_file_name_regexp--sc_prohibit_asprintf = \ ^(bootstrap.conf$$|src/util/virutil\.c$$|examples/domain-events/events-c/event-test\.c$$) diff --git a/configure.ac b/configure.ac index 3c97e4f..5a15531 100644 --- a/configure.ac +++ b/configure.ac @@ -174,7 +174,7 @@ dnl Availability of various common functions (non-fatal if missing), dnl and various less common threadsafe functions AC_CHECK_FUNCS_ONCE([cfmakeraw geteuid getgid getgrnam_r getmntent_r \ getpwuid_r getuid initgroups kill mmap newlocale posix_fallocate \ - posix_memalign regexec sched_getaffinity]) + posix_memalign regexec sched_getaffinity setns]) dnl Availability of pthread functions (if missing, win32 threading is dnl assumed). Because of $LIB_PTHREAD, we cannot use AC_CHECK_FUNCS_ONCE. @@ -2639,6 +2639,7 @@ CYGWIN_EXTRA_PYTHON_LIBADD= MINGW_EXTRA_LDFLAGS= WIN32_EXTRA_CFLAGS= LIBVIRT_SYMBOL_FILE=libvirt.syms +LIBVIRT_LXC_SYMBOL_FILE='$(srcdir)/libvirt_lxc.syms' LIBVIRT_QEMU_SYMBOL_FILE='$(srcdir)/libvirt_qemu.syms' MSCOM_LIBS= case "$host" in @@ -2672,6 +2673,7 @@ case "$host" in # Also set the symbol file to .def, so src/Makefile generates libvirt.def # from libvirt.syms and passes libvirt.def instead of libvirt.syms to the linker LIBVIRT_SYMBOL_FILE=libvirt.def + LIBVIRT_LXC_SYMBOL_FILE=libvirt_lxc.def LIBVIRT_QEMU_SYMBOL_FILE=libvirt_qemu.def # mingw's ld has the --version-script parameter, but it requires a .def file # instead to work properly, therefore clear --version-script here and use @@ -2687,6 +2689,7 @@ AC_SUBST([CYGWIN_EXTRA_PYTHON_LIBADD]) AC_SUBST([MINGW_EXTRA_LDFLAGS]) AC_SUBST([WIN32_EXTRA_CFLAGS]) AC_SUBST([LIBVIRT_SYMBOL_FILE]) +AC_SUBST([LIBVIRT_LXC_SYMBOL_FILE]) AC_SUBST([LIBVIRT_QEMU_SYMBOL_FILE]) AC_SUBST([VERSION_SCRIPT_FLAGS]) AC_SUBST([MSCOM_LIBS]) diff --git a/daemon/remote.c b/daemon/remote.c index 8767c18..a69dc5d 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -39,6 +39,7 @@ #include "stream.h" #include "viruuid.h" #include "libvirt/libvirt-qemu.h" +#include "libvirt/libvirt-lxc.h" #include "vircommand.h" #include "intprops.h" #include "virnetserverservice.h" diff --git a/docs/Makefile.am b/docs/Makefile.am index cba9d4b..6bd5681 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -95,8 +95,12 @@ qemu_xml = \ libvirt-qemu-api.xml \ libvirt-qemu-refs.xml +lxc_xml = \ + libvirt-lxc-api.xml \ + libvirt-lxc-refs.xml + apidir = $(pkgdatadir)/api -api_DATA = libvirt-api.xml libvirt-qemu-api.xml +api_DATA = libvirt-api.xml libvirt-qemu-api.xml libvirt-lxc-api.xml fig = \ libvirt-net-logical.fig \ @@ -133,6 +137,7 @@ all-am: web api: $(srcdir)/libvirt-api.xml $(srcdir)/libvirt-refs.xml qemu_api: $(srcdir)/libvirt-qemu-api.xml $(srcdir)/libvirt-qemu-refs.xml +lxc_api: $(srcdir)/libvirt-lxc-api.xml $(srcdir)/libvirt-lxc-refs.xml web: $(dot_html) $(internals_html) html/index.html devhelp/index.html \ $(dot_php) @@ -152,7 +157,8 @@ todo: $(MAKE) todo.html hvsupport.html.in: $(srcdir)/hvsupport.pl $(srcdir)/../src/libvirt_public.syms \ - $(srcdir)/../src/libvirt_qemu.syms $(srcdir)/../src/driver.h + $(srcdir)/../src/libvirt_qemu.syms $(srcdir)/../src/libvirt_lxc.syms \ + $(srcdir)/../src/driver.h $(AM_V_GEN)$(PERL) $(srcdir)/hvsupport.pl $(srcdir)/../src > $@ || { rm $@ && exit 1; } .PHONY: todo @@ -220,12 +226,16 @@ $(addprefix $(srcdir)/,$(devhelphtml)): $(srcdir)/libvirt-api.xml $(devhelpxsl) python_generated_files = \ $(srcdir)/html/libvirt-libvirt.html \ + $(srcdir)/html/libvirt-libvirt-lxc.html \ $(srcdir)/html/libvirt-libvirt-qemu.html \ $(srcdir)/html/libvirt-virterror.html \ $(srcdir)/libvirt-api.xml \ $(srcdir)/libvirt-refs.xml \ + $(srcdir)/libvirt-lxc-api.xml \ + $(srcdir)/libvirt-lxc-refs.xml \ $(srcdir)/libvirt-qemu-api.xml \ - $(srcdir)/libvirt-qemu-refs.xml + $(srcdir)/libvirt-qemu-refs.xml \ + $(NULL) APIBUILD=$(srcdir)/apibuild.py APIBUILD_STAMP=$(APIBUILD).stamp @@ -235,9 +245,11 @@ $(python_generated_files): $(APIBUILD_STAMP) $(APIBUILD_STAMP): $(srcdir)/apibuild.py \ $(srcdir)/../include/libvirt/libvirt.h.in \ + $(srcdir)/../include/libvirt/libvirt-lxc.h \ $(srcdir)/../include/libvirt/libvirt-qemu.h \ $(srcdir)/../include/libvirt/virterror.h \ $(srcdir)/../src/libvirt.c \ + $(srcdir)/../src/libvirt-lxc.c \ $(srcdir)/../src/libvirt-qemu.c \ $(srcdir)/../src/util/virerror.c $(AM_V_GEN)srcdir=$(srcdir) $(PYTHON) $(APIBUILD) @@ -252,9 +264,10 @@ clean-local: maintainer-clean-local: clean-local rm -rf $(srcdir)/libvirt-api.xml $(srcdir)/libvirt-refs.xml todo.html.in hvsupport.html.in rm -rf $(srcdir)/libvirt-qemu-api.xml $(srcdir)/libvirt-qemu-refs.xml + rm -rf $(srcdir)/libvirt-lxc-api.xml $(srcdir)/libvirt-lxc-refs.xml rm -rf $(APIBUILD_STAMP) -rebuild: api qemu_api all +rebuild: api qemu_api lxc_api all install-data-local: $(mkinstalldirs) $(DESTDIR)$(HTML_DIR) diff --git a/docs/apibuild.py b/docs/apibuild.py index e73a85d..eb8ddb2 100755 --- a/docs/apibuild.py +++ b/docs/apibuild.py @@ -32,6 +32,11 @@ qemu_included_files = { "libvirt-qemu.c": "Implementations for the QEMU specific APIs", } +lxc_included_files = { + "libvirt-lxc.h": "header with LXC specific API definitions", + "libvirt-lxc.c": "Implementations for the LXC specific APIs", +} + ignored_words = { "ATTRIBUTE_UNUSED": (0, "macro keyword"), "ATTRIBUTE_SENTINEL": (0, "macro keyword"), @@ -1955,6 +1960,8 @@ class docBuilder: self.includes = includes + included_files.keys() elif name == "libvirt-qemu": self.includes = includes + qemu_included_files.keys() + elif name == "libvirt-lxc": + self.includes = includes + lxc_included_files.keys() self.modules = {} self.headers = {} self.idx = index() @@ -2473,7 +2480,7 @@ class docBuilder: def rebuild(name): - if name not in ["libvirt", "libvirt-qemu"]: + if name not in ["libvirt", "libvirt-qemu", "libvirt-lxc"]: self.warning("rebuild() failed, unknown module %s") % name return None builder = None @@ -2516,6 +2523,7 @@ if __name__ == "__main__": else: rebuild("libvirt") rebuild("libvirt-qemu") + rebuild("libvirt-lxc") if warnings > 0: sys.exit(2) else: diff --git a/docs/hvsupport.pl b/docs/hvsupport.pl index 4871739..6230e3c 100755 --- a/docs/hvsupport.pl +++ b/docs/hvsupport.pl @@ -11,6 +11,7 @@ my $srcdir = shift @ARGV; my $symslibvirt = "$srcdir/libvirt_public.syms"; my $symsqemu = "$srcdir/libvirt_qemu.syms"; +my $symslxc = "$srcdir/libvirt_lxc.syms"; my $drivertable = "$srcdir/driver.h"; my %groupheaders = ( @@ -112,6 +113,45 @@ while (defined($line = <FILE>)) { close FILE; +# And the same for the LXC specific APIs + +open FILE, "<$symslxc" + or die "cannot read $symslxc: $!"; + +$prevvers = undef; +$vers = undef; +while (defined($line = <FILE>)) { + chomp $line; + next if $line =~ /^\s*#/; + next if $line =~ /^\s*$/; + next if $line =~ /^\s*(global|local):/; + if ($line =~ /^\s*LIBVIRT_LXC_(\d+\.\d+\.\d+)\s*{\s*$/) { + if (defined $vers) { + die "malformed syms file"; + } + $vers = $1; + } elsif ($line =~ /\s*}\s*;\s*$/) { + if (defined $prevvers) { + die "malformed syms file"; + } + $prevvers = $vers; + $vers = undef; + } elsif ($line =~ /\s*}\s*LIBVIRT_LXC_(\d+\.\d+\.\d+)\s*;\s*$/) { + if ($1 ne $prevvers) { + die "malformed syms file $1 != $vers"; + } + $prevvers = $vers; + $vers = undef; + } elsif ($line =~ /\s*(\w+)\s*;\s*$/) { + $apis{$1} = $vers; + } else { + die "unexpected data $line\n"; + } +} + +close FILE; + + # Some special things which aren't public APIs, # but we want to report $apis{virConnectDrvSupportsFeature} = "0.3.2"; diff --git a/include/libvirt/Makefile.am b/include/libvirt/Makefile.am index 8f36ae8..0ec7f04 100644 --- a/include/libvirt/Makefile.am +++ b/include/libvirt/Makefile.am @@ -6,6 +6,7 @@ virincdir = $(includedir)/libvirt virinc_HEADERS = libvirt.h \ + libvirt-lxc.h \ libvirt-qemu.h \ virterror.h diff --git a/include/libvirt/libvirt-lxc.h b/include/libvirt/libvirt-lxc.h new file mode 100644 index 0000000..f21538f --- /dev/null +++ b/include/libvirt/libvirt-lxc.h @@ -0,0 +1,50 @@ +/* -*- c -*- + * libvirt-lxc.h: Interfaces specific for LXC driver + * Summary: lxc specific interfaces + * Description: Provides the interfaces of the libvirt library to handle + * LXC specific methods + * + * Copyright (C) 2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_LXC_H__ +# define __VIR_LXC_H__ + +# include "libvirt/libvirt.h" + +# ifdef __cplusplus +extern "C" { +# endif + +int virDomainLxcOpenNamespace(virDomainPtr domain, + int **fdlist, + unsigned int flags); + +int virDomainLxcEnterNamespace(virDomainPtr domain, + unsigned int nfdlist, + int *fdlist, + unsigned int *noldfdlist, + int **oldfdlist, + unsigned int flags); + +# ifdef __cplusplus +} +# endif + +#endif /* __VIR_LXC_H__ */ diff --git a/libvirt.spec.in b/libvirt.spec.in index 3aaef02..e76c4db 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -1970,6 +1970,7 @@ rm -f $RPM_BUILD_ROOT%{_sysconfdir}/sysctl.d/libvirtd %dir %{_datadir}/libvirt/api/ %{_datadir}/libvirt/api/libvirt-api.xml %{_datadir}/libvirt/api/libvirt-qemu-api.xml +%{_datadir}/libvirt/api/libvirt-lxc-api.xml %doc docs/*.html docs/html docs/*.gif %doc docs/libvirt-api.xml diff --git a/mingw-libvirt.spec.in b/mingw-libvirt.spec.in index 4695895..eaccfe6 100644 --- a/mingw-libvirt.spec.in +++ b/mingw-libvirt.spec.in @@ -189,10 +189,12 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt_iohelper.exe %{mingw32_bindir}/virt-xml-validate %{mingw32_bindir}/virt-pki-validate %{mingw32_bindir}/virt-host-validate.exe +%{mingw32_bindir}/libvirt-lxc-0.dll %{mingw32_bindir}/libvirt-qemu-0.dll %{mingw32_libdir}/libvirt.dll.a %{mingw32_libdir}/pkgconfig/libvirt.pc +%{mingw32_libdir}/libvirt-lxc.dll.a %{mingw32_libdir}/libvirt-qemu.dll.a %dir %{mingw32_datadir}/libvirt/ @@ -213,6 +215,7 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt_iohelper.exe %{mingw32_datadir}/libvirt/schemas/storagevol.rng %dir %{mingw32_datadir}/libvirt/api/ %{mingw32_datadir}/libvirt/api/libvirt-api.xml +%{mingw32_datadir}/libvirt/api/libvirt-lxc-api.xml %{mingw32_datadir}/libvirt/api/libvirt-qemu-api.xml %{mingw32_datadir}/libvirt/cpu_map.xml @@ -222,6 +225,7 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt_iohelper.exe %dir %{mingw32_includedir}/libvirt %{mingw32_includedir}/libvirt/libvirt.h %{mingw32_includedir}/libvirt/virterror.h +%{mingw32_includedir}/libvirt/libvirt-lxc.h %{mingw32_includedir}/libvirt/libvirt-qemu.h %{mingw32_mandir}/man1/virsh.1* @@ -231,6 +235,7 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt_iohelper.exe %files -n mingw32-libvirt-static %{mingw32_libdir}/libvirt.a +%{mingw32_libdir}/libvirt-lxc.a %{mingw32_libdir}/libvirt-qemu.a # Mingw64 @@ -243,10 +248,12 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt_iohelper.exe %{mingw64_bindir}/virt-xml-validate %{mingw64_bindir}/virt-pki-validate %{mingw64_bindir}/virt-host-validate.exe +%{mingw64_bindir}/libvirt-lxc-0.dll %{mingw64_bindir}/libvirt-qemu-0.dll %{mingw64_libdir}/libvirt.dll.a %{mingw64_libdir}/pkgconfig/libvirt.pc +%{mingw64_libdir}/libvirt-lxc.dll.a %{mingw64_libdir}/libvirt-qemu.dll.a %dir %{mingw64_datadir}/libvirt/ @@ -267,6 +274,7 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt_iohelper.exe %{mingw64_datadir}/libvirt/schemas/storagevol.rng %dir %{mingw64_datadir}/libvirt/api/ %{mingw64_datadir}/libvirt/api/libvirt-api.xml +%{mingw64_datadir}/libvirt/api/libvirt-lxc-api.xml %{mingw64_datadir}/libvirt/api/libvirt-qemu-api.xml %{mingw64_datadir}/libvirt/cpu_map.xml @@ -276,6 +284,7 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt_iohelper.exe %dir %{mingw64_includedir}/libvirt %{mingw64_includedir}/libvirt/libvirt.h %{mingw64_includedir}/libvirt/virterror.h +%{mingw64_includedir}/libvirt/libvirt-lxc.h %{mingw64_includedir}/libvirt/libvirt-qemu.h %{mingw64_mandir}/man1/virsh.1* @@ -285,6 +294,7 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt_iohelper.exe %files -n mingw64-libvirt-static %{mingw64_libdir}/libvirt.a +%{mingw64_libdir}/libvirt-lxc.a %{mingw64_libdir}/libvirt-qemu.a diff --git a/python/Makefile.am b/python/Makefile.am index 97f21c3..dd69600 100644 --- a/python/Makefile.am +++ b/python/Makefile.am @@ -35,6 +35,8 @@ EXTRA_DIST = \ libvirt-override.c \ libvirt-override.py \ libvirt-override-api.xml \ + libvirt-lxc-override.c \ + libvirt-lxc-override-api.xml \ libvirt-qemu-override.c \ libvirt-qemu-override-api.xml \ $(CLASSES_EXTRA) \ @@ -47,10 +49,13 @@ mylibs = \ myqemulibs = \ $(top_builddir)/src/libvirt-qemu.la \ $(top_builddir)/gnulib/lib/libgnu.la +mylxclibs = \ + $(top_builddir)/src/libvirt-lxc.la \ + $(top_builddir)/gnulib/lib/libgnu.la -all-local: libvirt.py libvirt_qemu.py +all-local: libvirt.py libvirt_qemu.py libvirt_lxc.py -pyexec_LTLIBRARIES = libvirtmod.la libvirtmod_qemu.la +pyexec_LTLIBRARIES = libvirtmod.la libvirtmod_qemu.la libvirtmod_lxc.la libvirtmod_la_SOURCES = libvirt-override.c typewrappers.c nodist_libvirtmod_la_SOURCES = libvirt.c libvirt.h @@ -74,6 +79,17 @@ libvirtmod_qemu_la_LDFLAGS = -module -avoid-version -shared -L$(top_builddir)/sr libvirtmod_qemu_la_LIBADD = $(myqemulibs) \ $(CYGWIN_EXTRA_LIBADD) $(CYGWIN_EXTRA_PYTHON_LIBADD) +libvirtmod_lxc_la_SOURCES = libvirt-lxc-override.c typewrappers.c +nodist_libvirtmod_lxc_la_SOURCES = libvirt-lxc.c libvirt-lxc.h +# Python <= 2.4 header files contain a redundant decl, hence we +# need extra flags here +libvirtmod_lxc_la_CFLAGS = $(WARN_PYTHON_CFLAGS) + +libvirtmod_lxc_la_LDFLAGS = -module -avoid-version -shared -L$(top_builddir)/src/.libs \ + $(CYGWIN_EXTRA_LDFLAGS) +libvirtmod_lxc_la_LIBADD = $(mylxclibs) \ + $(CYGWIN_EXTRA_LIBADD) $(CYGWIN_EXTRA_PYTHON_LIBADD) + GENERATE = generator.py API_DESC = $(top_srcdir)/docs/libvirt-api.xml $(srcdir)/libvirt-override-api.xml GENERATED= libvirt-export.c \ @@ -87,18 +103,26 @@ QEMU_GENERATED= libvirt-qemu-export.c \ libvirt-qemu.h \ libvirt_qemu.py -$(GENERATE).stamp: $(srcdir)/$(GENERATE) $(API_DESC) $(QEMU_API_DESC) +LXC_API_DESC = $(top_srcdir)/docs/libvirt-lxc-api.xml $(srcdir)/libvirt-lxc-override-api.xml +LXC_GENERATED= libvirt-lxc-export.c \ + libvirt-lxc.c \ + libvirt-lxc.h \ + libvirt_lxc.py + +$(GENERATE).stamp: $(srcdir)/$(GENERATE) $(API_DESC) $(QEMU_API_DESC) $(LXC_API_DESC) $(AM_V_GEN)$(PYTHON) $(srcdir)/$(GENERATE) $(PYTHON) && \ touch $@ -$(GENERATED) $(QEMU_GENERATED): $(GENERATE).stamp +$(GENERATED) $(QEMU_GENERATED) $(LXC_GENERATED): $(GENERATE).stamp $(libvirtmod_la_OBJECTS): $(GENERATED) $(libvirtmod_qemu_la_OBJECTS): $(QEMU_GENERATED) +$(libvirtmod_lxc_la_OBJECTS): $(LXC_GENERATED) install-data-local: $(mkinstalldirs) $(DESTDIR)$(pyexecdir) $(INSTALL) -m 0644 libvirt.py $(DESTDIR)$(pyexecdir) + $(INSTALL) -m 0644 libvirt_lxc.py $(DESTDIR)$(pyexecdir) $(INSTALL) -m 0644 libvirt_qemu.py $(DESTDIR)$(pyexecdir) $(mkinstalldirs) $(DESTDIR)$(DOCS_DIR) @(for doc in $(DOCS) ; \ @@ -106,9 +130,10 @@ install-data-local: uninstall-local: rm -f $(DESTDIR)$(pyexecdir)/libvirt.py + rm -f $(DESTDIR)$(pyexecdir)/libvirt_lxc.py rm -f $(DESTDIR)$(pyexecdir)/libvirt_qemu.py -CLEANFILES= $(GENERATED) $(QEMU_GENERATED) $(GENERATE).stamp +CLEANFILES= $(GENERATED) $(QEMU_GENERATED) $(LXC_GENERATED) $(GENERATE).stamp else all: diff --git a/python/generator.py b/python/generator.py index e9b9270..902d5d0 100755 --- a/python/generator.py +++ b/python/generator.py @@ -4,8 +4,10 @@ # functions = {} +lxc_functions = {} qemu_functions = {} enums = {} # { enumType: { enumConstant: enumValue } } +lxc_enums = {} # { enumType: { enumConstant: enumValue } } qemu_enums = {} # { enumType: { enumConstant: enumValue } } import os @@ -123,6 +125,8 @@ class docParser(xml.sax.handler.ContentHandler): if (attrs['file'] == "libvirt" or attrs['file'] == "virterror"): enum(attrs['type'],attrs['name'],attrs['value']) + elif attrs['file'] == "libvirt-lxc": + lxc_enum(attrs['type'],attrs['name'],attrs['value']) elif attrs['file'] == "libvirt-qemu": qemu_enum(attrs['type'],attrs['name'],attrs['value']) @@ -138,6 +142,11 @@ class docParser(xml.sax.handler.ContentHandler): self.function_return, self.function_args, self.function_file, self.function_module, self.function_cond) + elif self.function_module == "libvirt-lxc": + lxc_function(self.function, self.function_descr, + self.function_return, self.function_args, + self.function_file, self.function_module, + self.function_cond) elif self.function_module == "libvirt-qemu": qemu_function(self.function, self.function_descr, self.function_return, self.function_args, @@ -148,6 +157,11 @@ class docParser(xml.sax.handler.ContentHandler): self.function_return, self.function_args, self.function_file, self.function_module, self.function_cond) + elif self.function_file == "python-lxc": + lxc_function(self.function, self.function_descr, + self.function_return, self.function_args, + self.function_file, self.function_module, + self.function_cond) elif self.function_file == "python-qemu": qemu_function(self.function, self.function_descr, self.function_return, self.function_args, @@ -184,6 +198,9 @@ def function(name, desc, ret, args, file, module, cond): def qemu_function(name, desc, ret, args, file, module, cond): qemu_functions[name] = (desc, ret, args, file, module, cond) +def lxc_function(name, desc, ret, args, file, module, cond): + lxc_functions[name] = (desc, ret, args, file, module, cond) + def enum(type, name, value): if not enums.has_key(type): enums[type] = {} @@ -208,6 +225,11 @@ def enum(type, name, value): if name[-5:] != '_LAST': enums[type][name] = value +def lxc_enum(type, name, value): + if not lxc_enums.has_key(type): + lxc_enums[type] = {} + lxc_enums[type][name] = value + def qemu_enum(type, name, value): if not qemu_enums.has_key(type): qemu_enums[type] = {} @@ -222,10 +244,12 @@ def qemu_enum(type, name, value): ####################################################################### functions_failed = [] +lxc_functions_failed = [] qemu_functions_failed = [] functions_skipped = [ "virConnectListDomains", ] +lxc_functions_skipped = [] qemu_functions_skipped = [] skipped_modules = { @@ -430,6 +454,10 @@ skip_impl = ( 'virNodeGetCPUMap', ) +lxc_skip_impl = ( + 'virDomainLxcOpenNamespace', +) + qemu_skip_impl = ( 'virDomainQemuMonitorCommand', 'virDomainQemuAgentCommand', @@ -501,6 +529,8 @@ skip_function = ( "virStorageVolGetConnect", ) +lxc_skip_function = ( +) qemu_skip_function = ( #"virDomainQemuAttach", ) @@ -511,6 +541,7 @@ function_skip_python_impl = ( # be exposed in bindings ) +lxc_function_skip_python_impl = () qemu_function_skip_python_impl = () function_skip_index_one = ( @@ -521,6 +552,7 @@ def print_function_wrapper(module, name, output, export, include): global py_types global unknown_types global functions + global lxc_functions global qemu_functions global skipped_modules global function_skip_python_impl @@ -528,6 +560,8 @@ def print_function_wrapper(module, name, output, export, include): try: if module == "libvirt": (desc, ret, args, file, mod, cond) = functions[name] + if module == "libvirt-lxc": + (desc, ret, args, file, mod, cond) = lxc_functions[name] if module == "libvirt-qemu": (desc, ret, args, file, mod, cond) = qemu_functions[name] except: @@ -543,6 +577,12 @@ def print_function_wrapper(module, name, output, export, include): if name in skip_impl: # Don't delete the function entry in the caller. return 1 + elif module == "libvirt-lxc": + if name in lxc_skip_function: + return 0 + if name in lxc_skip_impl: + # Don't delete the function entry in the caller. + return 1 elif module == "libvirt-qemu": if name in qemu_skip_function: return 0 @@ -643,6 +683,10 @@ def print_function_wrapper(module, name, output, export, include): include.write("libvirt_%s(PyObject *self, PyObject *args);\n" % (name)); export.write(" { (char *)\"%s\", libvirt_%s, METH_VARARGS, NULL },\n" % (name, name)) + elif module == "libvirt-lxc": + include.write("libvirt_lxc_%s(PyObject *self, PyObject *args);\n" % (name)); + export.write(" { (char *)\"%s\", libvirt_lxc_%s, METH_VARARGS, NULL },\n" % + (name, name)) elif module == "libvirt-qemu": include.write("libvirt_qemu_%s(PyObject *self, PyObject *args);\n" % (name)); export.write(" { (char *)\"%s\", libvirt_qemu_%s, METH_VARARGS, NULL },\n" % @@ -666,6 +710,8 @@ def print_function_wrapper(module, name, output, export, include): output.write("PyObject *\n") if module == "libvirt": output.write("libvirt_%s(PyObject *self ATTRIBUTE_UNUSED," % (name)) + elif module == "libvirt-lxc": + output.write("libvirt_lxc_%s(PyObject *self ATTRIBUTE_UNUSED," % (name)) elif module == "libvirt-qemu": output.write("libvirt_qemu_%s(PyObject *self ATTRIBUTE_UNUSED," % (name)) output.write(" PyObject *args") @@ -698,6 +744,9 @@ def print_function_wrapper(module, name, output, export, include): if module == "libvirt": if name in function_skip_python_impl: return 0 + elif module == "libvirt-lxc": + if name in lxc_function_skip_python_impl: + return 0 elif module == "libvirt-qemu": if name in qemu_function_skip_python_impl: return 0 @@ -708,7 +757,7 @@ def buildStubs(module): global py_return_types global unknown_types - if module not in ["libvirt", "libvirt-qemu"]: + if module not in ["libvirt", "libvirt-qemu", "libvirt-lxc"]: print "ERROR: Unknown module type: %s" % module return None @@ -716,6 +765,10 @@ def buildStubs(module): funcs = functions funcs_failed = functions_failed funcs_skipped = functions_skipped + elif module == "libvirt-lxc": + funcs = lxc_functions + funcs_failed = lxc_functions_failed + funcs_skipped = functions_skipped elif module == "libvirt-qemu": funcs = qemu_functions funcs_failed = qemu_functions_failed @@ -1111,6 +1164,8 @@ def functionCompare(info1, info2): def writeDoc(module, name, args, indent, output): if module == "libvirt": funcs = functions + elif module == "libvirt-lxc": + funcs = lxc_functions elif module == "libvirt-qemu": funcs = qemu_functions if funcs[name][0] is None or funcs[name][0] == "": @@ -1762,11 +1817,126 @@ def qemuBuildWrappers(module): fd.close() +def lxcBuildWrappers(module): + global lxc_functions + + if not module == "libvirt-lxc": + print "ERROR: only libvirt-lxc is supported" + return None + + extra_file = os.path.join(srcPref, "%s-override.py" % module) + extra = None + + fd = open("libvirt_lxc.py", "w") + + if os.path.exists(extra_file): + extra = open(extra_file, "r") + fd.write("#! " + python + " -i\n") + fd.write("#\n") + fd.write("# WARNING WARNING WARNING WARNING\n") + fd.write("#\n") + fd.write("# This file is automatically written by generator.py. Any changes\n") + fd.write("# made here will be lost.\n") + fd.write("#\n") + fd.write("# To change the manually written methods edit " + module + "-override.py\n") + fd.write("# To change the automatically written methods edit generator.py\n") + fd.write("#\n") + fd.write("# WARNING WARNING WARNING WARNING\n") + fd.write("#\n") + if extra != None: + fd.writelines(extra.readlines()) + fd.write("#\n") + fd.write("# WARNING WARNING WARNING WARNING\n") + fd.write("#\n") + fd.write("# Automatically written part of python bindings for libvirt\n") + fd.write("#\n") + fd.write("# WARNING WARNING WARNING WARNING\n") + if extra != None: + extra.close() + + fd.write("try:\n") + fd.write(" import libvirtmod_lxc\n") + fd.write("except ImportError, lib_e:\n") + fd.write(" try:\n") + fd.write(" import cygvirtmod_lxc as libvirtmod_lxc\n") + fd.write(" except ImportError, cyg_e:\n") + fd.write(" if str(cyg_e).count(\"No module named\"):\n") + fd.write(" raise lib_e\n\n") + + fd.write("import libvirt\n\n"); + fd.write("#\n# Functions from module %s\n#\n\n" % module) + # + # Generate functions directly, no classes + # + for name in lxc_functions.keys(): + func = nameFixup(name, 'None', None, None) + (desc, ret, args, file, mod, cond) = lxc_functions[name] + fd.write("def %s(" % func) + n = 0 + for arg in args: + if n != 0: + fd.write(", ") + fd.write("%s" % arg[0]) + n = n + 1 + fd.write("):\n") + writeDoc(module, name, args, ' ', fd); + + if ret[0] != "void": + fd.write(" ret = "); + else: + fd.write(" "); + fd.write("libvirtmod_lxc.%s(" % name) + n = 0 + + conn = None + + for arg in args: + if arg[1] == "virConnectPtr": + conn = arg[0] + + if n != 0: + fd.write(", "); + if arg[1] in ["virDomainPtr", "virConnectPtr"]: + # FIXME: This might have problem if the function + # has multiple args which are objects. + fd.write("%s.%s" % (arg[0], "_o")) + else: + fd.write("%s" % arg[0]) + n = n + 1 + fd.write(")\n"); + + if ret[0] != "void": + fd.write(" if ret is None: raise libvirt.libvirtError('" + name + "() failed')\n") + if ret[0] == "virDomainPtr": + fd.write(" __tmp = virDomain(" + conn + ",_obj=ret)\n") + fd.write(" return __tmp\n") + else: + fd.write(" return ret\n") + + fd.write("\n") + + # + # Generate enum constants + # + for type,enum in lxc_enums.items(): + fd.write("# %s\n" % type) + items = enum.items() + items.sort(lambda i1,i2: cmp(long(i1[1]),long(i2[1]))) + for name,value in items: + fd.write("%s = %s\n" % (name,value)) + fd.write("\n"); + + fd.close() + + quiet = 0 if buildStubs("libvirt") < 0: sys.exit(1) +if buildStubs("libvirt-lxc") < 0: + sys.exit(1) if buildStubs("libvirt-qemu") < 0: sys.exit(1) buildWrappers("libvirt") +lxcBuildWrappers("libvirt-lxc") qemuBuildWrappers("libvirt-qemu") sys.exit(0) diff --git a/python/libvirt-lxc-override-api.xml b/python/libvirt-lxc-override-api.xml new file mode 100644 index 0000000..db0d45d --- /dev/null +++ b/python/libvirt-lxc-override-api.xml @@ -0,0 +1,19 @@ +<?xml version="1.0"?> +<api name='libvir-lxc-python'> + <symbols> + <function name='virDomainLxcOpenNamespace' file='python-lxc'> + <info><![CDATA[This API is LXC specific, so it will only work with hypervisor +connections to the LXC driver. + +Open the namespaces associated with the container @domain +and return a list of file descriptors associated with the +container. + +The returned file descriptors are intended to be used with +the setns() system call.]]></info> + <return type='int' info='the list of open file descriptors, or -1 on error'/> + <arg name='domain' type='virDomainPtr' info='a domain object'/> + <arg name='flags' type='unsigned int' info='currently unused, pass 0'/> + </function> + </symbols> +</api> diff --git a/python/libvirt-lxc-override.c b/python/libvirt-lxc-override.c new file mode 100644 index 0000000..33c9c0a --- /dev/null +++ b/python/libvirt-lxc-override.c @@ -0,0 +1,141 @@ +/* + * libvir.c: this modules implements the main part of the glue of the + * libvir library and the Python interpreter. It provides the + * entry points where an automatically generated stub is + * unpractical + * + * Copyright (C) 2011-2012 Red Hat, Inc. + * + * Daniel Veillard <veillard@redhat.com> + */ + +#include <config.h> + +/* Horrible kludge to work around even more horrible name-space pollution + via Python.h. That file includes /usr/include/python2.5/pyconfig*.h, + which has over 180 autoconf-style HAVE_* definitions. Shame on them. */ +#undef HAVE_PTHREAD_H + +#include <Python.h> +#include "libvirt/libvirt-lxc.h" +#include "libvirt/virterror.h" +#include "typewrappers.h" +#include "libvirt-lxc.h" +#include "viralloc.h" +#include "virfile.h" + +#ifndef __CYGWIN__ +extern void initlibvirtmod_lxc(void); +#else +extern void initcygvirtmod_lxc(void); +#endif + +#if 0 +# define DEBUG_ERROR 1 +#endif + +#if DEBUG_ERROR +# define DEBUG(fmt, ...) \ + printf(fmt, __VA_ARGS__) +#else +# define DEBUG(fmt, ...) \ + do {} while (0) +#endif + +/* The two-statement sequence "Py_INCREF(Py_None); return Py_None;" + is so common that we encapsulate it here. Now, each use is simply + return VIR_PY_NONE; */ +#define VIR_PY_NONE (Py_INCREF (Py_None), Py_None) +#define VIR_PY_INT_FAIL (libvirt_intWrap(-1)) +#define VIR_PY_INT_SUCCESS (libvirt_intWrap(0)) + +/************************************************************************ + * * + * Statistics * + * * + ************************************************************************/ + +static PyObject * +libvirt_lxc_virDomainLxcOpenNamespace(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + PyObject *py_retval; + virDomainPtr domain; + PyObject *pyobj_domain; + unsigned int flags; + int c_retval; + int *fdlist = NULL; + int i; + + if (!PyArg_ParseTuple(args, (char *)"Oi:virDomainLxcOpenNamespace", + &pyobj_domain, &flags)) + return NULL; + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + if (domain == NULL) + return VIR_PY_NONE; + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virDomainLxcOpenNamespace(domain, &fdlist, flags); + LIBVIRT_END_ALLOW_THREADS; + + if (c_retval < 0) + return VIR_PY_NONE; + + py_retval = PyList_New(c_retval); + for (i = 0 ; i < c_retval ; i++) { + PyObject *item = NULL; + + if ((item = PyInt_FromLong(fdlist[i])) == NULL) + goto error; + + if (PyList_Append(py_retval, item) < 0) { + Py_DECREF(item); + goto error; + } + } + return py_retval; + +error: + for (i = 0 ; i < c_retval ; i++) { + VIR_FORCE_CLOSE(fdlist[i]); + } + VIR_FREE(fdlist); + return VIR_PY_NONE; +} +/************************************************************************ + * * + * The registration stuff * + * * + ************************************************************************/ +static PyMethodDef libvirtLxcMethods[] = { +#include "libvirt-lxc-export.c" + {(char *) "virDomainLxcOpenNamespace", libvirt_lxc_virDomainLxcOpenNamespace, METH_VARARGS, NULL}, + {NULL, NULL, 0, NULL} +}; + +void +#ifndef __CYGWIN__ +initlibvirtmod_lxc +#else +initcygvirtmod_lxc +#endif + (void) +{ + static int initialized = 0; + + if (initialized != 0) + return; + + if (virInitialize() < 0) + return; + + /* initialize the python extension module */ + Py_InitModule((char *) +#ifndef __CYGWIN__ + "libvirtmod_lxc" +#else + "cygvirtmod_lxc" +#endif + , libvirtLxcMethods); + + initialized = 1; +} diff --git a/src/Makefile.am b/src/Makefile.am index 494c184..13b7e30 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -36,7 +36,7 @@ if WITH_NETWORK UUID=$(shell uuidgen 2>/dev/null) endif -lib_LTLIBRARIES = libvirt.la libvirt-qemu.la +lib_LTLIBRARIES = libvirt.la libvirt-qemu.la libvirt-lxc.la moddir = $(libdir)/libvirt/connection-driver mod_LTLIBRARIES = @@ -1480,6 +1480,13 @@ libvirt_qemu.def: $(srcdir)/libvirt_qemu.syms chmod a-w $@-tmp && \ mv $@-tmp libvirt_qemu.def +libvirt_lxc.def: $(srcdir)/libvirt_lxc.syms + $(AM_V_GEN)rm -f -- $@-tmp $@ ; \ + printf 'EXPORTS\n' > $@-tmp && \ + sed -e '/^$$/d; /#/d; /:/d; /}/d; /\*/d; /LIBVIRT_/d; s/[ ]*\(.*\)\;/ \1/g' $^ >> $@-tmp && \ + chmod a-w $@-tmp && \ + mv $@-tmp libvirt_lxc.def + # Empty source list - it merely links a bunch of convenience libs together libvirt_la_SOURCES = libvirt_la_LDFLAGS = $(VERSION_SCRIPT_FLAGS)$(LIBVIRT_SYMBOL_FILE) \ @@ -1566,6 +1573,15 @@ libvirt_qemu_la_CFLAGS = $(AM_CFLAGS) libvirt_qemu_la_LIBADD = libvirt.la $(CYGWIN_EXTRA_LIBADD) EXTRA_DIST += $(LIBVIRT_QEMU_SYMBOL_FILE) +libvirt_lxc_la_SOURCES = libvirt-lxc.c +libvirt_lxc_la_LDFLAGS = $(VERSION_SCRIPT_FLAGS)$(LIBVIRT_LXC_SYMBOL_FILE) \ + -version-info $(LIBVIRT_VERSION_INFO) \ + $(CYGWIN_EXTRA_LDFLAGS) $(MINGW_EXTRA_LDFLAGS) \ + $(AM_LDFLAGS) +libvirt_lxc_la_CFLAGS = $(AM_CFLAGS) +libvirt_lxc_la_LIBADD = libvirt.la $(CYGWIN_EXTRA_LIBADD) +EXTRA_DIST += $(LIBVIRT_LXC_SYMBOL_FILE) + lockdriverdir = $(libdir)/libvirt/lock-driver lockdriver_LTLIBRARIES = diff --git a/src/driver.h b/src/driver.h index 64d652f..80f06bd 100644 --- a/src/driver.h +++ b/src/driver.h @@ -915,6 +915,11 @@ typedef int unsigned long long minimum, unsigned int flags); +typedef int + (*virDrvDomainLxcOpenNamespace)(virDomainPtr dom, + int **fdlist, + unsigned int flags); + /** * _virDriver: * @@ -1107,6 +1112,7 @@ struct _virDriver { virDrvNodeGetCPUMap nodeGetCPUMap; virDrvDomainFSTrim domainFSTrim; virDrvDomainSendProcessSignal domainSendProcessSignal; + virDrvDomainLxcOpenNamespace domainLxcOpenNamespace; }; typedef int diff --git a/src/internal.h b/src/internal.h index 8d96660..ebc91c7 100644 --- a/src/internal.h +++ b/src/internal.h @@ -40,6 +40,7 @@ # define N_(str) str # include "libvirt/libvirt.h" +# include "libvirt/libvirt-lxc.h" # include "libvirt/libvirt-qemu.h" # include "libvirt/virterror.h" diff --git a/src/libvirt-lxc.c b/src/libvirt-lxc.c new file mode 100644 index 0000000..0de498b --- /dev/null +++ b/src/libvirt-lxc.c @@ -0,0 +1,165 @@ +/* + * libvirt-lxc.c: Interfaces for the libvirt library to handle lxc-specific + * APIs. + * + * Copyright (C) 2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include "viralloc.h" +#include "virerror.h" +#include "virfile.h" +#include "virlog.h" +#include "virprocess.h" +#include "datatypes.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +#define virLibConnError(conn, error, info) \ + virReportErrorHelper(VIR_FROM_NONE, error, NULL, __FUNCTION__, \ + __LINE__, info) + +#define virLibDomainError(domain, error, info) \ + virReportErrorHelper(VIR_FROM_DOM, error, NULL, __FUNCTION__, \ + __LINE__, info) + +/** + * virDomainLxcOpenNamespace: + * @domain: a domain object + * @fdlist: pointer to an array to be filled with FDs + * @flags: currently unused, pass 0 + * + * This API is LXC specific, so it will only work with hypervisor + * connections to the LXC driver. + * + * Open the namespaces associated with the container @domain. + * The @fdlist array will be allocated to a suitable size, + * and filled with file descriptors for the namespaces. It + * is the caller's responsibility to close the file descriptors + * + * The returned file descriptors are intended to be used with + * the setns() system call. + * + * Returns the number of opened file descriptors, or -1 on error + */ +int +virDomainLxcOpenNamespace(virDomainPtr domain, + int **fdlist, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DEBUG("domain=%p, fdlist=%p flags=%x", + domain, fdlist, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = domain->conn; + + virCheckNonNullArgGoto(fdlist, error); + + if (conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->driver->domainLxcOpenNamespace) { + int ret; + ret = conn->driver->domainLxcOpenNamespace(domain, + fdlist, + flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} + + +/** + * virDomainLxcEnterNamespace: + * @domain: a domain object + * @nfdlist: number of FDs in @fdlist + * @fdlist: list of namespace file descriptors + * @noldfdlist: filled with number of old FDs + * @oldfdlist: pointer to hold list of old namespace file descriptors + * @flags: currently unused, pass 0 + * + * This API is LXC specific, so it will only work with hypervisor + * connections to the LXC driver. + * + * Attaches the process to the namespaces associated + * with the FDs in @fdlist + * + * If @oldfdlist is non-NULL, it will be populated with file + * descriptors representing the old namespace. This allows + * the caller to switch back to its current namespace later + * + * Returns 0 on success, -1 on error + */ +int +virDomainLxcEnterNamespace(virDomainPtr domain, + unsigned int nfdlist, + int *fdlist, + unsigned int *noldfdlist, + int **oldfdlist, + unsigned int flags) +{ + int i; + + virCheckFlags(0, -1); + + if (noldfdlist && oldfdlist) { + size_t nfds; + if (virProcessGetNamespaces(getpid(), + &nfds, + oldfdlist) < 0) + goto error; + *noldfdlist = nfds; + } + + if (virProcessSetNamespaces(nfdlist, fdlist) < 0) { + if (oldfdlist && noldfdlist) { + for (i = 0 ; i < *noldfdlist ; i++) { + VIR_FORCE_CLOSE((*oldfdlist)[i]); + } + VIR_FREE(*oldfdlist); + *noldfdlist = 0; + } + goto error; + } + + return 0; + +error: + virDispatchError(domain->conn); + return -1; +} diff --git a/src/libvirt_lxc.syms b/src/libvirt_lxc.syms new file mode 100644 index 0000000..b5be18b --- /dev/null +++ b/src/libvirt_lxc.syms @@ -0,0 +1,17 @@ +# +# Officially exported symbols, for which header +# file definitions are installed in /usr/include/libvirt +# from libvirt-lxc.h +# +# Versions here are *fixed* to match the libvirt version +# at which the symbol was introduced. This ensures that +# a new client app requiring symbol foo() can't accidentally +# run with old libvirt-lxc.so not providing foo() - the global +# soname version info can't enforce this since we never +# change the soname +# +LIBVIRT_LXC_1.0.2 { + global: + virDomainLxcEnterNamespace; + virDomainLxcOpenNamespace; +}; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 8050ce6..025c35f 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -4544,6 +4544,7 @@ static virDriver lxcDriver = { .domainShutdown = lxcDomainShutdown, /* 1.0.1 */ .domainShutdownFlags = lxcDomainShutdownFlags, /* 1.0.1 */ .domainReboot = lxcDomainReboot, /* 1.0.1 */ + .domainLxcOpenNamespace = lxcDomainOpenNamespace, /* 1.0.2 */ }; static virStateDriver lxcStateDriver = { diff --git a/tools/virsh.c b/tools/virsh.c index 283194a..51615ec 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -62,6 +62,7 @@ #include "viralloc.h" #include "virxml.h" #include "libvirt/libvirt-qemu.h" +#include "libvirt/libvirt-lxc.h" #include "virfile.h" #include "configmake.h" #include "virthread.h" -- 1.7.11.7

On 12/21/2012 10:08 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
This patch introduces support for LXC specific public APIs. In common with what was done for QEMU, this creates a libvirt_lxc.so library and libvirt/libvirt-lxc.h header file.
The actual APIs are
int virDomainLxcOpenNamespace(virDomainPtr domain, int **fdlist, unsigned int flags);
int virDomainLxcEnterNamespace(virDomainPtr domain, unsigned int flags);
This commit message signature...
which provide a way to use the setns() system call to move the calling process into the container's namespace. This is not practical to write in a generically applicable manner. The nearest that we could get to such an API would be an API which allows to pass a command + argv to be executed inside a container. Even if we had such a generic API, this LXC specific API is still useful, because it allows the caller to maintain the current process context, in particular any I/O streams they have open.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> ---
@@ -152,7 +157,8 @@ todo: $(MAKE) todo.html
hvsupport.html.in: $(srcdir)/hvsupport.pl $(srcdir)/../src/libvirt_public.syms \ - $(srcdir)/../src/libvirt_qemu.syms $(srcdir)/../src/driver.h + $(srcdir)/../src/libvirt_qemu.syms $(srcdir)/../src/libvirt_lxc.syms \ + $(srcdir)/../src/driver.h
Indentation looks weird here, but that's just cosmetic.
+++ b/include/libvirt/libvirt-lxc.h @@ -0,0 +1,50 @@ +/* -*- c -*- + * libvirt-lxc.h: Interfaces specific for LXC driver + * Summary: lxc specific interfaces + * Description: Provides the interfaces of the libvirt library to handle + * LXC specific methods + * + * Copyright (C) 2012 Red Hat, Inc.
2013.
+int virDomainLxcEnterNamespace(virDomainPtr domain, + unsigned int nfdlist, + int *fdlist, + unsigned int *noldfdlist, + int **oldfdlist, + unsigned int flags);
...doesn't match the actual code.
+++ b/python/libvirt-lxc-override-api.xml @@ -0,0 +1,19 @@ +<?xml version="1.0"?>
Should we be sticking copyright comments in these types of files? But that's fine for a followup, since there are other files needing the same cleanup.
+++ b/python/libvirt-lxc-override.c @@ -0,0 +1,141 @@ +/* + * libvir.c: this modules implements the main part of the glue of the + * libvir library and the Python interpreter. It provides the + * entry points where an automatically generated stub is + * unpractical + * + * Copyright (C) 2011-2012 Red Hat, Inc.
Also 2013.
+libvirt_lxc.def: $(srcdir)/libvirt_lxc.syms + $(AM_V_GEN)rm -f -- $@-tmp $@ ; \ + printf 'EXPORTS\n' > $@-tmp && \ + sed -e '/^$$/d; /#/d; /:/d; /}/d; /\*/d; /LIBVIRT_/d; s/[ ]*\(.*\)\;/ \1/g' $^ >> $@-tmp && \
Should we break this long line with multiple -e to fit in 80 columns? But that's copy and paste, so it would be a separate cleanup.
+++ b/src/driver.h @@ -915,6 +915,11 @@ typedef int unsigned long long minimum, unsigned int flags);
+typedef int + (*virDrvDomainLxcOpenNamespace)(virDomainPtr dom, + int **fdlist, + unsigned int flags); + /** * _virDriver: * @@ -1107,6 +1112,7 @@ struct _virDriver { virDrvNodeGetCPUMap nodeGetCPUMap; virDrvDomainFSTrim domainFSTrim; virDrvDomainSendProcessSignal domainSendProcessSignal; + virDrvDomainLxcOpenNamespace domainLxcOpenNamespace;
No EnterNamespace driver callback?
+++ b/src/libvirt-lxc.c @@ -0,0 +1,165 @@ +/* + * libvirt-lxc.c: Interfaces for the libvirt library to handle lxc-specific + * APIs. + * + * Copyright (C) 2012 Red Hat, Inc.
2013
+++ b/src/lxc/lxc_driver.c @@ -4544,6 +4544,7 @@ static virDriver lxcDriver = { .domainShutdown = lxcDomainShutdown, /* 1.0.1 */ .domainShutdownFlags = lxcDomainShutdownFlags, /* 1.0.1 */ .domainReboot = lxcDomainReboot, /* 1.0.1 */ + .domainLxcOpenNamespace = lxcDomainOpenNamespace, /* 1.0.2 */
Again, no EnterNamespace callback registration? ACK with those issues fixed. It made it nice that we already have libvirt-qemu to copy from. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On Tue, Jan 08, 2013 at 09:02:37AM -0700, Eric Blake wrote:
On 12/21/2012 10:08 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
This patch introduces support for LXC specific public APIs. In common with what was done for QEMU, this creates a libvirt_lxc.so library and libvirt/libvirt-lxc.h header file.
The actual APIs are
int virDomainLxcOpenNamespace(virDomainPtr domain, int **fdlist, unsigned int flags);
int virDomainLxcEnterNamespace(virDomainPtr domain, unsigned int flags);
This commit message signature...
Opps, that was from a work-in-progress version of the patch.
+++ b/src/driver.h @@ -915,6 +915,11 @@ typedef int unsigned long long minimum, unsigned int flags);
+typedef int + (*virDrvDomainLxcOpenNamespace)(virDomainPtr dom, + int **fdlist, + unsigned int flags); + /** * _virDriver: * @@ -1107,6 +1112,7 @@ struct _virDriver { virDrvNodeGetCPUMap nodeGetCPUMap; virDrvDomainFSTrim domainFSTrim; virDrvDomainSendProcessSignal domainSendProcessSignal; + virDrvDomainLxcOpenNamespace domainLxcOpenNamespace;
No EnterNamespace driver callback?
The 'virDomainLxcEnterNamespace' API is special in that it is rnu purely client-side, not in the daemon. It is pretty much just a dumb wrapper around 'setns()' and 'readdir(/proc/self/ns)' to avoid apps having to know about those low level impl details.
+++ b/src/lxc/lxc_driver.c @@ -4544,6 +4544,7 @@ static virDriver lxcDriver = { .domainShutdown = lxcDomainShutdown, /* 1.0.1 */ .domainShutdownFlags = lxcDomainShutdownFlags, /* 1.0.1 */ .domainReboot = lxcDomainReboot, /* 1.0.1 */ + .domainLxcOpenNamespace = lxcDomainOpenNamespace, /* 1.0.2 */
Again, no EnterNamespace callback registration?
Same note as above - the driver API isn't used for this code since it is client side.
ACK with those issues fixed. It made it nice that we already have libvirt-qemu to copy from.
Indeed, it would have taken me ages to get this right without copying from libvirt-qemu :-) Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

From: "Daniel P. Berrange" <berrange@redhat.com> Add the infrastructure for the libvirt-lxc.la library to the remote protocol client and daemon Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- daemon/Makefile.am | 13 +++++- daemon/libvirtd.c | 14 ++++++ daemon/libvirtd.h | 1 + daemon/remote.c | 52 +++++++++++++++++++++ daemon/remote.h | 3 ++ src/Makefile.am | 15 +++++- src/remote/lxc_protocol.x | 50 ++++++++++++++++++++ src/remote/remote_driver.c | 112 ++++++++++++++++++++++++++++++++++++--------- 8 files changed, 235 insertions(+), 25 deletions(-) create mode 100644 src/remote/lxc_protocol.x diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 18a4bca..88a27f2 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -17,7 +17,9 @@ CLEANFILES = DAEMON_GENERATED = \ $(srcdir)/remote_dispatch.h \ - $(srcdir)/qemu_dispatch.h + $(srcdir)/lxc_dispatch.h \ + $(srcdir)/qemu_dispatch.h \ + $(NULL) DAEMON_SOURCES = \ libvirtd.c libvirtd.h \ @@ -25,12 +27,14 @@ DAEMON_SOURCES = \ remote.c remote.h \ stream.c stream.h \ ../src/remote/remote_protocol.c \ + ../src/remote/lxc_protocol.c \ ../src/remote/qemu_protocol.c \ $(DAEMON_GENERATED) DISTCLEANFILES = EXTRA_DIST = \ remote_dispatch.h \ + lxc_dispatch.h \ qemu_dispatch.h \ libvirtd.conf \ libvirtd.init.in \ @@ -53,6 +57,7 @@ EXTRA_DIST = \ BUILT_SOURCES = REMOTE_PROTOCOL = $(top_srcdir)/src/remote/remote_protocol.x +LXC_PROTOCOL = $(top_srcdir)/src/remote/lxc_protocol.x QEMU_PROTOCOL = $(top_srcdir)/src/remote/qemu_protocol.x $(srcdir)/remote_dispatch.h: $(srcdir)/../src/rpc/gendispatch.pl \ @@ -60,6 +65,11 @@ $(srcdir)/remote_dispatch.h: $(srcdir)/../src/rpc/gendispatch.pl \ $(AM_V_GEN)$(PERL) -w $(srcdir)/../src/rpc/gendispatch.pl -b remote REMOTE \ $(REMOTE_PROTOCOL) > $@ +$(srcdir)/lxc_dispatch.h: $(srcdir)/../src/rpc/gendispatch.pl \ + $(LXC_PROTOCOL) + $(AM_V_GEN)$(PERL) -w $(srcdir)/../src/rpc/gendispatch.pl -b lxc LXC \ + $(LXC_PROTOCOL) > $@ + $(srcdir)/qemu_dispatch.h: $(srcdir)/../src/rpc/gendispatch.pl \ $(QEMU_PROTOCOL) $(AM_V_GEN)$(PERL) -w $(srcdir)/../src/rpc/gendispatch.pl -b qemu QEMU \ @@ -116,6 +126,7 @@ libvirtd_LDADD += ../src/libvirt_probes.lo endif libvirtd_LDADD += \ + ../src/libvirt-lxc.la \ ../src/libvirt-qemu.la if ! WITH_DRIVER_MODULES diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index fa4d129..0513312 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -103,6 +103,7 @@ virNetSASLContextPtr saslCtxt = NULL; #endif virNetServerProgramPtr remoteProgram = NULL; virNetServerProgramPtr qemuProgram = NULL; +virNetServerProgramPtr lxcProgram = NULL; enum { VIR_DAEMON_ERR_NONE = 0, @@ -1349,6 +1350,18 @@ int main(int argc, char **argv) { goto cleanup; } + if (!(lxcProgram = virNetServerProgramNew(LXC_PROGRAM, + LXC_PROTOCOL_VERSION, + lxcProcs, + lxcNProcs))) { + ret = VIR_DAEMON_ERR_INIT; + goto cleanup; + } + if (virNetServerAddProgram(srv, lxcProgram) < 0) { + ret = VIR_DAEMON_ERR_INIT; + goto cleanup; + } + if (!(qemuProgram = virNetServerProgramNew(QEMU_PROGRAM, QEMU_PROTOCOL_VERSION, qemuProcs, @@ -1454,6 +1467,7 @@ int main(int argc, char **argv) { cleanup: virNetlinkEventServiceStopAll(); virObjectUnref(remoteProgram); + virObjectUnref(lxcProgram); virObjectUnref(qemuProgram); virNetServerClose(srv); virObjectUnref(srv); diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h index 69a77ea..6254208 100644 --- a/daemon/libvirtd.h +++ b/daemon/libvirtd.h @@ -32,6 +32,7 @@ # include <rpc/types.h> # include <rpc/xdr.h> # include "remote_protocol.h" +# include "lxc_protocol.h" # include "qemu_protocol.h" # include "virlog.h" # include "virthread.h" diff --git a/daemon/remote.c b/daemon/remote.c index a69dc5d..a4cedbb 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -50,6 +50,7 @@ #include "virprocess.h" #include "remote_protocol.h" #include "qemu_protocol.h" +#include "lxc_protocol.h" #define VIR_FROM_THIS VIR_FROM_RPC @@ -105,6 +106,7 @@ remoteSerializeDomainDiskErrors(virDomainDiskErrorPtr errors, #include "remote_dispatch.h" #include "qemu_dispatch.h" +#include "lxc_dispatch.h" /* Prototypes */ @@ -4618,6 +4620,56 @@ cleanup: return rv; } +static int +lxcDispatchDomainOpenNamespace(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + lxc_domain_open_namespace_args *args) +{ + int rv = -1; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + int *fdlist = NULL; + int ret; + virDomainPtr dom = NULL; + size_t i; + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) + goto cleanup; + + ret = virDomainLxcOpenNamespace(dom, + &fdlist, + args->flags); + if (ret < 0) + goto cleanup; + + /* We shouldn't have received any from the client, + * but in case they're playing games with us, prevent + * a resource leak + */ + for (i = 0 ; i < msg->nfds ; i++) + VIR_FORCE_CLOSE(msg->fds[i]); + VIR_FREE(msg->fds); + msg->nfds = 0; + + msg->fds = fdlist; + msg->nfds = ret; + + rv = 1; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virDomainFree(dom); + return rv; +} + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff --git a/daemon/remote.h b/daemon/remote.h index 493171f..d42a19e 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -32,6 +32,9 @@ extern virNetServerProgramProc remoteProcs[]; extern size_t remoteNProcs; +extern virNetServerProgramProc lxcProcs[]; +extern size_t lxcNProcs; + extern virNetServerProgramProc qemuProcs[]; extern size_t qemuNProcs; diff --git a/src/Makefile.am b/src/Makefile.am index 13b7e30..d0f431e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -272,19 +272,28 @@ REMOTE_DRIVER_GENERATED = \ $(srcdir)/remote/remote_protocol.c \ $(srcdir)/remote/remote_protocol.h \ $(srcdir)/remote/remote_client_bodies.h \ + $(srcdir)/remote/lxc_protocol.c \ + $(srcdir)/remote/lxc_protocol.h \ + $(srcdir)/remote/lxc_client_bodies.h \ $(srcdir)/remote/qemu_protocol.c \ $(srcdir)/remote/qemu_protocol.h \ $(srcdir)/remote/qemu_client_bodies.h REMOTE_PROTOCOL = $(srcdir)/remote/remote_protocol.x +LXC_PROTOCOL = $(srcdir)/remote/lxc_protocol.x QEMU_PROTOCOL = $(srcdir)/remote/qemu_protocol.x -REMOTE_DRIVER_PROTOCOL = $(REMOTE_PROTOCOL) $(QEMU_PROTOCOL) +REMOTE_DRIVER_PROTOCOL = $(REMOTE_PROTOCOL) $(QEMU_PROTOCOL) $(LXC_PROTOCOL) $(srcdir)/remote/remote_client_bodies.h: $(srcdir)/rpc/gendispatch.pl \ $(REMOTE_PROTOCOL) $(AM_V_GEN)$(PERL) -w $(srcdir)/rpc/gendispatch.pl \ -k remote REMOTE $(REMOTE_PROTOCOL) > $@ +$(srcdir)/remote/lxc_client_bodies.h: $(srcdir)/rpc/gendispatch.pl \ + $(LXC_PROTOCOL) + $(AM_V_GEN)$(PERL) -w $(srcdir)/rpc/gendispatch.pl \ + -k lxc LXC $(LXC_PROTOCOL) > $@ + $(srcdir)/remote/qemu_client_bodies.h: $(srcdir)/rpc/gendispatch.pl \ $(QEMU_PROTOCOL) $(AM_V_GEN)$(PERL) -w $(srcdir)/rpc/gendispatch.pl \ @@ -374,6 +383,7 @@ EXTRA_DIST += check-symfile.pl check-symsorting.pl PROTOCOL_STRUCTS = \ $(srcdir)/remote_protocol-structs \ + $(srcdir)/lxc_protocol-structs \ $(srcdir)/qemu_protocol-structs \ $(srcdir)/virnetprotocol-structs \ $(srcdir)/virkeepaliveprotocol-structs @@ -382,7 +392,7 @@ check-protocol: $(PROTOCOL_STRUCTS) $(PROTOCOL_STRUCTS:structs=struct) # The .o file that pdwtags parses is created as a side effect of running # libtool; but from make's perspective we depend on the .lo file. -$(srcdir)/remote_protocol-struct $(srcdir)/qemu_protocol-struct: \ +$(srcdir)/remote_protocol-struct $(srcdir)/qemu_protocol-struct $(srcdir)/lxc_protocol-struct: \ $(srcdir)/%-struct: libvirt_driver_remote_la-%.lo $(PDWTAGS) $(srcdir)/virnetprotocol-struct $(srcdir)/virkeepaliveprotocol-struct: \ @@ -1546,6 +1556,7 @@ tapset_DATA = libvirt_probes.stp libvirt_qemu_probes.stp libvirt_functions.stp RPC_PROBE_FILES = $(srcdir)/rpc/virnetprotocol.x \ $(srcdir)/rpc/virkeepaliveprotocol.x \ $(srcdir)/remote/remote_protocol.x \ + $(srcdir)/remote/lxc_protocol.x \ $(srcdir)/remote/qemu_protocol.x libvirt_functions.stp: $(RPC_PROBE_FILES) $(srcdir)/rpc/gensystemtap.pl diff --git a/src/remote/lxc_protocol.x b/src/remote/lxc_protocol.x new file mode 100644 index 0000000..f2e0b44 --- /dev/null +++ b/src/remote/lxc_protocol.x @@ -0,0 +1,50 @@ +/* -*- c -*- + * lxc_protocol.x: private protocol for communicating between + * remote_internal driver and libvirtd. This protocol is + * internal and may change at any time. + * + * Copyright (C) 2010-2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Chris Lalancette <clalance@redhat.com> + */ + +%#include "internal.h" +%#include "remote_protocol.h" +%#include <arpa/inet.h> + +/*----- Protocol. -----*/ +struct lxc_domain_open_namespace_args { + remote_nonnull_domain dom; + unsigned int flags; +}; + + +/* Define the program number, protocol version and procedure numbers here. */ +const LXC_PROGRAM = 0x00068000; +const LXC_PROTOCOL_VERSION = 1; + +enum lxc_procedure { + /* Each function must have a three-word comment. The first word is + * whether gendispatch.pl handles daemon, the second whether + * it handles src/remote. + * The last argument describes priority of API. There are two accepted + * values: low, high; Each API that might eventually access hypervisor's + * monitor (and thus block) MUST fall into low priority. However, there + * are some exceptions to this rule, e.g. domainDestroy. Other APIs MAY + * be marked as high priority. If in doubt, it's safe to choose low. */ + LXC_PROC_DOMAIN_OPEN_NAMESPACE = 1 /* skipgen skipgen priority:low */ +}; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index ae861cc..5f9b936 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -37,6 +37,7 @@ #include "virbuffer.h" #include "remote_driver.h" #include "remote_protocol.h" +#include "lxc_protocol.h" #include "qemu_protocol.h" #include "viralloc.h" #include "virutil.h" @@ -77,6 +78,7 @@ struct private_data { virNetClientPtr client; virNetClientProgramPtr remoteProgram; virNetClientProgramPtr qemuProgram; + virNetClientProgramPtr lxcProgram; int counter; /* Serial number for RPC */ @@ -93,6 +95,7 @@ struct private_data { enum { REMOTE_CALL_QEMU = (1 << 0), + REMOTE_CALL_LXC = (1 << 1), }; @@ -110,10 +113,13 @@ static int call(virConnectPtr conn, struct private_data *priv, unsigned int flags, int proc_nr, xdrproc_t args_filter, char *args, xdrproc_t ret_filter, char *ret); -static int callWithFD(virConnectPtr conn, struct private_data *priv, - unsigned int flags, int fd, int proc_nr, - xdrproc_t args_filter, char *args, - xdrproc_t ret_filter, char *ret); +static int callFull(virConnectPtr conn, struct private_data *priv, + unsigned int flags, + int *fdin, size_t fdinlen, + int **fdout, size_t *fdoutlen, + int proc_nr, + xdrproc_t args_filter, char *args, + xdrproc_t ret_filter, char *ret); static int remoteAuthenticate(virConnectPtr conn, struct private_data *priv, virConnectAuthPtr auth, const char *authtype); #if HAVE_SASL @@ -757,6 +763,12 @@ doRemoteOpen(virConnectPtr conn, ARRAY_CARDINALITY(remoteDomainEvents), conn))) goto failed; + if (!(priv->lxcProgram = virNetClientProgramNew(LXC_PROGRAM, + LXC_PROTOCOL_VERSION, + NULL, + 0, + NULL))) + goto failed; if (!(priv->qemuProgram = virNetClientProgramNew(QEMU_PROGRAM, QEMU_PROTOCOL_VERSION, NULL, @@ -765,6 +777,7 @@ doRemoteOpen(virConnectPtr conn, goto failed; if (virNetClientAddProgram(priv->client, priv->remoteProgram) < 0 || + virNetClientAddProgram(priv->client, priv->lxcProgram) < 0 || virNetClientAddProgram(priv->client, priv->qemuProgram) < 0) goto failed; @@ -849,6 +862,7 @@ no_memory: failed: virObjectUnref(priv->remoteProgram); + virObjectUnref(priv->lxcProgram); virObjectUnref(priv->qemuProgram); virNetClientClose(priv->client); virObjectUnref(priv->client); @@ -1011,8 +1025,9 @@ doRemoteClose(virConnectPtr conn, struct private_data *priv) virObjectUnref(priv->client); priv->client = NULL; virObjectUnref(priv->remoteProgram); + virObjectUnref(priv->lxcProgram); virObjectUnref(priv->qemuProgram); - priv->remoteProgram = priv->qemuProgram = NULL; + priv->remoteProgram = priv->qemuProgram = priv->lxcProgram = NULL; /* Free hostname copy */ VIR_FREE(priv->hostname); @@ -5393,6 +5408,8 @@ remoteDomainOpenGraphics(virDomainPtr dom, int rv = -1; remote_domain_open_graphics_args args; struct private_data *priv = dom->conn->privateData; + int fdin[] = { fd }; + size_t fdinlen = ARRAY_CARDINALITY(fdin); remoteDriverLock(priv); @@ -5400,9 +5417,12 @@ remoteDomainOpenGraphics(virDomainPtr dom, args.idx = idx; args.flags = flags; - if (callWithFD(dom->conn, priv, 0, fd, REMOTE_PROC_DOMAIN_OPEN_GRAPHICS, - (xdrproc_t) xdr_remote_domain_open_graphics_args, (char *) &args, - (xdrproc_t) xdr_void, NULL) == -1) + if (callFull(dom->conn, priv, 0, + fdin, fdinlen, + NULL, NULL, + REMOTE_PROC_DOMAIN_OPEN_GRAPHICS, + (xdrproc_t) xdr_remote_domain_open_graphics_args, (char *) &args, + (xdrproc_t) xdr_void, NULL) == -1) goto done; rv = 0; @@ -5507,6 +5527,7 @@ done: } #include "remote_client_bodies.h" +#include "lxc_client_bodies.h" #include "qemu_client_bodies.h" /* @@ -5514,22 +5535,30 @@ done: * send that to the server and wait for reply */ static int -callWithFD(virConnectPtr conn ATTRIBUTE_UNUSED, - struct private_data *priv, - unsigned int flags, - int fd, - int proc_nr, - xdrproc_t args_filter, char *args, - xdrproc_t ret_filter, char *ret) +callFull(virConnectPtr conn ATTRIBUTE_UNUSED, + struct private_data *priv, + unsigned int flags, + int *fdin, + size_t fdinlen, + int **fdout, + size_t *fdoutlen, + int proc_nr, + xdrproc_t args_filter, char *args, + xdrproc_t ret_filter, char *ret) { int rv; - virNetClientProgramPtr prog = flags & REMOTE_CALL_QEMU ? priv->qemuProgram : priv->remoteProgram; + virNetClientProgramPtr prog; int counter = priv->counter++; virNetClientPtr client = priv->client; - int fds[] = { fd }; - size_t nfds = fd == -1 ? 0 : 1; priv->localUses++; + if (flags & REMOTE_CALL_QEMU) + prog = priv->qemuProgram; + else if (flags & REMOTE_CALL_LXC) + prog = priv->lxcProgram; + else + prog = priv->remoteProgram; + /* Unlock, so that if we get any async events/stream data * while processing the RPC, we don't deadlock when our * callbacks for those are invoked @@ -5539,7 +5568,8 @@ callWithFD(virConnectPtr conn ATTRIBUTE_UNUSED, client, counter, proc_nr, - nfds, nfds ? fds : NULL, NULL, NULL, + fdinlen, fdin, + fdoutlen, fdout, args_filter, args, ret_filter, ret); remoteDriverLock(priv); @@ -5556,9 +5586,12 @@ call(virConnectPtr conn, xdrproc_t args_filter, char *args, xdrproc_t ret_filter, char *ret) { - return callWithFD(conn, priv, flags, -1, proc_nr, - args_filter, args, - ret_filter, ret); + return callFull(conn, priv, flags, + NULL, 0, + NULL, NULL, + proc_nr, + args_filter, args, + ret_filter, ret); } @@ -5834,6 +5867,40 @@ done: return rv; } + +static int +remoteDomainLxcOpenNamespace(virDomainPtr domain, + int **fdlist, + unsigned int flags) +{ + int rv = -1; + lxc_domain_open_namespace_args args; + struct private_data *priv = domain->conn->privateData; + size_t nfds = 0; + + remoteDriverLock(priv); + + make_nonnull_domain(&args.dom, domain); + args.flags = flags; + + *fdlist = NULL; + + if (callFull(domain->conn, priv, REMOTE_CALL_LXC, + NULL, 0, + fdlist, &nfds, + LXC_PROC_DOMAIN_OPEN_NAMESPACE, + (xdrproc_t) xdr_lxc_domain_open_namespace_args, (char *) &args, + (xdrproc_t) xdr_void, NULL) == -1) + goto done; + + rv = nfds; + +done: + remoteDriverUnlock(priv); + return rv; +} + + static void remoteDomainEventQueue(struct private_data *priv, virDomainEventPtr event) { @@ -6153,6 +6220,7 @@ static virDriver remote_driver = { .nodeGetMemoryParameters = remoteNodeGetMemoryParameters, /* 0.10.2 */ .nodeGetCPUMap = remoteNodeGetCPUMap, /* 1.0.0 */ .domainFSTrim = remoteDomainFSTrim, /* 1.0.1 */ + .domainLxcOpenNamespace = remoteDomainLxcOpenNamespace, /* 1.0.2 */ }; static virNetworkDriver network_driver = { -- 1.7.11.7

On 12/21/2012 10:08 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Add the infrastructure for the libvirt-lxc.la library to the remote protocol client and daemon
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- daemon/Makefile.am | 13 +++++- daemon/libvirtd.c | 14 ++++++ daemon/libvirtd.h | 1 + daemon/remote.c | 52 +++++++++++++++++++++ daemon/remote.h | 3 ++ src/Makefile.am | 15 +++++- src/remote/lxc_protocol.x | 50 ++++++++++++++++++++ src/remote/remote_driver.c | 112 ++++++++++++++++++++++++++++++++++++--------- 8 files changed, 235 insertions(+), 25 deletions(-) create mode 100644 src/remote/lxc_protocol.x
@@ -382,7 +392,7 @@ check-protocol: $(PROTOCOL_STRUCTS) $(PROTOCOL_STRUCTS:structs=struct)
# The .o file that pdwtags parses is created as a side effect of running # libtool; but from make's perspective we depend on the .lo file. -$(srcdir)/remote_protocol-struct $(srcdir)/qemu_protocol-struct: \ +$(srcdir)/remote_protocol-struct $(srcdir)/qemu_protocol-struct $(srcdir)/lxc_protocol-struct: \ $(srcdir)/%-struct: libvirt_driver_remote_la-%.lo
This line is long, and we already used \-newline to break it; it's probably worth trying to keep it within 80 columns.
+++ b/src/remote/lxc_protocol.x @@ -0,0 +1,50 @@ +/* -*- c -*- + * lxc_protocol.x: private protocol for communicating between + * remote_internal driver and libvirtd. This protocol is + * internal and may change at any time. + * + * Copyright (C) 2010-2012 Red Hat, Inc.
also 2013
+ * + * Author: Chris Lalancette <clalance@redhat.com>
Really? There's so little left of his original .x that this feels like stale copy and paste.
+ */ + +%#include "internal.h" +%#include "remote_protocol.h" +%#include <arpa/inet.h>
Why <arpa/inet.h>? ACK with those things fixed. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> The virDomainLxcOpenNamespace method needs to open every file in /proc/$INITPID/ns and return the open file descriptor to the client application. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 025c35f..185b08f 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -63,6 +63,7 @@ #include "virnetdev.h" #include "virnetdevtap.h" #include "virnodesuspend.h" +#include "virprocess.h" #include "virtime.h" #include "virtypedparam.h" #include "viruri.h" @@ -4467,6 +4468,53 @@ static int lxcDomainDetachDevice(virDomainPtr dom, } +static int lxcDomainOpenNamespace(virDomainPtr dom, + int **fdlist, + unsigned int flags) +{ + virLXCDriverPtr driver = dom->conn->privateData; + virDomainObjPtr vm; + virLXCDomainObjPrivatePtr priv; + int ret = -1; + size_t nfds = 0; + + *fdlist = NULL; + virCheckFlags(0, -1); + + lxcDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + lxcDriverUnlock(driver); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + virReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + priv = vm->privateData; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("Domain is not running")); + goto cleanup; + } + + if (!priv->initpid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Init pid is not yet available")); + goto cleanup; + } + + if (virProcessGetNamespaces(priv->initpid, &nfds, fdlist) < 0) + goto cleanup; + + ret = nfds; +cleanup: + virDomainObjUnlock(vm); + return ret; +} + + /* Function Tables */ static virDriver lxcDriver = { .no = VIR_DRV_LXC, -- 1.7.11.7

On 12/21/2012 10:08 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
The virDomainLxcOpenNamespace method needs to open every file in /proc/$INITPID/ns and return the open file descriptor to the client application.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/lxc/lxc_driver.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+)
ACK. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

From: "Daniel P. Berrange" <berrange@redhat.com> Add a 'lxc-enter-namespace' command which accepts a domain name and then a command + args to run, attached to the container eg virsh -c lxc:/// lxc-enter-namespace demo -- /bin/ps -auxf Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- tools/Makefile.am | 1 + tools/virsh-domain.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/tools/Makefile.am b/tools/Makefile.am index 605bf3b..698db43 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -133,6 +133,7 @@ virsh_LDADD = \ $(STATIC_BINARIES) \ $(WARN_CFLAGS) \ ../src/libvirt.la \ + ../src/libvirt-lxc.la \ ../src/libvirt-qemu.la \ ../gnulib/lib/libgnu.la \ $(LIBXML_LIBS) \ diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index e91939c..cc79fd4 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -43,11 +43,13 @@ #include "conf/domain_conf.h" #include "console.h" #include "viralloc.h" +#include "vircommand.h" #include "virutil.h" #include "virfile.h" #include "virjson.h" #include "virkeycode.h" #include "virmacaddr.h" +#include "virprocess.h" #include "virstring.h" #include "virsh-domain-monitor.h" #include "virerror.h" @@ -6771,6 +6773,103 @@ cleanup: } /* + * "lxc-enter-namespace" namespace + */ +static const vshCmdInfo info_lxc_enter_namespace[] = { + {"help", N_("LXC Guest Enter Namespace")}, + {"desc", N_("Run an arbitrary lxc guest enter namespace; use at your own risk")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_lxc_enter_namespace[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"timeout", VSH_OT_INT, VSH_OFLAG_REQ_OPT, N_("timeout seconds. must be positive.")}, + {"async", VSH_OT_BOOL, 0, N_("execute namespace without waiting for timeout")}, + {"block", VSH_OT_BOOL, 0, N_("execute namespace without timeout")}, + {"cmd", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("namespace")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdLxcEnterNamespace(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + bool ret = false; + const vshCmdOpt *opt = NULL; + char **cmdargv = NULL; + size_t ncmdargv = 0; + pid_t pid; + int nfdlist; + int *fdlist; + size_t i; + + dom = vshCommandOptDomain(ctl, cmd, NULL); + if (dom == NULL) + goto cleanup; + + while ((opt = vshCommandOptArgv(cmd, opt))) { + if (VIR_EXPAND_N(cmdargv, ncmdargv, 1) < 0) { + vshError(ctl, _("%s: %d: failed to allocate argv"), + __FILE__, __LINE__); + exit(EXIT_FAILURE); + } + cmdargv[ncmdargv-1] = opt->data; + } + if (VIR_EXPAND_N(cmdargv, ncmdargv, 1) < 0) { + vshError(ctl, _("%s: %d: failed to allocate argv"), + __FILE__, __LINE__); + exit(EXIT_FAILURE); + } + cmdargv[ncmdargv-1] = NULL; + + if ((nfdlist = virDomainLxcOpenNamespace(dom, &fdlist, 0)) < 0) + goto cleanup; + + /* Fork once because we don't want to affect + * virsh's namespace itself + */ + if (virFork(&pid) < 0) + goto cleanup; + if (pid == 0) { + if (virDomainLxcEnterNamespace(dom, + nfdlist, + fdlist, + NULL, + NULL, + 0) < 0) + _exit(255); + + /* Fork a second time because entering the + * pid namespace only takes effect after fork + */ + if (virFork(&pid) < 0) + _exit(255); + if (pid == 0) { + execv(cmdargv[0], cmdargv); + _exit(255); + } else { + if (virProcessWait(pid, NULL) < 0) + _exit(255); + } + _exit(0); + } else { + for (i = 0 ; i < nfdlist ; i++) + VIR_FORCE_CLOSE(fdlist[i]); + VIR_FREE(fdlist); + if (virProcessWait(pid, NULL) < 0) + goto cleanup; + } + + ret = true; + +cleanup: + if (dom) + virDomainFree(dom); + VIR_FREE(cmdargv); + return ret; +} + +/* * "dumpxml" command */ static const vshCmdInfo info_dumpxml[] = { @@ -8760,6 +8859,7 @@ const vshCmdDef domManagementCmds[] = { {"inject-nmi", cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0}, {"send-key", cmdSendKey, opts_send_key, info_send_key, 0}, {"send-process-signal", cmdSendProcessSignal, opts_send_process_signal, info_send_process_signal, 0}, + {"lxc-enter-namespace", cmdLxcEnterNamespace, opts_lxc_enter_namespace, info_lxc_enter_namespace, 0}, {"managedsave", cmdManagedSave, opts_managedsave, info_managedsave, 0}, {"managedsave-remove", cmdManagedSaveRemove, opts_managedsaveremove, info_managedsaveremove, 0}, -- 1.7.11.7

On 12/21/2012 10:08 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Add a 'lxc-enter-namespace' command which accepts a domain name and then a command + args to run, attached to the container
eg
virsh -c lxc:/// lxc-enter-namespace demo -- /bin/ps -auxf
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- tools/Makefile.am | 1 + tools/virsh-domain.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+)
Missing documentation in virsh.pod. But the command itself looks nice.
+ dom = vshCommandOptDomain(ctl, cmd, NULL); + if (dom == NULL) + goto cleanup; + + while ((opt = vshCommandOptArgv(cmd, opt))) { + if (VIR_EXPAND_N(cmdargv, ncmdargv, 1) < 0) { + vshError(ctl, _("%s: %d: failed to allocate argv"), + __FILE__, __LINE__); + exit(EXIT_FAILURE);
vshError() already exits, so this is dead code.
+ } + cmdargv[ncmdargv-1] = opt->data; + } + if (VIR_EXPAND_N(cmdargv, ncmdargv, 1) < 0) { + vshError(ctl, _("%s: %d: failed to allocate argv"), + __FILE__, __LINE__); + exit(EXIT_FAILURE);
and again.
+ } + cmdargv[ncmdargv-1] = NULL;
space around '-'
@@ -8760,6 +8859,7 @@ const vshCmdDef domManagementCmds[] = { {"inject-nmi", cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0}, {"send-key", cmdSendKey, opts_send_key, info_send_key, 0}, {"send-process-signal", cmdSendProcessSignal, opts_send_process_signal, info_send_process_signal, 0}, + {"lxc-enter-namespace", cmdLxcEnterNamespace, opts_lxc_enter_namespace, info_lxc_enter_namespace, 0}, {"managedsave", cmdManagedSave, opts_managedsave, info_managedsave, 0}, {"managedsave-remove", cmdManagedSaveRemove, opts_managedsaveremove,
Hmm, pre-existing, but these aren't sorted. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 12/21/2012 10:08 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Add a 'lxc-enter-namespace' command which accepts a domain name and then a command + args to run, attached to the container
eg
virsh -c lxc:/// lxc-enter-namespace demo -- /bin/ps -auxf
+static const vshCmdOptDef opts_lxc_enter_namespace[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"timeout", VSH_OT_INT, VSH_OFLAG_REQ_OPT, N_("timeout seconds. must be positive.")}, + {"async", VSH_OT_BOOL, 0, N_("execute namespace without waiting for timeout")}, + {"block", VSH_OT_BOOL, 0, N_("execute namespace without timeout")},
Oh, and I don't see timeout, async, or block actually used in the implementation; did you mean to wire them up? Waiting for v2. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On Mon, Jan 14, 2013 at 05:42:18PM -0700, Eric Blake wrote:
On 12/21/2012 10:08 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Add a 'lxc-enter-namespace' command which accepts a domain name and then a command + args to run, attached to the container
eg
virsh -c lxc:/// lxc-enter-namespace demo -- /bin/ps -auxf
+static const vshCmdOptDef opts_lxc_enter_namespace[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"timeout", VSH_OT_INT, VSH_OFLAG_REQ_OPT, N_("timeout seconds. must be positive.")}, + {"async", VSH_OT_BOOL, 0, N_("execute namespace without waiting for timeout")}, + {"block", VSH_OT_BOOL, 0, N_("execute namespace without timeout")},
Oh, and I don't see timeout, async, or block actually used in the implementation; did you mean to wire them up? Waiting for v2.
-ETOOMUCHCOPYANDPASTE Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

(2012/12/22 2:08), Daniel P. Berrange wrote:
This series introduces an LXC specific library libvirt-lxc.so which adds ability for a process to connect to the namespaces used by an LXC container from outside. It uses FD passing magic to allow the caller to connect, even if it is not root.
Can any user can execute any commands in a LXC guest by # virsh -c lxc:/// lxc-enter-namespace demo -- <command> without any limitation ? Thanks, -Kame

On Wed, Dec 26, 2012 at 10:43:56AM +0900, Kamezawa Hiroyuki wrote:
(2012/12/22 2:08), Daniel P. Berrange wrote:
This series introduces an LXC specific library libvirt-lxc.so which adds ability for a process to connect to the namespaces used by an LXC container from outside. It uses FD passing magic to allow the caller to connect, even if it is not root.
Can any user can execute any commands in a LXC guest by
# virsh -c lxc:/// lxc-enter-namespace demo -- <command>
without any limitation ?
Well you need to be authorized to connect to lxc:/// first, which by default requires you to authorize as root on the host Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
participants (3)
-
Daniel P. Berrange
-
Eric Blake
-
Kamezawa Hiroyuki