[Libvir] PATCH 0/2: Support QEMU (+KVM) in libvirt

The following series of (2) patches adds a QEMU driver to libvirt. The first patch provides a daemon for managing QEMU instances, the second provides a driver letting libvirt manage QEMU via the daemon. Basic architecture ------------------ The reason for the daemon architecture is two fold: - At this time, there is no (practical) way to enumerate QEMU instances, or reliably connect to the monitor console of an existing process. There is also no way to determine the guest configuration associated with a daemon. - It is desirable to be able to manage QEMU instances using either an unprivilegd local client, or a remote client. The daemon can provide connectivity via UNIX domain sockets, or IPv4 / IPv6 and layer in suitable authentication / encryption via TLS and/or SASL protocols. Anthony Ligouri is working on patches for QEMU with the goal of addressing the first point. For example, an extra command line argument will cause QEMU to save a PID file and create a UNIX socket for its monitor at a well-defined path. More functionality in the monitor console will allow the guest configuration to be reverse engineered from a running guest. Even with those patches, however, it will still be desirable to have a daemon to provide more flexible connectivity, and to facilitate implementation libvirt APIs which are host (rather than guest) related. Thus I expect that over time we can simply enhance the daemon to take advantage of newer capabilities in the QEMU monitor, but keep the same basic libvirt driver architecture. Considering some of the other hypervisor technologies out there, in particular User Mode Linux, and lhype, it may well become possible to let this QEMU daemon also provide the management of these guests - allowing re-use of the single driver backend in the libvirt client library itself. XML format ---------- As discussed in the previous mail thread, the XML format for describing guests with the QEMU backend is the same structure as that for Xen guests, with following enhancements: - The 'type' attribute on the top level <domain> tag can take one of the values 'qemu', 'kqemu' or 'kvm' instead of 'xen'. This selects between the different virtualization approaches QEMU can provide. - The '<type>' attribute within the <os> block of the XML (for now) is still expected to the 'hvm' (indicating full virtualization), although I'm trying to think of a better name, since its not technically hardware accelerated unless you're using KVM - The '<type>' attribute within the <os> block of the XML can have two optional 'arch' and 'machine' attributes. The former selects the CPU architecture to be emulated; the latter the specific machine to have QEMU emulate (determine those supported by QEMU using 'qemu -M ?'). - The <kernel>, <initrd>, <cmdline> elements can be used to specify an explicit kernel to boot off[1], otherwise it'll do a boot of the cdrom, harddisk / floppy (based on <boot> element). Well,the kernel bits are parsed at least. I've not got around to using them when building the QEMU argv yet. - The disk devices are configured in same way as Xen HVM guests. eg you have to use hda -> hdd, and/or fda -> fdb. Only hdc can be selected as a cdrom device. - The network configuration is work in progress. QEMU has many ways to setup networking. I use the 'type' attribute to select between the different approachs 'user', 'tap', 'server', 'client', 'mcast' mapping them directly onto QEMU command line arguments. You can specify a MAC address as usual too. I need to implement auto-generation of MAC addresses if omitted. Most of them have extra bits of metadata though which I've not figured out appropriate XML for yet. Thus when building the QEMU argv I currently just hardcode 'user' networking. - The QEMU binary is determined automatically based on the requested CPU architecture, defaulting to i686 if non specified. It is possible to override the default binary using the <emulator> element within the <devices> section. This is different to previously discussed, because recent work by Anthony merging VMI + KVM to give paravirt guests means that the <loader> element is best kept to refer to the VMI ROM (or other ROM like files :-) - this is also closer to Xen semantics anyway. Connectivity ------------ The namespace under which all connection URIs come is 'qemud'. Thereafter there are several options. First, two well-known local hypervisor connections - qemud:///session This is a per-user private hypervisor connection. The libvirt daemon and qemu guest processes just run as whatever UNIX user your client app is running. This lets unprivileged users use the qemu driver without needing any kind admin rights. Obviously you can't use KQEMU or KVM accelerators unless the /dev/ device node is chmod/chown'd to give you access. The communication goes over a UNIX domain socket which is mode 0600 created in the abstract namespace at $HOME/.qemud.d/sock. - qemud:///system This is a system-wide privileged hypervisor connection. There is only one of these on any given machine. The libvirt_qemud daemon would be started ahead of time (by an init script), possibly running as root, or maybe under a dedicated system user account (and the KQEMU/KVM devices chown'd to match). The admin would optionally also make it listen on IPv4/6 addrs to allow remote communication. (see next URI example) The local communication goes over one of two possible UNIX domain sockets Both in the abstract namespace under the directory /var/run. The first socket called 'qemud' is mode 0600, so only privileged apps (ie root) can access it, and gives full control capabilities. The other called 'qemud-ro' is mode 0666 and any clients connecting to it will be restricted to only read-only libvirt operations by the server. - qemud://hostname:port/ This lets you connect to a daemon over IPv4 or IPv6. If omitted the port is 8123 (will probably change it). This lets you connect to a system daemon on a remote host - assuming it was configured to listen on IPv4/6 interfaces. Currently there is zero auth or encryption, but I'm planning to make it mandortory to use the TLS protocol - using the GNU TLS library. This will give encryption, and mutual authentication using either x509 certificates or PGP keys & trustdbs or perhaps both :-) Will probably start off by implementing PGP since I understand it better. So if you wanted to remotely manage a server, you'd copy the server's certificate/public key to the client into a well known location. Similarly you'd generate a keypair for the client & copy its public key to the server. Perhaps I'll allow clients without a key to connect in read-only mode. Need to prototype it first and then write up some ideas. Server architecture ------------------- The server is a fairly simple beast. It is single-threaded using non-blocking I/O and poll() for all operations. It will listen on multiple sockets for incoming connections. The protocol used for client-server comms is a very simple binary message format close to the existing libvirt_proxy. Client sends a message, server receives it, performs appropriate operation & sends a reply to the client. The client (ie libvirt driver) blocks after sending its message until it gets a reply. The server does non-blocking reads from the client buffering until it has a single complete message, then processes it and populates the buffer with a reply and does non-blocking writes to send it back to the client. It won't try to read a further message from the client until its sent the entire reply back. ie, it is a totally synchronous message flow - no batching/pipelining of messages. During the time the server is processes a message it is not dealing with any other I/O, but thus far all the operations are very fast to implement, so this isn't a serious issue, and there ways to deal with it if there are operations which turn out to take a long time. I certainly want to avoid multi-threading in the server at all costs! As well as monitoring the client & client sockets, the poll() event loop in the server also captures stdout & stderr from the QEMU processes. Currently we just dump this to stdout of the daemon, but I expect we can log it somewhere. When we start accessing the QEMU monitor there will be another fd in the event loop - ie the pseduo-TTY (or UNIX socket) on which we talk to the monitor. Inactive guests --------------- Guests created using 'virsh create' (or equiv API) are treated as 'transient' domains - ie their config files are not saved to disk. This is consistent with the behaviour in the Xen backend. Guests created using 'virsh define', however, are saved out to disk in $HOME/.qemud.d for the per-user session daemon. The system-wide daemon should use /etc/qemud.d, but currently its still /root/.qemud.d The config files are simply saved as the libvirt XML blob ensuring no data conversion issues. In any case, QEMU doesn't currently have any config file format we can leverage. The list of inactive guests is loaded at startup of the daemon. New config files are expected to be created via the API - files manually created in the directory after initial startup are not seen. Might like to change this later. XML Examples ------------ This is a guest using plain qemu, with x86_64 architecture and a ISA-only (ie no PCI) machine emulation. I was actually running this on a 32-bit host :-) VNC is configured to run on port 5906. QEMU can't automatically choose a VNC port, so if one isn't specified we assign one based on the domain ID. This should be fixed in QEMU.... <domain type='qemu'> <name>demo1</name> <uuid>4dea23b3-1d52-d8f3-2516-782e98a23fa0</uuid> <memory>131072</memory> <vcpu>1</vcpu> <os> <type arch='x86_64' machine='isapc'>hvm</type> </os> <devices> <disk type='file' device='disk'> <source file='/home/berrange/fedora/diskboot.img'/> <target dev='hda'/> </disk> <interface type='user'> <mac address='24:42:53:21:52:45'/> </interface> <graphics type='vnc' port='5906'/> </devices> </domain> A second example, this time using KVM acceleration. Note how I specify a non-default path to QEMU to pick up the KVM build of QEMU. Normally KVM binary will default to /usr/bin/qemu-kvm - this may change depending on how distro packaging of KVM turns out - it may even be merged into regular QEMU binaries. <domain type='kvm'> <name>demo2</name> <uuid>4dea24b3-1d52-d8f3-2516-782e98a23fa0</uuid> <memory>131072</memory> <vcpu>1</vcpu> <os> <type>hvm</type> </os> <devices> <emulator>/home/berrange/usr/kvm-devel/bin/qemu-system-x86_64</emulator> <disk type='file' device='disk'> <source file='/home/berrange/fedora/diskboot.img'/> <target dev='hda'/> </disk> <interface type='user'> <mac address='24:42:53:21:52:45'/> </interface> <graphics type='vnc' port='-1'/> </devices> </domain> Outstanding work ---------------- - TLS support. Need to add TLS encryption & authentication to both the client and server side for IPv4/6 communications. This will obviously add a dependancy on libgnutls.so in libvirt & the daemon. I don't consider this a major problem since every non-trivial network app these days uses TLS. The other possible impl of OpenSSL has GPL-compatability issues, so is not considered. - Change the wire format to use fixed size data types (ie, int8, int16, int32, etc) instead of the size-dependant int/long types. At same time define some rules for the byte ordering. Client must match server ordering ? Server must accept client's desired ordering ? Everyone must use BE regardless of server/client format ? I'm inclined to say client must match server, since it distributes the byte-swapping overhead to all clients and lets the common case of x86->x86 be a no-op. - Add a protocol version message as first option to let use protocol at will later while maintaining compat with older libvirt client libraries. - Improve support for describing the various QEMU network configurations - Finish boot options - boot device order & explicit kernel - Open & use connection to QEMU monitor which will let us implement pause/resume, suspend/restore drivers, and device hotplug / media changes. - Return sensible data for virNodeInfo - will need to have operating system dependant code here - parsing /proc for Linux to determine available RAM & CPU speed. Who knows what for Solaris / BSD ?!? Anyone know of remotely standard ways for doing this. Accurate host memory reporting is the only really critical data item we need. - There is a fair bit of duplicate in various helper functions between the daemon, and various libvirt driver backends. We should probably pull this stuff out into a separate lib/ directoy, build it into a static library and then link that into both libvirt, virsh & the qemud daemon as needed. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

The attached patch provides the QEMU daemon for managing the QEMU instances and providing a network protocol for the libvirt driver to talk to over UNIX domain sockets or IPv4/6. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Fri, Jan 05, 2007 at 09:16:54PM +0000, Daniel P. Berrange wrote:
+static int qemudParseUUID(const char *uuid, + unsigned char *rawuuid) { + const char *cur; + int i; + + /* + * do a liberal scan allowing '-' and ' ' anywhere between character + * pairs as long as there is 32 of them in the end. + */ + cur = uuid; + for (i = 0;i < 16;) { + rawuuid[i] = 0; + if (*cur == 0) + goto error; + if ((*cur == '-') || (*cur == ' ')) { + cur++; + continue; + } + if ((*cur >= '0') && (*cur <= '9'))
isdigit() ? :-)
+/* Return the default machine type for a given architecture */ +static const char *qemudDefaultMachineForArch(const char *arch) { + int i; + + for (i = 0 ; i < (int)(sizeof(archs) / sizeof(struct qemu_arch_info)) ; i++) {
#define NR_ITEMS(x) (int)(sizeof(x)/ sizeof(*x))
+/* + * Parses a libvirt XML definition of a guest, and populates the + * the qemud_vm struct with matching data about the guests config + */ +static int qemudParseXML(struct qemud_server *server, + xmlDocPtr xml, + struct qemud_vm *vm) { + xmlNodePtr root = NULL; + xmlChar *prop = NULL; + xmlXPathContextPtr ctxt = NULL; + xmlXPathObjectPtr obj = NULL; + char *conv = NULL; + + /* Prepare parser / xpath context */ + root = xmlDocGetRootElement(xml); + if ((root == NULL) || (!xmlStrEqual(root->name, BAD_CAST "domain"))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "incorrect root element"); + goto error; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + qemudReportError(server, VIR_ERR_NO_MEMORY, "xmlXPathContext"); + goto error; + } + + + /* Find out what type of QEMU virtualization to use */ + if (!(prop = xmlGetProp(root, BAD_CAST "type"))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "missing domain type attribute"); + goto error; + }
....
+ + error: + /* XXX free all the stuff in the qemud_vm struct, or leave it upto + the caller ? */ + if (prop) + free(prop); + if (obj) + xmlXPathFreeObject(obj);
if (ctxt) xmlXPathFreeContext(ctxt);
+ return -1; +} + +/* + * Constructs a argv suitable for launching qemu with config defined + * for a given virtual machine. + */ +int qemudBuildCommandLine(struct qemud_server *server, + struct qemud_vm *vm, + char ***argv, + int *argc) { + int n = -1; + char memory[50]; + char vcpus[50]; + struct qemud_vm_disk_def *disk = vm->def.disks; + struct qemud_vm_net_def *net = vm->def.nets; + + *argc = 1 + /* qemu */ + 2 + /* machine type */ + (vm->def.virtType == QEMUD_VIRT_QEMU ? 1 : 0) + /* Disable kqemu */ + 2 * vm->def.ndisks + /* disks*/ + (vm->def.nnets > 0 ? (4 * vm->def.nnets) : 2) + /* networks */ + 2 + /* memory*/ + 2 + /* cpus */ + (vm->def.graphicsType == QEMUD_GRAPHICS_VNC ? 2 : + (vm->def.graphicsType == QEMUD_GRAPHICS_SDL ? 0 : 1)); /* graphics */ + + sprintf(memory, "%d", vm->def.memory/1024);
vm->def.memory >> 10 (professional deformation from BaseOS packages... :-)
+ sprintf(vcpus, "%d", vm->def.vcpus); + + if (!(*argv = malloc(sizeof(char *) * (*argc +1)))) + goto no_memory; + if (!((*argv)[++n] = strdup(vm->def.os.binary))) + goto no_memory; + if (!((*argv)[++n] = strdup("-M"))) + goto no_memory;
Hmm... you reallocate static strings. I think you don't have to care about argv[] if you create it after fork() in a child process. It seems odd allocate and dealocate it in a parent if you need't it the parent process.
+/* Scan for all guest config files */ +int qemudScanConfigs(struct qemud_server *server) { + DIR *dir; + struct dirent *entry; + + if (!(dir = opendir(server->configDir))) { + if (errno == ENOENT) + return 0; + return -1; + } + + while ((entry = readdir(dir))) { + char file[PATH_MAX]; + if (entry->d_name[0] == '.') + continue;
is ".myconfig" forbidden filename? Otherwise: if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
+/* Simple grow-on-demand string buffer */
Hmm... It seems like duplicate code to virBufferAdd() (xml.c)
+/* XXX re-factor to shared library */ +struct qemudBuffer { + char *data; + int len; + int used; +}; + +static +int qemudBufferAdd(struct qemudBuffer *buf, const char *str) { + int need = strlen(str);
It's huge patch. Now (at 2:00 AM) I'm too tired... night! ;-) Karel -- Karel Zak <kzak@redhat.com>

On Mon, Jan 08, 2007 at 02:08:21AM +0100, Karel Zak wrote:
On Fri, Jan 05, 2007 at 09:16:54PM +0000, Daniel P. Berrange wrote:
+static int qemudParseUUID(const char *uuid, + unsigned char *rawuuid) { + const char *cur; + int i; + + /* + * do a liberal scan allowing '-' and ' ' anywhere between character + * pairs as long as there is 32 of them in the end. + */ + cur = uuid; + for (i = 0;i < 16;) { + rawuuid[i] = 0; + if (*cur == 0) + goto error; + if ((*cur == '-') || (*cur == ' ')) { + cur++; + continue; + } + if ((*cur >= '0') && (*cur <= '9'))
isdigit() ? :-)
locale dependant so please no no no :-)
+ sprintf(memory, "%d", vm->def.memory/1024);
vm->def.memory >> 10
(professional deformation from BaseOS packages... :-)
I would avoid that too, less readable, harder to maintaine, and the compiler is better at optimizing anyway.
+ while ((entry = readdir(dir))) { + char file[PATH_MAX]; + if (entry->d_name[0] == '.') + continue;
is ".myconfig" forbidden filename? Otherwise:
honnestly I would avoid it, yes :-) Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Mon, Jan 08, 2007 at 02:08:21AM +0100, Karel Zak wrote:
On Fri, Jan 05, 2007 at 09:16:54PM +0000, Daniel P. Berrange wrote:
+/* Return the default machine type for a given architecture */ +static const char *qemudDefaultMachineForArch(const char *arch) { + int i; + + for (i = 0 ; i < (int)(sizeof(archs) / sizeof(struct qemu_arch_info)) ; i++) {
#define NR_ITEMS(x) (int)(sizeof(x)/ sizeof(*x))
Good idea.
+ + error: + /* XXX free all the stuff in the qemud_vm struct, or leave it upto + the caller ? */ + if (prop) + free(prop); + if (obj) + xmlXPathFreeObject(obj);
if (ctxt) xmlXPathFreeContext(ctxt);
Yes, nice bug.
+ sprintf(vcpus, "%d", vm->def.vcpus); + + if (!(*argv = malloc(sizeof(char *) * (*argc +1)))) + goto no_memory; + if (!((*argv)[++n] = strdup(vm->def.os.binary))) + goto no_memory; + if (!((*argv)[++n] = strdup("-M"))) + goto no_memory;
Hmm... you reallocate static strings. I think you don't have to care about argv[] if you create it after fork() in a child process. It seems odd allocate and dealocate it in a parent if you need't it the parent process.
Currently I create the argv[] in the main daemon process, principally so I can easily log the command line to stdout (the child has stdout redirected). I might be able to move the argv[] creation to the child which would let me avoid strdup'ing the static strings. It might lead to subtle bug in future if someone didn't pay attention of the contract of the method.
+ if (entry->d_name[0] == '.') + continue;
is ".myconfig" forbidden filename? Otherwise:
Yeah, I was thinking to only allow config files / vm names with the letters a-Z, 0-9, -, _ and requiring that they start with a letter. Perhaps that's overly/unneccessarily strict though.
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
+/* Simple grow-on-demand string buffer */
Hmm... It seems like duplicate code to virBufferAdd() (xml.c)
Yes, this is one of the bits needing refactoring to be shared across various places in libvirt. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Fri, Jan 05, 2007 at 09:16:54PM +0000, Daniel P. Berrange wrote:
The attached patch provides the QEMU daemon for managing the QEMU instances and providing a network protocol for the libvirt driver to talk to over UNIX domain sockets or IPv4/6.
Okay, the size of the patch is big :-) I have tried to review it fully but certainly didn't grasped all of it ! Still I found a few things... comments in context inside. Daniel
Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|
diff -ruN libvirt/qemud/config.c libvirt-qemu/qemud/config.c --- libvirt/qemud/config.c 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/config.c 2007-01-04 12:11:49.000000000 -0500 @@ -0,0 +1,1234 @@ +/* + * config.c: VM configuration management + * + * Copyright (C) 2006, 2007 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <dirent.h> +#include <string.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> +#include <libxml/uri.h> + +#include <libvirt/virterror.h> + +#include "protocol.h" +#include "internal.h" +#include "config.h" +#include "driver.h" + +static int qemudParseUUID(const char *uuid, + unsigned char *rawuuid) { + const char *cur; + int i; + + /* + * do a liberal scan allowing '-' and ' ' anywhere between character + * pairs as long as there is 32 of them in the end. + */ + cur = uuid; + for (i = 0;i < 16;) { + rawuuid[i] = 0; + if (*cur == 0) + goto error; + if ((*cur == '-') || (*cur == ' ')) { + cur++; + continue; + } + if ((*cur >= '0') && (*cur <= '9')) + rawuuid[i] = *cur - '0'; + else if ((*cur >= 'a') && (*cur <= 'f')) + rawuuid[i] = *cur - 'a' + 10; + else if ((*cur >= 'A') && (*cur <= 'F')) + rawuuid[i] = *cur - 'A' + 10; + else + goto error; + rawuuid[i] *= 16; + cur++; + if (*cur == 0) + goto error; + if ((*cur >= '0') && (*cur <= '9')) + rawuuid[i] += *cur - '0'; + else if ((*cur >= 'a') && (*cur <= 'f')) + rawuuid[i] += *cur - 'a' + 10; + else if ((*cur >= 'A') && (*cur <= 'F')) + rawuuid[i] += *cur - 'A' + 10; + else + goto error; + i++; + cur++; + } + + return 0; + + error: + return -1; +} +
need to go in lib/
+struct qemu_arch_info { + const char *arch; + const char **machines; + const char *binary; +}; + +/* The list of possible machine types for various architectures, + as supported by QEMU - taken from 'qemu -M ?' for each arch */ +static const char *arch_info_x86_machines[] = { + "pc", "isapc" +}; +static const char *arch_info_mips_machines[] = { + "mips" +}; +static const char *arch_info_sparc_machines[] = { + "sun4m" +}; +static const char *arch_info_ppc_machines[] = { + "g3bw", "mac99", "prep" +};
Hum, I wonder how we are gonna keep the sync :-)
+/* The archicture tables for supported QEMU archs */ +static struct qemu_arch_info archs[] = { + { "i686", arch_info_x86_machines, "qemu" }, + { "x86_64", arch_info_x86_machines, "qemu-system-x86_64" }, + { "mips", arch_info_mips_machines, "qemu-system-mips" }, + { "mipsel", arch_info_mips_machines, "qemu-system-mipsel" }, + { "sparc", arch_info_sparc_machines, "qemu-system-sparc" }, + { "ppc", arch_info_ppc_machines, "qemu-system-ppc" }, +}; + +/* Return the default architecture if none is explicitly requested*/ +static const char *qemudDefaultArch(void) { + return archs[0].arch; +} + +/* Return the default machine type for a given architecture */ +static const char *qemudDefaultMachineForArch(const char *arch) { + int i; + + for (i = 0 ; i < (int)(sizeof(archs) / sizeof(struct qemu_arch_info)) ; i++) { + if (!strcmp(archs[i].arch, arch)) { + return archs[i].machines[0]; + } + } + + return NULL; +} + +/* Return the default binary name for a particular architecture */ +static const char *qemudDefaultBinaryForArch(const char *arch) { + int i; + + for (i = 0 ; i < (int)(sizeof(archs) / sizeof(struct qemu_arch_info)) ; i++) { + if (!strcmp(archs[i].arch, arch)) { + return archs[i].binary; + } + } + + return NULL; +} + +/* Find the fully qualified path to the binary for an architecture */ +static char *qemudLocateBinaryForArch(struct qemud_server *server, + int virtType, const char *arch) { + const char *name; + char *path; + + if (virtType == QEMUD_VIRT_KVM) + name = "qemu-kvm"; + else + name = qemudDefaultBinaryForArch(arch); + + if (!name) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot determin binary for architecture %s", arch); + return NULL; + } + + /* XXX lame. should actually use $PATH ... */ + path = malloc(strlen(name) + strlen("/usr/bin/") + 1); + if (!path) { + qemudReportError(server, VIR_ERR_NO_MEMORY, "path"); + return NULL; + } + strcpy(path, "/usr/bin/"); + strcat(path, name); + return path; +}
Shouldn't we walk $PATH to look up ?
+/* Parse the XML definition for a disk */ +static struct qemud_vm_disk_def *qemudParseDiskXML(struct qemud_server *server, + xmlNodePtr node) { + struct qemud_vm_disk_def *disk = calloc(1, sizeof(struct qemud_vm_disk_def)); + xmlNodePtr cur; + xmlChar *device = NULL; + xmlChar *source = NULL; + xmlChar *target = NULL; + xmlChar *type = NULL; + int typ = 0; + + if (!disk) { + qemudReportError(server, VIR_ERR_NO_MEMORY, "disk"); + return NULL; + } + + type = xmlGetProp(node, BAD_CAST "type"); + if (type != NULL) { + if (xmlStrEqual(type, BAD_CAST "file")) + typ = QEMUD_DISK_FILE; + else if (xmlStrEqual(type, BAD_CAST "block")) + typ = QEMUD_DISK_BLOCK; + else { + typ = QEMUD_DISK_FILE; + } + xmlFree(type); + type = NULL; + } + + device = xmlGetProp(node, BAD_CAST "device"); + + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if ((source == NULL) && + (xmlStrEqual(cur->name, BAD_CAST "source"))) { + + if (typ == QEMUD_DISK_FILE) + source = xmlGetProp(cur, BAD_CAST "file"); + else + source = xmlGetProp(cur, BAD_CAST "dev"); + } else if ((target == NULL) && + (xmlStrEqual(cur->name, BAD_CAST "target"))) { + target = xmlGetProp(cur, BAD_CAST "dev"); + } else if (xmlStrEqual(cur->name, BAD_CAST "readonly")) { + disk->readonly = 1; + } + } + cur = cur->next; + } + + if (source == NULL) { + qemudReportError(server, VIR_ERR_NO_SOURCE, target ? "%s" : NULL, target); + goto error; + } + if (target == NULL) { + qemudReportError(server, VIR_ERR_NO_TARGET, source ? "%s" : NULL, source); + goto error; + } + + if (device && + !strcmp((const char *)device, "floppy") && + strcmp((const char *)target, "fda") && + strcmp((const char *)target, "fdb")) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Invalid floppy device name: %s", target); + goto error; + } + + if (device && + !strcmp((const char *)device, "cdrom") && + strcmp((const char *)target, "hdc")) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Invalid cdrom device name: %s", target); + goto error; + } + + if (device && + !strcmp((const char *)device, "cdrom")) + disk->readonly = 1; + + if ((!device || !strcmp((const char *)device, "disk")) && + strcmp((const char *)target, "hda") && + strcmp((const char *)target, "hdb") && + strcmp((const char *)target, "hdc") && + strcmp((const char *)target, "hdd")) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Invalid harddisk device name: %s", target); + goto error; + } + + strncpy(disk->src, (const char *)source, NAME_MAX-1); + disk->src[NAME_MAX-1] = '\0'; + + strncpy(disk->dst, (const char *)target, NAME_MAX-1); + disk->dst[NAME_MAX-1] = '\0'; + disk->type = typ; + + if (!device) + disk->device = QEMUD_DISK_DISK; + else if (!strcmp((const char *)device, "disk")) + disk->device = QEMUD_DISK_DISK; + else if (!strcmp((const char *)device, "cdrom")) + disk->device = QEMUD_DISK_CDROM; + else if (!strcmp((const char *)device, "floppy")) + disk->device = QEMUD_DISK_FLOPPY; + else { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Invalid device type: %s", device); + goto error; + } + + xmlFree(device); + xmlFree(target); + xmlFree(source); + + return disk; + + error: + if (type) + xmlFree(type); + if (target) + xmlFree(target); + if (source) + xmlFree(source); + if (device) + xmlFree(device); + free(disk); + return NULL; +} + + +/* Parse the XML definition for a network interface */ +static struct qemud_vm_net_def *qemudParseInterfaceXML(struct qemud_server *server, + xmlNodePtr node) { + struct qemud_vm_net_def *net = calloc(1, sizeof(struct qemud_vm_net_def)); + xmlNodePtr cur; + xmlChar *macaddr = NULL; + xmlChar *type = NULL; + + if (!net) { + qemudReportError(server, VIR_ERR_NO_MEMORY, "net"); + return NULL; + } + + net->type = QEMUD_NET_USER; + + type = xmlGetProp(node, BAD_CAST "type"); + if (type != NULL) { + if (xmlStrEqual(type, BAD_CAST "user")) + net->type = QEMUD_NET_USER; + else if (xmlStrEqual(type, BAD_CAST "tap")) + net->type = QEMUD_NET_TAP; + else if (xmlStrEqual(type, BAD_CAST "server")) + net->type = QEMUD_NET_SERVER; + else if (xmlStrEqual(type, BAD_CAST "client")) + net->type = QEMUD_NET_CLIENT; + else if (xmlStrEqual(type, BAD_CAST "mcast")) + net->type = QEMUD_NET_MCAST; + /* + else if (xmlStrEqual(type, BAD_CAST "vde")) + typ = QEMUD_NET_VDE; + */ + else + net->type = QEMUD_NET_USER; + xmlFree(type); + type = NULL; + } + + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if ((macaddr == NULL) && + (xmlStrEqual(cur->name, BAD_CAST "mac"))) { + macaddr = xmlGetProp(cur, BAD_CAST "address"); + } + } + cur = cur->next; + } + + net->vlan = 0; + + if (macaddr) { + sscanf((const char *)macaddr, "%02x:%02x:%02x:%02x:%02x:%02x", + (unsigned int*)&net->mac[0], + (unsigned int*)&net->mac[1], + (unsigned int*)&net->mac[2], + (unsigned int*)&net->mac[3], + (unsigned int*)&net->mac[4], + (unsigned int*)&net->mac[5]); + } + + xmlFree(macaddr); + + return net; + + /* + error: + if (macaddr) + xmlFree(macaddr); + free(net); + return NULL; + */ +} + + +/* + * Parses a libvirt XML definition of a guest, and populates the + * the qemud_vm struct with matching data about the guests config + */ +static int qemudParseXML(struct qemud_server *server, + xmlDocPtr xml, + struct qemud_vm *vm) { + xmlNodePtr root = NULL; + xmlChar *prop = NULL; + xmlXPathContextPtr ctxt = NULL; + xmlXPathObjectPtr obj = NULL; + char *conv = NULL; + + /* Prepare parser / xpath context */ + root = xmlDocGetRootElement(xml); + if ((root == NULL) || (!xmlStrEqual(root->name, BAD_CAST "domain"))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "incorrect root element"); + goto error; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + qemudReportError(server, VIR_ERR_NO_MEMORY, "xmlXPathContext"); + goto error; + } + + + /* Find out what type of QEMU virtualization to use */ + if (!(prop = xmlGetProp(root, BAD_CAST "type"))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "missing domain type attribute"); + goto error; + } + + if (!strcmp((char *)prop, "qemu")) + vm->def.virtType = QEMUD_VIRT_QEMU; + else if (!strcmp((char *)prop, "kqemu")) + vm->def.virtType = QEMUD_VIRT_KQEMU; + else if (!strcmp((char *)prop, "kvm")) + vm->def.virtType = QEMUD_VIRT_KVM; + else { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "invalid domain type attribute"); + goto error; + } + free(prop); + prop = NULL; + + + /* Extract domain name */ + obj = xmlXPathEval(BAD_CAST "string(/domain/name[1])", ctxt); + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + qemudReportError(server, VIR_ERR_NO_NAME, NULL); + goto error; + } + if (strlen((const char *)obj->stringval) >= (QEMUD_MAX_NAME_LEN-1)) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "domain name length too long"); + goto error; + } + strcpy(vm->def.name, (const char *)obj->stringval); + xmlXPathFreeObject(obj); + + + /* Extract domain uuid */ + obj = xmlXPathEval(BAD_CAST "string(/domain/uuid[1])", ctxt); + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + /* XXX auto-generate a UUID */ + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "missing uuid element"); + goto error; + } + if (qemudParseUUID((const char *)obj->stringval, vm->def.uuid) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "malformed uuid element"); + goto error; + } + xmlXPathFreeObject(obj); + + + /* Extract domain memory */ + obj = xmlXPathEval(BAD_CAST "string(/domain/memory[1])", ctxt); + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + vm->def.memory = 131072; /* 128 MB of ram */ + } else { + conv = NULL; + vm->def.memory = strtoll((const char*)obj->stringval, &conv, 10); + if (conv == (const char*)obj->stringval) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "malformed memory information"); + goto error; + } + } + if (obj) + xmlXPathFreeObject(obj); + + + /* Extract domain vcpu info */ + obj = xmlXPathEval(BAD_CAST "string(/domain/vcpu[1])", ctxt); + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + vm->def.vcpus = 1; + } else { + conv = NULL; + vm->def.vcpus = strtoll((const char*)obj->stringval, &conv, 10); + if (conv == (const char*)obj->stringval) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "malformed vcpu information"); + goto error; + } + } + if (obj) + xmlXPathFreeObject(obj); + + + /* Extract OS type info */ + obj = xmlXPathEval(BAD_CAST "string(/domain/os/type[1])", ctxt); + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + qemudReportError(server, VIR_ERR_OS_TYPE, NULL); + goto error; + } + if (strcmp((const char *)obj->stringval, "hvm")) { + qemudReportError(server, VIR_ERR_OS_TYPE, "%s", obj->stringval); + goto error; + } + strcpy(vm->def.os.type, (const char *)obj->stringval); + xmlXPathFreeObject(obj); + + + obj = xmlXPathEval(BAD_CAST "string(/domain/os/type[1]/@arch)", ctxt); + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + const char *defaultArch = qemudDefaultArch(); + if (strlen(defaultArch) >= (QEMUD_OS_TYPE_MAX_LEN-1)) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "architecture type too long"); + goto error; + } + strcpy(vm->def.os.arch, defaultArch); + } else { + if (strlen((const char *)obj->stringval) >= (QEMUD_OS_TYPE_MAX_LEN-1)) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "architecture type too long"); + goto error; + } + strcpy(vm->def.os.arch, (const char *)obj->stringval); + } + if (obj) + xmlXPathFreeObject(obj); + + obj = xmlXPathEval(BAD_CAST "string(/domain/os/type[1]/@machine)", ctxt); + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + const char *defaultMachine = qemudDefaultMachineForArch(vm->def.os.arch); + if (strlen(defaultMachine) >= (QEMUD_OS_MACHINE_MAX_LEN-1)) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "machine type too long"); + goto error; + } + strcpy(vm->def.os.machine, defaultMachine); + } else { + if (strlen((const char *)obj->stringval) >= (QEMUD_OS_MACHINE_MAX_LEN-1)) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "architecture type too long"); + goto error; + } + strcpy(vm->def.os.machine, (const char *)obj->stringval); + } + if (obj) + xmlXPathFreeObject(obj); + + obj = xmlXPathEval(BAD_CAST "string(/domain/devices/emulator[1])", ctxt); + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + char *tmp = qemudLocateBinaryForArch(server, vm->def.virtType, vm->def.os.arch); + if (!tmp) { + goto error; + } + strcpy(vm->def.os.binary, tmp); + free(tmp); + } else { + if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "emulator path too long"); + goto error; + } + strcpy(vm->def.os.binary, (const char *)obj->stringval); + } + if (obj) + xmlXPathFreeObject(obj); + + obj = xmlXPathEval(BAD_CAST "/domain/devices/graphics", ctxt); + if ((obj == NULL) || (obj->type != XPATH_NODESET) || + (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) { + vm->def.graphicsType = QEMUD_GRAPHICS_NONE; + } else { + prop = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "type"); + if (!strcmp((char *)prop, "vnc")) { + vm->def.graphicsType = QEMUD_GRAPHICS_VNC; + prop = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "port"); + if (prop) { + conv = NULL; + vm->def.vncPort = strtoll((const char*)prop, &conv, 10); + } else { + vm->def.vncPort = -1; + } + } else if (!strcmp((char *)prop, "sdl")) { + vm->def.graphicsType = QEMUD_GRAPHICS_SDL; + } else { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Unsupported graphics type %s", prop); + goto error; + } + } + + /* analysis of the disk devices */ + obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt); + if ((obj != NULL) && (obj->type == XPATH_NODESET) && + (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) { + for (int i = 0; i < obj->nodesetval->nodeNr; i++) { + struct qemud_vm_disk_def *disk; + if (!(disk = qemudParseDiskXML(server, obj->nodesetval->nodeTab[i]))) { + goto error; + } + vm->def.ndisks++; + disk->next = vm->def.disks; + vm->def.disks = disk; + } + } + xmlXPathFreeObject(obj); + + + /* analysis of the network devices */ + obj = xmlXPathEval(BAD_CAST "/domain/devices/interface", ctxt); + if ((obj != NULL) && (obj->type == XPATH_NODESET) && + (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) { + for (int i = 0; i < obj->nodesetval->nodeNr; i++) { + struct qemud_vm_net_def *net; + if (!(net = qemudParseInterfaceXML(server, obj->nodesetval->nodeTab[i]))) { + goto error; + } + vm->def.nnets++; + net->next = vm->def.nets; + vm->def.nets = net; + } + } + xmlXPathFreeObject(obj); + + return 0; + + error: + /* XXX free all the stuff in the qemud_vm struct, or leave it upto + the caller ? */ + if (prop) + free(prop); + if (obj) + xmlXPathFreeObject(obj); + return -1; +} +
Hum, there is a lot of duplication in the parsing, at least I should try to provide libxml2 XPath lookup based functions instead of duplicating xmlXPathEval all over the place with relatively complex cleanup etc...
+/* + * Constructs a argv suitable for launching qemu with config defined + * for a given virtual machine. + */ +int qemudBuildCommandLine(struct qemud_server *server, + struct qemud_vm *vm, + char ***argv, + int *argc) { + int n = -1; + char memory[50]; + char vcpus[50]; + struct qemud_vm_disk_def *disk = vm->def.disks; + struct qemud_vm_net_def *net = vm->def.nets; + + *argc = 1 + /* qemu */ + 2 + /* machine type */ + (vm->def.virtType == QEMUD_VIRT_QEMU ? 1 : 0) + /* Disable kqemu */ + 2 * vm->def.ndisks + /* disks*/ + (vm->def.nnets > 0 ? (4 * vm->def.nnets) : 2) + /* networks */ + 2 + /* memory*/ + 2 + /* cpus */ + (vm->def.graphicsType == QEMUD_GRAPHICS_VNC ? 2 : + (vm->def.graphicsType == QEMUD_GRAPHICS_SDL ? 0 : 1)); /* graphics */ + + sprintf(memory, "%d", vm->def.memory/1024); + sprintf(vcpus, "%d", vm->def.vcpus); + + if (!(*argv = malloc(sizeof(char *) * (*argc +1)))) + goto no_memory; + if (!((*argv)[++n] = strdup(vm->def.os.binary))) + goto no_memory; + if (!((*argv)[++n] = strdup("-M"))) + goto no_memory; + if (!((*argv)[++n] = strdup(vm->def.os.machine))) + goto no_memory; + if (vm->def.virtType == QEMUD_VIRT_QEMU) { + if (!((*argv)[++n] = strdup("-no-kqemu"))) + goto no_memory; + } + if (!((*argv)[++n] = strdup("-m"))) + goto no_memory; + if (!((*argv)[++n] = strdup(memory))) + goto no_memory; + if (!((*argv)[++n] = strdup("-smp"))) + goto no_memory; + if (!((*argv)[++n] = strdup(vcpus))) + goto no_memory; + + while (disk) { + char dev[NAME_MAX]; + char file[PATH_MAX]; + if (!strcmp(disk->dst, "hdc") && + disk->device == QEMUD_DISK_CDROM) + snprintf(dev, NAME_MAX, "-%s", "cdrom"); + else + snprintf(dev, NAME_MAX, "-%s", disk->dst); + snprintf(file, PATH_MAX, "%s", disk->src); + + if (!((*argv)[++n] = strdup(dev))) + goto no_memory; + if (!((*argv)[++n] = strdup(file))) + goto no_memory; + + disk = disk->next; + } + + if (!net) { + if (!((*argv)[++n] = strdup("-net"))) + goto no_memory; + if (!((*argv)[++n] = strdup("none"))) + goto no_memory; + } else { + while (net) { + char nic[3+1+7+1+17+1]; + sprintf(nic, "nic,macaddr=%02x:%02x:%02x:%02x:%02x:%02x", + net->mac[0], net->mac[1], + net->mac[2], net->mac[3], + net->mac[4], net->mac[5]); + + if (!((*argv)[++n] = strdup("-net"))) + goto no_memory; + if (!((*argv)[++n] = strdup(nic))) + goto no_memory; + if (!((*argv)[++n] = strdup("-net"))) + goto no_memory; + /* XXX don't hardcode user */ + if (!((*argv)[++n] = strdup("user"))) + goto no_memory; + + net = net->next; + } + } + + if (vm->def.graphicsType == QEMUD_GRAPHICS_VNC) { + char port[10]; + snprintf(port, 10, "%d", vm->def.vncActivePort - 5900); + if (!((*argv)[++n] = strdup("-vnc"))) + goto no_memory; + if (!((*argv)[++n] = strdup(port))) + goto no_memory; + } else if (vm->def.graphicsType == QEMUD_GRAPHICS_NONE) { + if (!((*argv)[++n] = strdup("-nographic"))) + goto no_memory; + } else { + /* SDL is the default. no args needed */ + } + + (*argv)[++n] = NULL; + + return 0; + + no_memory: + if (argv) { + int i; > + for (i = 0 ; i < n ; i++) + free(argv[i]); + free(argv); + } + qemudReportError(server, VIR_ERR_NO_MEMORY, "argv"); + return -1; +} + +/* Free all memory associated with a struct qemud_vm object */ +void qemudFreeVM(struct qemud_vm *vm) { + struct qemud_vm_disk_def *disk = vm->def.disks; + struct qemud_vm_net_def *net = vm->def.nets; + + while (disk) { + struct qemud_vm_disk_def *prev = disk; + disk = disk->next; + free(prev); + } + while (net) { + struct qemud_vm_net_def *prev = net; + net = net->next; + free(prev); + } + + free(vm); +} + +/* Build up a fully qualfiied path for a config file to be
qualified :-) Can we try to keep the fuction comments in line /** * funcname: * @arg1: ... * * .... * * Returns .... */ even for internal ones, don't block commiting for that but I will probably make a pass over this once commited, that will force me to get deeper understanding of the code
+ * associated with a persistent guest */ +static +int qemudMakeConfigPath(struct qemud_server *server, + const char *name, + const char *ext, + char *buf, + unsigned int buflen) { + if ((strlen(server->configDir) + 1 + strlen(name) + (ext ? strlen(ext) : 0) + 1) > buflen) + return -1; + + strcpy(buf, server->configDir); + strcat(buf, "/"); + strcat(buf, name); + if (ext) + strcat(buf, ext); + return 0; +} + + +/* Save a guest's config data into a persistent file */ +static int qemudSaveConfig(struct qemud_server *server, + struct qemud_vm *vm) { + char *xml; + int fd, ret = -1; + int towrite; + struct stat sb; + + if (!(xml = qemudGenerateXML(server, vm))) { + return -1; + } + + if (stat(server->configDir, &sb) < 0) { + if (errno == ENOENT) { + if (mkdir(server->configDir, 0700) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot create config directory %s", server->configDir); + return -1; + } + } else { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot stat config directory %s", server->configDir); + return -1; + } + } else if (!S_ISDIR(sb.st_mode)) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "config directory %s is not a directory", server->configDir); + return -1; + } + + if ((fd = open(vm->configFile, + O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR )) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot create config file %s", vm->configFile); + goto cleanup; + } + + towrite = strlen(xml); + if (write(fd, xml, towrite) != towrite) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot write config file %s", vm->configFile); + goto cleanup; + } + + if (close(fd) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot save config file %s", vm->configFile); + goto cleanup; + } + + ret = 0; + + cleanup:
Shouldn't it close fd if write() fails ?
+ free(xml); + + return ret; +} + + +/* Create a qemud_vm instance, populating it based on the data + * in a libvirt XML document describing the guest */ +struct qemud_vm *qemudLoadConfigXML(struct qemud_server *server, + const char *file, + const char *doc, + int save) { + struct qemud_vm *vm = NULL; + xmlDocPtr xml; + + if (!(xml = xmlReadDoc(BAD_CAST doc, file ? file : "domain.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) { + qemudReportError(server, VIR_ERR_XML_ERROR, NULL); + return NULL; + } + + if (!(vm = calloc(1, sizeof(struct qemud_vm)))) { + qemudReportError(server, VIR_ERR_NO_MEMORY, "vm"); + return NULL; + } + + vm->stdout = -1; + vm->stderr = -1; + vm->monitor = -1; + vm->pid = -1; + vm->def.id = -1; + + if (qemudParseXML(server, xml, vm) < 0) { + xmlFreeDoc(xml); + qemudFreeVM(vm); + return NULL; + } + xmlFreeDoc(xml); + + if (qemudFindVMByUUID(server, vm->def.uuid)) { + qemudReportError(server, VIR_ERR_DOM_EXIST, vm->def.name); + qemudFreeVM(vm); + return NULL; + } + + if (qemudFindVMByName(server, vm->def.name)) { + qemudReportError(server, VIR_ERR_DOM_EXIST, vm->def.name); + qemudFreeVM(vm); + return NULL; + } + + if (file) { + strncpy(vm->configFile, file, PATH_MAX); + vm->configFile[PATH_MAX-1] = '\0'; + } else { + if (save) { + if (qemudMakeConfigPath(server, vm->def.name, ".xml", vm->configFile, PATH_MAX) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot construct config file path"); + qemudFreeVM(vm); + return NULL; + } + + if (qemudSaveConfig(server, vm) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot save config file for guest"); + qemudFreeVM(vm); + return NULL; + } + } else { + vm->configFile[0] = '\0'; + } + } + + return vm; +} + + +/* Load a guest from its persistent config file */ +static void qemudLoadConfig(struct qemud_server *server, + const char *file) { + FILE *fh; + struct stat st; + struct qemud_vm *vm; + char xml[QEMUD_MAX_XML_LEN]; + int ret; + + if (!(fh = fopen(file, "r"))) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot open guest config file %s", file); + return; + } + + if (fstat(fileno(fh), &st) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot stat config file %s", file); + goto cleanup; + } + + if (st.st_size >= QEMUD_MAX_XML_LEN) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "guest config too large in file %s", file); + goto cleanup; + } + + if ((ret = fread(xml, st.st_size, 1, fh)) != 1) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot read config file %s", file); + goto cleanup; + } + xml[st.st_size] = '\0'; + + if ((vm = qemudLoadConfigXML(server, file, xml, 1))) { + vm->next = server->inactivevms; + server->inactivevms = vm; + server->ninactivevms++; + } + + cleanup: + fclose(fh); +} + + +/* Scan for all guest config files */ +int qemudScanConfigs(struct qemud_server *server) { + DIR *dir; + struct dirent *entry; + + if (!(dir = opendir(server->configDir))) { + if (errno == ENOENT) + return 0; + return -1; + } + + while ((entry = readdir(dir))) { + char file[PATH_MAX]; + if (entry->d_name[0] == '.') + continue; + + if (qemudMakeConfigPath(server, entry->d_name, NULL, file, PATH_MAX) < 0) + continue; + + qemudLoadConfig(server, file); + } + + closedir(dir); + + return 0; +} + [...] + +/* Generate an XML document describing the guest's configuration */ +char *qemudGenerateXML(struct qemud_server *server, struct qemud_vm *vm) { + struct qemudBuffer buf; + unsigned char *uuid; + struct qemud_vm_disk_def *disk; + struct qemud_vm_net_def *net; + const char *type = NULL; + + buf.len = QEMUD_MAX_XML_LEN; + buf.used = 0; + buf.data = malloc(buf.len); + + switch (vm->def.virtType) { + case QEMUD_VIRT_QEMU: + type = "qemu"; + break; + case QEMUD_VIRT_KQEMU: + type = "kqemu"; + break; + case QEMUD_VIRT_KVM: + type = "kvm"; + break; + } + if (!type) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "unexpected domain type %d", vm->def.virtType); + goto cleanup; + } + + if (vm->def.id >= 0) { + if (qemudBufferPrintf(&buf, "<domain type='%s' id='%d'>\n", type, vm->def.id) < 0) + goto no_memory; + } else { + if (qemudBufferPrintf(&buf, "<domain type='%s'>\n", type) < 0) + goto no_memory; + } + + if (qemudBufferPrintf(&buf, " <name>%s</name>\n", vm->def.name) < 0) + goto no_memory; + + uuid = vm->def.uuid; + if (qemudBufferPrintf(&buf, " <uuid>%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x</uuid>\n", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]) < 0)
Hum, do we really want to use the format with '-' and not just the raw hex dump by default, I'm still wondering what this brings...
+ goto no_memory; + if (qemudBufferPrintf(&buf, " <memory>%d</memory>\n", vm->def.memory) < 0) + goto no_memory; + if (qemudBufferPrintf(&buf, " <vcpu>%d</vcpu>\n", vm->def.vcpus) < 0) + goto no_memory; + + if (qemudBufferAdd(&buf, " <os>\n") < 0) + goto no_memory; + + if (vm->def.virtType == QEMUD_VIRT_QEMU) { + if (qemudBufferPrintf(&buf, " <type arch='%s' machine='%s'>%s</type>\n", vm->def.os.arch, vm->def.os.machine, vm->def.os.type) < 0) + goto no_memory; + } else { + if (qemudBufferPrintf(&buf, " <type>%s</type>\n", vm->def.os.type) < 0) + goto no_memory; + } + + if (vm->def.os.kernel[0]) + if (qemudBufferPrintf(&buf, " <kernel>%s</kernel>\n", vm->def.os.kernel) < 0) + goto no_memory; + if (vm->def.os.initrd[0]) + if (qemudBufferPrintf(&buf, " <initrd>%s</initrd>\n", vm->def.os.initrd) < 0) + goto no_memory; + if (vm->def.os.cmdline[0]) + if (qemudBufferPrintf(&buf, " <cmdline>%s</cmdline>\n", vm->def.os.cmdline) < 0) + goto no_memory; + + if (qemudBufferAdd(&buf, " </os>\n") < 0) + goto no_memory; + + if (qemudBufferAdd(&buf, " <devices>\n") < 0) + goto no_memory; + + if (qemudBufferPrintf(&buf, " <emulator>%s</emulator>\n", vm->def.os.binary) < 0) + goto no_memory; + + disk = vm->def.disks; + while (disk) { + const char *types[] = { + "block", + "file", + }; + const char *typeAttrs[] = { + "dev", + "file", + }; + const char *devices[] = { + "disk", + "cdrom", + "floppy", + }; + if (qemudBufferPrintf(&buf, " <disk type='%s' device='%s'>\n", + types[disk->type], devices[disk->device]) < 0) + goto no_memory; + + if (qemudBufferPrintf(&buf, " <source %s='%s'/>\n", typeAttrs[disk->type], disk->src) < 0) + goto no_memory; + + if (qemudBufferPrintf(&buf, " <target dev='%s'/>\n", disk->dst) < 0) + goto no_memory; + + if (disk->readonly) + if (qemudBufferAdd(&buf, " <readonly/>\n") < 0) + goto no_memory; + + if (qemudBufferPrintf(&buf, " </disk>\n") < 0) + goto no_memory; + + disk = disk->next; + } + + net = vm->def.nets; + disk = vm->def.disks; + while (disk) { + const char *types[] = { + "user", + "tap", + "server", + "client", + "mcast", + "vde", + }; + if (qemudBufferPrintf(&buf, " <interface type='%s'>\n", + types[net->type]) < 0) + goto no_memory; + + if (qemudBufferPrintf(&buf, " <mac address='%02x:%02x:%02x:%02x:%02x:%02x'/>\n", + net->mac[0], net->mac[1], net->mac[2], + net->mac[3], net->mac[4], net->mac[5]) < 0) + goto no_memory; + + if (qemudBufferPrintf(&buf, " </interface>\n") < 0) + goto no_memory; + + disk = disk->next; + } + + if (vm->def.graphicsType == QEMUD_GRAPHICS_VNC) { + if (vm->def.vncPort) { + qemudBufferPrintf(&buf, " <graphics type='vnc' port='%d'/>\n", + vm->def.id == -1 ? vm->def.vncPort : vm->def.vncActivePort); + } else { + qemudBufferPrintf(&buf, " <graphics type='vnc'/>\n"); + } + } + + if (qemudBufferAdd(&buf, " </devices>\n") < 0) + goto no_memory; + + + if (qemudBufferAdd(&buf, "</domain>\n") < 0) + goto no_memory; + + return buf.data; + + no_memory: + qemudReportError(server, VIR_ERR_NO_MEMORY, "xml"); + cleanup: + free(buf.data); + return NULL; +} + + +int qemudDeleteConfigXML(struct qemud_server *server, struct qemud_vm *vm) { + if (!vm->configFile[0]) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "no config file for guest %s", vm->def.name); + return -1; + } + + if (unlink(vm->configFile) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot remove config for guest %s", vm->def.name); + return -1; + } + + vm->configFile[0] = '\0'; + + return 0; +} + + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -ruN libvirt/qemud/config.h libvirt-qemu/qemud/config.h --- libvirt/qemud/config.h 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/config.h 2007-01-04 11:23:23.000000000 -0500 @@ -0,2 +1,58 @@ +/* + * config.h: VM configuration management + * + * Copyright (C) 2006, 2007 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __QEMUD_CONFIG_H +#define __QEMUD_CONFIG_H + +#include "internal.h" + +int qemudBuildCommandLine(struct qemud_server *server, + struct qemud_vm *vm, + char ***argv, + int *argc); + +void qemudFreeVM(struct qemud_vm *vm); +struct qemud_vm *qemudLoadConfigXML(struct qemud_server *server, + const char *file, + const char *doc, + int persist); +int qemudScanConfigs(struct qemud_server *server); +char *qemudGenerateXML(struct qemud_server *server, + struct qemud_vm *vm); + +int qemudDeleteConfigXML(struct qemud_server *server, + struct qemud_vm *vm); + + +#endif + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -ruN libvirt/qemud/dispatch.c libvirt-qemu/qemud/dispatch.c --- libvirt/qemud/dispatch.c 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/dispatch.c 2007-01-04 19:31:56.000000000 -0500 @@ -0,0 +1,511 @@ +/* + * dispatch.c: (De-)marshall wire messages to driver functions. + * + * Copyright (C) 2006, 2007 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <limits.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <libvirt/virterror.h> + +#include "internal.h" +#include "driver.h" +#include "dispatch.h" + + +static int qemudDispatchFailure(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + struct qemud_packet *out) { + out->header.type = QEMUD_PKT_FAILURE; + out->header.dataSize = sizeof(out->data.failureReply); + out->data.failureReply.code = server->errorCode; + strcpy(out->data.failureReply.message, server->errorMessage); + return 0; +} + +static int qemudDispatchGetProtocolVersion(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != 0) + return -1; + + out->header.type = QEMUD_PKT_GET_PROTOCOL_VERSION; + out->header.dataSize = sizeof(out->data.getProtocolVersionReply); + out->data.getProtocolVersionReply.version = QEMUD_PROTOCOL_VERSION; + return 0; +} + +static int qemudDispatchGetVersion(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != 0) + return -1; + + int version = qemudGetVersion(server); + if (version < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_GET_VERSION; + out->header.dataSize = sizeof(out->data.getVersionReply); + out->data.getVersionReply.version = version; + } + return 0; +} +static int qemudDispatchListDomains(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != 0) + return -1; + + int ndomains = qemudListDomains(server, + out->data.listDomainsReply.domains, + QEMUD_MAX_NUM_DOMAINS); + if (ndomains < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_LIST_DOMAINS; + out->header.dataSize = sizeof(out->data.listDomainsReply); + out->data.listDomainsReply.numDomains = ndomains; + } + return 0; +} +static int qemudDispatchNumDomains(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != 0) + return -1; + + int ndomains = qemudNumDomains(server); + if (ndomains < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_NUM_DOMAINS; + out->header.dataSize = sizeof(out->data.numDomainsReply); + out->data.numDomainsReply.numDomains = ndomains; + } + return 0; +} +static int qemudDispatchDomainCreate(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainCreateRequest)) + return -1; + + in->data.domainCreateRequest.xml[QEMUD_MAX_XML_LEN-1] ='\0'; + + struct qemud_vm *vm = qemudDomainCreate(server, in->data.domainCreateRequest.xml); + if (!vm) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_CREATE; + out->header.dataSize = sizeof(out->data.domainCreateReply); + out->data.domainCreateReply.id = vm->def.id; + memcpy(out->data.domainCreateReply.uuid, vm->def.uuid, QEMUD_UUID_RAW_LEN); + strncpy(out->data.domainCreateReply.name, vm->def.name, QEMUD_MAX_NAME_LEN-1); + out->data.domainCreateReply.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + } + return 0; +}
Hum, when qemudDomainCreate returns, we get a new vm, is that a new structure which is gonna be ref-counted and cleaned up at some point ? I'm a bit lost there, is that exactly the same scheme as for Xen domain instances ?
+static int qemudDispatchDomainLookupByID(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainLookupByIDRequest)) + return -1; + + struct qemud_vm *vm = qemudFindVMByID(server, in->data.domainLookupByIDRequest.id); + if (!vm) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_ID; + out->header.dataSize = sizeof(out->data.domainLookupByIDReply); + memcpy(out->data.domainLookupByIDReply.uuid, vm->def.uuid, QEMUD_UUID_RAW_LEN); + strncpy(out->data.domainLookupByIDReply.name, vm->def.name, QEMUD_MAX_NAME_LEN-1); + out->data.domainLookupByIDReply.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + } + return 0; +} +static int qemudDispatchDomainLookupByUUID(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainLookupByUUIDRequest)) + return -1; + + struct qemud_vm *vm = qemudFindVMByUUID(server, in->data.domainLookupByUUIDRequest.uuid); + if (!vm) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_UUID; + out->header.dataSize = sizeof(out->data.domainLookupByUUIDReply); + out->data.domainLookupByUUIDReply.id = vm->def.id; + strncpy(out->data.domainLookupByUUIDReply.name, vm->def.name, QEMUD_MAX_NAME_LEN-1); + out->data.domainLookupByUUIDReply.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + } + return 0; +} +static int qemudDispatchDomainLookupByName(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainLookupByNameRequest)) + return -1; + + /* Paranoia NULL termination */ + in->data.domainLookupByNameRequest.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + struct qemud_vm *vm = qemudFindVMByName(server, in->data.domainLookupByNameRequest.name); + if (!vm) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_NAME; + out->header.dataSize = sizeof(out->data.domainLookupByNameReply); + out->data.domainLookupByNameReply.id = vm->def.id; + memcpy(out->data.domainLookupByNameReply.uuid, vm->def.uuid, QEMUD_UUID_RAW_LEN); + } + return 0; +} +static int qemudDispatchDomainSuspend(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainSuspendRequest)) + return -1; + + int ret = qemudDomainSuspend(server, in->data.domainSuspendRequest.id); + if (ret < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_SUSPEND; + out->header.dataSize = 0; + } + return 0; +} +static int qemudDispatchDomainResume(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainResumeRequest)) + return -1; + + int ret = qemudDomainResume(server, in->data.domainResumeRequest.id); + if (ret < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_RESUME; + out->header.dataSize =0; + } + return 0; +} +static int qemudDispatchDomainDestroy(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainDestroyRequest)) + return -1; + + int ret = qemudDomainDestroy(server, in->data.domainDestroyRequest.id); + if (ret < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_DESTROY; + out->header.dataSize = 0; + } + return 0; +} +static int qemudDispatchDomainGetInfo(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainGetInfoRequest)) + return -1; + + int ret = qemudDomainGetInfo(server, in->data.domainGetInfoRequest.uuid, + &out->data.domainGetInfoReply.runstate, + &out->data.domainGetInfoReply.cpuTime, + &out->data.domainGetInfoReply.memory, + &out->data.domainGetInfoReply.nrVirtCpu); + if (ret < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_GET_INFO; + out->header.dataSize = sizeof(out->data.domainGetInfoReply); + } + return 0; +} +static int qemudDispatchDomainSave(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainSaveRequest)) + return -1; + + /* Paranoia NULL termination */ + in->data.domainSaveRequest.file[PATH_MAX-1] ='\0'; + + int ret = qemudDomainSave(server, + in->data.domainSaveRequest.id, + in->data.domainSaveRequest.file); + if (ret < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_SAVE; + out->header.dataSize = 0; + } + return 0; +} +static int qemudDispatchDomainRestore(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainRestoreRequest)) + return -1; + + /* Paranoia null termination */ + in->data.domainRestoreRequest.file[PATH_MAX-1] ='\0'; + + int id = qemudDomainRestore(server, in->data.domainRestoreRequest.file); + if (id < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_RESTORE; + out->header.dataSize = sizeof(out->data.domainRestoreReply); + out->data.domainRestoreReply.id = id; + } + return 0; +} +static int qemudDispatchDumpXML(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainDumpXMLRequest)) + return -1; + + int ret = qemudDomainDumpXML(server, in->data.domainDumpXMLRequest.uuid, + out->data.domainDumpXMLReply.xml, QEMUD_MAX_XML_LEN); + if (ret < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DUMP_XML; + out->header.dataSize = sizeof(out->data.domainDumpXMLReply); + } + return 0; +} +static int qemudDispatchListDefinedDomains(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + char **names; + int i; + if (in->header.dataSize != 0) + return -1; + + if (!(names = malloc(sizeof(char *)*QEMUD_MAX_NUM_DOMAINS))) + return -1; + + for (i = 0 ; i < QEMUD_MAX_NUM_DOMAINS ; i++) { + names[i] = out->data.listDefinedDomainsReply.domains[i]; + } + + int ndomains = qemudListDefinedDomains(server, + names, + QEMUD_MAX_NUM_DOMAINS); + free(names); + if (ndomains < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_LIST_DEFINED_DOMAINS; + out->header.dataSize = sizeof(out->data.listDefinedDomainsReply); + out->data.listDefinedDomainsReply.numDomains = ndomains; + } + return 0; +} +static int qemudDispatchNumDefinedDomains(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != 0) + return -1; + + int ndomains = qemudNumDefinedDomains(server); + if (ndomains < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_NUM_DEFINED_DOMAINS; + out->header.dataSize = sizeof(out->data.numDefinedDomainsReply); + out->data.numDefinedDomainsReply.numDomains = ndomains; + } + return 0; +} +static int qemudDispatchDomainStart(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainStartRequest)) + return -1; + + struct qemud_vm *vm = qemudFindVMByUUID(server, in->data.domainStartRequest.uuid); + if (!vm || qemudDomainStart(server, vm) < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_START; + out->header.dataSize = sizeof(out->data.domainStartReply); + out->data.domainStartReply.id = vm->def.id; + } + return 0; +} +static int qemudDispatchDomainDefine(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainDefineRequest)) + return -1; + + in->data.domainDefineRequest.xml[QEMUD_MAX_XML_LEN-1] ='\0'; + + struct qemud_vm *vm = qemudDomainDefine(server, in->data.domainDefineRequest.xml); + if (!vm) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_DEFINE; + out->header.dataSize = sizeof(out->data.domainDefineReply); + memcpy(out->data.domainDefineReply.uuid, vm->def.uuid, QEMUD_UUID_RAW_LEN); + strncpy(out->data.domainDefineReply.name, vm->def.name, QEMUD_MAX_NAME_LEN-1); + out->data.domainDefineReply.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + } + return 0; +} +static int qemudDispatchDomainUndefine(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainUndefineRequest)) + return -1; + + int ret = qemudDomainUndefine(server, in->data.domainUndefineRequest.uuid); + if (ret < 0) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_UNDEFINE; + out->header.dataSize = 0; + } + return 0; +} + + +typedef int (*clientFunc)(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out); + + +/* One per message type recorded in qemud_packet_type enum */ +clientFunc funcsRW[] = { + NULL, /* FAILURE code */ + qemudDispatchGetProtocolVersion, + qemudDispatchGetVersion, + qemudDispatchListDomains, + qemudDispatchNumDomains, + qemudDispatchDomainCreate, + qemudDispatchDomainLookupByID, + qemudDispatchDomainLookupByUUID, + qemudDispatchDomainLookupByName, + qemudDispatchDomainSuspend, + qemudDispatchDomainResume, + qemudDispatchDomainDestroy, + qemudDispatchDomainGetInfo, + qemudDispatchDomainSave, + qemudDispatchDomainRestore, + qemudDispatchDumpXML, + qemudDispatchListDefinedDomains, + qemudDispatchNumDefinedDomains, + qemudDispatchDomainStart, + qemudDispatchDomainDefine, + qemudDispatchDomainUndefine +}; + +clientFunc funcsRO[] = { + NULL, /* FAILURE code */ + qemudDispatchGetProtocolVersion, + qemudDispatchGetVersion, + qemudDispatchListDomains, + qemudDispatchNumDomains, + NULL, + qemudDispatchDomainLookupByID, + qemudDispatchDomainLookupByUUID, + qemudDispatchDomainLookupByName, + NULL, + NULL, + NULL, + qemudDispatchDomainGetInfo, + NULL, + NULL, + qemudDispatchDumpXML, + qemudDispatchListDefinedDomains, + qemudDispatchNumDefinedDomains, + NULL, + NULL, + NULL, +}; + +/* + * Returns -1 if message processing failed - eg, illegal header sizes, + * a memory error dealing with stuff, or any other bad stuff which + * should trigger immediate client disconnect + * + * Return 0 if message processing succeeded. NB, this does not mean + * the operation itself succeeded - success/failure of the operation + * is recorded by the return message type - either it matches the + * incoming type, or is QEMUD_PKT_FAILURE + */ +int qemudDispatch(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + clientFunc *funcs; + printf("> Dispatching request %d readonly ? %d\n", in->header.type, client->readonly); + + server->errorCode = 0; + server->errorMessage[0] = '\0'; + + memset(out, 0, sizeof(struct qemud_packet)); + + if (in->header.type >= (sizeof(funcsRW)/sizeof(clientFunc))) { + printf("Illegal request type\n"); + return -1; + } + + if (in->header.type == QEMUD_PKT_FAILURE) { + printf("Illegal request type\n"); + return -1; + } + + if (client->readonly) + funcs = funcsRO; + else + funcs = funcsRW; + + if (!funcs[in->header.type]) { + qemudReportError(server, VIR_ERR_OPERATION_DENIED, NULL); + qemudDispatchFailure(server, client, out); + } else { + if ((funcs[in->header.type])(server, client, in, out) < 0) { + printf("Dispatch failed\n"); + return -1; + } + } + + printf("< Returning reply %d (%d bytes)\n", + out->header.type, out->header.dataSize); + + return 0; +} + + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -ruN libvirt/qemud/dispatch.h libvirt-qemu/qemud/dispatch.h --- libvirt/qemud/dispatch.h 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/dispatch.h 2007-01-04 08:37:59.000000000 -0500 @@ -0,0 +1,43 @@ +/* + * dispatch.h: (De-)marshall wire messages to driver functions. + * + * Copyright (C) 2006, 2007 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + + +#ifndef QEMUD_DISPATCH_H +#define QEMUD_DISPATCH_H + +#include "internal.h" + + +int qemudDispatch(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out); + +#endif + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -ruN libvirt/qemud/driver.c libvirt-qemu/qemud/driver.c --- libvirt/qemud/driver.c 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/driver.c 2007-01-04 12:21:02.000000000 -0500 @@ -0,0 +1,359 @@ +/* + * driver.c: core driver methods for managing qemu guests + * + * Copyright (C) 2006, 2007 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <sys/types.h> +#include <dirent.h> +#include <limits.h> +#include <string.h> +#include <stdio.h> +#include <stdarg.h> + +#include <libvirt/virterror.h> + +#include "internal.h" +#include "driver.h" +#include "config.h" + +void qemudReportError(struct qemud_server *server, + int code, const char *fmt, ...) { + va_list args; + server->errorCode = code; + if (fmt) { + va_start(args, fmt); + vsnprintf(server->errorMessage, QEMUD_MAX_ERROR_LEN-1, fmt, args); + va_end(args); + } else { + server->errorMessage[0] = '\0'; + } +} + +struct qemud_vm *qemudFindVMByID(const struct qemud_server *server, int id) { + struct qemud_vm *vm = server->activevms; + + while (vm) { + if (vm->def.id == id) + return vm; + vm = vm->next; + } + + return NULL; +} + +struct qemud_vm *qemudFindVMByUUID(const struct qemud_server *server, + const unsigned char *uuid) { + struct qemud_vm *vm = server->activevms; + + while (vm) { + if (!memcmp(vm->def.uuid, uuid, QEMUD_UUID_RAW_LEN)) + return vm; + vm = vm->next; + } + + vm = server->inactivevms; + while (vm) { + if (!memcmp(vm->def.uuid, uuid, QEMUD_UUID_RAW_LEN)) + return vm; + vm = vm->next; + } + + return NULL; +} + +struct qemud_vm *qemudFindVMByName(const struct qemud_server *server, + const char *name) { + struct qemud_vm *vm = server->activevms; + + while (vm) { + if (!strcmp(vm->def.name, name)) + return vm; + vm = vm->next; + } + + vm = server->inactivevms; + while (vm) { + if (!strcmp(vm->def.name, name)) + return vm; + vm = vm->next; + } + + return NULL; +} + +int qemudGetVersion(struct qemud_server *server) { + return server->qemuVersion; +} + +int qemudListDomains(struct qemud_server *server, int *ids, int nids) { + struct qemud_vm *vm = server->activevms; + int got = 0; + while (vm && got < nids) { + ids[got] = vm->def.id; + vm = vm->next; + got++; + } + return got; +} +int qemudNumDomains(struct qemud_server *server) { + return server->nactivevms; +} +struct qemud_vm *qemudDomainCreate(struct qemud_server *server, const char *xml) { + struct qemud_vm *vm; + + if (!(vm = qemudLoadConfigXML(server, NULL, xml, 0))) { + return NULL; + } + + if (qemudStartVMDaemon(server, vm) < 0) { + qemudFreeVM(vm); + return NULL; + } + + vm->next = server->activevms; + server->activevms = vm; + server->nactivevms++; + server->nvmfds += 2; + + return vm; +} + + +int qemudDomainSuspend(struct qemud_server *server, int id) { + struct qemud_vm *vm = qemudFindVMByID(server, id); + if (!vm) { + qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching id %d", id); + return -1; + } + if (vm->pid == -1) { + qemudReportError(server, VIR_ERR_OPERATION_FAILED, "domain is not running"); + return -1; + } + qemudReportError(server, VIR_ERR_OPERATION_FAILED, "suspend is not supported"); + return -1; +} + + +int qemudDomainResume(struct qemud_server *server, int id) { + struct qemud_vm *vm = qemudFindVMByID(server, id); + if (!vm) { + qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching id %d", id); + return -1; + } + if (vm->pid == -1) { + qemudReportError(server, VIR_ERR_OPERATION_FAILED, "domain is not running"); + return -1; + } + qemudReportError(server, VIR_ERR_OPERATION_FAILED, "resume is not supported"); + return -1; +} + + +int qemudDomainDestroy(struct qemud_server *server, int id) { + struct qemud_vm *vm = qemudFindVMByID(server, id); + if (!vm) { + qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching id %d", id); + return -1; + } + if (vm->pid == -1) { + qemudReportError(server, VIR_ERR_OPERATION_FAILED, "domain is not running"); + return -1; + } + + if (qemudShutdownVMDaemon(server, vm) < 0) + return -1; + return 0; +} + + +int qemudDomainGetInfo(struct qemud_server *server, const unsigned char *uuid, + int *runstate, + unsigned long long *cputime, + unsigned long *memory, + unsigned int *nrVirtCpu) { + struct qemud_vm *vm = qemudFindVMByUUID(server, uuid); + if (!vm) { + qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching uuid"); + return -1; + } + + if (vm->pid == -1) { + *runstate = QEMUD_STATE_STOPPED; + } else { + /* XXX in future need to add PAUSED */ + *runstate = QEMUD_STATE_RUNNING; + } + + *cputime = 0; + *memory = vm->def.memory; + *nrVirtCpu = vm->def.vcpus; + return 0; +} + + +int qemudDomainSave(struct qemud_server *server, int id, + const char *path ATTRIBUTE_UNUSED) { + struct qemud_vm *vm = qemudFindVMByID(server, id); + if (!vm) { + qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching id %d", id); + return -1; + } + if (vm->pid == -1) { + qemudReportError(server, VIR_ERR_OPERATION_FAILED, "domain is not running"); + return -1; + } + qemudReportError(server, VIR_ERR_OPERATION_FAILED, "save is not supported"); + return -1; +} + + +int qemudDomainRestore(struct qemud_server *server, + const char *path ATTRIBUTE_UNUSED) { + qemudReportError(server, VIR_ERR_OPERATION_FAILED, "restore is not supported"); + return -1; +} + + +int qemudDomainDumpXML(struct qemud_server *server, const unsigned char *uuid, char *xml, int xmllen) { + struct qemud_vm *vm = qemudFindVMByUUID(server, uuid); + char *vmxml; + if (!vm) { + qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching uuid"); + return -1; + } + + vmxml = qemudGenerateXML(server, vm); + if (!vmxml) + return -1; + + strncpy(xml, vmxml, xmllen); + xml[xmllen-1] = '\0'; + + return 0; +} + + +int qemudListDefinedDomains(struct qemud_server *server, char *const*names, int nnames) { + struct qemud_vm *vm = server->inactivevms; + int got = 0; + while (vm && got < nnames) { + strncpy(names[got], vm->def.name, QEMUD_MAX_NAME_LEN-1); + names[got][QEMUD_MAX_NAME_LEN-1] = '\0'; + vm = vm->next; + got++; + } + return got; +} + + +int qemudNumDefinedDomains(struct qemud_server *server) { + return server->ninactivevms; +} + + +int qemudDomainStart(struct qemud_server *server, struct qemud_vm *vm) { + struct qemud_vm *prev = NULL, *curr = server->inactivevms; + if (qemudStartVMDaemon(server, vm) < 0) { + return 1; + } + + while (curr) { + if (curr == vm) { + if (prev) + prev->next = curr->next; + else + server->inactivevms = curr->next; + server->ninactivevms--; + break; + } + prev = curr; + curr = curr->next; + } + + vm->next = server->activevms; + server->activevms = vm; + server->nactivevms++; + server->nvmfds += 2; + + return 0; +} + + +struct qemud_vm *qemudDomainDefine(struct qemud_server *server, const char *xml) { + struct qemud_vm *vm; + + if (!(vm = qemudLoadConfigXML(server, NULL, xml, 1))) { + return NULL; + } + + vm->next = server->inactivevms; + server->inactivevms = vm; + server->ninactivevms++; + + return vm; +} + +int qemudDomainUndefine(struct qemud_server *server, const unsigned char *uuid) { + struct qemud_vm *vm = qemudFindVMByUUID(server, uuid); + struct qemud_vm *prev = NULL, *curr = server->inactivevms; + + if (!vm) { + qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching uuid"); + return -1; + } + + if (vm->pid != -1) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot delete active domain"); + return -1; + } + + if (qemudDeleteConfigXML(server, vm) < 0) + return -1; + + while (curr) { + if (curr == vm) { + if (prev) { + prev->next = curr->next; + } else { + server->inactivevms = curr->next; + } + server->ninactivevms--; + break; + } + + prev = curr; + curr = curr->next; + } + + qemudFreeVM(vm); + + return 0; +} + + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -ruN libvirt/qemud/driver.h libvirt-qemu/qemud/driver.h --- libvirt/qemud/driver.h 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/driver.h 2007-01-04 08:38:07.000000000 -0500 @@ -0,0 +1,89 @@ +/* + * driver.h: core driver methods for managing qemu guests + * + * Copyright (C) 2006, 2007 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + + +#ifndef QEMUD_DRIVER_H +#define QEMUD_DRIVER_H + +#include "internal.h" + +void qemudReportError(struct qemud_server *server, + int code, const char *fmt, ...); + +struct qemud_vm *qemudFindVMByID(const struct qemud_server *server, + int id); +struct qemud_vm *qemudFindVMByUUID(const struct qemud_server *server, + const unsigned char *uuid); +struct qemud_vm *qemudFindVMByName(const struct qemud_server *server, + const char *name); + +int qemudGetVersion(struct qemud_server *server); +int qemudListDomains(struct qemud_server *server, + int *ids, + int nids); +int qemudNumDomains(struct qemud_server *server); +struct qemud_vm *qemudDomainCreate(struct qemud_server *server, + const char *xml); +int qemudDomainSuspend(struct qemud_server *server, + int id); +int qemudDomainResume(struct qemud_server *server, + int id); +int qemudDomainDestroy(struct qemud_server *server, + int id); +int qemudDomainGetInfo(struct qemud_server *server, + const unsigned char *uuid, + int *runstate, + unsigned long long *cputime, + unsigned long *memory, + unsigned int *nrVirtCpu); +int qemudDomainSave(struct qemud_server *server, + int id, + const char *path); +int qemudDomainRestore(struct qemud_server *server, + const char *path); +int qemudDomainDumpXML(struct qemud_server *server, + const unsigned char *uuid, + char *xml, + int xmllen); +int qemudListDefinedDomains(struct qemud_server *server, + char *const*names, + int nnames); +int qemudNumDefinedDomains(struct qemud_server *server); +int qemudDomainStart(struct qemud_server *server, + struct qemud_vm *vm); +struct qemud_vm *qemudDomainDefine(struct qemud_server *server, + const char *xml); +int qemudDomainUndefine(struct qemud_server *server, + const unsigned char *uuid); + +#endif + + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -ruN libvirt/qemud/internal.h libvirt-qemu/qemud/internal.h --- libvirt/qemud/internal.h 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/internal.h 2007-01-04 20:29:22.000000000 -0500 @@ -0,0 +1,239 @@ +/* + * internal.h: daemon data structure definitions + * + * Copyright (C) 2006, 2007 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + + +#ifndef QEMUD_INTERNAL_H__ +#define QEMUD_INTERNAL_H__ + +#include <sys/socket.h> +#include <netinet/in.h> + +#include "protocol.h" + +#ifdef __GNUC__ +#ifdef HAVE_ANSIDECL_H +#include <ansidecl.h> +#endif +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED __attribute__((unused)) +#endif +#else +#define ATTRIBUTE_UNUSED +#endif + + +#define UUID_LEN 16 + +/* Different types of QEMU acceleration possible */ +enum qemud_vm_virt_type { + QEMUD_VIRT_QEMU, + QEMUD_VIRT_KQEMU, + QEMUD_VIRT_KVM, +}; + +/* Stores the per-client connection state */ +struct qemud_client { + int fd; + int readonly; + struct qemud_packet incoming; + unsigned int incomingReceived; + struct qemud_packet outgoing; + unsigned int outgoingSent; + int tx; + struct qemud_client *next; +}; + + +/* Two types of disk backends */ +enum qemud_vm_disk_type { + QEMUD_DISK_BLOCK, + QEMUD_DISK_FILE +}; + +/* Three types of disk frontend */ +enum qemud_vm_disk_device { + QEMUD_DISK_DISK, + QEMUD_DISK_CDROM, + QEMUD_DISK_FLOPPY, +}; + +/* Stores the virtual disk configuration */ +struct qemud_vm_disk_def { + int type; + int device; + char src[PATH_MAX]; + char dst[NAME_MAX]; + int readonly; + + struct qemud_vm_disk_def *next; +}; + +#define QEMUD_MAC_ADDRESS_LEN 6 +#define QEMUD_OS_TYPE_MAX_LEN 10 +#define QEMUD_OS_ARCH_MAX_LEN 10 +#define QEMUD_OS_MACHINE_MAX_LEN 10 + +/* 5 different types of networking config */ +enum qemud_vm_net_type { + QEMUD_NET_USER, + QEMUD_NET_TAP, + QEMUD_NET_SERVER, + QEMUD_NET_CLIENT, + QEMUD_NET_MCAST, + /* QEMUD_NET_VDE*/ +}; + +/* Stores the virtual network interface configuration */ +struct qemud_vm_net_def { + int type; + int vlan; + unsigned char mac[QEMUD_MAC_ADDRESS_LEN]; + union { + struct { + char ifname[NAME_MAX]; + char script[PATH_MAX]; + } tap; + struct { + struct sockaddr_in listen; + int port; + } server; + struct { + struct sockaddr_in connect; + int port; + } client; + struct { + struct sockaddr_in group; + int port; + } mcast; + struct { + char vlan[PATH_MAX]; + } vde; + } dst; + + struct qemud_vm_net_def *next; +}; + +/* 3 possible boot devices */ +enum qemud_vm_boot_order { + QEMUD_BOOT_FLOPPY, + QEMUD_BOOT_CDROM, + QEMUD_BOOT_DISK +}; + +/* 3 possible graphics console modes */ +enum qemud_vm_grapics_type { + QEMUD_GRAPHICS_NONE, + QEMUD_GRAPHICS_SDL, + QEMUD_GRAPHICS_VNC, +}; + +/* Operating system configuration data & machine / arch */ +struct qemud_vm_os_def { + char type[QEMUD_OS_TYPE_MAX_LEN]; + char arch[QEMUD_OS_ARCH_MAX_LEN]; + char machine[QEMUD_OS_MACHINE_MAX_LEN]; + int bootOrder[3]; + char kernel[PATH_MAX]; + char initrd[PATH_MAX]; + char cmdline[PATH_MAX]; + char binary[PATH_MAX]; +}; + +/* Guest VM main configuration */ +struct qemud_vm_def { + int id; + int virtType; + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + char name[QEMUD_MAX_NAME_LEN]; + + int memory; + int vcpus; + + struct qemud_vm_os_def os; + + int graphicsType; + int vncPort; + int vncActivePort; + + int ndisks; + struct qemud_vm_disk_def *disks; + + int nnets; + struct qemud_vm_net_def *nets; +}; + +/* Guest VM runtime state */ +struct qemud_vm { + int stdout; + int stderr; + int monitor; + int pid; + + char configFile[PATH_MAX]; + + struct qemud_vm_def def; + + struct qemud_vm *next; +}; + +struct qemud_socket { + int fd; + int readonly; + struct qemud_socket *next; +}; + +/* Main server state */ +struct qemud_server { + int nsockets; + struct qemud_socket *sockets; + int qemuVersion; + int nclients; + struct qemud_client *clients; + int nvmfds; + int nactivevms; + struct qemud_vm *activevms; + int ninactivevms; + struct qemud_vm *inactivevms; + int nextvmid; + char configDir[PATH_MAX]; + char errorMessage[QEMUD_MAX_ERROR_LEN]; + int errorCode; +}; + +int qemudStartVMDaemon(struct qemud_server *server, + struct qemud_vm *vm); + +int qemudShutdownVMDaemon(struct qemud_server *server, + struct qemud_vm *vm); + + +#endif + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -ruN libvirt/qemud/Makefile libvirt-qemu/qemud/Makefile diff -ruN libvirt/qemud/Makefile.am libvirt-qemu/qemud/Makefile.am --- libvirt/qemud/Makefile.am 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/Makefile.am 2007-01-04 07:34:31.000000000 -0500 @@ -0,0 +1,17 @@ +## Process this file with automake to produce Makefile.in + +INCLUDES = @LIBXML_CFLAGS@ + +libexec_PROGRAMS = libvirt_qemud + +libvirt_qemud_SOURCES = qemud.c internal.h protocol.h \ + driver.c driver.h \ + dispatch.c dispatch.h \ + config.c config.h +libvirt_qemud_CFLAGS = -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_POSIX_C_SOURCE=199506L -std=c99 \ + -I$(top_srcdir)/include -I$(top_builddir)/include $(LIBXML_CFLAGS) \ + -Werror -Wall -Wextra +libvirt_qemud_LDFLAGS = $(LIBXML_LIBS) +libvirt_qemud_DEPENDENCIES = +libvirt_qemud_LDADD = + diff -ruN libvirt/qemud/protocol.h libvirt-qemu/qemud/protocol.h --- libvirt/qemud/protocol.h 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/protocol.h 2007-01-04 20:32:08.000000000 -0500 @@ -0,0 +1,206 @@ +/* + * protocol.h: wire protocol message format & data structures + * + * Copyright (C) 2006, 2007 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + + +#ifndef QEMUD_PROTOCOL_H__ +#define QEMUD_PROTOCOL_H__ + +/* List of different packet types which can be sent */ +enum { + QEMUD_PKT_FAILURE, + QEMUD_PKT_GET_PROTOCOL_VERSION,
I always feel safer to initialize at least the first values in an enum. for example QEMUD_PKT_FAILURE = 0, not strictly necessary but for client/server stuff values I feel just better ... somehow I don't trust the way compilers allocate enums .
+ QEMUD_PKT_GET_VERSION, + QEMUD_PKT_LIST_DOMAINS, + QEMUD_PKT_NUM_DOMAINS, + QEMUD_PKT_DOMAIN_CREATE, + QEMUD_PKT_DOMAIN_LOOKUP_BY_ID, + QEMUD_PKT_DOMAIN_LOOKUP_BY_UUID, + QEMUD_PKT_DOMAIN_LOOKUP_BY_NAME, + QEMUD_PKT_DOMAIN_SUSPEND, + QEMUD_PKT_DOMAIN_RESUME, + QEMUD_PKT_DOMAIN_DESTROY, + QEMUD_PKT_DOMAIN_GET_INFO, + QEMUD_PKT_DOMAIN_SAVE, + QEMUD_PKT_DOMAIN_RESTORE, + QEMUD_PKT_DUMP_XML, + QEMUD_PKT_LIST_DEFINED_DOMAINS, + QEMUD_PKT_NUM_DEFINED_DOMAINS, + QEMUD_PKT_DOMAIN_START, + QEMUD_PKT_DOMAIN_DEFINE, + QEMUD_PKT_DOMAIN_UNDEFINE, + QEMUD_PKT_MAX +} qemud_packet_type; + + +#define QEMUD_DEFAULT_PORT_STR "8123" + +#define QEMUD_PROTOCOL_VERSION 1 +#define QEMUD_UUID_RAW_LEN 16 +#define QEMUD_MAX_NAME_LEN 50 +#define QEMUD_MAX_XML_LEN 4096 +#define QEMUD_MAX_NUM_DOMAINS 100 +#define QEMUD_MAX_ERROR_LEN 1024 + +/* Possible guest VM states */ +enum { + QEMUD_STATE_RUNNING = 1, + QEMUD_STATE_PAUSED, + QEMUD_STATE_STOPPED, +} qemud_domain_runstate; + +/* Each packets has at least a fixed size header */ +struct qemud_packet_header { + unsigned int type; + /* Stores the size of the data struct matching + the type arg. + Must be <= sizeof(union qemudPacketData) */ + unsigned int dataSize; +}; + +/* Most packets also have some message specific data */ +union qemud_packet_data { + struct { + int code; + char message[QEMUD_MAX_ERROR_LEN]; + } failureReply; + struct { + int version; + } getProtocolVersionReply; + struct { + int version; + } getVersionReply; + struct { + int numDomains; + int domains[QEMUD_MAX_NUM_DOMAINS]; + } listDomainsReply; + struct { + int numDomains; + } numDomainsReply; + struct { + char xml[QEMUD_MAX_XML_LEN]; + } domainCreateRequest; + struct { + int id; + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + char name[QEMUD_MAX_NAME_LEN]; + } domainCreateReply; + struct { + int id; + } domainLookupByIDRequest; + struct { + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + char name[QEMUD_MAX_NAME_LEN]; + } domainLookupByIDReply; + struct { + char name[QEMUD_MAX_NAME_LEN]; + } domainLookupByNameRequest; + struct { + int id; + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + } domainLookupByNameReply; + struct { + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + } domainLookupByUUIDRequest; + struct { + int id; + char name[QEMUD_MAX_NAME_LEN]; + } domainLookupByUUIDReply; + struct { + int id; + } domainSuspendRequest; + struct { + int id; + } domainResumeRequest; + struct { + } domainResumeReply; + struct { + int id; + } domainDestroyRequest; + struct { + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + } domainGetInfoRequest; + struct { + int runstate; + unsigned long long cpuTime; + unsigned long memory; + unsigned int nrVirtCpu; + } domainGetInfoReply; + struct { + int id; + char file[PATH_MAX]; + } domainSaveRequest; + struct { + char file[PATH_MAX]; + } domainRestoreRequest; + struct { + int id; + } domainRestoreReply; + struct { + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + } domainDumpXMLRequest; + struct { + char xml[QEMUD_MAX_XML_LEN]; + } domainDumpXMLReply; + struct { + int numDomains; + char domains[QEMUD_MAX_NUM_DOMAINS][QEMUD_MAX_NAME_LEN]; + } listDefinedDomainsReply; + struct { + int numDomains; + } numDefinedDomainsReply; + struct { + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + } domainStartRequest; + struct { + int id; + } domainStartReply; + struct { + char xml[QEMUD_MAX_XML_LEN]; + } domainDefineRequest; + struct { + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + char name[QEMUD_MAX_NAME_LEN]; + } domainDefineReply; + struct { + unsigned char uuid[QEMUD_UUID_RAW_LEN]; + } domainUndefineRequest; +};
As suggested better to stick with predefined types with size here.
+/* Each packet has header & data */ +struct qemud_packet { + struct qemud_packet_header header; + union qemud_packet_data data; +}; + + +#endif + + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -ruN libvirt/qemud/qemud.c libvirt-qemu/qemud/qemud.c --- libvirt/qemud/qemud.c 1969-12-31 19:00:00.000000000 -0500 +++ libvirt-qemu/qemud/qemud.c 2007-01-04 20:55:25.000000000 -0500 @@ -0,0 +1,946 @@ +/* + * qemud.c: daemon start of day, guest process & i/o management + * + * Copyright (C) 2006, 2007 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <paths.h> +#include <limits.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/poll.h> +#include <netinet/in.h> +#include <netdb.h> +#include <stdlib.h> +#include <pwd.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <getopt.h> + +#include <libvirt/virterror.h> + +#include "internal.h" +#include "dispatch.h" +#include "driver.h" +#include "config.h" + +static void reapchild(int sig ATTRIBUTE_UNUSED) { + /* We explicitly waitpid the child later */ +} +static int qemudSetCloseExec(int fd) { + int flags; + if ((flags = fcntl(fd, F_GETFD)) < 0) { + return -1; + } + flags |= FD_CLOEXEC; + if ((fcntl(fd, F_SETFD, flags)) < 0) { + return -1; + } + return 0; +} + + +static int qemudSetNonBlock(int fd) { + int flags; + if ((flags = fcntl(fd, F_GETFL)) < 0) { + return -1; + } + flags |= O_NONBLOCK; + if ((fcntl(fd, F_SETFL, flags)) < 0) { + return -1; + } + return 0; +} + +static int qemudGoDaemon(void) { + int pid = fork(); + switch (pid) { + case 0: + { + int stdinfd = -1; + int stdoutfd = -1; + if ((stdinfd = open(_PATH_DEVNULL, O_RDONLY)) < 0) + goto cleanup; + if ((stdoutfd = open(_PATH_DEVNULL, O_WRONLY)) < 0) + goto cleanup; + if (dup2(stdinfd, STDIN_FILENO) != STDIN_FILENO) + goto cleanup; + if (dup2(stdoutfd, STDOUT_FILENO) != STDOUT_FILENO) + goto cleanup; + if (dup2(stdoutfd, STDERR_FILENO) != STDERR_FILENO) + goto cleanup; + if (close(stdinfd) < 0) + goto cleanup; + stdinfd = -1; + if (close(stdoutfd) < 0) + goto cleanup; + stdoutfd = -1; + + int open_max = sysconf (_SC_OPEN_MAX); + for (int i = 0; i < open_max; i++) + if (i != STDIN_FILENO && + i != STDOUT_FILENO && + i != STDERR_FILENO) + close(i); + + if (setsid() < 0) + goto cleanup; + + int nextpid = fork(); + switch (nextpid) { + case 0: + return 0; + case -1: + return -1; + default: + return nextpid; + } + + cleanup: + if (stdoutfd != -1) + close(stdoutfd); + if (stdinfd != -1) + close(stdinfd); + return -1; + + } + + case -1: + return -1; + + default: + { + int got, status = 0; + /* We wait to make sure the next child forked + successfully */ + if ((got = waitpid(pid, &status, 0)) < 0 || + got != pid || + status != 0) { + return -1; + } + + return pid; + } + } +} + +static int qemudListenUnix(struct qemud_server *server, + const char *path, int readonly) { + struct qemud_socket *sock = calloc(1, sizeof(struct qemud_socket)); + struct sockaddr_un addr; + mode_t oldmask; + + if (!sock) + return -1; + + sock->readonly = readonly; + sock->next = server->sockets; + server->sockets = sock; + server->nsockets++; + + if ((sock->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) + return -1; + + if (qemudSetCloseExec(sock->fd) < 0) + return -1; + if (qemudSetNonBlock(sock->fd) < 0) + return -1; + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1); + /* Use '@' to indicate abstract socket namespace */ + if (path[0] == '@') { + addr.sun_path[0] = '\0'; + } else { + unlink(addr.sun_path); + } + + if (readonly) + oldmask = umask(~(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)); + else + oldmask = umask(~(S_IRUSR | S_IWUSR)); + if (bind(sock->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
sock is not freed here, it's still linked in the server sockets list, rather bizarre, no ?
+ return -1; + umask(oldmask); + + if (listen(sock->fd, 30) < 0)
Same here, I don't understand the error handling :-)
+ return -1; + + return 0; +} + +static int qemudListenAddr(struct qemud_server *server, + const char *addr, int readonly) { + struct addrinfo *ai; + struct addrinfo hints; + struct addrinfo *tmp; + char *node, *offset; + const char *service; + + node = strdup(addr); + if (!node) + return -1; + + if (!(offset = strstr(node, ","))) { + service = QEMUD_DEFAULT_PORT_STR; + } else { + offset[0] = '\0'; + service = offset + 1; + } + + memset (&hints, '\0', sizeof (hints)); + hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG | AI_NUMERICSERV; + hints.ai_socktype = SOCK_STREAM; + if (getaddrinfo (node, service, &hints, &ai) < 0) { + free(node); + return -1; + } + free(node); + + tmp = ai; + while (tmp != NULL) { + int opt = 1; + struct qemud_socket *sock = calloc(1, sizeof(struct qemud_socket)); + if (!sock) + goto error; + + sock->readonly = readonly; + sock->next = server->sockets; + server->sockets = sock; + server->nsockets++; + + if ((sock->fd = socket (tmp->ai_family, tmp->ai_socktype, + tmp->ai_protocol)) < 0) + goto error; + + if (qemudSetCloseExec(sock->fd) < 0) + goto error; + + if (qemudSetNonBlock(sock->fd) < 0) + goto error; + + if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, + &opt, sizeof (opt)) < 0) + goto error; + + if (bind (sock->fd, tmp->ai_addr, tmp->ai_addrlen) < 0) + goto error; + + if (listen (sock->fd, SOMAXCONN) < 0) + goto error; + + tmp = tmp->ai_next; + } + + freeaddrinfo (ai); + return 0; + + error: + freeaddrinfo (ai); + return -1; +} + +static int qemudListen(struct qemud_server *server, + const char *listenAddr) { + int readonly = 0; + + if (!strncmp(listenAddr, "ro:", 3)) { + readonly = 1; + listenAddr += 3; + }
wouldn't "ro-" (or ro+ or ro.) be cleaner, allowing to stick it in front of any URI and still keeping it URI-valid ?
+ if (listenAddr[0] == '/' || + (listenAddr[0] == '@' && listenAddr[1] == '/')) { + if (qemudListenUnix(server, listenAddr, readonly) < 0) + return -1; + } else { + if (qemudListenAddr(server, listenAddr, readonly) < 0) + return -1; + } + + return 0; +} + +static struct qemud_server *qemudInitialize(char **listenAddrs, + int naddrs) { + struct qemud_server *server; + int i; + char path[PATH_MAX]; + struct passwd *pw; + int uid, ret; + + if (!(server = calloc(1, sizeof(struct qemud_server)))) + return NULL; + + /* XXX extract actual lversion */ + server->qemuVersion = (0*1000000)+(8*1000)+(0); + /* We don't have a dom-0, so start from 1 */ + server->nextvmid = 1; + + if ((uid = geteuid()) < 0) { + goto cleanup; + } + if (!(pw = getpwuid(uid))) { + goto cleanup; + } + + if ((ret = snprintf(path, PATH_MAX, "%s/.qemud", pw->pw_dir)) > PATH_MAX) { + goto cleanup; + } + + if ((ret = snprintf(server->configDir, PATH_MAX, "%s/.qemud.d", pw->pw_dir)) > PATH_MAX) { + goto cleanup; + }
Hum, at some point the directory modes and ownership should be tested and possibly created, and I guess the sooner the better, why not there ? But I may have missed the check (or not gone there yet :-)
+ for (i = 0 ; i < naddrs ; i++) { + if (qemudListen(server, listenAddrs[i]) < 0) + goto cleanup; + } + + if (qemudScanConfigs(server) < 0) { + goto cleanup; + } + + return server; + + cleanup: + if (server) { + struct qemud_socket *sock = server->sockets; + while (sock) { + close(sock->fd); + sock = sock->next; + } + + free(server); + } + return NULL; +} + + +static int qemudDispatchServer(struct qemud_server *server, struct qemud_socket *sock) { + int fd; + struct sockaddr_storage addr; + unsigned int addrlen = sizeof(addr); + struct qemud_client *client; + + if ((fd = accept(sock->fd, (struct sockaddr *)&addr, &addrlen)) < 0) { + if (errno == EAGAIN) + return 0; + return -1; + } + + if (qemudSetCloseExec(fd) < 0) { + close(fd); + return -1; + } + + if (qemudSetNonBlock(fd) < 0) { + close(fd); + return -1; + } + + client = calloc(1, sizeof(struct qemud_client));
calloc return check missing, close of fd in that case
+ client->fd = fd; + client->next = server->clients; + client->readonly = sock->readonly; + server->clients = client; + server->nclients++; + + return 0; +} + + +int qemudStartVMDaemon(struct qemud_server *server, + struct qemud_vm *vm) { + char **argv = NULL; + int argc = 0; + int pid; + int i, ret = -1; + int stdinfd = -1; + int pipeout[2] = {-1,-1}; + int pipeerr[2] = {-1,-1}; + + if (vm->def.vncPort < 0) + vm->def.vncActivePort = 5900 + server->nextvmid; + else + vm->def.vncActivePort = vm->def.vncPort; + + if (qemudBuildCommandLine(server, vm, &argv, &argc) < 0) + return -1; + + if (1) { /* XXX debug stuff */ + printf("Spawn QEMU '"); + for (i = 0 ; i < argc; i++) { + printf("%s", argv[i]); + if (i == (argc-1)) + printf("'\n"); + else + printf(" "); + } + } + + if ((stdinfd = open(_PATH_DEVNULL, O_RDONLY)) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot open %s", _PATH_DEVNULL); + goto cleanup; + } + + if (pipe(pipeout) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot create pipe"); + goto cleanup; + } + + if (pipe(pipeerr) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot create pipe"); + goto cleanup; + } + + if ((pid = fork()) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot fork child process"); + goto cleanup; + } + + if (pid) { /* parent */ + close(stdinfd); + close(pipeout[1]); + close(pipeerr[1]); + qemudSetNonBlock(pipeout[0]); + qemudSetNonBlock(pipeerr[0]); + vm->def.id = server->nextvmid++; + vm->pid = pid; + vm->stdout = pipeout[0]; + vm->stderr = pipeerr[0]; + + } else { /* child */ + int null; + if ((null = open(_PATH_DEVNULL, O_RDONLY)) < 0) + _exit(1); + + if (close(pipeout[0]) < 0) + _exit(1); + if (close(pipeerr[0]) < 0) + _exit(1); + + if (dup2(stdinfd, STDIN_FILENO) < 0) + _exit(1); + if (dup2(pipeout[1], STDOUT_FILENO) < 0) + _exit(1); + if (dup2(pipeerr[1], STDERR_FILENO) < 0) + _exit(1); + + int open_max = sysconf (_SC_OPEN_MAX); + for (i = 0; i < open_max; i++) + if (i != STDOUT_FILENO && + i != STDERR_FILENO && + i != STDIN_FILENO) + close(i); + + execvp(argv[0], argv); + + _exit(1); + } + + ret = 0; + + cleanup: + + for (i = 0 ; i < argc ; i++) { + free(argv[i]); + } + free(argv); + + return ret; +} + + +static void qemudDispatchClientFailure(struct qemud_server *server, struct qemud_client *client) { + struct qemud_client *tmp = server->clients; + struct qemud_client *prev = NULL; + while (tmp) { + if (tmp == client) { + if (prev == NULL) + server->clients = client->next; + else + prev->next = client->next; + server->nclients--; + break; + } + prev = tmp; + tmp = tmp->next; + } + close(client->fd); + free(client); +} + + +static int qemudDispatchClientRequest(struct qemud_server *server, struct qemud_client *client) { + if (qemudDispatch(server, + client, + &client->incoming, + &client->outgoing) < 0) { + return -1; + } + + client->outgoingSent = 0; + client->tx = 1; + client->incomingReceived = 0; + + return 0; +} + +static void qemudDispatchClientRead(struct qemud_server *server, struct qemud_client *client) { + char *data = (char *)&client->incoming; + unsigned int got = client->incomingReceived; + int want; + int ret; + + restart: + if (got >= sizeof(struct qemud_packet_header)) { + want = sizeof(struct qemud_packet_header) + client->incoming.header.dataSize - got; + } else { + want = sizeof(struct qemud_packet_header) - got; + } + + if ((ret = read(client->fd, data+got, want)) <= 0) { + if (errno != EAGAIN) { + qemudDispatchClientFailure(server, client); + return; + } + return; + } + got += ret; + client->incomingReceived += ret; + + /* If we've finished header, move onto body */ + if (client->incomingReceived == sizeof(struct qemud_packet_header)) { + /* Client lied about dataSize */ + if (client->incoming.header.dataSize > sizeof(union qemud_packet_data)) { + printf("Bogus data size %u\n", client->incoming.header.dataSize); + qemudDispatchClientFailure(server, client); + return; + } + if (client->incoming.header.dataSize) { + printf("- Restarting recv to process body (%d bytes)\n", client->incoming.header.dataSize); + goto restart; + } + } + + /* If we've finished body, dispatch the request */ + if (ret == want) { + if (qemudDispatchClientRequest(server, client) < 0) + qemudDispatchClientFailure(server, client); + } +} + +static void qemudDispatchClientWrite(struct qemud_server *server, struct qemud_client *client) { + char *data = (char *)&client->outgoing; + int sent = client->outgoingSent; + int todo = sizeof(struct qemud_packet_header) + client->outgoing.header.dataSize - sent; + int ret; + if ((ret = write(client->fd, data+sent, todo)) < 0) { + if (errno != EAGAIN) { + qemudDispatchClientFailure(server, client); + return; + } + return; + } + client->outgoingSent += ret; + + if (todo == ret) { + client->tx = 0; + } +} + +static int qemudVMData(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_vm *vm, int fd) { + char buf[4096]; + if (vm->pid < 0) + return 0; + + for (;;) { + int ret = read(fd, buf, 4096); + if (ret < 0) { + if (errno == EAGAIN) + return 0; + return -1; + } + if (ret == 0) { + return 0; + } + write(STDOUT_FILENO, "[", 1); + write(STDOUT_FILENO, buf, ret); + write(STDOUT_FILENO, "]", 1); + } +} + +int qemudShutdownVMDaemon(struct qemud_server *server, struct qemud_vm *vm) { + struct qemud_vm *prev = NULL, *curr = server->activevms; + + /* Already cleaned-up */ + if (vm->pid < 0) + return 0; + + kill(vm->pid, SIGTERM); + + /* Move it to inactive vm list */ + while (curr) { + if (curr == vm) { + if (prev) { + prev->next = curr->next; + } else { + server->activevms = curr->next; + } + server->nactivevms--; + + curr->next = server->inactivevms; + server->inactivevms = curr; + server->ninactivevms++; + break; + } + prev = curr; + curr = curr->next; + } + + qemudVMData(server, vm, curr->stdout); + qemudVMData(server, vm, curr->stderr); + close(curr->stdout); + close(curr->stderr); + curr->stdout = -1; + curr->stderr = -1; + server->nvmfds -= 2; + + if (waitpid(vm->pid, NULL, WNOHANG) != vm->pid) { + kill(vm->pid, SIGKILL); + if (waitpid(vm->pid, NULL, 0) != vm->pid) { + printf("Got unexpected pid, damn\n"); + } + } + + vm->pid = -1; + vm->def.id = -1; + + return 0; +} + +static int qemudDispatchVMLog(struct qemud_server *server, struct qemud_vm *vm, int fd) { + if (qemudVMData(server, vm, fd) < 0) + if (qemudShutdownVMDaemon(server, vm) < 0) + return -1; + return 0; +} +static int qemudDispatchVMMonitor(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_vm *vm ATTRIBUTE_UNUSED) { + return -1; +} +static int qemudDispatchVMFailure(struct qemud_server *server, struct qemud_vm *vm, + int fd ATTRIBUTE_UNUSED) { + if (qemudShutdownVMDaemon(server, vm) < 0) + return -1; + return 0; +} + + +static int qemudDispatchPoll(struct qemud_server *server, struct pollfd *fds) { + struct qemud_socket *sock = server->sockets; + struct qemud_client *client = server->clients; + struct qemud_vm *vm = server->activevms; + struct qemud_vm *tmp; + int ret = 0; + int fd = 0; + + while (sock) { + struct qemud_socket *next = sock->next; + if (fds[fd].revents) + if (qemudDispatchServer(server, sock) < 0) + return -1; + fd++; + sock = next; + } + + while (client) { + struct qemud_client *next = client->next; + if (fds[fd].revents) { + if (fds[fd].revents == POLLOUT) + qemudDispatchClientWrite(server, client); + else if (fds[fd].revents == POLLIN) + qemudDispatchClientRead(server, client); + else + qemudDispatchClientFailure(server, client); + } + fd++; + client = next; + } + while (vm) { + struct qemud_vm *next = vm->next; + int failed = 0, + stdoutfd = vm->stdout, + stderrfd = vm->stderr, + monitorfd = vm->monitor; + if (stdoutfd != -1) { + if (fds[fd].revents) { + if (fds[fd].revents == POLLIN) { + if (qemudDispatchVMLog(server, vm, fds[fd].fd) < 0) + failed = 1; + } else { + if (qemudDispatchVMFailure(server, vm, fds[fd].fd) < 0) + failed = 1; + } + } + fd++; + } + if (stderrfd != -1) { + if (!failed) { + if (fds[fd].revents) { + if (fds[fd].revents == POLLIN) { + if (qemudDispatchVMLog(server, vm, fds[fd].fd) < 0) + failed = 1; + } else { + if (qemudDispatchVMFailure(server, vm, fds[fd].fd) < 0) + failed = 1; + } + } + } + fd++; + } + if (monitorfd != -1) { + if (!failed) { + if (fds[fd].revents) { + if (fds[fd].revents == POLLIN) { + if (qemudDispatchVMMonitor(server, vm) < 0) + failed = 1; + } else { + if (qemudDispatchVMFailure(server, vm, fds[fd].fd) < 0) + failed = 1; + } + } + } + fd++; + } + vm = next; + if (failed) + ret = -1; + } + + /* Cleanup any VMs which shutdown & dont have an associated + config file */ + vm = server->inactivevms; + tmp = NULL; + while (vm) { + if (!vm->configFile[0]) { + struct qemud_vm *next = vm->next; + if (tmp) { + tmp->next = next; + } else { + server->inactivevms = next; + } + qemudFreeVM(vm); + vm = next; + } else { + tmp = vm; + vm = vm->next; + } + } + + return ret; +} + +static void qemudPreparePoll(struct qemud_server *server, struct pollfd *fds) { + int fd = 0; + + for (struct qemud_socket *sock = server->sockets ; sock ; sock = sock->next) { + fds[fd].fd = sock->fd; + fds[fd].events = POLLIN; + fd++; + } + + for (struct qemud_client *client = server->clients ; client ; client = client->next) { + fds[fd].fd = client->fd; + /* Refuse to read more from client if tx is pending to + rate limit */ + if (client->tx) + fds[fd].events = POLLOUT | POLLERR | POLLHUP; + else + fds[fd].events = POLLIN | POLLERR | POLLHUP; + fd++; + } + for (struct qemud_vm *vm = server->activevms ; vm ; vm = vm->next) { + if (vm->stdout != -1) { + fds[fd].fd = vm->stdout; + fds[fd].events = POLLIN | POLLERR | POLLHUP; + fd++; + } + if (vm->stderr != -1) { + fds[fd].fd = vm->stderr; + fds[fd].events = POLLIN | POLLERR | POLLHUP; + fd++; + } + if (vm->monitor != -1) { + fds[fd].fd = vm->monitor; + fds[fd].events = POLLIN | POLLERR | POLLHUP; + fd++; + } + } +} + + + +static int qemudOneLoop(struct qemud_server *server, int timeout) { + int nfds = server->nsockets + server->nclients + server->nvmfds; + struct pollfd fds[nfds]; + int thistimeout = -1; + int ret; + + /* If we have no clients or vms, then timeout after + 30 seconds, letting daemon exit */ + if (timeout > 0 && + !server->nclients && + !server->nactivevms) + thistimeout = timeout; + + qemudPreparePoll(server, fds); + + retry: + + if ((ret = poll(fds, nfds, thistimeout * 1000)) < 0) { + if (errno == EINTR) { + goto retry; + } + return -1; + } + + /* Must have timed out */ + if (ret == 0) + return -1; + + if (qemudDispatchPoll(server, fds) < 0) + return -1; + + return 0; +} + +static int qemudRunLoop(struct qemud_server *server, int timeout) { + int ret; + + while ((ret = qemudOneLoop(server, timeout)) == 0) + ; + + return ret == -1 ? -1 : 0; +} + +static void qemudCleanup(struct qemud_server *server) { + struct qemud_socket *sock = server->sockets; + while (sock) { + close(sock->fd); + sock = sock->next; + } + free(server); +} + +#define MAX_LISTEN 5 +int main(int argc, char **argv) { + int daemon = 0; + int verbose = 0; + int timeout = -1; + char *listenAddrs[MAX_LISTEN]; + int naddrs = 0; + + struct option opts[] = { + { "verbose", no_argument, &verbose, 1}, + { "daemon", no_argument, &daemon, 1}, + { "timeout", required_argument, 0, 't'}, + { "listen", required_argument, 0, 'l' }, + {0, 0, 0, 0} + }; + + while (1) { + int optidx = 0; + int c; + char *tmp; + + c = getopt_long(argc, argv, "vdt:u:U:l:L:", opts, &optidx); + + if (c == -1) + break; + + switch (c) { + case 0: + /* Got one of the flags */ + break; + case 't': + timeout = strtol(optarg, &tmp, 10); + if (!tmp) + timeout = -1; + if (timeout <= 0) + timeout = -1; + break; + case 'l': + if (naddrs >= MAX_LISTEN) + abort(); + if (!(listenAddrs[naddrs] = strdup(optarg))) + abort(); + naddrs++; + break; + case '?': + /* getopt_long already printed error message */ + break; + default: + abort(); + } + } + + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) + return 3; + if (signal(SIGCHLD, reapchild) == SIG_ERR) + return 3; + + if (daemon) { + int pid = qemudGoDaemon(); + if (pid < 0) + return 1; + if (pid > 0) + return 0; + } + + struct qemud_server *server = qemudInitialize(listenAddrs, naddrs); + + if (!server) + return 2; + + qemudRunLoop(server, timeout); + + qemudCleanup(server); + + return 0; +} + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */
-- Libvir-list mailing list Libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
-- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Tue, Jan 09, 2007 at 07:00:57AM -0500, Daniel Veillard wrote:
On Fri, Jan 05, 2007 at 09:16:54PM +0000, Daniel P. Berrange wrote:
The attached patch provides the QEMU daemon for managing the QEMU instances and providing a network protocol for the libvirt driver to talk to over UNIX domain sockets or IPv4/6.
+static int qemudParseUUID(const char *uuid, + unsigned char *rawuuid) {
need to go in lib/
Yes, +1
+/* The list of possible machine types for various architectures, + as supported by QEMU - taken from 'qemu -M ?' for each arch */ +static const char *arch_info_x86_machines[] = { + "pc", "isapc" +}; +static const char *arch_info_mips_machines[] = { + "mips" +}; +static const char *arch_info_sparc_machines[] = { + "sun4m" +}; +static const char *arch_info_ppc_machines[] = { + "g3bw", "mac99", "prep" +};
Hum, I wonder how we are gonna keep the sync :-)
I figure its not much hassle, since the list of supported machine types hasn't significantly changed over time I've been monitoring QEMU development. If neccessary we can switch to spawning 'qemu -M ?' and parsing the list of machines.
+ /* XXX lame. should actually use $PATH ... */ + path = malloc(strlen(name) + strlen("/usr/bin/") + 1); + if (!path) { + qemudReportError(server, VIR_ERR_NO_MEMORY, "path"); + return NULL; + } + strcpy(path, "/usr/bin/"); + strcat(path, name); + return path; +}
Shouldn't we walk $PATH to look up ?
Yes, for sure.
+ obj = xmlXPathEval(BAD_CAST "string(/domain/os/type[1]/@machine)", ctxt); + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + const char *defaultMachine = qemudDefaultMachineForArch(vm->def.os.arch); + if (strlen(defaultMachine) >= (QEMUD_OS_MACHINE_MAX_LEN-1)) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "machine type too long"); + goto error; + } + strcpy(vm->def.os.machine, defaultMachine); + } else { + if (strlen((const char *)obj->stringval) >= (QEMUD_OS_MACHINE_MAX_LEN-1)) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "architecture type too long"); + goto error; + } + strcpy(vm->def.os.machine, (const char *)obj->stringval); + } + if (obj) + xmlXPathFreeObject(obj);
Hum, there is a lot of duplication in the parsing, at least I should try to provide libxml2 XPath lookup based functions instead of duplicating xmlXPathEval all over the place with relatively complex cleanup etc...
Yeah, there's quite alot of this similar code across all the libvirt backends. At the very least it probably makes sense to provide some convenience functions in libvirt to simplify this repeated code. I think just 3-4 helper functions would make a huge difference.
Can we try to keep the fuction comments in line
/** * funcname: * @arg1: ... * * .... * * Returns .... */
even for internal ones, don't block commiting for that but I will probably make a pass over this once commited, that will force me to get deeper understanding of the code
Yeah, i just added in the comments at the last minute before sending the patch so didn't get around to formatting them nicely.
+ towrite = strlen(xml); + if (write(fd, xml, towrite) != towrite) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot write config file %s", vm->configFile); + goto cleanup; + } + + if (close(fd) < 0) { + qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot save config file %s", vm->configFile); + goto cleanup; + } + + ret = 0; + + cleanup:
Shouldn't it close fd if write() fails ?
Yes, bugtastic.
+ uuid = vm->def.uuid; + if (qemudBufferPrintf(&buf, " <uuid>%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x</uuid>\n", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]) < 0)
Hum, do we really want to use the format with '-' and not just the raw hex dump by default, I'm still wondering what this brings...
I don't really mind. We should just provide a convenience function 'virFormatUUID' so we can be consistent everywhere in libvirt. The dashes make it slightly easier to read when comparing two UUIDs, but that's basically only difference.
+static int qemudDispatchDomainCreate(struct qemud_server *server, struct qemud_client *client, + struct qemud_packet *in, struct qemud_packet *out) { + if (in->header.dataSize != sizeof(in->data.domainCreateRequest)) + return -1; + + in->data.domainCreateRequest.xml[QEMUD_MAX_XML_LEN-1] ='\0'; + + struct qemud_vm *vm = qemudDomainCreate(server, in->data.domainCreateRequest.xml); + if (!vm) { + if (qemudDispatchFailure(server, client, out) < 0) + return -1; + } else { + out->header.type = QEMUD_PKT_DOMAIN_CREATE; + out->header.dataSize = sizeof(out->data.domainCreateReply); + out->data.domainCreateReply.id = vm->def.id; + memcpy(out->data.domainCreateReply.uuid, vm->def.uuid, QEMUD_UUID_RAW_LEN); + strncpy(out->data.domainCreateReply.name, vm->def.name, QEMUD_MAX_NAME_LEN-1); + out->data.domainCreateReply.name[QEMUD_MAX_NAME_LEN-1] = '\0'; + } + return 0; +}
Hum, when qemudDomainCreate returns, we get a new vm, is that a new structure which is gonna be ref-counted and cleaned up at some point ? I'm a bit lost there, is that exactly the same scheme as for Xen domain instances ?
The qemudDomainCreate method is within the libvirt_qemud daemon, so we don't need any reference counting in the server itself. The memory associated with the object allocated to 'struct qemud_vm *' is free'd later in qemud.c when the guest shuts down. This function is only used for transient guests. Persistent guests (ie those with an explicit saved config file) are started by qemudDomainStart.
+/* List of different packet types which can be sent */ +enum { + QEMUD_PKT_FAILURE, + QEMUD_PKT_GET_PROTOCOL_VERSION,
I always feel safer to initialize at least the first values in an enum. for example QEMUD_PKT_FAILURE = 0, not strictly necessary but for client/server stuff values I feel just better ... somehow I don't trust the way compilers allocate enums .
Yeah, it also makes it explicit that it starts at 0 for those who aren't so familiar with C rules for enums.
+ if (readonly) + oldmask = umask(~(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)); + else + oldmask = umask(~(S_IRUSR | S_IWUSR)); + if (bind(sock->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
sock is not freed here, it's still linked in the server sockets list, rather bizarre, no ?
Yeha, its a little odd - complicated by the fact that there can be many sockets open. The caller, qemudInitializer, iterates over all the addresses it needs to listen on, and if any one fails, then it takes care of free'ing the qemud_socket structure. This means we don't have to duplicate the funky cleanup code in both qemudListenUNIX and qemudListenAddr.
+ if (listen(sock->fd, 30) < 0)
Same here, I don't understand the error handling :-)
Same rule as above - caller cleans up.
+ if (!strncmp(listenAddr, "ro:", 3)) { + readonly = 1; + listenAddr += 3; + }
wouldn't "ro-" (or ro+ or ro.) be cleaner, allowing to stick it in front of any URI and still keeping it URI-valid ?
Yeah, I'm not entirely happy with current syntax here
+ goto cleanup; + } + + if ((ret = snprintf(server->configDir, PATH_MAX, "%s/.qemud.d", pw->pw_dir)) > PATH_MAX) { + goto cleanup; + }
Hum, at some point the directory modes and ownership should be tested and possibly created, and I guess the sooner the better, why not there ? But I may have missed the check (or not gone there yet :-)
Yeah, actually I need to set umask to ~0600 here to ensure the config files are writeable only by the user. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

The attached patch provides a new driver backend for libvirt which allows management of QEMU instances by talking to the libvirt QEMU daemon. It basically just marshalls libvirt API calls onto the wire & de-marshalls the reply. All the interesting functionality stuff is in the daemon. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Fri, Jan 05, 2007 at 09:18:53PM +0000, Daniel P. Berrange wrote:
The attached patch provides a new driver backend for libvirt which allows management of QEMU instances by talking to the libvirt QEMU daemon. It basically just marshalls libvirt API calls onto the wire & de-marshalls the reply. All the interesting functionality stuff is in the daemon.
Okay, but I'm getting a bit lost about the potential daemons running, there one can be autostarted, in the previous patch we also potentially fork(fork(daemon)) so I'm wondering how many process are actually running in the end, maybe I will need a couple of pictures, like in the current architecture page http://libvirt.org/architecture.html (which will have to be updated too :-) But this looks fun ! A lot of code looks familiar, seems there is many path we could identify as duplicate and put in a library ... [...]
+int qemudNodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED, + virNodeInfoPtr info){ + info->cores = 1; + info->threads = 1; + info->sockets = 1; + info->nodes = 1; + strcpy(info->model, "i686"); + info->mhz = 6000; + info->cpus = 1; + info->memory = 1024*1024; + return 0; +}
haha :-) At least worth a big TODO ! Shouldn't qemud_internal.c gets its own error reporting function like the other _internal.c which does the right stuff, I only spotted returns of error values, and this is linked to the client binary so this seems to be missing, right ?
Index: src/qemud_internal.h +#ifdef __cplusplus +extern "C" { +#endif + + void qemudRegister(void); + +#ifdef __cplusplus
ideally clean ;-)
Index: src/virsh.c =================================================================== RCS file: /data/cvs/libvirt/src/virsh.c,v retrieving revision 1.41 diff -u -p -r1.41 virsh.c --- src/virsh.c 22 Nov 2006 17:48:29 -0000 1.41 +++ src/virsh.c 5 Jan 2007 21:09:08 -0000 @@ -292,7 +292,7 @@ cmdConnect(vshControl * ctl, vshCmd * cm
if (ctl->name) free(ctl->name); - ctl->name = vshCommandOptString(cmd, "name", NULL); + ctl->name = vshStrdup(ctl, vshCommandOptString(cmd, "name", NULL));
I'm a bit lost on that one, was that a separate bug ? We switch from a static string to dynamically allocated one, right ?
if (!ro) ctl->conn = virConnectOpen(ctl->name);
...
Index: src/virterror.c =================================================================== RCS file: /data/cvs/libvirt/src/virterror.c,v retrieving revision 1.19 diff -u -p -r1.19 virterror.c --- src/virterror.c 8 Nov 2006 16:55:20 -0000 1.19 +++ src/virterror.c 5 Jan 2007 21:09:08 -0000 @@ -268,6 +268,9 @@ virDefaultErrorFunc(virErrorPtr err) case VIR_FROM_RPC: dom = "XML-RPC "; break; + case VIR_FROM_QEMUD: + dom = "QEMUD "; + break; } if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) { domain = err->dom->name;
I guess some new error code will need to be added too once quemud_internal.c is being updated ... Hum, how do we process errors provided by the server ? Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Tue, Jan 09, 2007 at 09:44:44AM -0500, Daniel Veillard wrote:
On Fri, Jan 05, 2007 at 09:18:53PM +0000, Daniel P. Berrange wrote:
The attached patch provides a new driver backend for libvirt which allows management of QEMU instances by talking to the libvirt QEMU daemon. It basically just marshalls libvirt API calls onto the wire & de-marshalls the reply. All the interesting functionality stuff is in the daemon.
Okay, but I'm getting a bit lost about the potential daemons running, there one can be autostarted, in the previous patch we also potentially fork(fork(daemon)) so I'm wondering how many process are actually running in the end, maybe I will need a couple of pictures, like in the current architecture page http://libvirt.org/architecture.html (which will have to be updated too :-)
There's two ways its launched: - The session bus is auto-launched by libvirt. In this scenaro, libvirt does the double fork() magic to dis-inherit the libvirt_qemud process - The system bus is started manually. In this case, it runs in foreground unless you use the --daemon command line argument to make it do the double-fork() into background.
[...]
+int qemudNodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED, + virNodeInfoPtr info){ + info->cores = 1; + info->threads = 1; + info->sockets = 1; + info->nodes = 1; + strcpy(info->model, "i686"); + info->mhz = 6000; + info->cpus = 1; + info->memory = 1024*1024; + return 0; +}
haha :-) At least worth a big TODO !
Shouldn't qemud_internal.c gets its own error reporting function like the other _internal.c which does the right stuff, I only spotted returns of error values, and this is linked to the client binary so this seems to be missing, right ?
The only errors that should be occurring in qemud_internal.c are two classes: - Data transport errors (eg, failure to connect, TLS failures, etc) - Errors forwarded back from the daemon over the wire. I already deal with the latter. The former probably needs a little more work.
RCS file: /data/cvs/libvirt/src/virsh.c,v retrieving revision 1.41 diff -u -p -r1.41 virsh.c --- src/virsh.c 22 Nov 2006 17:48:29 -0000 1.41 +++ src/virsh.c 5 Jan 2007 21:09:08 -0000 @@ -292,7 +292,7 @@ cmdConnect(vshControl * ctl, vshCmd * cm
if (ctl->name) free(ctl->name); - ctl->name = vshCommandOptString(cmd, "name", NULL); + ctl->name = vshStrdup(ctl, vshCommandOptString(cmd, "name", NULL));
I'm a bit lost on that one, was that a separate bug ? We switch from a static string to dynamically allocated one, right ?
Opps, this is compeltely unrelated to QEMU - its a general bug. The string from vshCommandOptString is just a pointer to its internal buffer. This is free'd next time user enters a command in virsh. So by storing it in ctl->name we keep a pointer to a free'd string, with predictable SEGV a short while later. Simply dup'ing the string is enough.
@@ -268,6 +268,9 @@ virDefaultErrorFunc(virErrorPtr err) case VIR_FROM_RPC: dom = "XML-RPC "; break; + case VIR_FROM_QEMUD: + dom = "QEMUD "; + break; } if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) { domain = err->dom->name;
I guess some new error code will need to be added too once quemud_internal.c is being updated ... Hum, how do we process errors provided by the server ?
The libvirt_qemud daemon includes <libvirt/virterror.h> and sends back the appropriate error codes over the wire, along with a message. This is used to call __virRaiseError - see the qemudPacketError() function at the top of the qemud_internal.c file - this function is called whenver the wire protocol gives back an unexpected result Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Jan 09, 2007 at 02:57:24PM +0000, Daniel P. Berrange wrote:
On Tue, Jan 09, 2007 at 09:44:44AM -0500, Daniel Veillard wrote:
On Fri, Jan 05, 2007 at 09:18:53PM +0000, Daniel P. Berrange wrote:
The attached patch provides a new driver backend for libvirt which allows management of QEMU instances by talking to the libvirt QEMU daemon. It basically just marshalls libvirt API calls onto the wire & de-marshalls the reply. All the interesting functionality stuff is in the daemon.
Okay, but I'm getting a bit lost about the potential daemons running, there one can be autostarted, in the previous patch we also potentially fork(fork(daemon)) so I'm wondering how many process are actually running in the end, maybe I will need a couple of pictures, like in the current architecture page http://libvirt.org/architecture.html (which will have to be updated too :-)
There's two ways its launched:
- The session bus is auto-launched by libvirt. In this scenaro, libvirt does the double fork() magic to dis-inherit the libvirt_qemud process - The system bus is started manually. In this case, it runs in foreground unless you use the --daemon command line argument to make it do the double-fork() into background.
okay, and the former auto exits while the latter sticks until stopped I there any way we can avoid /etc/init.d/libvirt <grin/> ?
Shouldn't qemud_internal.c gets its own error reporting function like the other _internal.c which does the right stuff, I only spotted returns of error values, and this is linked to the client binary so this seems to be missing, right ?
The only errors that should be occurring in qemud_internal.c are two classes:
- Data transport errors (eg, failure to connect, TLS failures, etc) - Errors forwarded back from the daemon over the wire.
I already deal with the latter. The former probably needs a little more work.
okay
RCS file: /data/cvs/libvirt/src/virsh.c,v retrieving revision 1.41 diff -u -p -r1.41 virsh.c --- src/virsh.c 22 Nov 2006 17:48:29 -0000 1.41 +++ src/virsh.c 5 Jan 2007 21:09:08 -0000 @@ -292,7 +292,7 @@ cmdConnect(vshControl * ctl, vshCmd * cm
if (ctl->name) free(ctl->name); - ctl->name = vshCommandOptString(cmd, "name", NULL); + ctl->name = vshStrdup(ctl, vshCommandOptString(cmd, "name", NULL));
I'm a bit lost on that one, was that a separate bug ? We switch from a static string to dynamically allocated one, right ?
Opps, this is compeltely unrelated to QEMU - its a general bug. The string from vshCommandOptString is just a pointer to its internal buffer. This is free'd next time user enters a command in virsh. So by storing it in ctl->name we keep a pointer to a free'd string, with predictable SEGV a short while later. Simply dup'ing the string is enough.
okay, logical. maybe fix it as a separate commit :-)
@@ -268,6 +268,9 @@ virDefaultErrorFunc(virErrorPtr err) case VIR_FROM_RPC: dom = "XML-RPC "; break; + case VIR_FROM_QEMUD: + dom = "QEMUD "; + break; } if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) { domain = err->dom->name;
I guess some new error code will need to be added too once quemud_internal.c is being updated ... Hum, how do we process errors provided by the server ?
The libvirt_qemud daemon includes <libvirt/virterror.h> and sends back the appropriate error codes over the wire, along with a message. This is used to call __virRaiseError - see the qemudPacketError() function at the top of the qemud_internal.c file - this function is called whenver the wire protocol gives back an unexpected result
So QEmu/KVM runtime generates no new errors ? Sounds surprizing to me ... Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Tue, Jan 09, 2007 at 10:19:17AM -0500, Daniel Veillard wrote:
On Tue, Jan 09, 2007 at 02:57:24PM +0000, Daniel P. Berrange wrote:
On Tue, Jan 09, 2007 at 09:44:44AM -0500, Daniel Veillard wrote:
On Fri, Jan 05, 2007 at 09:18:53PM +0000, Daniel P. Berrange wrote:
The attached patch provides a new driver backend for libvirt which allows management of QEMU instances by talking to the libvirt QEMU daemon. It basically just marshalls libvirt API calls onto the wire & de-marshalls the reply. All the interesting functionality stuff is in the daemon.
Okay, but I'm getting a bit lost about the potential daemons running, there one can be autostarted, in the previous patch we also potentially fork(fork(daemon)) so I'm wondering how many process are actually running in the end, maybe I will need a couple of pictures, like in the current architecture page http://libvirt.org/architecture.html (which will have to be updated too :-)
There's two ways its launched:
- The session bus is auto-launched by libvirt. In this scenaro, libvirt does the double fork() magic to dis-inherit the libvirt_qemud process - The system bus is started manually. In this case, it runs in foreground unless you use the --daemon command line argument to make it do the double-fork() into background.
okay, and the former auto exits while the latter sticks until stopped I there any way we can avoid /etc/init.d/libvirt <grin/> ?
I'm not sure we can really - if the admin wants to allow remote connections when we need to start the daemon ahead of time to make it listen on TCP port. It means we also avoid a setuid() binary - I think its preferrable that for QEMU users's have to stick with the personal qemud://session instance unless the admin explicitly turns on the qemu:///system instance.
dom = "XML-RPC "; break; + case VIR_FROM_QEMUD: + dom = "QEMUD "; + break; } if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) { domain = err->dom->name;
I guess some new error code will need to be added too once quemud_internal.c is being updated ... Hum, how do we process errors provided by the server ?
The libvirt_qemud daemon includes <libvirt/virterror.h> and sends back the appropriate error codes over the wire, along with a message. This is used to call __virRaiseError - see the qemudPacketError() function at the top of the qemud_internal.c file - this function is called whenver the wire protocol gives back an unexpected result
So QEmu/KVM runtime generates no new errors ? Sounds surprizing to me ...
Mostly I've just encoded everything as a generic 'internal error'. There is definitely scope for adding some new specific error codes - particular for dealing with inactive domains - that would help the same code in Xen backend too. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Fri, Jan 05, 2007 at 09:14:30PM +0000, Daniel P. Berrange wrote:
The following series of (2) patches adds a QEMU driver to libvirt. The first patch provides a daemon for managing QEMU instances, the second provides a driver letting libvirt manage QEMU via the daemon.
Basic architecture ------------------
The reason for the daemon architecture is two fold:
- At this time, there is no (practical) way to enumerate QEMU instances, or reliably connect to the monitor console of an existing process. There is also no way to determine the guest configuration associated with a daemon.
Okay, we admitted that principle in the first round of QEmu patches last year. The only question I have is about the multiplication of running daemons for libvirt, as we also have another one already for read only xen hypervisor access. We could either decide to keep daemon usages very specific (allows to also easilly restrict their priviledge) or try to unify them. I guess from a security POV it's still better to keep them separate, and anyway they are relatively unlikely to be run at the same time (KVM and Xen on the same Node).
- It is desirable to be able to manage QEMU instances using either an unprivilegd local client, or a remote client. The daemon can provide connectivity via UNIX domain sockets, or IPv4 / IPv6 and layer in suitable authentication / encryption via TLS and/or SASL protocols.
C.f. my previous mail, yes authentication is key. Could you elaborate in some way how the remote access and the authentication is set up, see my previous mail on the remote xend access, we should try to unify and set up a specific page to document remote accesses.
Anthony Ligouri is working on patches for QEMU with the goal of addressing the first point. For example, an extra command line argument will cause QEMU to save a PID file and create a UNIX socket for its monitor at a well-defined path. More functionality in the monitor console will allow the guest configuration to be reverse engineered from a running guest. Even with those patches, however, it will still be desirable to have a daemon to provide more flexible connectivity, and to facilitate implementation libvirt APIs which are host (rather than guest) related. Thus I expect that over time we can simply enhance the daemon to take advantage of newer capabilities in the QEMU monitor, but keep the same basic libvirt driver architecture.
Okay, Work in Progress.
Considering some of the other hypervisor technologies out there, in particular User Mode Linux, and lhype, it may well become possible to let this QEMU daemon also provide the management of these guests - allowing re-use of the single driver backend in the libvirt client library itself.
Which reopen the question, one multi-featured daemon or multiple simpler (but possibly redundant) daemons ?
XML format ----------
As discussed in the previous mail thread, the XML format for describing guests with the QEMU backend is the same structure as that for Xen guests, with following enhancements:
- The 'type' attribute on the top level <domain> tag can take one of the values 'qemu', 'kqemu' or 'kvm' instead of 'xen'. This selects between the different virtualization approaches QEMU can provide.
- The '<type>' attribute within the <os> block of the XML (for now) is still expected to the 'hvm' (indicating full virtualization), although I'm trying to think of a better name, since its not technically hardware accelerated unless you're using KVM
yeah I don't have a good value to suggest except "unknown" bacause basically we don't know a priori what the running OS will be.
- The '<type>' attribute within the <os> block of the XML can have two optional 'arch' and 'machine' attributes. The former selects the CPU architecture to be emulated; the latter the specific machine to have QEMU emulate (determine those supported by QEMU using 'qemu -M ?').
Okay, I hope we will have enough flexibility in the virNodeInfo model to express the various combinations, we have a 32 char string for this, I guess that should be sufficient, but I don't know how to express that in the best way I will see how the pacth does it. From my recollection of posts on qemu-devel some of the machines names can be a bit long on specific emulated target. At least we should be okay for a PC architecture.
- The <kernel>, <initrd>, <cmdline> elements can be used to specify an explicit kernel to boot off[1], otherwise it'll do a boot of the cdrom, harddisk / floppy (based on <boot> element). Well,the kernel bits are parsed at least. I've not got around to using them when building the QEMU argv yet.
Okay
- The disk devices are configured in same way as Xen HVM guests. eg you have to use hda -> hdd, and/or fda -> fdb. Only hdc can be selected as a cdrom device.
Good !
- The network configuration is work in progress. QEMU has many ways to setup networking. I use the 'type' attribute to select between the different approachs 'user', 'tap', 'server', 'client', 'mcast' mapping them directly onto QEMU command line arguments. You can specify a MAC address as usual too. I need to implement auto-generation of MAC addresses if omitted. Most of them have extra bits of metadata though which I've not figured out appropriate XML for yet. Thus when building the QEMU argv I currently just hardcode 'user' networking.
Okay, since user is the default in QEmu (assuming I remember correctly :-)
- The QEMU binary is determined automatically based on the requested CPU architecture, defaulting to i686 if non specified. It is possible to override the default binary using the <emulator> element within the <devices> section. This is different to previously discussed, because recent work by Anthony merging VMI + KVM to give paravirt guests means that the <loader> element is best kept to refer to the VMI ROM (or other ROM like files :-) - this is also closer to Xen semantics anyway.
Hum, the ROM, one more parameter, actually we may once need to provide for mutiple of them at some point if they start mapping non contiguous area.
Connectivity ------------
The namespace under which all connection URIs come is 'qemud'. Thereafter there are several options. First, two well-known local hypervisor connections
- qemud:///session
This is a per-user private hypervisor connection. The libvirt daemon and qemu guest processes just run as whatever UNIX user your client app is running. This lets unprivileged users use the qemu driver without needing any kind admin rights. Obviously you can't use KQEMU or KVM accelerators unless the /dev/ device node is chmod/chown'd to give you access.
The communication goes over a UNIX domain socket which is mode 0600 created in the abstract namespace at $HOME/.qemud.d/sock.
okay, makes sense. Everything runs under the user privilege and there is no escalation.
- qemud:///system
This is a system-wide privileged hypervisor connection. There is only one of these on any given machine. The libvirt_qemud daemon would be started ahead of time (by an init script), possibly running as root, or maybe under a dedicated system user account (and the KQEMU/KVM devices chown'd to match).
Would that be hard to allow autostart ? That's what we do for the read-only xen hypervisor access. Avoiding starting up stuff in init.d when we have no garantee it will be used, and auto-shutdown when there is no client is IMHO generally nicer, but that feature can just be added later possibly, main drawback is that it requires an suid binary.
The admin would optionally also make it listen on IPv4/6 addrs to allow remote communication. (see next URI example)
The local communication goes over one of two possible UNIX domain sockets Both in the abstract namespace under the directory /var/run. The first socket called 'qemud' is mode 0600, so only privileged apps (ie root) can access it, and gives full control capabilities. The other called 'qemud-ro' is mode 0666 and any clients connecting to it will be restricted to only read-only libvirt operations by the server.
- qemud://hostname:port/
This lets you connect to a daemon over IPv4 or IPv6. If omitted the port is 8123 (will probably change it). This lets you connect to a system daemon on a remote host - assuming it was configured to listen on IPv4/6 interfaces.
Hum, for that the daemon requires to be started statically too.
Currently there is zero auth or encryption, but I'm planning to make it mandortory to use the TLS protocol - using the GNU TLS library. This will give encryption, and mutual authentication using either x509 certificates or PGP keys & trustdbs or perhaps both :-) Will probably start off by implementing PGP since I understand it better.
So if you wanted to remotely manage a server, you'd copy the server's certificate/public key to the client into a well known location. Similarly you'd generate a keypair for the client & copy its public key to the server. Perhaps I'll allow clients without a key to connect in read-only mode. Need to prototype it first and then write up some ideas.
okay, though there is multiple authentication and encryption libraries, and picking the Right One may not be possible, there is so many options, and people may have specific infrastructure in place. Anyway the current state is no-auth so anything will be better :-)
Server architecture -------------------
The server is a fairly simple beast. It is single-threaded using non-blocking I/O and poll() for all operations. It will listen on multiple sockets for incoming connections. The protocol used for client-server comms is a very simple binary message format close to the existing libvirt_proxy.
Good so we keep similar implementations. Any possibility of sharing part of that code, it's always very sensitive areas, both for security and edge case in the communication.
Client sends a message, server receives it, performs appropriate operation & sends a reply to the client. The client (ie libvirt driver) blocks after sending its message until it gets a reply. The server does non-blocking reads from the client buffering until it has a single complete message, then processes it and populates the buffer with a reply and does non-blocking writes to send it back to the client. It won't try to read a further message from the client until its sent the entire reply back. ie, it is a totally synchronous message flow - no batching/pipelining of messages.
Honnestly I think that's good enough, I don't see hundreds of QEmu instances having to be monitored remotely from a single Node. On an monitoring machine things may be accelerated by multithreading the gathering process to talk to multiple Nodes in parallel. At least on the server side I prefer to keep things as straight as possible.
During the time the server is processes a message it is not dealing with any other I/O, but thus far all the operations are very fast to implement, so this isn't a serious issue, and there ways to deal with it if there are operations which turn out to take a long time. I certainly want to avoid multi-threading in the server at all costs!
completely agree :-)
As well as monitoring the client & client sockets, the poll() event loop in the server also captures stdout & stderr from the QEMU processes. Currently we just dump this to stdout of the daemon, but I expect we can log it somewhere. When we start accessing the QEMU monitor there will be another fd in the event loop - ie the pseduo-TTY (or UNIX socket) on which we talk to the monitor.
At some point we will need to look at adding a Console dump API, that will be doable for Xen too, but it's not urgent since nobody requested it yet :-)
Inactive guests ---------------
Guests created using 'virsh create' (or equiv API) are treated as 'transient' domains - ie their config files are not saved to disk. This is consistent with the behaviour in the Xen backend. Guests created using 'virsh define', however, are saved out to disk in $HOME/.qemud.d for the per-user session daemon. The system-wide daemon should use /etc/qemud.d, but currently its still /root/.qemud.d
Maybe this should be asked on the qemu-devel list, Fabrice and Co. may have a preference on where to store config related stuff for QEmu even if it's not directly part of QEmu.
The config files are simply saved as the libvirt XML blob ensuring no data conversion issues. In any case, QEMU doesn't currently have any config file format we can leverage. The list of inactive guests is loaded at startup of the daemon. New config files are expected to be created via the API - files manually created in the directory after initial startup are not seen. Might like to change this later.
Hum, maybe we could use FAM/gamin if found at configure time, but well it's just an additional feature, let's just avoid any uneeeded timer.
XML Examples ------------
This is a guest using plain qemu, with x86_64 architecture and a ISA-only (ie no PCI) machine emulation. I was actually running this on a 32-bit host :-) VNC is configured to run on port 5906. QEMU can't automatically choose a VNC port, so if one isn't specified we assign one based on the domain ID. This should be fixed in QEMU....
<domain type='qemu'> <name>demo1</name> <uuid>4dea23b3-1d52-d8f3-2516-782e98a23fa0</uuid> <memory>131072</memory> <vcpu>1</vcpu> <os> <type arch='x86_64' machine='isapc'>hvm</type> </os> <devices> <disk type='file' device='disk'> <source file='/home/berrange/fedora/diskboot.img'/> <target dev='hda'/> </disk> <interface type='user'> <mac address='24:42:53:21:52:45'/> </interface> <graphics type='vnc' port='5906'/> </devices> </domain>
A second example, this time using KVM acceleration. Note how I specify a non-default path to QEMU to pick up the KVM build of QEMU. Normally KVM binary will default to /usr/bin/qemu-kvm - this may change depending on how distro packaging of KVM turns out - it may even be merged into regular QEMU binaries.
<domain type='kvm'> <name>demo2</name> <uuid>4dea24b3-1d52-d8f3-2516-782e98a23fa0</uuid> <memory>131072</memory> <vcpu>1</vcpu> <os> <type>hvm</type> </os> <devices> <emulator>/home/berrange/usr/kvm-devel/bin/qemu-system-x86_64</emulator> <disk type='file' device='disk'> <source file='/home/berrange/fedora/diskboot.img'/> <target dev='hda'/> </disk> <interface type='user'> <mac address='24:42:53:21:52:45'/> </interface> <graphics type='vnc' port='-1'/> </devices> </domain>
Okay, I'm nearing completion of a Relax-NG schemas allowing to validate XML instances, I will augment to allow the changes, but based on last week discussion it should not bee too hard and still retain good validation properties.
Outstanding work ----------------
- TLS support. Need to add TLS encryption & authentication to both the client and server side for IPv4/6 communications. This will obviously add a dependancy on libgnutls.so in libvirt & the daemon. I don't consider this a major problem since every non-trivial network app these days uses TLS. The other possible impl of OpenSSL has GPL-compatability issues, so is not considered.
- Change the wire format to use fixed size data types (ie, int8, int16, int32, etc) instead of the size-dependant int/long types. At same time define some rules for the byte ordering. Client must match server ordering ? Server must accept client's desired ordering ? Everyone must use BE regardless of server/client format ? I'm inclined to say client must match server, since it distributes the byte-swapping overhead to all clients and lets the common case of x86->x86 be a no-op.
Hum, on the other hand if you do the conversion as suggested by IETF rules it's easier to find the places where the conversion is missing, unless you forgot to ntoh and hton on both client and server code. Honnestly I would not take the performance hit in consideration at that level and not now, the RPC is gonna totally dominate it by order of magnitudes in my opinion.
- Add a protocol version message as first option to let use protocol at will later while maintaining compat with older libvirt client libraries.
Yeah, this also ensure you get a functionning server on that port !
- Improve support for describing the various QEMU network configurations
- Finish boot options - boot device order & explicit kernel
- Open & use connection to QEMU monitor which will let us implement pause/resume, suspend/restore drivers, and device hotplug / media changes.
- Return sensible data for virNodeInfo - will need to have operating system dependant code here - parsing /proc for Linux to determine available RAM & CPU speed. Who knows what for Solaris / BSD ?!? Anyone know of remotely standard ways for doing this. Accurate host memory reporting is the only really critical data item we need.
The GNOME guys tried that, maybe dig up the gst (gnome system tools) code base :-)
- There is a fair bit of duplicate in various helper functions between the daemon, and various libvirt driver backends. We should probably pull this stuff out into a separate lib/ directoy, build it into a static library and then link that into both libvirt, virsh & the qemud daemon as needed.
Yes definitely ! This all sounds excellent, thanks a lot !!! Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/
participants (3)
-
Daniel P. Berrange
-
Daniel Veillard
-
Karel Zak