libvirt/src/qemu_conf.c

4277 lines
141 KiB
C
Raw Normal View History

2007-02-14 01:40:09 +00:00
/*
* config.c: VM configuration management
*
* Copyright (C) 2006, 2007, 2008 Red Hat, Inc.
2007-02-14 01:40:09 +00:00
* 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 <config.h>
#ifdef WITH_QEMU
2007-02-14 01:40:09 +00:00
#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 <sys/wait.h>
#include <arpa/inet.h>
2007-04-16 13:14:28 +00:00
#include <sys/utsname.h>
2007-02-14 01:40:09 +00:00
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/uri.h>
#if HAVE_NUMACTL
#include <numa.h>
#endif
#include "libvirt/virterror.h"
2007-02-14 01:40:09 +00:00
#include "qemu_conf.h"
#include "uuid.h"
#include "buf.h"
2007-10-12 16:05:44 +00:00
#include "conf.h"
#include "util.h"
#include "memory.h"
#include "verify.h"
#include "c-ctype.h"
#include "xml.h"
#define qemudLog(level, msg...) fprintf(stderr, msg)
void qemudReportError(virConnectPtr conn,
virDomainPtr dom,
virNetworkPtr net,
int code, const char *fmt, ...) {
va_list args;
char errorMessage[QEMUD_MAX_ERROR_LEN];
const char *virerr;
if (fmt) {
va_start(args, fmt);
vsnprintf(errorMessage, QEMUD_MAX_ERROR_LEN-1, fmt, args);
va_end(args);
} else {
errorMessage[0] = '\0';
}
virerr = __virErrorMsg(code, (errorMessage[0] ? errorMessage : NULL));
__virRaiseError(conn, dom, net, VIR_FROM_QEMU, code, VIR_ERR_ERROR,
virerr, errorMessage, NULL, -1, -1, virerr, errorMessage);
}
2007-10-12 16:05:44 +00:00
int qemudLoadDriverConfig(struct qemud_driver *driver,
const char *filename) {
virConfPtr conf;
virConfValuePtr p;
/* Setup 2 critical defaults */
strcpy(driver->vncListen, "127.0.0.1");
if (!(driver->vncTLSx509certdir = strdup(SYSCONF_DIR "/pki/libvirt-vnc"))) {
qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s", _("failed to allocate vncTLSx509certdir"));
2007-10-12 16:05:44 +00:00
return -1;
}
/* Just check the file is readable before opening it, otherwise
* libvirt emits an error.
*/
if (access (filename, R_OK) == -1) return 0;
conf = virConfReadFile (filename);
if (!conf) return 0;
#define CHECK_TYPE(name,typ) if (p && p->type != (typ)) { \
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, \
"remoteReadConfigFile: %s: %s: expected type " #typ "\n", \
filename, (name)); \
virConfFree(conf); \
return -1; \
}
p = virConfGetValue (conf, "vnc_tls");
CHECK_TYPE ("vnc_tls", VIR_CONF_LONG);
if (p) driver->vncTLS = p->l;
p = virConfGetValue (conf, "vnc_tls_x509_verify");
CHECK_TYPE ("vnc_tls_x509_verify", VIR_CONF_LONG);
if (p) driver->vncTLSx509verify = p->l;
p = virConfGetValue (conf, "vnc_tls_x509_cert_dir");
CHECK_TYPE ("vnc_tls_x509_cert_dir", VIR_CONF_STRING);
if (p && p->str) {
free(driver->vncTLSx509certdir);
if (!(driver->vncTLSx509certdir = strdup(p->str))) {
qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s", _("failed to allocate vncTLSx509certdir"));
2007-10-12 16:05:44 +00:00
virConfFree(conf);
return -1;
}
}
p = virConfGetValue (conf, "vnc_listen");
CHECK_TYPE ("vnc_listen", VIR_CONF_STRING);
if (p && p->str) {
strncpy(driver->vncListen, p->str, sizeof(driver->vncListen));
driver->vncListen[sizeof(driver->vncListen)-1] = '\0';
}
virConfFree (conf);
return 0;
}
struct qemud_vm *qemudFindVMByID(const struct qemud_driver *driver, int id) {
struct qemud_vm *vm = driver->vms;
while (vm) {
if (qemudIsActiveVM(vm) && vm->id == id)
return vm;
vm = vm->next;
}
return NULL;
}
struct qemud_vm *qemudFindVMByUUID(const struct qemud_driver *driver,
const unsigned char *uuid) {
struct qemud_vm *vm = driver->vms;
while (vm) {
2007-08-09 20:19:12 +00:00
if (!memcmp(vm->def->uuid, uuid, VIR_UUID_BUFLEN))
return vm;
vm = vm->next;
}
return NULL;
}
struct qemud_vm *qemudFindVMByName(const struct qemud_driver *driver,
const char *name) {
struct qemud_vm *vm = driver->vms;
while (vm) {
if (STREQ(vm->def->name, name))
return vm;
vm = vm->next;
}
return NULL;
}
struct qemud_network *qemudFindNetworkByUUID(const struct qemud_driver *driver,
const unsigned char *uuid) {
struct qemud_network *network = driver->networks;
while (network) {
2007-08-09 20:19:12 +00:00
if (!memcmp(network->def->uuid, uuid, VIR_UUID_BUFLEN))
return network;
network = network->next;
}
return NULL;
}
struct qemud_network *qemudFindNetworkByName(const struct qemud_driver *driver,
const char *name) {
struct qemud_network *network = driver->networks;
while (network) {
if (STREQ(network->def->name, name))
return network;
network = network->next;
}
return NULL;
}
/* Free all memory associated with a struct qemud_vm object */
void qemudFreeVMDef(struct qemud_vm_def *def) {
struct qemud_vm_disk_def *disk = def->disks;
struct qemud_vm_net_def *net = def->nets;
2007-07-31 14:27:12 +00:00
struct qemud_vm_input_def *input = def->inputs;
struct qemud_vm_chr_def *serial = def->serials;
struct qemud_vm_chr_def *parallel = def->parallels;
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);
}
2007-07-31 14:27:12 +00:00
while (input) {
struct qemud_vm_input_def *prev = input;
input = input->next;
free(prev);
}
while (serial) {
struct qemud_vm_chr_def *prev = serial;
serial = serial->next;
free(prev);
}
while (parallel) {
struct qemud_vm_chr_def *prev = parallel;
parallel = parallel->next;
free(prev);
}
xmlFree(def->keymap);
free(def);
}
void qemudFreeVM(struct qemud_vm *vm) {
qemudFreeVMDef(vm->def);
if (vm->newDef)
qemudFreeVMDef(vm->newDef);
free(vm);
}
2007-02-14 01:40:09 +00:00
2007-02-14 01:40:09 +00:00
/* The list of possible machine types for various architectures,
as supported by QEMU - taken from 'qemu -M ?' for each arch */
static const char *const arch_info_hvm_x86_machines[] = {
"pc", "isapc"
2007-02-14 01:40:09 +00:00
};
static const char *const arch_info_hvm_mips_machines[] = {
"mips"
2007-02-14 01:40:09 +00:00
};
static const char *const arch_info_hvm_sparc_machines[] = {
"sun4m"
2007-02-14 01:40:09 +00:00
};
static const char *const arch_info_hvm_ppc_machines[] = {
"g3bw", "mac99", "prep"
};
static const char *const arch_info_xen_x86_machines[] = {
"xenner"
};
struct qemu_feature_flags {
const char *name;
const int default_on;
const int toggle;
};
struct qemu_arch_info {
const char *arch;
int wordsize;
const char *const *machines;
int nmachines;
const char *binary;
const struct qemu_feature_flags *flags;
int nflags;
2007-02-14 01:40:09 +00:00
};
/* Feature flags for the architecture info */
static const struct qemu_feature_flags const arch_info_i686_flags [] = {
{ "pae", 1, 0 },
{ "nonpae", 1, 0 },
{ "acpi", 1, 1 },
{ "apic", 1, 0 },
};
static const struct qemu_feature_flags const arch_info_x86_64_flags [] = {
{ "acpi", 1, 1 },
{ "apic", 1, 0 },
};
2007-02-14 01:40:09 +00:00
/* The archicture tables for supported QEMU archs */
static const struct qemu_arch_info const arch_info_hvm[] = {
{ "i686", 32, arch_info_hvm_x86_machines, 2,
"/usr/bin/qemu", arch_info_i686_flags, 4 },
{ "x86_64", 64, arch_info_hvm_x86_machines, 2,
"/usr/bin/qemu-system-x86_64", arch_info_x86_64_flags, 2 },
{ "mips", 32, arch_info_hvm_mips_machines, 1,
"/usr/bin/qemu-system-mips", NULL, 0 },
{ "mipsel", 32, arch_info_hvm_mips_machines, 1,
"/usr/bin/qemu-system-mipsel", NULL, 0 },
{ "sparc", 32, arch_info_hvm_sparc_machines, 1,
"/usr/bin/qemu-system-sparc", NULL, 0 },
{ "ppc", 32, arch_info_hvm_ppc_machines, 3,
"/usr/bin/qemu-system-ppc", NULL, 0 },
2007-02-14 01:40:09 +00:00
};
static const struct qemu_arch_info const arch_info_xen[] = {
{ "i686", 32, arch_info_xen_x86_machines, 1,
"/usr/bin/xenner", arch_info_i686_flags, 4 },
{ "x86_64", 64, arch_info_xen_x86_machines, 1,
"/usr/bin/xenner", arch_info_x86_64_flags, 2 },
};
2007-02-14 01:40:09 +00:00
static int
qemudCapsInitGuest(virCapsPtr caps,
const char *hostmachine,
const struct qemu_arch_info *info,
int hvm) {
virCapsGuestPtr guest;
2007-02-14 01:40:09 +00:00
int i;
if ((guest = virCapabilitiesAddGuest(caps,
hvm ? "hvm" : "xen",
info->arch,
info->wordsize,
info->binary,
NULL,
info->nmachines,
info->machines)) == NULL)
return -1;
2007-02-14 01:40:09 +00:00
if (hvm) {
/* Check for existance of base emulator */
if (access(info->binary, X_OK) == 0 &&
virCapabilitiesAddGuestDomain(guest,
"qemu",
NULL,
NULL,
0,
NULL) == NULL)
return -1;
2007-02-14 01:40:09 +00:00
/* If guest & host match, then we can accelerate */
if (STREQ(info->arch, hostmachine)) {
if (access("/dev/kqemu", F_OK) == 0 &&
virCapabilitiesAddGuestDomain(guest,
"kqemu",
NULL,
NULL,
0,
NULL) == NULL)
return -1;
if (access("/dev/kvm", F_OK) == 0 &&
virCapabilitiesAddGuestDomain(guest,
"kvm",
"/usr/bin/qemu-kvm",
NULL,
0,
NULL) == NULL)
return -1;
}
} else {
if (virCapabilitiesAddGuestDomain(guest,
"kvm",
NULL,
NULL,
0,
NULL) == NULL)
return -1;
}
2007-02-14 01:40:09 +00:00
if (info->nflags) {
for (i = 0 ; i < info->nflags ; i++) {
if (virCapabilitiesAddGuestFeature(guest,
info->flags[i].name,
info->flags[i].default_on,
info->flags[i].toggle) == NULL)
return -1;
2007-02-14 01:40:09 +00:00
}
}
return 0;
2007-02-14 01:40:09 +00:00
}
#if HAVE_NUMACTL
#define MAX_CPUS 4096
#define MAX_CPUS_MASK_SIZE (sizeof(unsigned long))
#define MAX_CPUS_MASK_LEN (MAX_CPUS / MAX_CPUS_MASK_SIZE)
#define MAX_CPUS_MASK_BYTES (MAX_CPUS / 8)
#define MASK_CPU_ISSET(mask, cpu) \
(((mask)[((cpu) / MAX_CPUS_MASK_SIZE)] >> ((cpu) % MAX_CPUS_MASK_SIZE)) & 1)
static int
qemudCapsInitNUMA(virCapsPtr caps)
{
int n, i;
unsigned long *mask = NULL;
int ncpus;
int *cpus = NULL;
int ret = -1;
if (numa_available() < 0)
return 0;
if (VIR_ALLOC_N(mask, MAX_CPUS_MASK_LEN) < 0)
goto cleanup;
for (n = 0 ; n <= numa_max_node() ; n++) {
if (numa_node_to_cpus(n, mask, MAX_CPUS_MASK_BYTES) < 0)
goto cleanup;
for (ncpus = 0, i = 0 ; i < MAX_CPUS ; i++)
if (MASK_CPU_ISSET(mask, i))
ncpus++;
if (VIR_ALLOC_N(cpus, ncpus) < 0)
goto cleanup;
for (ncpus = 0, i = 0 ; i < MAX_CPUS ; i++)
if (MASK_CPU_ISSET(mask, i))
cpus[ncpus++] = i;
if (virCapabilitiesAddHostNUMACell(caps,
n,
ncpus,
cpus) < 0)
goto cleanup;
VIR_FREE(cpus);
}
ret = 0;
cleanup:
VIR_FREE(cpus);
VIR_FREE(mask);
return ret;
}
#else
static int qemudCapsInitNUMA(virCapsPtr caps ATTRIBUTE_UNUSED) { return 0; }
#endif
virCapsPtr qemudCapsInit(void) {
struct utsname utsname;
virCapsPtr caps;
int i;
2007-02-14 01:40:09 +00:00
/* Really, this never fails - look at the man-page. */
uname (&utsname);
if ((caps = virCapabilitiesNew(utsname.machine,
0, 0)) == NULL)
goto no_memory;
if (qemudCapsInitNUMA(caps) < 0)
goto no_memory;
for (i = 0 ; i < (sizeof(arch_info_hvm)/sizeof(arch_info_hvm[0])) ; i++)
if (qemudCapsInitGuest(caps,
utsname.machine,
&arch_info_hvm[i], 1) < 0)
goto no_memory;
if (access("/usr/bin/xenner", X_OK) == 0 &&
access("/dev/kvm", F_OK) == 0) {
for (i = 0 ; i < (sizeof(arch_info_xen)/sizeof(arch_info_xen[0])) ; i++)
/* Allow Xen 32-on-32, 32-on-64 and 64-on-64 */
if (STREQ(arch_info_xen[i].arch, utsname.machine) ||
(STREQ(utsname.machine, "x86_64") &&
STREQ(arch_info_xen[i].arch, "i686"))) {
if (qemudCapsInitGuest(caps,
utsname.machine,
&arch_info_xen[i], 0) < 0)
goto no_memory;
}
2007-02-14 01:40:09 +00:00
}
return caps;
no_memory:
virCapabilitiesFree(caps);
return NULL;
2007-02-14 01:40:09 +00:00
}
static int qemudExtractVersionInfo(const char *qemu, int *version, int *flags) {
pid_t child;
int newstdout[2];
*flags = 0;
*version = 0;
if (pipe(newstdout) < 0) {
return -1;
}
if ((child = fork()) < 0) {
close(newstdout[0]);
close(newstdout[1]);
return -1;
}
if (child == 0) { /* Kid */
/* Just in case QEMU is translated someday we force to C locale.. */
const char *const qemuenv[] = { "LANG=C", NULL };
if (close(STDIN_FILENO) < 0)
goto cleanup1;
if (close(STDERR_FILENO) < 0)
goto cleanup1;
if (close(newstdout[0]) < 0)
goto cleanup1;
if (dup2(newstdout[1], STDOUT_FILENO) < 0)
goto cleanup1;
/* Passing -help, rather than relying on no-args which doesn't
always work */
execle(qemu, qemu, "-help", (char*)NULL, qemuenv);
cleanup1:
_exit(-1); /* Just in case */
} else { /* Parent */
2007-05-03 16:10:40 +00:00
char help[8192]; /* Ought to be enough to hold QEMU help screen */
int got = 0, ret = -1;
int major, minor, micro;
if (close(newstdout[1]) < 0)
goto cleanup2;
2007-05-03 16:10:40 +00:00
while (got < (sizeof(help)-1)) {
int len;
if ((len = read(newstdout[0], help+got, sizeof(help)-got-1)) <= 0) {
if (!len)
break;
if (errno == EINTR)
continue;
goto cleanup2;
}
got += len;
}
help[got] = '\0';
if (sscanf(help, "QEMU PC emulator version %d.%d.%d", &major,&minor, &micro) != 3) {
goto cleanup2;
}
*version = (major * 1000 * 1000) + (minor * 1000) + micro;
if (strstr(help, "-no-kqemu"))
*flags |= QEMUD_CMD_FLAG_KQEMU;
2007-05-03 16:10:40 +00:00
if (strstr(help, "-no-reboot"))
*flags |= QEMUD_CMD_FLAG_NO_REBOOT;
2008-05-15 16:15:17 +00:00
if (strstr(help, "-name"))
*flags |= QEMUD_CMD_FLAG_NAME;
if (strstr(help, "-drive"))
*flags |= QEMUD_CMD_FLAG_DRIVE;
if (strstr(help, "boot=on"))
2008-05-15 16:15:17 +00:00
*flags |= QEMUD_CMD_FLAG_DRIVE_BOOT;
if (*version >= 9000)
*flags |= QEMUD_CMD_FLAG_VNC_COLON;
ret = 0;
qemudDebug("Version %d %d %d Cooked version: %d, with flags ? %d",
major, minor, micro, *version, *flags);
cleanup2:
if (close(newstdout[0]) < 0)
ret = -1;
rewait:
if (waitpid(child, &got, 0) != child) {
if (errno == EINTR) {
goto rewait;
}
Mark all qemudLog diagnostics for translation. * po/POTFILES.in: Add names of many new files. * Makefile.maint (err_func_re): Add qemudLog. Mark diagnostics with _(...). Split some long lines. * qemud/qemud.c (remoteCheckCertFile, remoteInitializeGnuTLS): (qemudDispatchSignalEvent, qemudSetCloseExec, qemudSetNonBlock): (qemudWritePidFile, qemudListenUnix, remoteMakeSockets): (remoteListenTCP, qemudInitPaths, qemudInitialize): (qemudNetworkInit, remoteInitializeTLSSession, remoteCheckDN): (remoteCheckCertificate, remoteCheckAccess, qemudDispatchServer): (qemudClientReadBuf, qemudDispatchClientRead): (qemudClientWriteBuf, qemudDispatchClientWrite, qemudOneLoop): (remoteConfigGetStringList, checkType, GET_CONF_STR): (remoteConfigGetAuth, remoteReadConfigFile, main): * qemud/remote.c (remoteDispatchAuthSaslInit, remoteSASLCheckSSF): (remoteSASLCheckAccess, remoteDispatchAuthSaslStart): (remoteDispatchAuthSaslStep, remoteDispatchAuthSaslInit): (remoteDispatchAuthSaslStart, remoteDispatchAuthSaslStep): (qemudGetSocketIdentity, remoteDispatchAuthPolkit): * src/iptables.c (notifyRulesUpdated, MAX_FILE_LEN, iptRulesSave): (iptRulesReload): * src/qemu_conf.c (qemudExtractVersionInfo, qemudLoadConfig): (qemudLoadNetworkConfig, qemudScanConfigDir): * src/qemu_driver.c (qemudSetCloseExec, qemudSetNonBlock): (qemudAutostartConfigs, qemudStartup, qemudReload): (qemudWaitForMonitor, qemudStartVMDaemon, qemudVMData): (qemudShutdownVMDaemon, qemudStartNetworkDaemon): (qemudShutdownNetworkDaemon, qemudMonitorCommand): (qemudDomainUndefine, qemudNetworkUndefine): * src/uuid.c (virUUIDGenerate): * src/xm_internal.c (xenXMAttachInterface):
2008-02-07 16:50:17 +00:00
qemudLog(QEMUD_ERR,
_("Unexpected exit status from qemu %d pid %lu"),
got, (unsigned long)child);
ret = -1;
}
/* Check & log unexpected exit status, but don't fail,
* as there's really no need to throw an error if we did
* actually read a valid version number above */
2008-05-21 21:14:36 +00:00
if (WEXITSTATUS(got) != 0) {
Mark all qemudLog diagnostics for translation. * po/POTFILES.in: Add names of many new files. * Makefile.maint (err_func_re): Add qemudLog. Mark diagnostics with _(...). Split some long lines. * qemud/qemud.c (remoteCheckCertFile, remoteInitializeGnuTLS): (qemudDispatchSignalEvent, qemudSetCloseExec, qemudSetNonBlock): (qemudWritePidFile, qemudListenUnix, remoteMakeSockets): (remoteListenTCP, qemudInitPaths, qemudInitialize): (qemudNetworkInit, remoteInitializeTLSSession, remoteCheckDN): (remoteCheckCertificate, remoteCheckAccess, qemudDispatchServer): (qemudClientReadBuf, qemudDispatchClientRead): (qemudClientWriteBuf, qemudDispatchClientWrite, qemudOneLoop): (remoteConfigGetStringList, checkType, GET_CONF_STR): (remoteConfigGetAuth, remoteReadConfigFile, main): * qemud/remote.c (remoteDispatchAuthSaslInit, remoteSASLCheckSSF): (remoteSASLCheckAccess, remoteDispatchAuthSaslStart): (remoteDispatchAuthSaslStep, remoteDispatchAuthSaslInit): (remoteDispatchAuthSaslStart, remoteDispatchAuthSaslStep): (qemudGetSocketIdentity, remoteDispatchAuthPolkit): * src/iptables.c (notifyRulesUpdated, MAX_FILE_LEN, iptRulesSave): (iptRulesReload): * src/qemu_conf.c (qemudExtractVersionInfo, qemudLoadConfig): (qemudLoadNetworkConfig, qemudScanConfigDir): * src/qemu_driver.c (qemudSetCloseExec, qemudSetNonBlock): (qemudAutostartConfigs, qemudStartup, qemudReload): (qemudWaitForMonitor, qemudStartVMDaemon, qemudVMData): (qemudShutdownVMDaemon, qemudStartNetworkDaemon): (qemudShutdownNetworkDaemon, qemudMonitorCommand): (qemudDomainUndefine, qemudNetworkUndefine): * src/uuid.c (virUUIDGenerate): * src/xm_internal.c (xenXMAttachInterface):
2008-02-07 16:50:17 +00:00
qemudLog(QEMUD_WARN,
_("Unexpected exit status '%d', qemu probably failed"),
got);
}
return ret;
}
}
int qemudExtractVersion(virConnectPtr conn,
struct qemud_driver *driver) {
const char *binary;
2007-04-16 13:14:28 +00:00
struct stat sb;
int ignored;
if (driver->qemuVersion > 0)
return 0;
if ((binary = virCapabilitiesDefaultGuestEmulator(driver->caps,
"hvm",
"i686",
"qemu")) == NULL)
return -1;
2007-04-16 13:14:28 +00:00
if (stat(binary, &sb) < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("Cannot find QEMU binary %s: %s"), binary,
2007-04-16 13:14:28 +00:00
strerror(errno));
return -1;
}
if (qemudExtractVersionInfo(binary, &driver->qemuVersion, &ignored) < 0) {
return -1;
}
return 0;
}
/* Converts def->virtType to applicable string type
* @param type integer virt type
* @return string type on success, NULL on fail
*/
const char * qemudVirtTypeToString(int type) {
switch (type) {
case QEMUD_VIRT_QEMU:
return "qemu";
case QEMUD_VIRT_KQEMU:
return "kqemu";
case QEMUD_VIRT_KVM:
return "kvm";
}
return NULL;
}
2007-10-27 01:18:38 +00:00
/* Parse the XML definition for a disk
* @param disk pre-allocated & zero'd disk record
* @param node XML nodeset to parse for disk definition
* @return 0 on success, -1 on failure
*/
static int qemudParseDiskXML(virConnectPtr conn,
struct qemud_vm_disk_def *disk,
xmlNodePtr node) {
2007-02-14 01:40:09 +00:00
xmlNodePtr cur;
xmlChar *device = NULL;
xmlChar *source = NULL;
xmlChar *target = NULL;
xmlChar *type = NULL;
xmlChar *bus = NULL;
2007-02-14 01:40:09 +00:00
int typ = 0;
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");
2007-02-14 01:40:09 +00:00
cur = node->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE) {
if ((source == NULL) &&
(xmlStrEqual(cur->name, BAD_CAST "source"))) {
2007-02-14 01:40:09 +00:00
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");
bus = xmlGetProp(cur, BAD_CAST "bus");
2007-02-14 01:40:09 +00:00
} else if (xmlStrEqual(cur->name, BAD_CAST "readonly")) {
disk->readonly = 1;
}
}
cur = cur->next;
}
if (source == NULL) {
/* There is a case without the source
* to the CD-ROM device
*/
if (!device || STRNEQ((const char *) device, "cdrom")) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_SOURCE,
target ? "%s" : NULL, target);
goto error;
}
2007-02-14 01:40:09 +00:00
}
2007-02-14 01:40:09 +00:00
if (target == NULL) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_TARGET, source ? "%s" : NULL, source);
2007-02-14 01:40:09 +00:00
goto error;
}
if (device &&
STREQ((const char *)device, "floppy") &&
STRNEQ((const char *)target, "fda") &&
STRNEQ((const char *)target, "fdb")) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("Invalid floppy device name: %s"), target);
2007-02-14 01:40:09 +00:00
goto error;
}
2007-02-14 01:40:09 +00:00
if (device &&
STREQ((const char *)device, "cdrom") &&
STRNEQ((const char *)target, "hdc")) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("Invalid cdrom device name: %s"), target);
2007-02-14 01:40:09 +00:00
goto error;
}
if (device &&
STREQ((const char *)device, "cdrom"))
2007-02-14 01:40:09 +00:00
disk->readonly = 1;
if ((!device || STREQ((const char *)device, "disk")) &&
!STRPREFIX((const char *)target, "hd") &&
!STRPREFIX((const char *)target, "sd") &&
!STRPREFIX((const char *)target, "vd") &&
!STRPREFIX((const char *)target, "xvd")) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("Invalid harddisk device name: %s"), target);
2007-02-14 01:40:09 +00:00
goto error;
}
strncpy(disk->src, (source ? (const char *) source : "\0"), NAME_MAX-1);
2007-02-14 01:40:09 +00:00
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 (STREQ((const char *)device, "disk"))
2007-02-14 01:40:09 +00:00
disk->device = QEMUD_DISK_DISK;
else if (STREQ((const char *)device, "cdrom"))
2007-02-14 01:40:09 +00:00
disk->device = QEMUD_DISK_CDROM;
else if (STREQ((const char *)device, "floppy"))
2007-02-14 01:40:09 +00:00
disk->device = QEMUD_DISK_FLOPPY;
else {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("Invalid device type: %s"), device);
2007-02-14 01:40:09 +00:00
goto error;
}
if (!bus) {
2008-05-15 16:24:26 +00:00
if (disk->device == QEMUD_DISK_FLOPPY) {
disk->bus = QEMUD_DISK_BUS_FDC;
2008-05-15 16:24:26 +00:00
} else {
if (STRPREFIX((const char *)target, "hd"))
disk->bus = QEMUD_DISK_BUS_IDE;
else if (STRPREFIX((const char *)target, "sd"))
disk->bus = QEMUD_DISK_BUS_SCSI;
else if (STRPREFIX((const char *)target, "vd"))
disk->bus = QEMUD_DISK_BUS_VIRTIO;
else if (STRPREFIX((const char *)target, "xvd"))
disk->bus = QEMUD_DISK_BUS_XEN;
else
disk->bus = QEMUD_DISK_BUS_IDE;
}
} else if (STREQ((const char *)bus, "ide"))
disk->bus = QEMUD_DISK_BUS_IDE;
else if (STREQ((const char *)bus, "fdc"))
disk->bus = QEMUD_DISK_BUS_FDC;
else if (STREQ((const char *)bus, "scsi"))
disk->bus = QEMUD_DISK_BUS_SCSI;
else if (STREQ((const char *)bus, "virtio"))
disk->bus = QEMUD_DISK_BUS_VIRTIO;
else if (STREQ((const char *)bus, "xen"))
disk->bus = QEMUD_DISK_BUS_XEN;
else {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("Invalid bus type: %s"), bus);
goto error;
}
if (disk->device == QEMUD_DISK_FLOPPY &&
disk->bus != QEMUD_DISK_BUS_FDC) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("Invalid bus type '%s' for floppy disk"), bus);
goto error;
}
2007-02-14 01:40:09 +00:00
xmlFree(device);
xmlFree(target);
xmlFree(source);
xmlFree(bus);
2007-02-14 01:40:09 +00:00
2007-10-27 01:18:38 +00:00
return 0;
2007-02-14 01:40:09 +00:00
error:
xmlFree(bus);
xmlFree(type);
xmlFree(target);
xmlFree(source);
xmlFree(device);
2007-10-27 01:18:38 +00:00
return -1;
2007-02-14 01:40:09 +00:00
}
2007-03-13 22:43:22 +00:00
static void qemudRandomMAC(struct qemud_vm_net_def *net) {
net->mac[0] = 0x52;
net->mac[1] = 0x54;
net->mac[2] = 0x00;
net->mac[3] = 1 + (int)(256*(rand()/(RAND_MAX+1.0)));
net->mac[4] = 1 + (int)(256*(rand()/(RAND_MAX+1.0)));
net->mac[5] = 1 + (int)(256*(rand()/(RAND_MAX+1.0)));
}
2007-02-14 01:40:09 +00:00
2007-10-27 01:18:38 +00:00
/* Parse the XML definition for a network interface
* @param net pre-allocated & zero'd net record
* @param node XML nodeset to parse for net definition
* @return 0 on success, -1 on failure
*/
static int qemudParseInterfaceXML(virConnectPtr conn,
struct qemud_vm_net_def *net,
xmlNodePtr node) {
2007-02-14 01:40:09 +00:00
xmlNodePtr cur;
xmlChar *macaddr = NULL;
xmlChar *type = NULL;
xmlChar *network = NULL;
2007-03-13 22:43:22 +00:00
xmlChar *bridge = NULL;
xmlChar *ifname = NULL;
xmlChar *script = NULL;
xmlChar *address = NULL;
xmlChar *port = NULL;
xmlChar *model = NULL;
2007-02-14 01:40:09 +00:00
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;
2007-03-13 22:43:22 +00:00
else if (xmlStrEqual(type, BAD_CAST "ethernet"))
net->type = QEMUD_NET_ETHERNET;
2007-02-14 01:40:09 +00:00
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 "network"))
net->type = QEMUD_NET_NETWORK;
2007-03-13 22:43:22 +00:00
else if (xmlStrEqual(type, BAD_CAST "bridge"))
net->type = QEMUD_NET_BRIDGE;
2007-02-14 01:40:09 +00:00
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");
} else if ((network == NULL) &&
(net->type == QEMUD_NET_NETWORK) &&
(xmlStrEqual(cur->name, BAD_CAST "source"))) {
network = xmlGetProp(cur, BAD_CAST "network");
2007-03-13 22:43:22 +00:00
} else if ((network == NULL) &&
(net->type == QEMUD_NET_BRIDGE) &&
(xmlStrEqual(cur->name, BAD_CAST "source"))) {
2007-03-27 14:46:45 +00:00
bridge = xmlGetProp(cur, BAD_CAST "bridge");
2007-03-13 22:43:22 +00:00
} else if ((network == NULL) &&
((net->type == QEMUD_NET_SERVER) ||
(net->type == QEMUD_NET_CLIENT) ||
(net->type == QEMUD_NET_MCAST)) &&
(xmlStrEqual(cur->name, BAD_CAST "source"))) {
address = xmlGetProp(cur, BAD_CAST "address");
port = xmlGetProp(cur, BAD_CAST "port");
} else if ((ifname == NULL) &&
((net->type == QEMUD_NET_NETWORK) ||
(net->type == QEMUD_NET_ETHERNET) ||
(net->type == QEMUD_NET_BRIDGE)) &&
xmlStrEqual(cur->name, BAD_CAST "target")) {
ifname = xmlGetProp(cur, BAD_CAST "dev");
if (STRPREFIX((const char*)ifname, "vnet")) {
2007-12-05 15:08:23 +00:00
/* An auto-generated target name, blank it out */
xmlFree(ifname);
ifname = NULL;
}
2007-03-13 22:43:22 +00:00
} else if ((script == NULL) &&
(net->type == QEMUD_NET_ETHERNET) &&
xmlStrEqual(cur->name, BAD_CAST "script")) {
script = xmlGetProp(cur, BAD_CAST "path");
} else if (xmlStrEqual (cur->name, BAD_CAST "model")) {
model = xmlGetProp (cur, BAD_CAST "type");
2007-02-14 01:40:09 +00:00
}
}
cur = cur->next;
}
if (macaddr) {
unsigned int mac[6];
2007-02-14 01:40:09 +00:00
sscanf((const char *)macaddr, "%02x:%02x:%02x:%02x:%02x:%02x",
(unsigned int*)&mac[0],
(unsigned int*)&mac[1],
(unsigned int*)&mac[2],
(unsigned int*)&mac[3],
(unsigned int*)&mac[4],
(unsigned int*)&mac[5]);
net->mac[0] = mac[0];
net->mac[1] = mac[1];
net->mac[2] = mac[2];
net->mac[3] = mac[3];
net->mac[4] = mac[4];
net->mac[5] = mac[5];
2007-02-14 01:40:09 +00:00
xmlFree(macaddr);
2007-03-13 22:43:22 +00:00
macaddr = NULL;
} else {
qemudRandomMAC(net);
2007-02-14 01:40:09 +00:00
}
if (net->type == QEMUD_NET_NETWORK) {
int len;
if (network == NULL) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("No <source> 'network' attribute specified with <interface type='network'/>"));
goto error;
2007-03-13 22:43:22 +00:00
} else if ((len = xmlStrlen(network)) >= (QEMUD_MAX_NAME_LEN-1)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("Network name '%s' too long"), network);
goto error;
} else {
strncpy(net->dst.network.name, (char *)network, len);
net->dst.network.name[len] = '\0';
}
2007-03-13 22:43:22 +00:00
if (network) {
xmlFree(network);
2007-03-13 22:43:22 +00:00
network = NULL;
}
if (ifname != NULL) {
if ((len = xmlStrlen(ifname)) >= (BR_IFNAME_MAXLEN-1)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("TAP interface name '%s' is too long"),
ifname);
2007-03-13 22:43:22 +00:00
goto error;
} else {
strncpy(net->dst.network.ifname, (char *)ifname, len);
net->dst.network.ifname[len] = '\0';
}
xmlFree(ifname);
ifname = NULL;
}
} else if (net->type == QEMUD_NET_ETHERNET) {
int len;
2007-03-13 22:43:22 +00:00
if (script != NULL) {
if ((len = xmlStrlen(script)) >= (PATH_MAX-1)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("TAP script path '%s' is too long"), script);
2007-03-13 22:43:22 +00:00
goto error;
} else {
strncpy(net->dst.ethernet.script, (char *)script, len);
net->dst.ethernet.script[len] = '\0';
}
xmlFree(script);
script = NULL;
}
if (ifname != NULL) {
if ((len = xmlStrlen(ifname)) >= (BR_IFNAME_MAXLEN-1)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("TAP interface name '%s' is too long"),
ifname);
goto error;
} else {
2007-03-13 22:43:22 +00:00
strncpy(net->dst.ethernet.ifname, (char *)ifname, len);
net->dst.ethernet.ifname[len] = '\0';
}
2007-03-13 22:43:22 +00:00
xmlFree(ifname);
}
} else if (net->type == QEMUD_NET_BRIDGE) {
int len;
if (bridge == NULL) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("No <source> 'dev' attribute specified with <interface type='bridge'/>"));
2007-03-13 22:43:22 +00:00
goto error;
} else if ((len = xmlStrlen(bridge)) >= (BR_IFNAME_MAXLEN-1)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("TAP bridge path '%s' is too long"), bridge);
2007-03-13 22:43:22 +00:00
goto error;
} else {
strncpy(net->dst.bridge.brname, (char *)bridge, len);
net->dst.bridge.brname[len] = '\0';
}
2007-03-13 22:43:22 +00:00
xmlFree(bridge);
bridge = NULL;
if (ifname != NULL) {
if ((len = xmlStrlen(ifname)) >= (BR_IFNAME_MAXLEN-1)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("TAP interface name '%s' is too long"), ifname);
2007-03-13 22:43:22 +00:00
goto error;
} else {
strncpy(net->dst.bridge.ifname, (char *)ifname, len);
net->dst.bridge.ifname[len] = '\0';
}
xmlFree(ifname);
}
} else if (net->type == QEMUD_NET_CLIENT ||
net->type == QEMUD_NET_SERVER ||
net->type == QEMUD_NET_MCAST) {
int len = 0;
2007-03-13 22:43:22 +00:00
char *ret;
if (port == NULL) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("No <source> 'port' attribute specified with socket interface"));
2007-03-13 22:43:22 +00:00
goto error;
}
if (!(net->dst.socket.port = strtol((char*)port, &ret, 10)) &&
ret == (char*)port) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("Cannot parse <source> 'port' attribute with socket interface"));
2007-03-13 22:43:22 +00:00
goto error;
}
xmlFree(port);
port = NULL;
if (address == NULL) {
if (net->type == QEMUD_NET_CLIENT ||
net->type == QEMUD_NET_MCAST) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("No <source> 'address' attribute specified with socket interface"));
2007-03-13 22:43:22 +00:00
goto error;
}
} else if ((len = xmlStrlen(address)) >= (BR_INET_ADDR_MAXLEN)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("IP address '%s' is too long"), address);
2007-03-13 22:43:22 +00:00
goto error;
}
if (address == NULL) {
net->dst.socket.address[0] = '\0';
} else {
strncpy(net->dst.socket.address, (char*)address,len);
net->dst.socket.address[len] = '\0';
}
xmlFree(address);
}
/* NIC model (see -net nic,model=?). We only check that it looks
* reasonable, not that it is a supported NIC type. FWIW kvm
* supports these types as of April 2008:
* i82551 i82557b i82559er ne2k_pci pcnet rtl8139 e1000 virtio
*/
if (model != NULL) {
int i, len;
len = xmlStrlen (model);
if (len >= QEMUD_MODEL_MAX_LEN) {
qemudReportError (conn, NULL, NULL, VIR_ERR_INVALID_ARG,
_("Model name '%s' is too long"), model);
goto error;
}
for (i = 0; i < len; ++i) {
int char_ok = c_isalnum(model[i]) || model[i] == '_';
if (!char_ok) {
qemudReportError (conn, NULL, NULL, VIR_ERR_INVALID_ARG, "%s",
_("Model name contains invalid characters"));
goto error;
}
}
strncpy (net->model, (const char*) model, len);
net->model[len] = '\0';
xmlFree (model);
model = NULL;
} else
net->model[0] = '\0';
2007-10-27 01:18:38 +00:00
return 0;
error:
xmlFree(network);
xmlFree(address);
xmlFree(port);
xmlFree(ifname);
xmlFree(script);
xmlFree(bridge);
xmlFree(model);
2007-10-27 01:18:38 +00:00
return -1;
2007-02-14 01:40:09 +00:00
}
/* Parse the XML definition for a character device
* @param net pre-allocated & zero'd net record
* @param node XML nodeset to parse for net definition
* @return 0 on success, -1 on failure
*
* The XML we're dealing with looks like
*
* <serial type="pty">
* <source path="/dev/pts/3"/>
* <target port="1"/>
* </serial>
*
* <serial type="dev">
* <source path="/dev/ttyS0"/>
* <target port="1"/>
* </serial>
*
* <serial type="tcp">
* <source mode="connect" host="0.0.0.0" service="2445"/>
* <target port="1"/>
* </serial>
*
* <serial type="tcp">
* <source mode="bind" host="0.0.0.0" service="2445"/>
* <target port="1"/>
* </serial>
*
* <serial type="udp">
* <source mode="bind" host="0.0.0.0" service="2445"/>
* <source mode="connect" host="0.0.0.0" service="2445"/>
* <target port="1"/>
* </serial>
*
* <serial type="unix">
* <source mode="bind" path="/tmp/foo"/>
* <target port="1"/>
* </serial>
*
*/
static int qemudParseCharXML(virConnectPtr conn,
struct qemud_vm_chr_def *chr,
int portNum,
xmlNodePtr node) {
xmlNodePtr cur;
xmlChar *type = NULL;
xmlChar *bindHost = NULL;
xmlChar *bindService = NULL;
xmlChar *connectHost = NULL;
xmlChar *connectService = NULL;
xmlChar *path = NULL;
xmlChar *mode = NULL;
xmlChar *protocol = NULL;
int ret = -1;
chr->srcType = QEMUD_CHR_SRC_TYPE_PTY;
type = xmlGetProp(node, BAD_CAST "type");
if (type != NULL) {
if (xmlStrEqual(type, BAD_CAST "null"))
chr->srcType = QEMUD_CHR_SRC_TYPE_NULL;
else if (xmlStrEqual(type, BAD_CAST "vc"))
chr->srcType = QEMUD_CHR_SRC_TYPE_VC;
else if (xmlStrEqual(type, BAD_CAST "pty"))
chr->srcType = QEMUD_CHR_SRC_TYPE_PTY;
else if (xmlStrEqual(type, BAD_CAST "dev"))
chr->srcType = QEMUD_CHR_SRC_TYPE_DEV;
else if (xmlStrEqual(type, BAD_CAST "file"))
chr->srcType = QEMUD_CHR_SRC_TYPE_FILE;
else if (xmlStrEqual(type, BAD_CAST "pipe"))
chr->srcType = QEMUD_CHR_SRC_TYPE_PIPE;
else if (xmlStrEqual(type, BAD_CAST "stdio"))
chr->srcType = QEMUD_CHR_SRC_TYPE_STDIO;
else if (xmlStrEqual(type, BAD_CAST "udp"))
chr->srcType = QEMUD_CHR_SRC_TYPE_UDP;
else if (xmlStrEqual(type, BAD_CAST "tcp"))
chr->srcType = QEMUD_CHR_SRC_TYPE_TCP;
else if (xmlStrEqual(type, BAD_CAST "unix"))
chr->srcType = QEMUD_CHR_SRC_TYPE_UNIX;
else
chr->srcType = QEMUD_CHR_SRC_TYPE_NULL;
}
cur = node->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE) {
if (xmlStrEqual(cur->name, BAD_CAST "source")) {
if (mode == NULL)
mode = xmlGetProp(cur, BAD_CAST "mode");
switch (chr->srcType) {
case QEMUD_CHR_SRC_TYPE_PTY:
case QEMUD_CHR_SRC_TYPE_DEV:
case QEMUD_CHR_SRC_TYPE_FILE:
case QEMUD_CHR_SRC_TYPE_PIPE:
case QEMUD_CHR_SRC_TYPE_UNIX:
if (path == NULL)
path = xmlGetProp(cur, BAD_CAST "path");
break;
case QEMUD_CHR_SRC_TYPE_UDP:
case QEMUD_CHR_SRC_TYPE_TCP:
if (mode == NULL ||
STREQ((const char *)mode, "connect")) {
if (connectHost == NULL)
connectHost = xmlGetProp(cur, BAD_CAST "host");
if (connectService == NULL)
connectService = xmlGetProp(cur, BAD_CAST "service");
} else {
if (bindHost == NULL)
bindHost = xmlGetProp(cur, BAD_CAST "host");
if (bindService == NULL)
bindService = xmlGetProp(cur, BAD_CAST "service");
}
if (chr->srcType == QEMUD_CHR_SRC_TYPE_UDP) {
xmlFree(mode);
mode = NULL;
}
}
} else if (xmlStrEqual(cur->name, BAD_CAST "protocol")) {
if (protocol == NULL)
protocol = xmlGetProp(cur, BAD_CAST "type");
}
}
cur = cur->next;
}
chr->dstPort = portNum;
switch (chr->srcType) {
case QEMUD_CHR_SRC_TYPE_NULL:
/* Nada */
break;
case QEMUD_CHR_SRC_TYPE_VC:
break;
case QEMUD_CHR_SRC_TYPE_PTY:
/* @path attribute is an output only property - pty is auto-allocted */
break;
case QEMUD_CHR_SRC_TYPE_DEV:
case QEMUD_CHR_SRC_TYPE_FILE:
case QEMUD_CHR_SRC_TYPE_PIPE:
if (path == NULL) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("Missing source path attribute for char device"));
goto cleanup;
}
strncpy(chr->srcData.file.path, (const char *)path,
sizeof(chr->srcData.file.path));
NUL_TERMINATE(chr->srcData.file.path);
break;
case QEMUD_CHR_SRC_TYPE_STDIO:
/* Nada */
break;
case QEMUD_CHR_SRC_TYPE_TCP:
if (mode == NULL ||
STREQ((const char *)mode, "connect")) {
if (connectHost == NULL) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("Missing source host attribute for char device"));
goto cleanup;
}
if (connectService == NULL) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("Missing source service attribute for char device"));
goto cleanup;
}
strncpy(chr->srcData.tcp.host, (const char *)connectHost,
sizeof(chr->srcData.tcp.host));
NUL_TERMINATE(chr->srcData.tcp.host);
strncpy(chr->srcData.tcp.service, (const char *)connectService,
sizeof(chr->srcData.tcp.service));
NUL_TERMINATE(chr->srcData.tcp.service);
chr->srcData.tcp.listen = 0;
} else {
if (bindHost == NULL) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("Missing source host attribute for char device"));
goto cleanup;
}
if (bindService == NULL) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("Missing source service attribute for char device"));
goto cleanup;
}
strncpy(chr->srcData.tcp.host, (const char *)bindHost,
sizeof(chr->srcData.tcp.host));
NUL_TERMINATE(chr->srcData.tcp.host);
strncpy(chr->srcData.tcp.service, (const char *)bindService,
sizeof(chr->srcData.tcp.service));
NUL_TERMINATE(chr->srcData.tcp.service);
chr->srcData.tcp.listen = 1;
}
if (protocol != NULL &&
STREQ((const char *)protocol, "telnet"))
chr->srcData.tcp.protocol = QEMUD_CHR_SRC_TCP_PROTOCOL_TELNET;
else
chr->srcData.tcp.protocol = QEMUD_CHR_SRC_TCP_PROTOCOL_RAW;
break;
case QEMUD_CHR_SRC_TYPE_UDP:
if (connectService == NULL) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("Missing source service attribute for char device"));
goto cleanup;
}
if (connectHost != NULL) {
strncpy(chr->srcData.udp.connectHost, (const char *)connectHost,
sizeof(chr->srcData.udp.connectHost));
NUL_TERMINATE(chr->srcData.udp.connectHost);
}
strncpy(chr->srcData.udp.connectService, (const char *)connectService,
sizeof(chr->srcData.udp.connectService));
NUL_TERMINATE(chr->srcData.udp.connectService);
if (bindHost != NULL) {
strncpy(chr->srcData.udp.bindHost, (const char *)bindHost,
sizeof(chr->srcData.udp.bindHost));
NUL_TERMINATE(chr->srcData.udp.bindHost);
}
if (bindService != NULL) {
strncpy(chr->srcData.udp.bindService, (const char *)bindService,
sizeof(chr->srcData.udp.bindService));
NUL_TERMINATE(chr->srcData.udp.bindService);
}
break;
case QEMUD_CHR_SRC_TYPE_UNIX:
if (path == NULL) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("Missing source path attribute for char device"));
goto cleanup;
}
if (mode != NULL &&
STRNEQ((const char *)mode, "connect"))
chr->srcData.nix.listen = 1;
else
chr->srcData.nix.listen = 0;
strncpy(chr->srcData.nix.path, (const char *)path,
sizeof(chr->srcData.nix.path));
NUL_TERMINATE(chr->srcData.nix.path);
break;
}
ret = 0;
cleanup:
xmlFree(mode);
xmlFree(protocol);
xmlFree(type);
xmlFree(bindHost);
xmlFree(bindService);
xmlFree(connectHost);
xmlFree(connectService);
xmlFree(path);
return ret;
}
static int qemudParseCharXMLDevices(virConnectPtr conn,
xmlXPathContextPtr ctxt,
const char *xpath,
unsigned int *ndevs,
struct qemud_vm_chr_def **devs)
{
xmlXPathObjectPtr obj;
int i, ret = -1;
obj = xmlXPathEval(BAD_CAST xpath, ctxt);
if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
(obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) {
struct qemud_vm_chr_def *prev = *devs;
if (ndevs == NULL &&
obj->nodesetval->nodeNr > 1) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("too many character devices"));
goto cleanup;
}
for (i = 0; i < obj->nodesetval->nodeNr; i++) {
struct qemud_vm_chr_def *chr = calloc(1, sizeof(*chr));
if (!chr) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s",
_("failed to allocate space for char device"));
goto cleanup;
}
if (qemudParseCharXML(conn, chr, i, obj->nodesetval->nodeTab[i]) < 0) {
free(chr);
goto cleanup;
}
if (ndevs)
(*ndevs)++;
chr->next = NULL;
if (i == 0) {
*devs = chr;
} else {
prev->next = chr;
}
prev = chr;
}
}
ret = 0;
cleanup:
xmlXPathFreeObject(obj);
return ret;
}
2007-07-18 21:08:22 +00:00
/* Parse the XML definition for a network interface */
2007-10-27 01:18:38 +00:00
static int qemudParseInputXML(virConnectPtr conn,
const struct qemud_vm_def *vm,
2007-10-27 01:18:38 +00:00
struct qemud_vm_input_def *input,
xmlNodePtr node) {
2007-07-18 21:08:22 +00:00
xmlChar *type = NULL;
xmlChar *bus = NULL;
type = xmlGetProp(node, BAD_CAST "type");
bus = xmlGetProp(node, BAD_CAST "bus");
if (!type) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("missing input device type"));
2007-07-18 21:08:22 +00:00
goto error;
}
if (STREQ((const char *)type, "mouse")) {
2007-07-18 21:08:22 +00:00
input->type = QEMU_INPUT_TYPE_MOUSE;
} else if (STREQ((const char *)type, "tablet")) {
2007-07-18 21:08:22 +00:00
input->type = QEMU_INPUT_TYPE_TABLET;
} else {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("unsupported input device type %s"),
(const char*)type);
2007-07-18 21:08:22 +00:00
goto error;
}
if (bus) {
if (STREQ(vm->os.type, "hvm")) {
if (STREQ((const char*)bus, "ps2")) { /* Only allow mouse */
if (input->type != QEMU_INPUT_TYPE_MOUSE) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("ps2 bus does not support %s input device"),
(const char*)type);
goto error;
}
input->bus = QEMU_INPUT_BUS_PS2;
} else if (STREQ((const char *)bus, "usb")) { /* Allow mouse & tablet */
input->bus = QEMU_INPUT_BUS_USB;
} else {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("unsupported input bus %s"), (const char*)bus);
2007-07-18 21:08:22 +00:00
goto error;
}
} else {
if (STREQ((const char *)bus, "xen")) { /* Allow mouse only */
input->bus = QEMU_INPUT_BUS_XEN;
if (input->type != QEMU_INPUT_TYPE_MOUSE) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("xen bus does not support %s input device"),
(const char*)type);
goto error;
}
} else {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("unsupported input bus %s"), (const char*)bus);
goto error;
}
2007-07-18 21:08:22 +00:00
}
} else {
2008-05-15 20:07:34 +00:00
if (STREQ(vm->os.type, "hvm")) {
if (input->type == QEMU_INPUT_TYPE_MOUSE)
input->bus = QEMU_INPUT_BUS_PS2;
else
input->bus = QEMU_INPUT_BUS_USB;
} else {
input->bus = QEMU_INPUT_BUS_XEN;
}
2007-07-18 21:08:22 +00:00
}
xmlFree(type);
xmlFree(bus);
2007-07-18 21:08:22 +00:00
2007-10-27 01:18:38 +00:00
return 0;
2007-07-18 21:08:22 +00:00
error:
xmlFree(type);
xmlFree(bus);
2007-07-18 21:08:22 +00:00
2007-10-27 01:18:38 +00:00
return -1;
2007-07-18 21:08:22 +00:00
}
static int qemudDiskCompare(const void *aptr, const void *bptr) {
struct qemud_vm_disk_def *a = (struct qemud_vm_disk_def *) aptr;
struct qemud_vm_disk_def *b = (struct qemud_vm_disk_def *) bptr;
if (a->bus == b->bus)
return virDiskNameToIndex(a->dst) - virDiskNameToIndex(b->dst);
else
return a->bus - b->bus;
}
static const char *qemudBusIdToName(int busId, int qemuIF) {
const char *busnames[] = { "ide",
(qemuIF ? "floppy" : "fdc"),
"scsi",
"virtio",
"xen"};
verify_true(ARRAY_CARDINALITY(busnames) == QEMUD_DISK_BUS_LAST);
return busnames[busId];
}
/* Sound device helper functions */
static int qemudSoundModelFromString(const char *model) {
if (STREQ(model, "sb16")) {
return QEMU_SOUND_SB16;
} else if (STREQ(model, "es1370")) {
return QEMU_SOUND_ES1370;
} else if (STREQ(model, "pcspk")) {
return QEMU_SOUND_PCSPK;
}
return -1;
}
static const char *qemudSoundModelToString(const int model) {
if (model == QEMU_SOUND_SB16) {
return "sb16";
} else if (model == QEMU_SOUND_ES1370) {
return "es1370";
} else if (model == QEMU_SOUND_PCSPK) {
return "pcspk";
}
return NULL;
}
static int qemudParseSoundXML(virConnectPtr conn,
struct qemud_vm_sound_def *sound,
const xmlNodePtr node) {
int err = -1;
xmlChar *model = NULL;
model = xmlGetProp(node, BAD_CAST "model");
if (!model) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("missing sound model"));
goto error;
}
if ((sound->model = qemudSoundModelFromString((char *) model)) < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("invalid sound model '%s'"), (char *) model);
goto error;
}
err = 0;
error:
xmlFree(model);
return err;
}
2007-02-14 01:40:09 +00:00
/*
* Parses a libvirt XML definition of a guest, and populates the
* the qemud_vm struct with matching data about the guests config
*/
static struct qemud_vm_def *qemudParseXML(virConnectPtr conn,
struct qemud_driver *driver,
xmlDocPtr xml) {
2007-02-14 01:40:09 +00:00
xmlNodePtr root = NULL;
xmlChar *prop = NULL;
xmlXPathContextPtr ctxt = NULL;
xmlXPathObjectPtr obj = NULL;
char *conv = NULL;
int i;
struct qemud_vm_def *def;
if (!(def = calloc(1, sizeof(*def)))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s", _("failed to allocate space for xmlXPathContext"));
return NULL;
}
2007-02-14 01:40:09 +00:00
/* Prepare parser / xpath context */
root = xmlDocGetRootElement(xml);
if ((root == NULL) || (!xmlStrEqual(root->name, BAD_CAST "domain"))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("incorrect root element"));
2007-02-14 01:40:09 +00:00
goto error;
}
ctxt = xmlXPathNewContext(xml);
if (ctxt == NULL) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s", _("failed to allocate space for xmlXPathContext"));
2007-02-14 01:40:09 +00:00
goto error;
}
/* Find out what type of QEMU virtualization to use */
if (!(prop = xmlGetProp(root, BAD_CAST "type"))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("missing domain type attribute"));
2007-02-14 01:40:09 +00:00
goto error;
}
if (STREQ((char *)prop, "qemu"))
def->virtType = QEMUD_VIRT_QEMU;
else if (STREQ((char *)prop, "kqemu"))
def->virtType = QEMUD_VIRT_KQEMU;
else if (STREQ((char *)prop, "kvm"))
def->virtType = QEMUD_VIRT_KVM;
2007-02-14 01:40:09 +00:00
else {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("invalid domain type attribute"));
2007-02-14 01:40:09 +00:00
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(conn, NULL, NULL, VIR_ERR_NO_NAME, NULL);
2007-02-14 01:40:09 +00:00
goto error;
}
if (strlen((const char *)obj->stringval) >= (QEMUD_MAX_NAME_LEN-1)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("domain name length too long"));
2007-02-14 01:40:09 +00:00
goto error;
}
strcpy(def->name, (const char *)obj->stringval);
2007-02-14 01:40:09 +00:00
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)) {
int err;
2007-06-26 22:19:38 +00:00
if ((err = virUUIDGenerate(def->uuid))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("Failed to generate UUID: %s"), strerror(err));
goto error;
}
2007-06-26 22:19:38 +00:00
} else if (virUUIDParse((const char *)obj->stringval, def->uuid) < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("malformed uuid element"));
2007-02-14 01:40:09 +00:00
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)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("missing memory element"));
2007-02-14 01:40:09 +00:00
goto error;
} else {
conv = NULL;
def->maxmem = strtoll((const char*)obj->stringval, &conv, 10);
2007-02-14 01:40:09 +00:00
if (conv == (const char*)obj->stringval) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("malformed memory information"));
2007-02-14 01:40:09 +00:00
goto error;
}
}
xmlXPathFreeObject(obj);
2007-02-14 01:40:09 +00:00
/* Extract domain memory */
obj = xmlXPathEval(BAD_CAST "string(/domain/currentMemory[1])", ctxt);
if ((obj == NULL) || (obj->type != XPATH_STRING) ||
(obj->stringval == NULL) || (obj->stringval[0] == 0)) {
def->memory = def->maxmem;
2007-02-14 01:40:09 +00:00
} else {
conv = NULL;
def->memory = strtoll((const char*)obj->stringval, &conv, 10);
if (def->memory > def->maxmem)
def->memory = def->maxmem;
2007-02-14 01:40:09 +00:00
if (conv == (const char*)obj->stringval) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("malformed memory information"));
2007-02-14 01:40:09 +00:00
goto error;
}
}
xmlXPathFreeObject(obj);
2007-02-14 01:40:09 +00:00
/* 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)) {
def->vcpus = 1;
2007-02-14 01:40:09 +00:00
} else {
conv = NULL;
def->vcpus = strtoll((const char*)obj->stringval, &conv, 10);
2007-02-14 01:40:09 +00:00
if (conv == (const char*)obj->stringval) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("malformed vcpu information"));
2007-02-14 01:40:09 +00:00
goto error;
}
}
xmlXPathFreeObject(obj);
2007-02-14 01:40:09 +00:00
/* Extract domain vcpu info */
obj = xmlXPathEval(BAD_CAST "string(/domain/vcpu[1]/@cpuset)", ctxt);
if ((obj == NULL) || (obj->type != XPATH_STRING) ||
(obj->stringval == NULL) || (obj->stringval[0] == 0)) {
/* Allow use on all CPUS */
memset(def->cpumask, 1, QEMUD_CPUMASK_LEN);
} else {
char *set = (char *)obj->stringval;
memset(def->cpumask, 0, QEMUD_CPUMASK_LEN);
if (virParseCpuSet(conn, (const char **)&set,
0, def->cpumask,
QEMUD_CPUMASK_LEN) < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("malformed vcpu mask information"));
goto error;
}
}
xmlXPathFreeObject(obj);
2007-02-14 01:40:09 +00:00
/* See if ACPI feature is requested */
obj = xmlXPathEval(BAD_CAST "/domain/features/acpi", ctxt);
if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
(obj->nodesetval != NULL) && (obj->nodesetval->nodeNr == 1)) {
def->features |= QEMUD_FEATURE_ACPI;
2007-02-14 01:40:09 +00:00
}
xmlXPathFreeObject(obj);
2007-02-14 01:40:09 +00:00
2007-05-03 16:10:40 +00:00
/* See if we disable reboots */
obj = xmlXPathEval(BAD_CAST "string(/domain/on_reboot)", ctxt);
if ((obj == NULL) || (obj->type != XPATH_STRING) ||
(obj->stringval == NULL) || (obj->stringval[0] == 0)) {
def->noReboot = 0;
} else {
if (STREQ((char*)obj->stringval, "destroy"))
2007-05-03 16:10:40 +00:00
def->noReboot = 1;
else
def->noReboot = 0;
}
xmlXPathFreeObject(obj);
2007-05-03 16:10:40 +00:00
/* See if we set clock to localtime */
obj = xmlXPathEval(BAD_CAST "string(/domain/clock/@offset)", ctxt);
if ((obj == NULL) || (obj->type != XPATH_STRING) ||
(obj->stringval == NULL) || (obj->stringval[0] == 0)) {
def->localtime = 0;
} else {
if (STREQ((char*)obj->stringval, "localtime"))
def->localtime = 1;
else
def->localtime = 0;
}
xmlXPathFreeObject(obj);
2007-05-03 16:10:40 +00:00
2008-05-15 16:21:11 +00:00
/* Extract bootloader */
obj = xmlXPathEval(BAD_CAST "string(/domain/bootloader)", ctxt);
if ((obj != NULL) && (obj->type == XPATH_STRING) &&
(obj->stringval != NULL) && (obj->stringval[0] != 0)) {
strncpy(def->os.bootloader, (const char*)obj->stringval, sizeof(def->os.bootloader));
NUL_TERMINATE(def->os.bootloader);
/* Set a default OS type, since <type> is optional with bootloader */
strcpy(def->os.type, "xen");
}
xmlXPathFreeObject(obj);
2008-05-15 16:21:11 +00:00
2007-02-14 01:40:09 +00:00
/* 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)) {
2008-05-15 16:21:11 +00:00
if (!def->os.type[0]) {
qemudReportError(conn, NULL, NULL, VIR_ERR_OS_TYPE,
"%s", _("no OS type"));
goto error;
}
} else {
strcpy(def->os.type, (const char *)obj->stringval);
2007-02-14 01:40:09 +00:00
}
xmlXPathFreeObject(obj);
obj = NULL;
2008-05-15 16:21:11 +00:00
if (!virCapabilitiesSupportsGuestOSType(driver->caps, def->os.type)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_OS_TYPE,
2008-05-15 16:21:11 +00:00
"%s", def->os.type);
2007-02-14 01:40:09 +00:00
goto error;
}
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 = virCapabilitiesDefaultGuestArch(driver->caps, def->os.type);
if (defaultArch == NULL) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("unsupported architecture"));
goto error;
}
2007-02-14 01:40:09 +00:00
if (strlen(defaultArch) >= (QEMUD_OS_TYPE_MAX_LEN-1)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("architecture type too long"));
2007-02-14 01:40:09 +00:00
goto error;
}
strcpy(def->os.arch, defaultArch);
2007-02-14 01:40:09 +00:00
} else {
if (strlen((const char *)obj->stringval) >= (QEMUD_OS_TYPE_MAX_LEN-1)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("architecture type too long"));
2007-02-14 01:40:09 +00:00
goto error;
}
strcpy(def->os.arch, (const char *)obj->stringval);
2007-02-14 01:40:09 +00:00
}
xmlXPathFreeObject(obj);
2007-02-14 01:40:09 +00:00
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 = virCapabilitiesDefaultGuestMachine(driver->caps,
def->os.type,
def->os.arch);
if (defaultMachine == NULL) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("unsupported architecture"));
goto error;
}
2007-02-14 01:40:09 +00:00
if (strlen(defaultMachine) >= (QEMUD_OS_MACHINE_MAX_LEN-1)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("machine type too long"));
2007-02-14 01:40:09 +00:00
goto error;
}
strcpy(def->os.machine, defaultMachine);
2007-02-14 01:40:09 +00:00
} else {
if (strlen((const char *)obj->stringval) >= (QEMUD_OS_MACHINE_MAX_LEN-1)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("architecture type too long"));
2007-02-14 01:40:09 +00:00
goto error;
}
strcpy(def->os.machine, (const char *)obj->stringval);
2007-02-14 01:40:09 +00:00
}
xmlXPathFreeObject(obj);
2007-02-14 01:40:09 +00:00
2008-05-15 16:21:11 +00:00
if (!def->os.bootloader[0]) {
obj = xmlXPathEval(BAD_CAST "string(/domain/os/kernel[1])", ctxt);
if ((obj != NULL) && (obj->type == XPATH_STRING) &&
(obj->stringval != NULL) && (obj->stringval[0] != 0)) {
if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("kernel path too long"));
goto error;
}
strcpy(def->os.kernel, (const char *)obj->stringval);
2007-02-14 01:40:09 +00:00
}
2008-05-15 16:21:11 +00:00
xmlXPathFreeObject(obj);
2007-02-14 01:40:09 +00:00
2008-05-15 16:21:11 +00:00
obj = xmlXPathEval(BAD_CAST "string(/domain/os/initrd[1])", ctxt);
if ((obj != NULL) && (obj->type == XPATH_STRING) &&
(obj->stringval != NULL) && (obj->stringval[0] != 0)) {
if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("initrd path too long"));
goto error;
}
strcpy(def->os.initrd, (const char *)obj->stringval);
2007-02-14 01:40:09 +00:00
}
2008-05-15 16:21:11 +00:00
xmlXPathFreeObject(obj);
2007-02-14 01:40:09 +00:00
2008-05-15 16:21:11 +00:00
obj = xmlXPathEval(BAD_CAST "string(/domain/os/cmdline[1])", ctxt);
if ((obj != NULL) && (obj->type == XPATH_STRING) &&
(obj->stringval != NULL) && (obj->stringval[0] != 0)) {
if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("cmdline arguments too long"));
goto error;
}
strcpy(def->os.cmdline, (const char *)obj->stringval);
2007-02-14 01:40:09 +00:00
}
2008-05-15 16:21:11 +00:00
xmlXPathFreeObject(obj);
2007-02-14 01:40:09 +00:00
2008-05-15 16:21:11 +00:00
/* analysis of the disk devices */
obj = xmlXPathEval(BAD_CAST "/domain/os/boot", ctxt);
if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
(obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) {
for (i = 0; i < obj->nodesetval->nodeNr && i < QEMUD_MAX_BOOT_DEVS ; i++) {
if (!(prop = xmlGetProp(obj->nodesetval->nodeTab[i], BAD_CAST "dev")))
continue;
if (STREQ((char *)prop, "hd")) {
def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_DISK;
} else if (STREQ((char *)prop, "fd")) {
def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_FLOPPY;
} else if (STREQ((char *)prop, "cdrom")) {
def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_CDROM;
} else if (STREQ((char *)prop, "network")) {
def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_NET;
} else {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("unknown boot device \'%s\'"), (char*)prop);
goto error;
}
xmlFree(prop);
prop = NULL;
2007-02-14 01:40:09 +00:00
}
}
2008-05-15 16:21:11 +00:00
xmlXPathFreeObject(obj);
if (def->os.nBootDevs == 0) {
def->os.nBootDevs = 1;
def->os.bootDevs[0] = QEMUD_BOOT_DISK;
}
2007-02-14 01:40:09 +00:00
}
obj = xmlXPathEval(BAD_CAST "string(/domain/devices/emulator[1])", ctxt);
if ((obj == NULL) || (obj->type != XPATH_STRING) ||
(obj->stringval == NULL) || (obj->stringval[0] == 0)) {
const char *type = qemudVirtTypeToString(def->virtType);
if (!type) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("unknown virt type"));
goto error;
}
const char *emulator = virCapabilitiesDefaultGuestEmulator(driver->caps,
def->os.type,
def->os.arch,
type);
if (!emulator) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("unsupported guest type"));
2007-02-14 01:40:09 +00:00
goto error;
}
strcpy(def->os.binary, emulator);
2007-02-14 01:40:09 +00:00
} else {
if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("emulator path too long"));
2007-02-14 01:40:09 +00:00
goto error;
}
strcpy(def->os.binary, (const char *)obj->stringval);
2007-02-14 01:40:09 +00:00
}
xmlXPathFreeObject(obj);
2007-02-14 01:40:09 +00:00
obj = xmlXPathEval(BAD_CAST "/domain/devices/graphics", ctxt);
if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
(obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) {
def->graphicsType = QEMUD_GRAPHICS_NONE;
} else if ((prop = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "type"))) {
if (STREQ((char *)prop, "vnc")) {
xmlChar *vncport, *vnclisten;
def->graphicsType = QEMUD_GRAPHICS_VNC;
vncport = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "port");
if (vncport) {
2007-02-14 01:40:09 +00:00
conv = NULL;
def->vncPort = strtoll((const char*)vncport, &conv, 10);
2007-02-14 01:40:09 +00:00
} else {
def->vncPort = -1;
2007-02-14 01:40:09 +00:00
}
vnclisten = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "listen");
if (vnclisten && *vnclisten)
strncpy(def->vncListen, (char *)vnclisten, BR_INET_ADDR_MAXLEN-1);
else
2007-10-12 16:05:44 +00:00
strcpy(def->vncListen, driver->vncListen);
def->vncListen[BR_INET_ADDR_MAXLEN-1] = '\0';
def->keymap = (char *) xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "keymap");
xmlFree(vncport);
xmlFree(vnclisten);
} else if (STREQ((char *)prop, "sdl")) {
def->graphicsType = QEMUD_GRAPHICS_SDL;
2007-02-14 01:40:09 +00:00
} else {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("Unsupported graphics type %s"), prop);
2007-02-14 01:40:09 +00:00
goto error;
}
xmlFree(prop);
2007-03-09 03:08:34 +00:00
prop = NULL;
2007-02-14 01:40:09 +00:00
}
xmlXPathFreeObject(obj);
2007-02-14 01:40:09 +00:00
/* 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 (i = 0; i < obj->nodesetval->nodeNr; i++) {
struct qemud_vm_disk_def *disk = calloc(1, sizeof(*disk));
2007-10-27 01:18:38 +00:00
if (!disk) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s", _("failed to allocate space for disk string"));
2007-10-27 01:18:38 +00:00
goto error;
}
if (qemudParseDiskXML(conn, disk, obj->nodesetval->nodeTab[i]) < 0) {
free(disk);
2007-02-14 01:40:09 +00:00
goto error;
}
def->ndisks++;
2007-03-13 22:43:22 +00:00
if (i == 0) {
disk->next = NULL;
2007-03-13 22:43:22 +00:00
def->disks = disk;
} else {
struct qemud_vm_disk_def *ptr = def->disks;
while (ptr) {
if (!ptr->next || qemudDiskCompare(disk, ptr->next) < 0) {
disk->next = ptr->next;
ptr->next = disk;
break;
}
ptr = ptr->next;
}
2007-03-13 22:43:22 +00:00
}
2007-02-14 01:40:09 +00:00
}
}
xmlXPathFreeObject(obj);
obj = NULL;
/* analysis of the character devices */
if (qemudParseCharXMLDevices(conn, ctxt,
"/domain/devices/parallel",
&def->nparallels,
&def->parallels) < 0)
goto error;
if (qemudParseCharXMLDevices(conn, ctxt,
"/domain/devices/serial",
&def->nserials,
&def->serials) < 0)
goto error;
/*
* If no serial devices were listed, then look for console
* devices which is the legacy syntax for the same thing
*/
if (def->nserials == 0) {
obj = xmlXPathEval(BAD_CAST "/domain/devices/console", ctxt);
if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
(obj->nodesetval != NULL) && (obj->nodesetval->nodeNr == 1)) {
struct qemud_vm_chr_def *chr = calloc(1, sizeof(*chr));
if (!chr) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s",
_("failed to allocate space for char device"));
goto error;
}
if (qemudParseCharXML(conn, chr, 0, obj->nodesetval->nodeTab[0]) < 0) {
free(chr);
goto error;
}
def->nserials = 1;
def->serials = chr;
}
xmlXPathFreeObject(obj);
}
2007-02-14 01:40:09 +00:00
/* 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)) {
2007-03-13 22:43:22 +00:00
struct qemud_vm_net_def *prev = NULL;
2007-02-14 01:40:09 +00:00
for (i = 0; i < obj->nodesetval->nodeNr; i++) {
struct qemud_vm_net_def *net = calloc(1, sizeof(*net));
2007-10-27 01:18:38 +00:00
if (!net) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s", _("failed to allocate space for net string"));
2007-10-27 01:18:38 +00:00
goto error;
}
if (qemudParseInterfaceXML(conn, net, obj->nodesetval->nodeTab[i]) < 0) {
free(net);
2007-02-14 01:40:09 +00:00
goto error;
}
def->nnets++;
2007-03-13 22:43:22 +00:00
net->next = NULL;
if (i == 0) {
def->nets = net;
} else {
prev->next = net;
}
prev = net;
2007-02-14 01:40:09 +00:00
}
}
xmlXPathFreeObject(obj);
2007-07-18 21:08:22 +00:00
/* analysis of the input devices */
obj = xmlXPathEval(BAD_CAST "/domain/devices/input", ctxt);
if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
(obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) {
struct qemud_vm_input_def *prev = NULL;
for (i = 0; i < obj->nodesetval->nodeNr; i++) {
struct qemud_vm_input_def *input = calloc(1, sizeof(*input));
2007-10-27 01:18:38 +00:00
if (!input) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s", _("failed to allocate space for input string"));
2007-10-27 01:18:38 +00:00
goto error;
}
if (qemudParseInputXML(conn, def, input, obj->nodesetval->nodeTab[i]) < 0) {
2007-10-27 01:18:38 +00:00
free(input);
2007-07-18 21:08:22 +00:00
goto error;
}
/* Mouse + PS/2 is implicit with graphics, so don't store it */
if (input->bus == QEMU_INPUT_BUS_PS2 &&
input->type == QEMU_INPUT_TYPE_MOUSE) {
free(input);
continue;
}
def->ninputs++;
input->next = NULL;
2007-09-28 20:47:58 +00:00
if (def->inputs == NULL) {
2007-07-18 21:08:22 +00:00
def->inputs = input;
} else {
prev->next = input;
}
prev = input;
}
}
xmlXPathFreeObject(obj);
/* Parse sound driver xml */
obj = xmlXPathEval(BAD_CAST "/domain/devices/sound", ctxt);
if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
(obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) {
struct qemud_vm_sound_def *prev = NULL;
for (i = 0; i < obj->nodesetval->nodeNr; i++) {
struct qemud_vm_sound_def *sound = calloc(1, sizeof(*sound));
struct qemud_vm_sound_def *check = def->sounds;
int collision = 0;
if (!sound) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s", _("failed to allocate space for sound dev"));
goto error;
}
if (qemudParseSoundXML(conn, sound,
obj->nodesetval->nodeTab[i]) < 0) {
free(sound);
goto error;
}
// Check that model type isn't already present in sound dev list
while(check) {
if (check->model == sound->model) {
collision = 1;
break;
}
check = check->next;
}
if (collision)
continue;
def->nsounds++;
sound->next = NULL;
if (def->sounds == NULL) {
def->sounds = sound;
} else {
prev->next = sound;
}
prev = sound;
}
}
xmlXPathFreeObject(obj);
2007-07-18 21:08:22 +00:00
obj = NULL;
/* If graphics are enabled, there's an implicit PS2 mouse */
if (def->graphicsType != QEMUD_GRAPHICS_NONE) {
int hasPS2mouse = 0;
struct qemud_vm_input_def *input = def->inputs;
while (input) {
if (input->type == QEMU_INPUT_TYPE_MOUSE &&
input->bus == QEMU_INPUT_BUS_PS2)
hasPS2mouse = 1;
input = input->next;
}
if (!hasPS2mouse) {
input = calloc(1, sizeof(*input));
2007-07-18 21:08:22 +00:00
if (!input) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s", _("failed to allocate space for input string"));
2007-07-18 21:08:22 +00:00
goto error;
}
input->type = QEMU_INPUT_TYPE_MOUSE;
input->bus = QEMU_INPUT_BUS_PS2;
input->next = def->inputs;
def->inputs = input;
def->ninputs++;
}
}
2007-02-14 01:40:09 +00:00
xmlXPathFreeContext(ctxt);
return def;
2007-02-14 01:40:09 +00:00
error:
free(prop);
xmlXPathFreeObject(obj);
xmlXPathFreeContext(ctxt);
qemudFreeVMDef(def);
return NULL;
2007-02-14 01:40:09 +00:00
}
static char *
qemudNetworkIfaceConnect(virConnectPtr conn,
struct qemud_driver *driver,
struct qemud_vm *vm,
2007-03-13 22:43:22 +00:00
struct qemud_vm_net_def *net,
int vlan)
{
2007-03-13 22:43:22 +00:00
struct qemud_network *network = NULL;
char *brname;
char *ifname;
char tapfdstr[4+3+32+7];
char *retval = NULL;
int err;
int tapfd = -1;
int *tapfds;
2007-03-13 22:43:22 +00:00
if (net->type == QEMUD_NET_NETWORK) {
if (!(network = qemudFindNetworkByName(driver, net->dst.network.name))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("Network '%s' not found"),
net->dst.network.name);
2007-03-13 22:43:22 +00:00
goto error;
} else if (network->bridge[0] == '\0') {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("Network '%s' not active"),
net->dst.network.name);
2007-03-13 22:43:22 +00:00
goto error;
}
brname = network->bridge;
if (net->dst.network.ifname[0] == '\0' ||
STRPREFIX(net->dst.network.ifname, "vnet") ||
2007-03-13 22:43:22 +00:00
strchr(net->dst.network.ifname, '%')) {
strcpy(net->dst.network.ifname, "vnet%d");
}
ifname = net->dst.network.ifname;
} else if (net->type == QEMUD_NET_BRIDGE) {
brname = net->dst.bridge.brname;
if (net->dst.bridge.ifname[0] == '\0' ||
STRPREFIX(net->dst.bridge.ifname, "vnet") ||
2007-03-13 22:43:22 +00:00
strchr(net->dst.bridge.ifname, '%')) {
strcpy(net->dst.bridge.ifname, "vnet%d");
}
ifname = net->dst.bridge.ifname;
} else {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("Network type %d is not supported"), net->type);
goto error;
}
if (!driver->brctl && (err = brInit(&driver->brctl))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("cannot initialize bridge support: %s"),
strerror(err));
goto error;
}
if ((err = brAddTap(driver->brctl, brname,
2007-03-13 22:43:22 +00:00
ifname, BR_IFNAME_MAXLEN, &tapfd))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("Failed to add tap interface '%s' to bridge '%s' : %s"),
2007-03-13 22:43:22 +00:00
ifname, brname, strerror(err));
goto error;
}
snprintf(tapfdstr, sizeof(tapfdstr),
"tap,fd=%d,script=,vlan=%d,ifname=%s",
tapfd, vlan, ifname);
if (!(retval = strdup(tapfdstr)))
goto no_memory;
if (!(tapfds = realloc(vm->tapfds, sizeof(*tapfds) * (vm->ntapfds+2))))
goto no_memory;
vm->tapfds = tapfds;
vm->tapfds[vm->ntapfds++] = tapfd;
vm->tapfds[vm->ntapfds] = -1;
return retval;
no_memory:
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s", _("failed to allocate space for tapfds string"));
error:
free(retval);
if (tapfd != -1)
close(tapfd);
return NULL;
}
static int qemudBuildCommandLineChrDevStr(struct qemud_vm_chr_def *dev,
char *buf,
int buflen)
{
switch (dev->srcType) {
case QEMUD_CHR_SRC_TYPE_NULL:
strncpy(buf, "null", buflen);
buf[buflen-1] = '\0';
break;
case QEMUD_CHR_SRC_TYPE_VC:
strncpy(buf, "vc", buflen);
buf[buflen-1] = '\0';
break;
case QEMUD_CHR_SRC_TYPE_PTY:
strncpy(buf, "pty", buflen);
buf[buflen-1] = '\0';
break;
case QEMUD_CHR_SRC_TYPE_DEV:
if (snprintf(buf, buflen, "%s",
dev->srcData.file.path) >= buflen)
return -1;
break;
case QEMUD_CHR_SRC_TYPE_FILE:
if (snprintf(buf, buflen, "file:%s",
dev->srcData.file.path) >= buflen)
return -1;
break;
case QEMUD_CHR_SRC_TYPE_PIPE:
if (snprintf(buf, buflen, "pipe:%s",
dev->srcData.file.path) >= buflen)
return -1;
break;
case QEMUD_CHR_SRC_TYPE_STDIO:
strncpy(buf, "stdio", buflen);
buf[buflen-1] = '\0';
break;
case QEMUD_CHR_SRC_TYPE_UDP:
if (snprintf(buf, buflen, "udp:%s:%s@%s:%s",
dev->srcData.udp.connectHost,
dev->srcData.udp.connectService,
dev->srcData.udp.bindHost,
dev->srcData.udp.bindService) >= buflen)
return -1;
break;
case QEMUD_CHR_SRC_TYPE_TCP:
if (snprintf(buf, buflen, "%s:%s:%s%s",
dev->srcData.tcp.protocol == QEMUD_CHR_SRC_TCP_PROTOCOL_TELNET ? "telnet" : "tcp",
dev->srcData.tcp.host,
dev->srcData.tcp.service,
dev->srcData.tcp.listen ? ",listen" : "") >= buflen)
return -1;
break;
case QEMUD_CHR_SRC_TYPE_UNIX:
if (snprintf(buf, buflen, "unix:%s%s",
dev->srcData.nix.path,
dev->srcData.nix.listen ? ",listen" : "") >= buflen)
return -1;
break;
}
return 0;
}
2007-02-14 01:40:09 +00:00
/*
* Constructs a argv suitable for launching qemu with config defined
* for a given virtual machine.
*/
int qemudBuildCommandLine(virConnectPtr conn,
struct qemud_driver *driver,
2007-02-14 01:40:09 +00:00
struct qemud_vm *vm,
char ***argv) {
int len, n = -1, i;
2007-02-14 01:40:09 +00:00
char memory[50];
char vcpus[50];
char boot[QEMUD_MAX_BOOT_DEVS+1];
struct qemud_vm_disk_def *disk = vm->def->disks;
struct qemud_vm_net_def *net = vm->def->nets;
2007-07-18 21:08:22 +00:00
struct qemud_vm_input_def *input = vm->def->inputs;
struct qemud_vm_sound_def *sound = vm->def->sounds;
struct qemud_vm_chr_def *serial = vm->def->serials;
struct qemud_vm_chr_def *parallel = vm->def->parallels;
2007-04-16 13:14:28 +00:00
struct utsname ut;
int disableKQEMU = 0;
2007-02-14 01:40:09 +00:00
if (vm->qemuVersion == 0) {
if (qemudExtractVersionInfo(vm->def->os.binary,
&(vm->qemuVersion),
&(vm->qemuCmdFlags)) < 0)
return -1;
}
2007-04-16 13:14:28 +00:00
uname(&ut);
2007-05-18 18:36:24 +00:00
/* Nasty hack make i?86 look like i686 to simplify next comparison */
2007-04-16 13:14:28 +00:00
if (ut.machine[0] == 'i' &&
ut.machine[2] == '8' &&
ut.machine[3] == '6' &&
!ut.machine[4])
2007-05-18 18:36:24 +00:00
ut.machine[1] = '6';
2007-04-16 13:14:28 +00:00
/* Need to explicitly disable KQEMU if
* 1. Arch matches host arch
* 2. Guest is 'qemu'
* 3. The qemu binary has the -no-kqemu flag
*/
if ((vm->qemuCmdFlags & QEMUD_CMD_FLAG_KQEMU) &&
STREQ(ut.machine, vm->def->os.arch) &&
2007-04-16 13:14:28 +00:00
vm->def->virtType == QEMUD_VIRT_QEMU)
disableKQEMU = 1;
len = 1 + /* qemu */
1 + /* Stopped */
2007-02-14 01:40:09 +00:00
2 + /* machine type */
2007-04-16 13:14:28 +00:00
disableKQEMU + /* Disable kqemu */
2008-05-15 16:15:17 +00:00
(vm->qemuCmdFlags & QEMUD_CMD_FLAG_NAME ? 2 : 0) + /* -name XXX */
2 * vm->def->ndisks + /* disks*/
(vm->def->nnets > 0 ? (4 * vm->def->nnets) : 2) + /* networks */
2007-07-18 21:08:22 +00:00
1 + /* usb */
2 * vm->def->ninputs + /* input devices */
((vm->def->nsounds > 0) ? 2 : 0) + /* sound */
(vm->def->nserials > 0 ? (2 * vm->def->nserials) : 2) + /* character devices */
(vm->def->nparallels > 0 ? (2 * vm->def->nparallels) : 2) + /* character devices */
2007-02-14 01:40:09 +00:00
2 + /* memory*/
2 + /* cpus */
2 + /* boot device */
2 + /* monitor */
(vm->def->localtime ? 1 : 0) + /* localtime */
(vm->qemuCmdFlags & QEMUD_CMD_FLAG_NO_REBOOT &&
2007-05-03 16:10:40 +00:00
vm->def->noReboot ? 1 : 0) + /* no-reboot */
(vm->def->features & QEMUD_FEATURE_ACPI ? 0 : 1) + /* acpi */
(vm->def->os.kernel[0] ? 2 : 0) + /* kernel */
(vm->def->os.initrd[0] ? 2 : 0) + /* initrd */
(vm->def->os.cmdline[0] ? 2 : 0) + /* cmdline */
2008-05-15 16:21:11 +00:00
(vm->def->os.bootloader[0] ? 2 : 0) + /* bootloader */
(vm->def->graphicsType == QEMUD_GRAPHICS_VNC ? 2 :
(vm->def->graphicsType == QEMUD_GRAPHICS_SDL ? 0 : 1)) + /* graphics */
(vm->migrateFrom[0] ? 2 : 0); /* migrateFrom */
2007-02-14 01:40:09 +00:00
snprintf(memory, sizeof(memory), "%lu", vm->def->memory/1024);
snprintf(vcpus, sizeof(vcpus), "%d", vm->def->vcpus);
2007-02-14 01:40:09 +00:00
if (!(*argv = calloc(len+1, sizeof(**argv))))
2007-02-14 01:40:09 +00:00
goto no_memory;
if (!((*argv)[++n] = strdup(vm->def->os.binary)))
2007-02-14 01:40:09 +00:00
goto no_memory;
if (!((*argv)[++n] = strdup("-S")))
goto no_memory;
2007-02-14 01:40:09 +00:00
if (!((*argv)[++n] = strdup("-M")))
goto no_memory;
if (!((*argv)[++n] = strdup(vm->def->os.machine)))
2007-02-14 01:40:09 +00:00
goto no_memory;
2007-04-16 13:14:28 +00:00
if (disableKQEMU) {
2007-02-14 01:40:09 +00:00
if (!((*argv)[++n] = strdup("-no-kqemu")))
goto no_memory;
2007-02-14 01:40:09 +00:00
}
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;
2008-05-15 16:15:17 +00:00
if (vm->qemuCmdFlags & QEMUD_CMD_FLAG_NAME) {
if (!((*argv)[++n] = strdup("-name")))
goto no_memory;
if (!((*argv)[++n] = strdup(vm->def->name)))
goto no_memory;
}
/*
* NB, -nographic *MUST* come before any serial, or monitor
* or parallel port flags due to QEMU craziness, where it
* decides to change the serial port & monitor to be on stdout
* if you ask for nographic. So we have to make sure we override
* these defaults ourselves...
*/
if (vm->def->graphicsType == QEMUD_GRAPHICS_NONE) {
if (!((*argv)[++n] = strdup("-nographic")))
goto no_memory;
}
2007-02-14 01:40:09 +00:00
if (!((*argv)[++n] = strdup("-monitor")))
goto no_memory;
if (!((*argv)[++n] = strdup("pty")))
goto no_memory;
if (vm->def->localtime) {
if (!((*argv)[++n] = strdup("-localtime")))
goto no_memory;
}
if (vm->qemuCmdFlags & QEMUD_CMD_FLAG_NO_REBOOT &&
2007-05-03 16:10:40 +00:00
vm->def->noReboot) {
if (!((*argv)[++n] = strdup("-no-reboot")))
goto no_memory;
}
if (!(vm->def->features & QEMUD_FEATURE_ACPI)) {
if (!((*argv)[++n] = strdup("-no-acpi")))
goto no_memory;
2007-02-14 01:40:09 +00:00
}
2008-05-15 16:21:11 +00:00
if (!vm->def->os.bootloader[0]) {
for (i = 0 ; i < vm->def->os.nBootDevs ; i++) {
switch (vm->def->os.bootDevs[i]) {
case QEMUD_BOOT_CDROM:
boot[i] = 'd';
break;
case QEMUD_BOOT_FLOPPY:
boot[i] = 'a';
break;
case QEMUD_BOOT_DISK:
boot[i] = 'c';
break;
case QEMUD_BOOT_NET:
boot[i] = 'n';
break;
default:
boot[i] = 'c';
break;
}
}
boot[vm->def->os.nBootDevs] = '\0';
if (!((*argv)[++n] = strdup("-boot")))
2007-02-14 01:40:09 +00:00
goto no_memory;
2008-05-15 16:21:11 +00:00
if (!((*argv)[++n] = strdup(boot)))
2007-02-14 01:40:09 +00:00
goto no_memory;
2008-05-15 16:21:11 +00:00
if (vm->def->os.kernel[0]) {
if (!((*argv)[++n] = strdup("-kernel")))
goto no_memory;
if (!((*argv)[++n] = strdup(vm->def->os.kernel)))
goto no_memory;
}
if (vm->def->os.initrd[0]) {
if (!((*argv)[++n] = strdup("-initrd")))
goto no_memory;
if (!((*argv)[++n] = strdup(vm->def->os.initrd)))
goto no_memory;
}
if (vm->def->os.cmdline[0]) {
if (!((*argv)[++n] = strdup("-append")))
goto no_memory;
if (!((*argv)[++n] = strdup(vm->def->os.cmdline)))
goto no_memory;
}
} else {
if (!((*argv)[++n] = strdup("-bootloader")))
2007-02-14 01:40:09 +00:00
goto no_memory;
2008-05-15 16:21:11 +00:00
if (!((*argv)[++n] = strdup(vm->def->os.bootloader)))
2007-02-14 01:40:09 +00:00
goto no_memory;
}
/* If QEMU supports -drive param instead of old -hda, -hdb, -cdrom .. */
2008-05-15 16:15:17 +00:00
if (vm->qemuCmdFlags & QEMUD_CMD_FLAG_DRIVE) {
int bootCD = 0, bootFloppy = 0, bootDisk = 0;
/* If QEMU supports boot=on for -drive param... */
2008-05-15 16:15:17 +00:00
if (vm->qemuCmdFlags & QEMUD_CMD_FLAG_DRIVE_BOOT) {
for (i = 0 ; i < vm->def->os.nBootDevs ; i++) {
switch (vm->def->os.bootDevs[i]) {
case QEMUD_BOOT_CDROM:
bootCD = 1;
break;
case QEMUD_BOOT_FLOPPY:
bootFloppy = 1;
break;
case QEMUD_BOOT_DISK:
bootDisk = 1;
break;
}
}
}
2007-02-14 01:40:09 +00:00
while (disk) {
char opt[PATH_MAX];
const char *media = NULL;
int bootable = 0;
int idx = virDiskNameToIndex(disk->dst);
if (!((*argv)[++n] = strdup("-drive")))
goto no_memory;
2007-02-14 01:40:09 +00:00
if (idx < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("unsupported disk type '%s'"), disk->dst);
goto error;
}
if (disk->device == QEMUD_DISK_CDROM)
media = "media=cdrom,";
switch (disk->device) {
case QEMUD_DISK_CDROM:
bootable = bootCD;
bootCD = 0;
break;
case QEMUD_DISK_FLOPPY:
bootable = bootFloppy;
bootFloppy = 0;
break;
case QEMUD_DISK_DISK:
bootable = bootDisk;
bootDisk = 0;
break;
}
snprintf(opt, PATH_MAX, "file=%s,if=%s,%sindex=%d%s",
disk->src, qemudBusIdToName(disk->bus, 1),
media ? media : "",
idx,
bootable ? ",boot=on" : "");
if (!((*argv)[++n] = strdup(opt)))
goto no_memory;
disk = disk->next;
}
} else {
while (disk) {
char dev[NAME_MAX];
char file[PATH_MAX];
if (STREQ(disk->dst, "hdc") &&
disk->device == QEMUD_DISK_CDROM) {
if (disk->src[0]) {
snprintf(dev, NAME_MAX, "-%s", "cdrom");
} else {
disk = disk->next;
continue;
}
} else {
if (STRPREFIX(disk->dst, "hd") ||
STRPREFIX(disk->dst, "fd")) {
snprintf(dev, NAME_MAX, "-%s", disk->dst);
} else {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("unsupported disk type '%s'"), disk->dst);
goto error;
}
}
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;
}
2007-02-14 01:40:09 +00:00
}
if (!net) {
if (!((*argv)[++n] = strdup("-net")))
goto no_memory;
if (!((*argv)[++n] = strdup("none")))
goto no_memory;
} else {
2007-03-13 22:43:22 +00:00
int vlan = 0;
2007-02-14 01:40:09 +00:00
while (net) {
char nic[100];
if (snprintf(nic, sizeof(nic),
"nic,macaddr=%02x:%02x:%02x:%02x:%02x:%02x,vlan=%d%s%s",
net->mac[0], net->mac[1],
net->mac[2], net->mac[3],
net->mac[4], net->mac[5],
vlan,
(net->model[0] ? ",model=" : ""), net->model) >= sizeof(nic))
goto error;
2007-02-14 01:40:09 +00:00
if (!((*argv)[++n] = strdup("-net")))
goto no_memory;
if (!((*argv)[++n] = strdup(nic)))
goto no_memory;
2007-03-13 22:43:22 +00:00
2007-02-14 01:40:09 +00:00
if (!((*argv)[++n] = strdup("-net")))
goto no_memory;
2007-03-13 22:43:22 +00:00
switch (net->type) {
case QEMUD_NET_NETWORK:
case QEMUD_NET_BRIDGE:
if (!((*argv)[++n] = qemudNetworkIfaceConnect(conn, driver, vm, net, vlan)))
goto error;
2007-03-13 22:43:22 +00:00
break;
case QEMUD_NET_ETHERNET:
{
char arg[PATH_MAX];
if (snprintf(arg, PATH_MAX-1, "tap,ifname=%s,script=%s,vlan=%d",
net->dst.ethernet.ifname,
net->dst.ethernet.script,
vlan) >= (PATH_MAX-1))
goto error;
if (!((*argv)[++n] = strdup(arg)))
goto no_memory;
}
break;
case QEMUD_NET_CLIENT:
case QEMUD_NET_SERVER:
case QEMUD_NET_MCAST:
{
char arg[PATH_MAX];
const char *mode = NULL;
switch (net->type) {
case QEMUD_NET_CLIENT:
mode = "connect";
break;
case QEMUD_NET_SERVER:
mode = "listen";
break;
case QEMUD_NET_MCAST:
mode = "mcast";
break;
}
if (snprintf(arg, PATH_MAX-1, "socket,%s=%s:%d,vlan=%d",
mode,
net->dst.socket.address,
net->dst.socket.port,
vlan) >= (PATH_MAX-1))
goto error;
if (!((*argv)[++n] = strdup(arg)))
goto no_memory;
}
break;
case QEMUD_NET_USER:
default:
{
char arg[PATH_MAX];
if (snprintf(arg, PATH_MAX-1, "user,vlan=%d", vlan) >= (PATH_MAX-1))
goto error;
if (!((*argv)[++n] = strdup(arg)))
goto no_memory;
}
}
2007-02-14 01:40:09 +00:00
net = net->next;
2007-03-13 22:43:22 +00:00
vlan++;
2007-02-14 01:40:09 +00:00
}
}
if (!serial) {
if (!((*argv)[++n] = strdup("-serial")))
goto no_memory;
if (!((*argv)[++n] = strdup("none")))
goto no_memory;
} else {
while (serial) {
char buf[4096];
if (qemudBuildCommandLineChrDevStr(serial, buf, sizeof(buf)) < 0)
goto error;
if (!((*argv)[++n] = strdup("-serial")))
goto no_memory;
if (!((*argv)[++n] = strdup(buf)))
goto no_memory;
serial = serial->next;
}
}
if (!parallel) {
if (!((*argv)[++n] = strdup("-parallel")))
goto no_memory;
if (!((*argv)[++n] = strdup("none")))
goto no_memory;
} else {
while (parallel) {
char buf[4096];
if (qemudBuildCommandLineChrDevStr(parallel, buf, sizeof(buf)) < 0)
goto error;
if (!((*argv)[++n] = strdup("-parallel")))
goto no_memory;
if (!((*argv)[++n] = strdup(buf)))
goto no_memory;
parallel = parallel->next;
}
}
2007-07-18 21:08:22 +00:00
if (!((*argv)[++n] = strdup("-usb")))
goto no_memory;
while (input) {
if (input->bus == QEMU_INPUT_BUS_USB) {
if (!((*argv)[++n] = strdup("-usbdevice")))
goto no_memory;
if (!((*argv)[++n] = strdup(input->type == QEMU_INPUT_TYPE_MOUSE ? "mouse" : "tablet")))
goto no_memory;
}
input = input->next;
}
if (vm->def->graphicsType == QEMUD_GRAPHICS_VNC) {
2007-10-12 16:05:44 +00:00
char vncdisplay[PATH_MAX];
int ret;
2007-10-12 16:05:44 +00:00
if (vm->qemuCmdFlags & QEMUD_CMD_FLAG_VNC_COLON) {
char options[PATH_MAX] = "";
if (driver->vncTLS) {
strcat(options, ",tls");
if (driver->vncTLSx509verify) {
strcat(options, ",x509verify=");
} else {
strcat(options, ",x509=");
}
strncat(options, driver->vncTLSx509certdir,
sizeof(options) - (strlen(driver->vncTLSx509certdir)-1));
options[sizeof(options)-1] = '\0';
}
ret = snprintf(vncdisplay, sizeof(vncdisplay), "%s:%d%s",
vm->def->vncListen,
2007-10-12 16:05:44 +00:00
vm->def->vncActivePort - 5900,
options);
} else {
ret = snprintf(vncdisplay, sizeof(vncdisplay), "%d",
vm->def->vncActivePort - 5900);
2007-10-12 16:05:44 +00:00
}
if (ret < 0 || ret >= (int)sizeof(vncdisplay))
goto error;
2007-02-14 01:40:09 +00:00
if (!((*argv)[++n] = strdup("-vnc")))
goto no_memory;
if (!((*argv)[++n] = strdup(vncdisplay)))
2007-02-14 01:40:09 +00:00
goto no_memory;
if (vm->def->keymap) {
if (!((*argv)[++n] = strdup("-k")))
goto no_memory;
if (!((*argv)[++n] = strdup(vm->def->keymap)))
goto no_memory;
}
} else if (vm->def->graphicsType == QEMUD_GRAPHICS_NONE) {
/* Nada - we added -nographic earlier in this function */
2007-02-14 01:40:09 +00:00
} else {
/* SDL is the default. no args needed */
}
/* Add sound hardware */
if (sound) {
int size = 100;
char *modstr = calloc(1, size+1);
if (!modstr)
goto no_memory;
if (!((*argv)[++n] = strdup("-soundhw")))
goto no_memory;
while(sound && size > 0) {
const char *model = qemudSoundModelToString(sound->model);
if (!model) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("invalid sound model"));
goto error;
}
strncat(modstr, model, size);
size -= strlen(model);
sound = sound->next;
if (sound)
strncat(modstr, ",", size--);
}
if (!((*argv)[++n] = modstr))
goto no_memory;
}
if (vm->migrateFrom[0]) {
if (!((*argv)[++n] = strdup("-incoming")))
goto no_memory;
if (!((*argv)[++n] = strdup(vm->migrateFrom)))
goto no_memory;
}
2007-02-14 01:40:09 +00:00
(*argv)[++n] = NULL;
return 0;
no_memory:
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s", _("failed to allocate space for argv string"));
error:
if (vm->tapfds) {
for (i = 0; vm->tapfds[i] != -1; i++)
close(vm->tapfds[i]);
free(vm->tapfds);
vm->tapfds = NULL;
vm->ntapfds = 0;
}
2007-02-14 01:40:09 +00:00
if (argv) {
for (i = 0 ; i < n ; i++)
free((*argv)[i]);
free(*argv);
*argv = NULL;
2007-02-14 01:40:09 +00:00
}
return -1;
}
/* Save a guest's config data into a persistent file */
static int qemudSaveConfig(virConnectPtr conn,
struct qemud_driver *driver,
struct qemud_vm *vm,
struct qemud_vm_def *def) {
2007-02-14 01:40:09 +00:00
char *xml;
int fd = -1, ret = -1;
int towrite;
if (!(xml = qemudGenerateXML(conn, driver, vm, def, 0)))
2007-02-14 01:40:09 +00:00
return -1;
if ((fd = open(vm->configFile,
O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR )) < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("cannot create config file %s: %s"),
vm->configFile, strerror(errno));
2007-02-14 01:40:09 +00:00
goto cleanup;
}
towrite = strlen(xml);
if (safewrite(fd, xml, towrite) < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("cannot write config file %s: %s"),
vm->configFile, strerror(errno));
2007-02-14 01:40:09 +00:00
goto cleanup;
}
if (close(fd) < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("cannot save config file %s: %s"),
vm->configFile, strerror(errno));
2007-02-14 01:40:09 +00:00
goto cleanup;
}
ret = 0;
cleanup:
if (fd != -1)
close(fd);
free(xml);
return ret;
}
2007-10-27 01:18:38 +00:00
struct qemud_vm_device_def *
qemudParseVMDeviceDef(virConnectPtr conn,
const struct qemud_vm_def *def,
2007-10-27 01:18:38 +00:00
const char *xmlStr)
{
xmlDocPtr xml;
xmlNodePtr node;
struct qemud_vm_device_def *dev = calloc(1, sizeof(*dev));
2007-10-27 01:18:38 +00:00
if (!(xml = xmlReadDoc(BAD_CAST xmlStr, "device.xml", NULL,
XML_PARSE_NOENT | XML_PARSE_NONET |
XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_XML_ERROR, NULL);
return NULL;
}
node = xmlDocGetRootElement(xml);
if (node == NULL) {
qemudReportError(conn, NULL, NULL, VIR_ERR_XML_ERROR,
"%s", _("missing root element"));
2007-10-27 01:18:38 +00:00
goto error;
}
if (xmlStrEqual(node->name, BAD_CAST "disk")) {
dev->type = QEMUD_DEVICE_DISK;
qemudParseDiskXML(conn, &(dev->data.disk), node);
} else if (xmlStrEqual(node->name, BAD_CAST "net")) {
dev->type = QEMUD_DEVICE_NET;
qemudParseInterfaceXML(conn, &(dev->data.net), node);
} else if (xmlStrEqual(node->name, BAD_CAST "input")) {
dev->type = QEMUD_DEVICE_DISK;
qemudParseInputXML(conn, def, &(dev->data.input), node);
} else if (xmlStrEqual(node->name, BAD_CAST "sound")) {
dev->type = QEMUD_DEVICE_SOUND;
qemudParseSoundXML(conn, &(dev->data.sound), node);
2007-10-27 01:18:38 +00:00
} else {
qemudReportError(conn, NULL, NULL, VIR_ERR_XML_ERROR,
"%s", _("unknown device type"));
2007-10-27 01:18:38 +00:00
goto error;
}
xmlFreeDoc(xml);
return dev;
error:
if (xml) xmlFreeDoc(xml);
free(dev);
2007-10-27 01:18:38 +00:00
return NULL;
}
struct qemud_vm_def *
qemudParseVMDef(virConnectPtr conn,
struct qemud_driver *driver,
const char *xmlStr,
const char *displayName) {
2007-02-14 01:40:09 +00:00
xmlDocPtr xml;
struct qemud_vm_def *def = NULL;
2007-02-14 01:40:09 +00:00
if (!(xml = xmlReadDoc(BAD_CAST xmlStr, displayName ? displayName : "domain.xml", NULL,
2007-02-14 01:40:09 +00:00
XML_PARSE_NOENT | XML_PARSE_NONET |
XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_XML_ERROR, NULL);
2007-02-14 01:40:09 +00:00
return NULL;
}
def = qemudParseXML(conn, driver, xml);
2007-02-14 01:40:09 +00:00
xmlFreeDoc(xml);
return def;
}
struct qemud_vm *
qemudAssignVMDef(virConnectPtr conn,
struct qemud_driver *driver,
struct qemud_vm_def *def)
{
struct qemud_vm *vm = NULL;
if ((vm = qemudFindVMByName(driver, def->name))) {
if (!qemudIsActiveVM(vm)) {
qemudFreeVMDef(vm->def);
vm->def = def;
} else {
if (vm->newDef)
qemudFreeVMDef(vm->newDef);
vm->newDef = def;
}
/* Reset version, because the emulator path might have changed */
vm->qemuVersion = 0;
vm->qemuCmdFlags = 0;
return vm;
2007-02-14 01:40:09 +00:00
}
if (!(vm = calloc(1, sizeof(*vm)))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s", _("failed to allocate space for vm string"));
return NULL;
}
2007-02-14 01:40:09 +00:00
vm->stdin = -1;
vm->stdout = -1;
vm->stderr = -1;
vm->monitor = -1;
vm->pid = -1;
vm->id = -1;
vm->state = VIR_DOMAIN_SHUTOFF;
vm->def = def;
vm->next = driver->vms;
driver->vms = vm;
driver->ninactivevms++;
return vm;
}
void
qemudRemoveInactiveVM(struct qemud_driver *driver,
struct qemud_vm *vm)
{
struct qemud_vm *prev = NULL, *curr;
curr = driver->vms;
while (curr != vm) {
prev = curr;
curr = curr->next;
2007-02-14 01:40:09 +00:00
}
if (curr) {
if (prev)
prev->next = curr->next;
else
driver->vms = curr->next;
driver->ninactivevms--;
}
qemudFreeVM(vm);
2007-02-14 01:40:09 +00:00
}
int
qemudSaveVMDef(virConnectPtr conn,
struct qemud_driver *driver,
struct qemud_vm *vm,
struct qemud_vm_def *def) {
if (vm->configFile[0] == '\0') {
int err;
if ((err = virFileMakePath(driver->configDir))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("cannot create config directory %s: %s"),
driver->configDir, strerror(err));
return -1;
}
if (virFileBuildPath(driver->configDir, def->name, ".xml",
vm->configFile, PATH_MAX) < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("cannot construct config file path"));
return -1;
}
if (virFileBuildPath(driver->autostartDir, def->name, ".xml",
vm->autostartLink, PATH_MAX) < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("cannot construct autostart link path"));
vm->configFile[0] = '\0';
return -1;
}
}
return qemudSaveConfig(conn, driver, vm, def);
}
2007-02-14 01:40:09 +00:00
static int qemudSaveNetworkConfig(virConnectPtr conn,
struct qemud_driver *driver,
struct qemud_network *network,
struct qemud_network_def *def) {
char *xml;
int fd, ret = -1;
int towrite;
int err;
if (!(xml = qemudGenerateNetworkXML(conn, driver, network, def))) {
return -1;
}
if ((err = virFileMakePath(driver->networkConfigDir))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("cannot create config directory %s: %s"),
driver->networkConfigDir, strerror(err));
goto cleanup;
}
if ((fd = open(network->configFile,
O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR )) < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("cannot create config file %s: %s"),
network->configFile, strerror(errno));
goto cleanup;
}
towrite = strlen(xml);
if (safewrite(fd, xml, towrite) < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("cannot write config file %s: %s"),
network->configFile, strerror(errno));
goto cleanup;
}
if (close(fd) < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("cannot save config file %s: %s"),
network->configFile, strerror(errno));
goto cleanup;
}
ret = 0;
cleanup:
free(xml);
return ret;
}
void qemudFreeNetworkDef(struct qemud_network_def *def) {
struct qemud_dhcp_range_def *range = def->ranges;
while (range) {
struct qemud_dhcp_range_def *next = range->next;
free(range);
range = next;
}
free(def);
}
void qemudFreeNetwork(struct qemud_network *network) {
qemudFreeNetworkDef(network->def);
if (network->newDef)
qemudFreeNetworkDef(network->newDef);
free(network);
}
static int qemudParseBridgeXML(struct qemud_driver *driver ATTRIBUTE_UNUSED,
struct qemud_network_def *def,
xmlNodePtr node) {
xmlChar *name, *stp, *delay;
name = xmlGetProp(node, BAD_CAST "name");
if (name != NULL) {
strncpy(def->bridge, (const char *)name, IF_NAMESIZE-1);
def->bridge[IF_NAMESIZE-1] = '\0';
xmlFree(name);
name = NULL;
}
stp = xmlGetProp(node, BAD_CAST "stp");
if (stp != NULL) {
if (xmlStrEqual(stp, BAD_CAST "off")) {
def->disableSTP = 1;
}
xmlFree(stp);
stp = NULL;
}
delay = xmlGetProp(node, BAD_CAST "delay");
if (delay != NULL) {
def->forwardDelay = strtol((const char *)delay, NULL, 10);
xmlFree(delay);
delay = NULL;
}
return 1;
}
static int qemudParseDhcpRangesXML(virConnectPtr conn,
struct qemud_driver *driver ATTRIBUTE_UNUSED,
struct qemud_network_def *def,
xmlNodePtr node) {
xmlNodePtr cur;
cur = node->children;
while (cur != NULL) {
struct qemud_dhcp_range_def *range;
xmlChar *start, *end;
if (cur->type != XML_ELEMENT_NODE ||
!xmlStrEqual(cur->name, BAD_CAST "range")) {
cur = cur->next;
continue;
}
if (!(range = calloc(1, sizeof(*range)))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s", _("failed to allocate space for range string"));
return 0;
}
start = xmlGetProp(cur, BAD_CAST "start");
end = xmlGetProp(cur, BAD_CAST "end");
if (start && start[0] && end && end[0]) {
strncpy(range->start, (const char *)start, BR_INET_ADDR_MAXLEN-1);
range->start[BR_INET_ADDR_MAXLEN-1] = '\0';
strncpy(range->end, (const char *)end, BR_INET_ADDR_MAXLEN-1);
range->end[BR_INET_ADDR_MAXLEN-1] = '\0';
range->next = def->ranges;
def->ranges = range;
def->nranges++;
} else {
free(range);
}
xmlFree(start);
xmlFree(end);
cur = cur->next;
}
return 1;
}
static int qemudParseInetXML(virConnectPtr conn,
struct qemud_driver *driver ATTRIBUTE_UNUSED,
struct qemud_network_def *def,
xmlNodePtr node) {
xmlChar *address, *netmask;
xmlNodePtr cur;
address = xmlGetProp(node, BAD_CAST "address");
if (address != NULL) {
strncpy(def->ipAddress, (const char *)address, BR_INET_ADDR_MAXLEN-1);
def->ipAddress[BR_INET_ADDR_MAXLEN-1] = '\0';
xmlFree(address);
address = NULL;
}
netmask = xmlGetProp(node, BAD_CAST "netmask");
if (netmask != NULL) {
strncpy(def->netmask, (const char *)netmask, BR_INET_ADDR_MAXLEN-1);
def->netmask[BR_INET_ADDR_MAXLEN-1] = '\0';
xmlFree(netmask);
netmask = NULL;
}
if (def->ipAddress[0] && def->netmask[0]) {
struct in_addr inaddress, innetmask;
char *netaddr;
inet_aton((const char*)def->ipAddress, &inaddress);
inet_aton((const char*)def->netmask, &innetmask);
inaddress.s_addr &= innetmask.s_addr;
netaddr = inet_ntoa(inaddress);
snprintf(def->network,sizeof(def->network)-1,
"%s/%s", netaddr, (const char *)def->netmask);
}
cur = node->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE &&
xmlStrEqual(cur->name, BAD_CAST "dhcp") &&
!qemudParseDhcpRangesXML(conn, driver, def, cur))
return 0;
cur = cur->next;
}
return 1;
}
static struct qemud_network_def *qemudParseNetworkXML(virConnectPtr conn,
struct qemud_driver *driver,
xmlDocPtr xml) {
xmlNodePtr root = NULL;
xmlXPathContextPtr ctxt = NULL;
2007-03-13 22:43:22 +00:00
xmlXPathObjectPtr obj = NULL, tmp = NULL;
struct qemud_network_def *def;
if (!(def = calloc(1, sizeof(*def)))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s", _("failed to allocate space for network_def string"));
return NULL;
}
/* Prepare parser / xpath context */
root = xmlDocGetRootElement(xml);
if ((root == NULL) || (!xmlStrEqual(root->name, BAD_CAST "network"))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("incorrect root element"));
goto error;
}
ctxt = xmlXPathNewContext(xml);
if (ctxt == NULL) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s", _("failed to allocate space for xmlXPathContext string"));
goto error;
}
/* Extract network name */
obj = xmlXPathEval(BAD_CAST "string(/network/name[1])", ctxt);
if ((obj == NULL) || (obj->type != XPATH_STRING) ||
(obj->stringval == NULL) || (obj->stringval[0] == 0)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_NAME, NULL);
goto error;
}
if (strlen((const char *)obj->stringval) >= (QEMUD_MAX_NAME_LEN-1)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("network name length too long"));
goto error;
}
strcpy(def->name, (const char *)obj->stringval);
xmlXPathFreeObject(obj);
/* Extract network uuid */
obj = xmlXPathEval(BAD_CAST "string(/network/uuid[1])", ctxt);
if ((obj == NULL) || (obj->type != XPATH_STRING) ||
(obj->stringval == NULL) || (obj->stringval[0] == 0)) {
int err;
2007-06-26 22:19:38 +00:00
if ((err = virUUIDGenerate(def->uuid))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("Failed to generate UUID: %s"), strerror(err));
goto error;
}
2007-06-26 22:19:38 +00:00
} else if (virUUIDParse((const char *)obj->stringval, def->uuid) < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("malformed uuid element"));
goto error;
}
xmlXPathFreeObject(obj);
/* Parse bridge information */
obj = xmlXPathEval(BAD_CAST "/network/bridge[1]", ctxt);
if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
(obj->nodesetval != NULL) && (obj->nodesetval->nodeNr > 0)) {
if (!qemudParseBridgeXML(driver, def, obj->nodesetval->nodeTab[0])) {
goto error;
}
}
xmlXPathFreeObject(obj);
/* Parse IP information */
obj = xmlXPathEval(BAD_CAST "/network/ip[1]", ctxt);
if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
(obj->nodesetval != NULL) && (obj->nodesetval->nodeNr > 0)) {
if (!qemudParseInetXML(conn, driver, def, obj->nodesetval->nodeTab[0])) {
goto error;
}
}
xmlXPathFreeObject(obj);
/* IPv4 forwarding setup */
2007-03-13 22:43:22 +00:00
obj = xmlXPathEval(BAD_CAST "count(/network/forward) > 0", ctxt);
if ((obj != NULL) && (obj->type == XPATH_BOOLEAN) &&
obj->boolval) {
if (!def->ipAddress[0] ||
!def->netmask[0]) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("Forwarding requested, but no IPv4 address/netmask provided"));
goto error;
}
2007-03-13 22:43:22 +00:00
def->forward = 1;
tmp = xmlXPathEval(BAD_CAST "string(/network/forward[1]/@mode)", ctxt);
if ((tmp != NULL) && (tmp->type == XPATH_STRING) &&
(tmp->stringval != NULL) && (xmlStrEqual(tmp->stringval, BAD_CAST "route"))) {
def->forwardMode = QEMUD_NET_FORWARD_ROUTE;
} else {
def->forwardMode = QEMUD_NET_FORWARD_NAT;
}
xmlXPathFreeObject(tmp);
tmp = NULL;
2007-03-13 22:43:22 +00:00
tmp = xmlXPathEval(BAD_CAST "string(/network/forward[1]/@dev)", ctxt);
if ((tmp != NULL) && (tmp->type == XPATH_STRING) &&
(tmp->stringval != NULL) && (tmp->stringval[0] != 0)) {
int len;
if ((len = xmlStrlen(tmp->stringval)) >= (BR_IFNAME_MAXLEN-1)) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("forward device name '%s' is too long"),
2007-03-13 22:43:22 +00:00
(char*)tmp->stringval);
goto error;
}
strcpy(def->forwardDev, (char*)tmp->stringval);
} else {
def->forwardDev[0] = '\0';
}
xmlXPathFreeObject(tmp);
tmp = NULL;
} else {
def->forward = 0;
}
xmlXPathFreeObject(obj);
xmlXPathFreeContext(ctxt);
return def;
error:
/* XXX free all the stuff in the qemud_network struct, or leave it upto
the caller ? */
xmlXPathFreeObject(obj);
xmlXPathFreeObject(tmp);
xmlXPathFreeContext(ctxt);
qemudFreeNetworkDef(def);
return NULL;
}
struct qemud_network_def *
qemudParseNetworkDef(virConnectPtr conn,
struct qemud_driver *driver,
const char *xmlStr,
const char *displayName) {
xmlDocPtr xml;
struct qemud_network_def *def;
if (!(xml = xmlReadDoc(BAD_CAST xmlStr, displayName ? displayName : "network.xml", NULL,
XML_PARSE_NOENT | XML_PARSE_NONET |
XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_XML_ERROR, NULL);
return NULL;
}
def = qemudParseNetworkXML(conn, driver, xml);
xmlFreeDoc(xml);
return def;
}
struct qemud_network *
qemudAssignNetworkDef(virConnectPtr conn,
struct qemud_driver *driver,
struct qemud_network_def *def) {
struct qemud_network *network;
if ((network = qemudFindNetworkByName(driver, def->name))) {
if (!qemudIsActiveNetwork(network)) {
qemudFreeNetworkDef(network->def);
network->def = def;
} else {
if (network->newDef)
qemudFreeNetworkDef(network->newDef);
network->newDef = def;
}
return network;
}
if (!(network = calloc(1, sizeof(*network)))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s", _("failed to allocate space for network string"));
return NULL;
}
network->def = def;
network->next = driver->networks;
driver->networks = network;
driver->ninactivenetworks++;
return network;
}
void
qemudRemoveInactiveNetwork(struct qemud_driver *driver,
struct qemud_network *network)
{
struct qemud_network *prev = NULL, *curr;
curr = driver->networks;
while (curr != network) {
prev = curr;
curr = curr->next;
}
if (curr) {
if (prev)
prev->next = curr->next;
else
driver->networks = curr->next;
driver->ninactivenetworks--;
}
qemudFreeNetwork(network);
}
int
qemudSaveNetworkDef(virConnectPtr conn,
struct qemud_driver *driver,
struct qemud_network *network,
struct qemud_network_def *def) {
if (network->configFile[0] == '\0') {
int err;
if ((err = virFileMakePath(driver->networkConfigDir))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("cannot create config directory %s: %s"),
driver->networkConfigDir, strerror(err));
return -1;
}
if (virFileBuildPath(driver->networkConfigDir, def->name, ".xml",
network->configFile, PATH_MAX) < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("cannot construct config file path"));
return -1;
}
if (virFileBuildPath(driver->networkAutostartDir, def->name, ".xml",
network->autostartLink, PATH_MAX) < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("cannot construct autostart link path"));
network->configFile[0] = '\0';
return -1;
}
}
return qemudSaveNetworkConfig(conn, driver, network, def);
}
static struct qemud_vm *
qemudLoadConfig(struct qemud_driver *driver,
const char *file,
const char *path,
const char *xml,
const char *autostartLink) {
struct qemud_vm_def *def;
struct qemud_vm *vm;
if (!(def = qemudParseVMDef(NULL, driver, xml, file))) {
virErrorPtr err = virGetLastError();
Mark all qemudLog diagnostics for translation. * po/POTFILES.in: Add names of many new files. * Makefile.maint (err_func_re): Add qemudLog. Mark diagnostics with _(...). Split some long lines. * qemud/qemud.c (remoteCheckCertFile, remoteInitializeGnuTLS): (qemudDispatchSignalEvent, qemudSetCloseExec, qemudSetNonBlock): (qemudWritePidFile, qemudListenUnix, remoteMakeSockets): (remoteListenTCP, qemudInitPaths, qemudInitialize): (qemudNetworkInit, remoteInitializeTLSSession, remoteCheckDN): (remoteCheckCertificate, remoteCheckAccess, qemudDispatchServer): (qemudClientReadBuf, qemudDispatchClientRead): (qemudClientWriteBuf, qemudDispatchClientWrite, qemudOneLoop): (remoteConfigGetStringList, checkType, GET_CONF_STR): (remoteConfigGetAuth, remoteReadConfigFile, main): * qemud/remote.c (remoteDispatchAuthSaslInit, remoteSASLCheckSSF): (remoteSASLCheckAccess, remoteDispatchAuthSaslStart): (remoteDispatchAuthSaslStep, remoteDispatchAuthSaslInit): (remoteDispatchAuthSaslStart, remoteDispatchAuthSaslStep): (qemudGetSocketIdentity, remoteDispatchAuthPolkit): * src/iptables.c (notifyRulesUpdated, MAX_FILE_LEN, iptRulesSave): (iptRulesReload): * src/qemu_conf.c (qemudExtractVersionInfo, qemudLoadConfig): (qemudLoadNetworkConfig, qemudScanConfigDir): * src/qemu_driver.c (qemudSetCloseExec, qemudSetNonBlock): (qemudAutostartConfigs, qemudStartup, qemudReload): (qemudWaitForMonitor, qemudStartVMDaemon, qemudVMData): (qemudShutdownVMDaemon, qemudStartNetworkDaemon): (qemudShutdownNetworkDaemon, qemudMonitorCommand): (qemudDomainUndefine, qemudNetworkUndefine): * src/uuid.c (virUUIDGenerate): * src/xm_internal.c (xenXMAttachInterface):
2008-02-07 16:50:17 +00:00
qemudLog(QEMUD_WARN, _("Error parsing QEMU guest config '%s' : %s"),
path, (err ? err->message :
_("BUG: unknown error - please report it\n")));
return NULL;
}
if (!virFileMatchesNameSuffix(file, def->name, ".xml")) {
Mark all qemudLog diagnostics for translation. * po/POTFILES.in: Add names of many new files. * Makefile.maint (err_func_re): Add qemudLog. Mark diagnostics with _(...). Split some long lines. * qemud/qemud.c (remoteCheckCertFile, remoteInitializeGnuTLS): (qemudDispatchSignalEvent, qemudSetCloseExec, qemudSetNonBlock): (qemudWritePidFile, qemudListenUnix, remoteMakeSockets): (remoteListenTCP, qemudInitPaths, qemudInitialize): (qemudNetworkInit, remoteInitializeTLSSession, remoteCheckDN): (remoteCheckCertificate, remoteCheckAccess, qemudDispatchServer): (qemudClientReadBuf, qemudDispatchClientRead): (qemudClientWriteBuf, qemudDispatchClientWrite, qemudOneLoop): (remoteConfigGetStringList, checkType, GET_CONF_STR): (remoteConfigGetAuth, remoteReadConfigFile, main): * qemud/remote.c (remoteDispatchAuthSaslInit, remoteSASLCheckSSF): (remoteSASLCheckAccess, remoteDispatchAuthSaslStart): (remoteDispatchAuthSaslStep, remoteDispatchAuthSaslInit): (remoteDispatchAuthSaslStart, remoteDispatchAuthSaslStep): (qemudGetSocketIdentity, remoteDispatchAuthPolkit): * src/iptables.c (notifyRulesUpdated, MAX_FILE_LEN, iptRulesSave): (iptRulesReload): * src/qemu_conf.c (qemudExtractVersionInfo, qemudLoadConfig): (qemudLoadNetworkConfig, qemudScanConfigDir): * src/qemu_driver.c (qemudSetCloseExec, qemudSetNonBlock): (qemudAutostartConfigs, qemudStartup, qemudReload): (qemudWaitForMonitor, qemudStartVMDaemon, qemudVMData): (qemudShutdownVMDaemon, qemudStartNetworkDaemon): (qemudShutdownNetworkDaemon, qemudMonitorCommand): (qemudDomainUndefine, qemudNetworkUndefine): * src/uuid.c (virUUIDGenerate): * src/xm_internal.c (xenXMAttachInterface):
2008-02-07 16:50:17 +00:00
qemudLog(QEMUD_WARN,
_("QEMU guest config filename '%s'"
" does not match guest name '%s'"),
path, def->name);
qemudFreeVMDef(def);
return NULL;
}
if (!(vm = qemudAssignVMDef(NULL, driver, def))) {
Mark all qemudLog diagnostics for translation. * po/POTFILES.in: Add names of many new files. * Makefile.maint (err_func_re): Add qemudLog. Mark diagnostics with _(...). Split some long lines. * qemud/qemud.c (remoteCheckCertFile, remoteInitializeGnuTLS): (qemudDispatchSignalEvent, qemudSetCloseExec, qemudSetNonBlock): (qemudWritePidFile, qemudListenUnix, remoteMakeSockets): (remoteListenTCP, qemudInitPaths, qemudInitialize): (qemudNetworkInit, remoteInitializeTLSSession, remoteCheckDN): (remoteCheckCertificate, remoteCheckAccess, qemudDispatchServer): (qemudClientReadBuf, qemudDispatchClientRead): (qemudClientWriteBuf, qemudDispatchClientWrite, qemudOneLoop): (remoteConfigGetStringList, checkType, GET_CONF_STR): (remoteConfigGetAuth, remoteReadConfigFile, main): * qemud/remote.c (remoteDispatchAuthSaslInit, remoteSASLCheckSSF): (remoteSASLCheckAccess, remoteDispatchAuthSaslStart): (remoteDispatchAuthSaslStep, remoteDispatchAuthSaslInit): (remoteDispatchAuthSaslStart, remoteDispatchAuthSaslStep): (qemudGetSocketIdentity, remoteDispatchAuthPolkit): * src/iptables.c (notifyRulesUpdated, MAX_FILE_LEN, iptRulesSave): (iptRulesReload): * src/qemu_conf.c (qemudExtractVersionInfo, qemudLoadConfig): (qemudLoadNetworkConfig, qemudScanConfigDir): * src/qemu_driver.c (qemudSetCloseExec, qemudSetNonBlock): (qemudAutostartConfigs, qemudStartup, qemudReload): (qemudWaitForMonitor, qemudStartVMDaemon, qemudVMData): (qemudShutdownVMDaemon, qemudStartNetworkDaemon): (qemudShutdownNetworkDaemon, qemudMonitorCommand): (qemudDomainUndefine, qemudNetworkUndefine): * src/uuid.c (virUUIDGenerate): * src/xm_internal.c (xenXMAttachInterface):
2008-02-07 16:50:17 +00:00
qemudLog(QEMUD_WARN,
_("Failed to load QEMU guest config '%s': out of memory"),
path);
qemudFreeVMDef(def);
return NULL;
}
strncpy(vm->configFile, path, PATH_MAX);
vm->configFile[PATH_MAX-1] = '\0';
strncpy(vm->autostartLink, autostartLink, PATH_MAX);
vm->autostartLink[PATH_MAX-1] = '\0';
vm->autostart = virFileLinkPointsTo(vm->autostartLink, vm->configFile);
return vm;
}
static struct qemud_network *
qemudLoadNetworkConfig(struct qemud_driver *driver,
const char *file,
const char *path,
const char *xml,
const char *autostartLink) {
struct qemud_network_def *def;
struct qemud_network *network;
if (!(def = qemudParseNetworkDef(NULL, driver, xml, file))) {
virErrorPtr err = virGetLastError();
Mark all qemudLog diagnostics for translation. * po/POTFILES.in: Add names of many new files. * Makefile.maint (err_func_re): Add qemudLog. Mark diagnostics with _(...). Split some long lines. * qemud/qemud.c (remoteCheckCertFile, remoteInitializeGnuTLS): (qemudDispatchSignalEvent, qemudSetCloseExec, qemudSetNonBlock): (qemudWritePidFile, qemudListenUnix, remoteMakeSockets): (remoteListenTCP, qemudInitPaths, qemudInitialize): (qemudNetworkInit, remoteInitializeTLSSession, remoteCheckDN): (remoteCheckCertificate, remoteCheckAccess, qemudDispatchServer): (qemudClientReadBuf, qemudDispatchClientRead): (qemudClientWriteBuf, qemudDispatchClientWrite, qemudOneLoop): (remoteConfigGetStringList, checkType, GET_CONF_STR): (remoteConfigGetAuth, remoteReadConfigFile, main): * qemud/remote.c (remoteDispatchAuthSaslInit, remoteSASLCheckSSF): (remoteSASLCheckAccess, remoteDispatchAuthSaslStart): (remoteDispatchAuthSaslStep, remoteDispatchAuthSaslInit): (remoteDispatchAuthSaslStart, remoteDispatchAuthSaslStep): (qemudGetSocketIdentity, remoteDispatchAuthPolkit): * src/iptables.c (notifyRulesUpdated, MAX_FILE_LEN, iptRulesSave): (iptRulesReload): * src/qemu_conf.c (qemudExtractVersionInfo, qemudLoadConfig): (qemudLoadNetworkConfig, qemudScanConfigDir): * src/qemu_driver.c (qemudSetCloseExec, qemudSetNonBlock): (qemudAutostartConfigs, qemudStartup, qemudReload): (qemudWaitForMonitor, qemudStartVMDaemon, qemudVMData): (qemudShutdownVMDaemon, qemudStartNetworkDaemon): (qemudShutdownNetworkDaemon, qemudMonitorCommand): (qemudDomainUndefine, qemudNetworkUndefine): * src/uuid.c (virUUIDGenerate): * src/xm_internal.c (xenXMAttachInterface):
2008-02-07 16:50:17 +00:00
qemudLog(QEMUD_WARN, _("Error parsing network config '%s' : %s"),
path, err->message);
return NULL;
}
if (!virFileMatchesNameSuffix(file, def->name, ".xml")) {
Mark all qemudLog diagnostics for translation. * po/POTFILES.in: Add names of many new files. * Makefile.maint (err_func_re): Add qemudLog. Mark diagnostics with _(...). Split some long lines. * qemud/qemud.c (remoteCheckCertFile, remoteInitializeGnuTLS): (qemudDispatchSignalEvent, qemudSetCloseExec, qemudSetNonBlock): (qemudWritePidFile, qemudListenUnix, remoteMakeSockets): (remoteListenTCP, qemudInitPaths, qemudInitialize): (qemudNetworkInit, remoteInitializeTLSSession, remoteCheckDN): (remoteCheckCertificate, remoteCheckAccess, qemudDispatchServer): (qemudClientReadBuf, qemudDispatchClientRead): (qemudClientWriteBuf, qemudDispatchClientWrite, qemudOneLoop): (remoteConfigGetStringList, checkType, GET_CONF_STR): (remoteConfigGetAuth, remoteReadConfigFile, main): * qemud/remote.c (remoteDispatchAuthSaslInit, remoteSASLCheckSSF): (remoteSASLCheckAccess, remoteDispatchAuthSaslStart): (remoteDispatchAuthSaslStep, remoteDispatchAuthSaslInit): (remoteDispatchAuthSaslStart, remoteDispatchAuthSaslStep): (qemudGetSocketIdentity, remoteDispatchAuthPolkit): * src/iptables.c (notifyRulesUpdated, MAX_FILE_LEN, iptRulesSave): (iptRulesReload): * src/qemu_conf.c (qemudExtractVersionInfo, qemudLoadConfig): (qemudLoadNetworkConfig, qemudScanConfigDir): * src/qemu_driver.c (qemudSetCloseExec, qemudSetNonBlock): (qemudAutostartConfigs, qemudStartup, qemudReload): (qemudWaitForMonitor, qemudStartVMDaemon, qemudVMData): (qemudShutdownVMDaemon, qemudStartNetworkDaemon): (qemudShutdownNetworkDaemon, qemudMonitorCommand): (qemudDomainUndefine, qemudNetworkUndefine): * src/uuid.c (virUUIDGenerate): * src/xm_internal.c (xenXMAttachInterface):
2008-02-07 16:50:17 +00:00
qemudLog(QEMUD_WARN,
_("Network config filename '%s'"
" does not match network name '%s'"),
path, def->name);
qemudFreeNetworkDef(def);
return NULL;
}
if (!(network = qemudAssignNetworkDef(NULL, driver, def))) {
Mark all qemudLog diagnostics for translation. * po/POTFILES.in: Add names of many new files. * Makefile.maint (err_func_re): Add qemudLog. Mark diagnostics with _(...). Split some long lines. * qemud/qemud.c (remoteCheckCertFile, remoteInitializeGnuTLS): (qemudDispatchSignalEvent, qemudSetCloseExec, qemudSetNonBlock): (qemudWritePidFile, qemudListenUnix, remoteMakeSockets): (remoteListenTCP, qemudInitPaths, qemudInitialize): (qemudNetworkInit, remoteInitializeTLSSession, remoteCheckDN): (remoteCheckCertificate, remoteCheckAccess, qemudDispatchServer): (qemudClientReadBuf, qemudDispatchClientRead): (qemudClientWriteBuf, qemudDispatchClientWrite, qemudOneLoop): (remoteConfigGetStringList, checkType, GET_CONF_STR): (remoteConfigGetAuth, remoteReadConfigFile, main): * qemud/remote.c (remoteDispatchAuthSaslInit, remoteSASLCheckSSF): (remoteSASLCheckAccess, remoteDispatchAuthSaslStart): (remoteDispatchAuthSaslStep, remoteDispatchAuthSaslInit): (remoteDispatchAuthSaslStart, remoteDispatchAuthSaslStep): (qemudGetSocketIdentity, remoteDispatchAuthPolkit): * src/iptables.c (notifyRulesUpdated, MAX_FILE_LEN, iptRulesSave): (iptRulesReload): * src/qemu_conf.c (qemudExtractVersionInfo, qemudLoadConfig): (qemudLoadNetworkConfig, qemudScanConfigDir): * src/qemu_driver.c (qemudSetCloseExec, qemudSetNonBlock): (qemudAutostartConfigs, qemudStartup, qemudReload): (qemudWaitForMonitor, qemudStartVMDaemon, qemudVMData): (qemudShutdownVMDaemon, qemudStartNetworkDaemon): (qemudShutdownNetworkDaemon, qemudMonitorCommand): (qemudDomainUndefine, qemudNetworkUndefine): * src/uuid.c (virUUIDGenerate): * src/xm_internal.c (xenXMAttachInterface):
2008-02-07 16:50:17 +00:00
qemudLog(QEMUD_WARN,
_("Failed to load network config '%s': out of memory"), path);
qemudFreeNetworkDef(def);
return NULL;
}
strncpy(network->configFile, path, PATH_MAX);
network->configFile[PATH_MAX-1] = '\0';
strncpy(network->autostartLink, autostartLink, PATH_MAX);
network->autostartLink[PATH_MAX-1] = '\0';
network->autostart = virFileLinkPointsTo(network->autostartLink, network->configFile);
return network;
}
2007-02-14 01:40:09 +00:00
static
int qemudScanConfigDir(struct qemud_driver *driver,
const char *configDir,
const char *autostartDir,
int isGuest) {
2007-02-14 01:40:09 +00:00
DIR *dir;
struct dirent *entry;
if (!(dir = opendir(configDir))) {
2007-02-14 01:40:09 +00:00
if (errno == ENOENT)
return 0;
Mark all qemudLog diagnostics for translation. * po/POTFILES.in: Add names of many new files. * Makefile.maint (err_func_re): Add qemudLog. Mark diagnostics with _(...). Split some long lines. * qemud/qemud.c (remoteCheckCertFile, remoteInitializeGnuTLS): (qemudDispatchSignalEvent, qemudSetCloseExec, qemudSetNonBlock): (qemudWritePidFile, qemudListenUnix, remoteMakeSockets): (remoteListenTCP, qemudInitPaths, qemudInitialize): (qemudNetworkInit, remoteInitializeTLSSession, remoteCheckDN): (remoteCheckCertificate, remoteCheckAccess, qemudDispatchServer): (qemudClientReadBuf, qemudDispatchClientRead): (qemudClientWriteBuf, qemudDispatchClientWrite, qemudOneLoop): (remoteConfigGetStringList, checkType, GET_CONF_STR): (remoteConfigGetAuth, remoteReadConfigFile, main): * qemud/remote.c (remoteDispatchAuthSaslInit, remoteSASLCheckSSF): (remoteSASLCheckAccess, remoteDispatchAuthSaslStart): (remoteDispatchAuthSaslStep, remoteDispatchAuthSaslInit): (remoteDispatchAuthSaslStart, remoteDispatchAuthSaslStep): (qemudGetSocketIdentity, remoteDispatchAuthPolkit): * src/iptables.c (notifyRulesUpdated, MAX_FILE_LEN, iptRulesSave): (iptRulesReload): * src/qemu_conf.c (qemudExtractVersionInfo, qemudLoadConfig): (qemudLoadNetworkConfig, qemudScanConfigDir): * src/qemu_driver.c (qemudSetCloseExec, qemudSetNonBlock): (qemudAutostartConfigs, qemudStartup, qemudReload): (qemudWaitForMonitor, qemudStartVMDaemon, qemudVMData): (qemudShutdownVMDaemon, qemudStartNetworkDaemon): (qemudShutdownNetworkDaemon, qemudMonitorCommand): (qemudDomainUndefine, qemudNetworkUndefine): * src/uuid.c (virUUIDGenerate): * src/xm_internal.c (xenXMAttachInterface):
2008-02-07 16:50:17 +00:00
qemudLog(QEMUD_ERR, _("Failed to open dir '%s': %s"),
configDir, strerror(errno));
2007-02-14 01:40:09 +00:00
return -1;
}
while ((entry = readdir(dir))) {
char *xml;
char path[PATH_MAX];
char autostartLink[PATH_MAX];
2007-02-14 01:40:09 +00:00
if (entry->d_name[0] == '.')
continue;
if (!virFileHasSuffix(entry->d_name, ".xml"))
continue;
if (virFileBuildPath(configDir, entry->d_name, NULL, path, PATH_MAX) < 0) {
Mark all qemudLog diagnostics for translation. * po/POTFILES.in: Add names of many new files. * Makefile.maint (err_func_re): Add qemudLog. Mark diagnostics with _(...). Split some long lines. * qemud/qemud.c (remoteCheckCertFile, remoteInitializeGnuTLS): (qemudDispatchSignalEvent, qemudSetCloseExec, qemudSetNonBlock): (qemudWritePidFile, qemudListenUnix, remoteMakeSockets): (remoteListenTCP, qemudInitPaths, qemudInitialize): (qemudNetworkInit, remoteInitializeTLSSession, remoteCheckDN): (remoteCheckCertificate, remoteCheckAccess, qemudDispatchServer): (qemudClientReadBuf, qemudDispatchClientRead): (qemudClientWriteBuf, qemudDispatchClientWrite, qemudOneLoop): (remoteConfigGetStringList, checkType, GET_CONF_STR): (remoteConfigGetAuth, remoteReadConfigFile, main): * qemud/remote.c (remoteDispatchAuthSaslInit, remoteSASLCheckSSF): (remoteSASLCheckAccess, remoteDispatchAuthSaslStart): (remoteDispatchAuthSaslStep, remoteDispatchAuthSaslInit): (remoteDispatchAuthSaslStart, remoteDispatchAuthSaslStep): (qemudGetSocketIdentity, remoteDispatchAuthPolkit): * src/iptables.c (notifyRulesUpdated, MAX_FILE_LEN, iptRulesSave): (iptRulesReload): * src/qemu_conf.c (qemudExtractVersionInfo, qemudLoadConfig): (qemudLoadNetworkConfig, qemudScanConfigDir): * src/qemu_driver.c (qemudSetCloseExec, qemudSetNonBlock): (qemudAutostartConfigs, qemudStartup, qemudReload): (qemudWaitForMonitor, qemudStartVMDaemon, qemudVMData): (qemudShutdownVMDaemon, qemudStartNetworkDaemon): (qemudShutdownNetworkDaemon, qemudMonitorCommand): (qemudDomainUndefine, qemudNetworkUndefine): * src/uuid.c (virUUIDGenerate): * src/xm_internal.c (xenXMAttachInterface):
2008-02-07 16:50:17 +00:00
qemudLog(QEMUD_WARN, _("Config filename '%s/%s' is too long"),
configDir, entry->d_name);
continue;
}
Mark all qemudLog diagnostics for translation. * po/POTFILES.in: Add names of many new files. * Makefile.maint (err_func_re): Add qemudLog. Mark diagnostics with _(...). Split some long lines. * qemud/qemud.c (remoteCheckCertFile, remoteInitializeGnuTLS): (qemudDispatchSignalEvent, qemudSetCloseExec, qemudSetNonBlock): (qemudWritePidFile, qemudListenUnix, remoteMakeSockets): (remoteListenTCP, qemudInitPaths, qemudInitialize): (qemudNetworkInit, remoteInitializeTLSSession, remoteCheckDN): (remoteCheckCertificate, remoteCheckAccess, qemudDispatchServer): (qemudClientReadBuf, qemudDispatchClientRead): (qemudClientWriteBuf, qemudDispatchClientWrite, qemudOneLoop): (remoteConfigGetStringList, checkType, GET_CONF_STR): (remoteConfigGetAuth, remoteReadConfigFile, main): * qemud/remote.c (remoteDispatchAuthSaslInit, remoteSASLCheckSSF): (remoteSASLCheckAccess, remoteDispatchAuthSaslStart): (remoteDispatchAuthSaslStep, remoteDispatchAuthSaslInit): (remoteDispatchAuthSaslStart, remoteDispatchAuthSaslStep): (qemudGetSocketIdentity, remoteDispatchAuthPolkit): * src/iptables.c (notifyRulesUpdated, MAX_FILE_LEN, iptRulesSave): (iptRulesReload): * src/qemu_conf.c (qemudExtractVersionInfo, qemudLoadConfig): (qemudLoadNetworkConfig, qemudScanConfigDir): * src/qemu_driver.c (qemudSetCloseExec, qemudSetNonBlock): (qemudAutostartConfigs, qemudStartup, qemudReload): (qemudWaitForMonitor, qemudStartVMDaemon, qemudVMData): (qemudShutdownVMDaemon, qemudStartNetworkDaemon): (qemudShutdownNetworkDaemon, qemudMonitorCommand): (qemudDomainUndefine, qemudNetworkUndefine): * src/uuid.c (virUUIDGenerate): * src/xm_internal.c (xenXMAttachInterface):
2008-02-07 16:50:17 +00:00
if (virFileBuildPath(autostartDir, entry->d_name, NULL,
autostartLink, PATH_MAX) < 0) {
qemudLog(QEMUD_WARN, _("Autostart link path '%s/%s' is too long"),
autostartDir, entry->d_name);
continue;
}
if (virFileReadAll(path, QEMUD_MAX_XML_LEN, &xml) < 0)
2007-02-14 01:40:09 +00:00
continue;
if (isGuest)
qemudLoadConfig(driver, entry->d_name, path, xml, autostartLink);
else
qemudLoadNetworkConfig(driver, entry->d_name, path, xml, autostartLink);
free(xml);
2007-02-14 01:40:09 +00:00
}
closedir(dir);
2007-02-14 01:40:09 +00:00
return 0;
}
/* Scan for all guest and network config files */
int qemudScanConfigs(struct qemud_driver *driver) {
if (qemudScanConfigDir(driver, driver->configDir, driver->autostartDir, 1) < 0)
return -1;
if (qemudScanConfigDir(driver, driver->networkConfigDir, driver->networkAutostartDir, 0) < 0)
return -1;
return 0;
}
2007-02-14 01:40:09 +00:00
static int qemudGenerateXMLChar(virBufferPtr buf,
const struct qemud_vm_chr_def *dev,
const char *type)
{
const char *const types[] = {
"null",
"vc",
"pty",
"dev",
"file",
"pipe",
"stdio",
"udp",
"tcp",
"unix"
};
verify_true(ARRAY_CARDINALITY(types) == QEMUD_CHR_SRC_TYPE_LAST);
/* Compat with legacy <console tty='/dev/pts/5'/> syntax */
if (STREQ(type, "console") &&
dev->srcType == QEMUD_CHR_SRC_TYPE_PTY &&
dev->srcData.file.path[0] != '\0') {
virBufferVSprintf(buf, " <%s type='%s' tty='%s'>\n",
type, types[dev->srcType],
dev->srcData.file.path);
} else {
virBufferVSprintf(buf, " <%s type='%s'>\n",
type, types[dev->srcType]);
}
switch (dev->srcType) {
case QEMUD_CHR_SRC_TYPE_NULL:
case QEMUD_CHR_SRC_TYPE_VC:
case QEMUD_CHR_SRC_TYPE_STDIO:
/* nada */
break;
case QEMUD_CHR_SRC_TYPE_PTY:
case QEMUD_CHR_SRC_TYPE_DEV:
case QEMUD_CHR_SRC_TYPE_FILE:
case QEMUD_CHR_SRC_TYPE_PIPE:
if (dev->srcType != QEMUD_CHR_SRC_TYPE_PTY ||
dev->srcData.file.path[0]) {
virBufferVSprintf(buf, " <source path='%s'/>\n",
dev->srcData.file.path);
}
break;
case QEMUD_CHR_SRC_TYPE_UDP:
if (dev->srcData.udp.bindService[0] != '\0' &&
dev->srcData.udp.bindHost[0] != '\0') {
virBufferVSprintf(buf, " <source mode='bind' host='%s' service='%s'/>\n",
dev->srcData.udp.bindHost,
dev->srcData.udp.bindService);
} else if (dev->srcData.udp.bindHost[0] !='\0') {
virBufferVSprintf(buf, " <source mode='bind' host='%s'/>\n",
dev->srcData.udp.bindHost);
} else if (dev->srcData.udp.bindService[0] != '\0') {
virBufferVSprintf(buf, " <source mode='bind' service='%s'/>\n",
dev->srcData.udp.bindService);
}
if (dev->srcData.udp.connectService[0] != '\0' &&
dev->srcData.udp.connectHost[0] != '\0') {
virBufferVSprintf(buf, " <source mode='connect' host='%s' service='%s'/>\n",
dev->srcData.udp.connectHost,
dev->srcData.udp.connectService);
} else if (dev->srcData.udp.connectHost[0] != '\0') {
virBufferVSprintf(buf, " <source mode='connect' host='%s'/>\n",
dev->srcData.udp.connectHost);
} else if (dev->srcData.udp.connectService[0] != '\0') {
virBufferVSprintf(buf, " <source mode='connect' service='%s'/>\n",
dev->srcData.udp.connectService);
}
break;
case QEMUD_CHR_SRC_TYPE_TCP:
virBufferVSprintf(buf, " <source mode='%s' host='%s' service='%s'/>\n",
dev->srcData.tcp.listen ? "bind" : "connect",
dev->srcData.tcp.host,
dev->srcData.tcp.service);
virBufferVSprintf(buf, " <protocol type='%s'/>\n",
dev->srcData.tcp.protocol == QEMUD_CHR_SRC_TCP_PROTOCOL_TELNET
? "telnet" : "raw");
break;
case QEMUD_CHR_SRC_TYPE_UNIX:
virBufferVSprintf(buf, " <source mode='%s' path='%s'/>\n",
dev->srcData.nix.listen ? "bind" : "connect",
dev->srcData.nix.path);
break;
}
virBufferVSprintf(buf, " <target port='%d'/>\n",
dev->dstPort);
virBufferVSprintf(buf, " </%s>\n",
type);
return 0;
}
2007-02-14 01:40:09 +00:00
/* Generate an XML document describing the guest's configuration */
char *qemudGenerateXML(virConnectPtr conn,
struct qemud_driver *driver ATTRIBUTE_UNUSED,
struct qemud_vm *vm,
struct qemud_vm_def *def,
int live) {
virBuffer buf = VIR_BUFFER_INITIALIZER;
2007-02-14 01:40:09 +00:00
unsigned char *uuid;
2007-08-09 20:19:12 +00:00
char uuidstr[VIR_UUID_STRING_BUFLEN];
const struct qemud_vm_disk_def *disk;
const struct qemud_vm_net_def *net;
const struct qemud_vm_input_def *input;
const struct qemud_vm_sound_def *sound;
const struct qemud_vm_chr_def *chr;
2007-02-14 01:40:09 +00:00
const char *type = NULL;
int n, allones = 1;
2007-02-14 01:40:09 +00:00
if (!(type = qemudVirtTypeToString(def->virtType))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("unexpected domain type %d"), def->virtType);
2007-02-14 01:40:09 +00:00
goto cleanup;
}
if (qemudIsActiveVM(vm) && live)
virBufferVSprintf(&buf, "<domain type='%s' id='%d'>\n", type, vm->id);
else
virBufferVSprintf(&buf, "<domain type='%s'>\n", type);
2007-02-14 01:40:09 +00:00
virBufferVSprintf(&buf, " <name>%s</name>\n", def->name);
2007-02-14 01:40:09 +00:00
uuid = def->uuid;
2007-08-09 20:19:12 +00:00
virUUIDFormat(uuid, uuidstr);
virBufferVSprintf(&buf, " <uuid>%s</uuid>\n", uuidstr);
2007-02-14 01:40:09 +00:00
virBufferVSprintf(&buf, " <memory>%lu</memory>\n", def->maxmem);
virBufferVSprintf(&buf, " <currentMemory>%lu</currentMemory>\n", def->memory);
for (n = 0 ; n < QEMUD_CPUMASK_LEN ; n++)
if (def->cpumask[n] != 1)
allones = 0;
if (allones) {
virBufferVSprintf(&buf, " <vcpu>%d</vcpu>\n", def->vcpus);
} else {
char *cpumask = NULL;
if ((cpumask = virSaveCpuSet(conn, def->cpumask, QEMUD_CPUMASK_LEN)) == NULL) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s", _("allocating cpu mask"));
goto cleanup;
}
virBufferVSprintf(&buf, " <vcpu cpuset='%s'>%d</vcpu>\n", cpumask, def->vcpus);
free(cpumask);
}
2008-05-15 16:21:11 +00:00
if (def->os.bootloader[0])
virBufferVSprintf(&buf, " <bootloader>%s</bootloader>\n", def->os.bootloader);
virBufferAddLit(&buf, " <os>\n");
2007-02-14 01:40:09 +00:00
if (def->virtType == QEMUD_VIRT_QEMU)
virBufferVSprintf(&buf, " <type arch='%s' machine='%s'>%s</type>\n",
def->os.arch, def->os.machine, def->os.type);
else
virBufferVSprintf(&buf, " <type>%s</type>\n", def->os.type);
2007-02-14 01:40:09 +00:00
2008-05-15 16:21:11 +00:00
if (!def->os.bootloader[0]) {
if (def->os.kernel[0])
virBufferVSprintf(&buf, " <kernel>%s</kernel>\n", def->os.kernel);
if (def->os.initrd[0])
virBufferVSprintf(&buf, " <initrd>%s</initrd>\n", def->os.initrd);
if (def->os.cmdline[0])
virBufferVSprintf(&buf, " <cmdline>%s</cmdline>\n", def->os.cmdline);
for (n = 0 ; n < def->os.nBootDevs ; n++) {
const char *boottype = "hd";
switch (def->os.bootDevs[n]) {
case QEMUD_BOOT_FLOPPY:
boottype = "fd";
break;
case QEMUD_BOOT_DISK:
boottype = "hd";
break;
case QEMUD_BOOT_CDROM:
boottype = "cdrom";
break;
case QEMUD_BOOT_NET:
boottype = "network";
break;
}
virBufferVSprintf(&buf, " <boot dev='%s'/>\n", boottype);
}
2007-02-14 01:40:09 +00:00
}
virBufferAddLit(&buf, " </os>\n");
2007-02-14 01:40:09 +00:00
if (def->features & QEMUD_FEATURE_ACPI) {
virBufferAddLit(&buf, " <features>\n");
virBufferAddLit(&buf, " <acpi/>\n");
virBufferAddLit(&buf, " </features>\n");
}
virBufferVSprintf(&buf, " <clock offset='%s'/>\n", def->localtime ? "localtime" : "utc");
virBufferAddLit(&buf, " <on_poweroff>destroy</on_poweroff>\n");
if (def->noReboot)
virBufferAddLit(&buf, " <on_reboot>destroy</on_reboot>\n");
else
virBufferAddLit(&buf, " <on_reboot>restart</on_reboot>\n");
virBufferAddLit(&buf, " <on_crash>destroy</on_crash>\n");
virBufferAddLit(&buf, " <devices>\n");
2007-02-14 01:40:09 +00:00
virBufferVSprintf(&buf, " <emulator>%s</emulator>\n", def->os.binary);
2007-02-14 01:40:09 +00:00
disk = def->disks;
2007-02-14 01:40:09 +00:00
while (disk) {
const char *types[] = {
"block",
"file",
};
const char *typeAttrs[] = {
"dev",
"file",
};
const char *devices[] = {
"disk",
"cdrom",
"floppy",
};
virBufferVSprintf(&buf, " <disk type='%s' device='%s'>\n",
types[disk->type], devices[disk->device]);
2007-02-14 01:40:09 +00:00
if (disk->src[0])
virBufferVSprintf(&buf, " <source %s='%s'/>\n",
typeAttrs[disk->type], disk->src);
2007-02-14 01:40:09 +00:00
virBufferVSprintf(&buf, " <target dev='%s' bus='%s'/>\n",
disk->dst, qemudBusIdToName(disk->bus, 0));
2007-02-14 01:40:09 +00:00
if (disk->readonly)
virBufferAddLit(&buf, " <readonly/>\n");
2007-02-14 01:40:09 +00:00
virBufferAddLit(&buf, " </disk>\n");
2007-02-14 01:40:09 +00:00
disk = disk->next;
}
net = def->nets;
while (net) {
2007-02-14 01:40:09 +00:00
const char *types[] = {
"user",
2007-03-13 22:43:22 +00:00
"ethernet",
2007-02-14 01:40:09 +00:00
"server",
"client",
"mcast",
"network",
2007-03-13 22:43:22 +00:00
"bridge",
2007-02-14 01:40:09 +00:00
};
virBufferVSprintf(&buf, " <interface type='%s'>\n",
types[net->type]);
2007-02-14 01:40:09 +00:00
virBufferVSprintf(&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]);
2007-02-14 01:40:09 +00:00
2007-03-13 22:43:22 +00:00
switch (net->type) {
case QEMUD_NET_NETWORK:
virBufferVSprintf(&buf, " <source network='%s'/>\n", net->dst.network.name);
if (net->dst.network.ifname[0] != '\0')
virBufferVSprintf(&buf, " <target dev='%s'/>\n", net->dst.network.ifname);
2007-03-13 22:43:22 +00:00
break;
2007-03-13 22:43:22 +00:00
case QEMUD_NET_ETHERNET:
if (net->dst.ethernet.ifname[0] != '\0')
virBufferVSprintf(&buf, " <target dev='%s'/>\n", net->dst.ethernet.ifname);
if (net->dst.ethernet.script[0] != '\0')
virBufferVSprintf(&buf, " <script path='%s'/>\n", net->dst.ethernet.script);
2007-03-13 22:43:22 +00:00
break;
case QEMUD_NET_BRIDGE:
virBufferVSprintf(&buf, " <source bridge='%s'/>\n", net->dst.bridge.brname);
if (net->dst.bridge.ifname[0] != '\0')
virBufferVSprintf(&buf, " <target dev='%s'/>\n", net->dst.bridge.ifname);
2007-03-13 22:43:22 +00:00
break;
case QEMUD_NET_SERVER:
case QEMUD_NET_CLIENT:
case QEMUD_NET_MCAST:
if (net->dst.socket.address[0] != '\0')
virBufferVSprintf(&buf, " <source address='%s' port='%d'/>\n",
net->dst.socket.address, net->dst.socket.port);
else
virBufferVSprintf(&buf, " <source port='%d'/>\n",
net->dst.socket.port);
}
if (net->model && net->model[0] != '\0') {
virBufferVSprintf(&buf, " <model type='%s'/>\n",
net->model);
}
virBufferAddLit(&buf, " </interface>\n");
2007-02-14 01:40:09 +00:00
net = net->next;
2007-02-14 01:40:09 +00:00
}
chr = def->serials;
while (chr) {
if (qemudGenerateXMLChar(&buf, chr, "serial") < 0)
goto no_memory;
chr = chr->next;
}
chr = def->parallels;
while (chr) {
if (qemudGenerateXMLChar(&buf, chr, "parallel") < 0)
goto no_memory;
chr = chr->next;
}
/* First serial device is the primary console */
if (def->nserials > 0 &&
qemudGenerateXMLChar(&buf, def->serials, "console") < 0)
goto no_memory;
2007-07-18 21:08:22 +00:00
input = def->inputs;
while (input) {
if (input->bus == QEMU_INPUT_BUS_USB)
virBufferVSprintf(&buf, " <input type='%s' bus='usb'/>\n",
input->type == QEMU_INPUT_TYPE_MOUSE ? "mouse" : "tablet");
2007-07-18 21:08:22 +00:00
input = input->next;
}
/* If graphics is enable, add implicit mouse */
if (def->graphicsType != QEMUD_GRAPHICS_NONE)
virBufferVSprintf(&buf, " <input type='mouse' bus='%s'/>\n",
STREQ(def->os.type, "hvm") ? "ps2" : "xen");
2007-07-18 21:08:22 +00:00
switch (def->graphicsType) {
case QEMUD_GRAPHICS_VNC:
virBufferAddLit(&buf, " <graphics type='vnc'");
if (def->vncPort)
virBufferVSprintf(&buf, " port='%d'",
qemudIsActiveVM(vm) && live ? def->vncActivePort : def->vncPort);
if (def->vncListen[0])
virBufferVSprintf(&buf, " listen='%s'",
def->vncListen);
if (def->keymap)
virBufferVSprintf(&buf, " keymap='%s'",
def->keymap);
virBufferAddLit(&buf, "/>\n");
break;
case QEMUD_GRAPHICS_SDL:
virBufferAddLit(&buf, " <graphics type='sdl'/>\n");
break;
case QEMUD_GRAPHICS_NONE:
default:
break;
}
sound = def->sounds;
while(sound) {
const char *model = qemudSoundModelToString(sound->model);
if (!model) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
"%s", _("invalid sound model"));
goto cleanup;
}
virBufferVSprintf(&buf, " <sound model='%s'/>\n", model);
sound = sound->next;
}
virBufferAddLit(&buf, " </devices>\n");
virBufferAddLit(&buf, "</domain>\n");
2007-02-14 01:40:09 +00:00
if (virBufferError(&buf))
2007-02-14 01:40:09 +00:00
goto no_memory;
return virBufferContentAndReset(&buf);
2007-02-14 01:40:09 +00:00
no_memory:
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s", _("failed to generate XML: out of memory"));
2007-02-14 01:40:09 +00:00
cleanup:
free(virBufferContentAndReset(&buf));
2007-02-14 01:40:09 +00:00
return NULL;
}
char *qemudGenerateNetworkXML(virConnectPtr conn,
struct qemud_driver *driver ATTRIBUTE_UNUSED,
struct qemud_network *network,
struct qemud_network_def *def) {
virBuffer buf = VIR_BUFFER_INITIALIZER;
unsigned char *uuid;
2007-08-09 20:19:12 +00:00
char uuidstr[VIR_UUID_STRING_BUFLEN];
virBufferAddLit(&buf, "<network>\n");
virBufferVSprintf(&buf, " <name>%s</name>\n", def->name);
uuid = def->uuid;
2007-08-09 20:19:12 +00:00
virUUIDFormat(uuid, uuidstr);
virBufferVSprintf(&buf, " <uuid>%s</uuid>\n", uuidstr);
2007-03-13 22:43:22 +00:00
if (def->forward) {
if (def->forwardDev[0]) {
virBufferVSprintf(&buf, " <forward dev='%s' mode='%s'/>\n",
def->forwardDev, (def->forwardMode == QEMUD_NET_FORWARD_ROUTE ? "route" : "nat"));
2007-03-13 22:43:22 +00:00
} else {
virBufferVSprintf(&buf, " <forward mode='%s'/>\n", (def->forwardMode == QEMUD_NET_FORWARD_ROUTE ? "route" : "nat"));
2007-03-13 22:43:22 +00:00
}
}
virBufferAddLit(&buf, " <bridge");
if (qemudIsActiveNetwork(network)) {
virBufferVSprintf(&buf, " name='%s'", network->bridge);
} else if (def->bridge[0]) {
virBufferVSprintf(&buf, " name='%s'", def->bridge);
}
virBufferVSprintf(&buf, " stp='%s' forwardDelay='%d' />\n",
def->disableSTP ? "off" : "on",
def->forwardDelay);
if (def->ipAddress[0] || def->netmask[0]) {
virBufferAddLit(&buf, " <ip");
if (def->ipAddress[0])
virBufferVSprintf(&buf, " address='%s'", def->ipAddress);
if (def->netmask[0])
virBufferVSprintf(&buf, " netmask='%s'", def->netmask);
virBufferAddLit(&buf, ">\n");
if (def->ranges) {
struct qemud_dhcp_range_def *range = def->ranges;
virBufferAddLit(&buf, " <dhcp>\n");
while (range) {
virBufferVSprintf(&buf, " <range start='%s' end='%s' />\n",
range->start, range->end);
range = range->next;
}
virBufferAddLit(&buf, " </dhcp>\n");
}
virBufferAddLit(&buf, " </ip>\n");
2007-02-14 01:40:09 +00:00
}
virBufferAddLit(&buf, "</network>\n");
if (virBufferError(&buf))
goto no_memory;
return virBufferContentAndReset(&buf);
no_memory:
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
"%s", _("failed to generate XML: out of memory"));
free(virBufferContentAndReset(&buf));
return NULL;
}
int qemudDeleteConfig(virConnectPtr conn,
struct qemud_driver *driver ATTRIBUTE_UNUSED,
const char *configFile,
const char *name) {
if (!configFile[0]) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("no config file for %s"), name);
2007-02-14 01:40:09 +00:00
return -1;
}
if (unlink(configFile) < 0) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("cannot remove config for %s"), name);
return -1;
}
2007-02-14 01:40:09 +00:00
return 0;
}
#endif /* WITH_QEMU */