qemu_conf.c 100.8 KB
Newer Older
D
Daniel P. Berrange 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * config.c: VM configuration management
 *
 * Copyright (C) 2006, 2007 Red Hat, Inc.
 * Copyright (C) 2006 Daniel P. Berrange
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 * Author: Daniel P. Berrange <berrange@redhat.com>
 */

24 25
#include <config.h>

D
Daniel P. Berrange 已提交
26 27 28 29 30 31 32 33
#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>
34
#include <sys/wait.h>
35
#include <arpa/inet.h>
36
#include <sys/utsname.h>
D
Daniel P. Berrange 已提交
37 38 39 40 41 42 43 44

#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/uri.h>

#include <libvirt/virterror.h>

45
#include "qemu_conf.h"
46
#include "uuid.h"
47
#include "buf.h"
D
Daniel P. Berrange 已提交
48
#include "conf.h"
49

50 51
#define qemudLog(level, msg...) fprintf(stderr, msg)

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
void qemudReportError(virConnectPtr conn,
                      virDomainPtr dom,
                      virNetworkPtr net,
                      int code, const char *fmt, ...) {
    va_list args;
    char errorMessage[QEMUD_MAX_ERROR_LEN];

    if (fmt) {
        va_start(args, fmt);
        vsnprintf(errorMessage, QEMUD_MAX_ERROR_LEN-1, fmt, args);
        va_end(args);
    } else {
        errorMessage[0] = '\0';
    }
    __virRaiseError(conn, dom, net, VIR_FROM_QEMU, code, VIR_ERR_ERROR,
                    NULL, NULL, NULL, -1, -1, errorMessage);
}

D
Daniel P. Berrange 已提交
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
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,
                         "vncTLSx509certdir");
        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,
                             "vncTLSx509certdir");
            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;
}


132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
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) {
149
        if (!memcmp(vm->def->uuid, uuid, VIR_UUID_BUFLEN))
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
            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 (!strcmp(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) {
175
        if (!memcmp(network->def->uuid, uuid, VIR_UUID_BUFLEN))
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
            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 (!strcmp(network->def->name, name))
            return network;
        network = network->next;
    }

    return NULL;
}

196

197 198 199 200
/* 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;
D
Daniel P. Berrange 已提交
201
    struct qemud_vm_input_def *input = def->inputs;
202 203 204 205 206 207 208 209 210 211 212

    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);
    }
D
Daniel P. Berrange 已提交
213 214 215 216 217
    while (input) {
        struct qemud_vm_input_def *prev = input;
        input = input->next;
        free(prev);
    }
218
    free(def);
219 220 221 222 223 224 225 226
}

void qemudFreeVM(struct qemud_vm *vm) {
    qemudFreeVMDef(vm->def);
    if (vm->newDef)
        qemudFreeVMDef(vm->newDef);
    free(vm);
}
D
Daniel P. Berrange 已提交
227

228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
/* Build up a fully qualfiied path for a config file to be
 * associated with a persistent guest or network */
static int
qemudMakeConfigPath(const char *configDir,
                    const char *name,
                    const char *ext,
                    char *buf,
                    unsigned int buflen) {
    if ((strlen(configDir) + 1 + strlen(name) + (ext ? strlen(ext) : 0) + 1) > buflen)
        return -1;

    strcpy(buf, configDir);
    strcat(buf, "/");
    strcat(buf, name);
    if (ext)
        strcat(buf, ext);
    return 0;
}

247
int
248 249 250 251 252 253
qemudEnsureDir(const char *path)
{
    struct stat st;
    char parent[PATH_MAX];
    char *p;
    int err;
254

255 256
    if (stat(path, &st) >= 0)
        return 0;
257

258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
    strncpy(parent, path, PATH_MAX);
    parent[PATH_MAX - 1] = '\0';

    if (!(p = strrchr(parent, '/')))
        return EINVAL;

    if (p == parent)
        return EPERM;

    *p = '\0';

    if ((err = qemudEnsureDir(parent)))
        return err;

    if (mkdir(path, 0777) < 0 && errno != EEXIST)
        return errno;
274 275 276 277

    return 0;
}

D
Daniel P. Berrange 已提交
278 279 280
/* The list of possible machine types for various architectures,
   as supported by QEMU - taken from 'qemu -M ?' for each arch */
static const char *arch_info_x86_machines[] = {
281
    "pc", "isapc", NULL
D
Daniel P. Berrange 已提交
282 283
};
static const char *arch_info_mips_machines[] = {
284
    "mips", NULL
D
Daniel P. Berrange 已提交
285 286
};
static const char *arch_info_sparc_machines[] = {
287
    "sun4m", NULL
D
Daniel P. Berrange 已提交
288 289
};
static const char *arch_info_ppc_machines[] = {
290
    "g3bw", "mac99", "prep", NULL
D
Daniel P. Berrange 已提交
291 292
};

293 294 295 296 297 298 299 300 301 302 303 304 305 306
/* Feature flags for the architecture info */
struct qemu_feature_flags arch_info_i686_flags [] = {
    { "pae",  1, 1 },
    { "acpi", 1, 1 },
    { "apic", 1, 0 },
    { NULL, -1, -1 }
};

struct qemu_feature_flags arch_info_x86_64_flags [] = {
    { "acpi", 1, 1 },
    { "apic", 1, 0 },
    { NULL, -1, -1 }
};

D
Daniel P. Berrange 已提交
307
/* The archicture tables for supported QEMU archs */
308 309
struct qemu_arch_info qemudArchs[] = { 
    /* i686 must be in position 0 */
310
    {  "i686", 32, arch_info_x86_machines, "qemu", arch_info_i686_flags },
311
    /* x86_64 must be in position 1 */
312 313 314 315 316 317
    {  "x86_64", 64, arch_info_x86_machines, "qemu-system-x86_64", arch_info_x86_64_flags },
    {  "mips", 32, arch_info_mips_machines, "qemu-system-mips", NULL },
    {  "mipsel", 32, arch_info_mips_machines, "qemu-system-mipsel", NULL },
    {  "sparc", 32, arch_info_sparc_machines, "qemu-system-sparc", NULL },
    {  "ppc", 32, arch_info_ppc_machines, "qemu-system-ppc", NULL },
    { NULL, -1, NULL, NULL, NULL }
D
Daniel P. Berrange 已提交
318 319 320 321
};

/* Return the default architecture if none is explicitly requested*/
static const char *qemudDefaultArch(void) {
322
    return qemudArchs[0].arch;
D
Daniel P. Berrange 已提交
323 324 325 326 327 328
}

/* Return the default machine type for a given architecture */
static const char *qemudDefaultMachineForArch(const char *arch) {
    int i;

329 330 331
    for (i = 0; qemudArchs[i].arch; i++) {
        if (!strcmp(qemudArchs[i].arch, arch)) {
            return qemudArchs[i].machines[0];
D
Daniel P. Berrange 已提交
332 333 334 335 336 337 338 339 340 341
        }
    }

    return NULL;
}

/* Return the default binary name for a particular architecture */
static const char *qemudDefaultBinaryForArch(const char *arch) {
    int i;

342 343 344
    for (i = 0 ; qemudArchs[i].arch; i++) {
        if (!strcmp(qemudArchs[i].arch, arch)) {
            return qemudArchs[i].binary;
D
Daniel P. Berrange 已提交
345 346 347 348 349 350 351
        }
    }

    return NULL;
}

/* Find the fully qualified path to the binary for an architecture */
352
static char *qemudLocateBinaryForArch(virConnectPtr conn,
D
Daniel P. Berrange 已提交
353 354 355 356 357 358 359 360 361 362
                                      int virtType, const char *arch) {
    const char *name;
    char *path;

    if (virtType == QEMUD_VIRT_KVM)
        name = "qemu-kvm";
    else
        name = qemudDefaultBinaryForArch(arch);

    if (!name) {
363
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "cannot determin binary for architecture %s", arch);
D
Daniel P. Berrange 已提交
364 365 366 367 368 369
        return NULL;
    }

    /* XXX lame. should actually use $PATH ... */
    path = malloc(strlen(name) + strlen("/usr/bin/") + 1);
    if (!path) {
370
        qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "path");
D
Daniel P. Berrange 已提交
371 372 373 374 375 376 377
        return NULL;
    }
    strcpy(path, "/usr/bin/");
    strcat(path, name);
    return path;
}

378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412

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 */
        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;

        /* Just in case QEMU is translated someday.. */
        setenv("LANG", "C", 1);
        execl(qemu, qemu, (char*)NULL);

    cleanup1:
        _exit(-1); /* Just in case */
    } else { /* Parent */
D
Daniel P. Berrange 已提交
413
        char help[8192]; /* Ought to be enough to hold QEMU help screen */
414
        int got = 0, ret = -1;
415 416 417 418 419
        int major, minor, micro;

        if (close(newstdout[1]) < 0)
            goto cleanup2;

D
Daniel P. Berrange 已提交
420 421 422 423 424 425 426 427 428 429
        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;
430 431 432 433 434 435 436 437 438 439
        }
        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;
D
Daniel P. Berrange 已提交
440 441
        if (strstr(help, "-no-reboot"))
            *flags |= QEMUD_CMD_FLAG_NO_REBOOT;
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
        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;
            }
            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 */
        if (WEXITSTATUS(got) != 1) {
            qemudLog(QEMUD_WARN, "Unexpected exit status '%d', qemu probably failed", got);
        }

        return ret;
    }
}

472
int qemudExtractVersion(virConnectPtr conn,
473
                        struct qemud_driver *driver ATTRIBUTE_UNUSED) {
474
    char *binary = NULL;
475
    struct stat sb;
476
    int ignored;
477

478
    if (driver->qemuVersion > 0)
479 480
        return 0;

481
    if (!(binary = qemudLocateBinaryForArch(conn, QEMUD_VIRT_QEMU, "i686")))
482 483
        return -1;

484
    if (stat(binary, &sb) < 0) {
485
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
486 487 488 489 490 491
                         "Cannot find QEMU binary %s: %s", binary,
                         strerror(errno));
        free(binary);
        return -1;
    }

492
    if (qemudExtractVersionInfo(binary, &driver->qemuVersion, &ignored) < 0) {
493 494 495 496 497 498 499 500 501
        free(binary);
        return -1;
    }

    free(binary);
    return 0;
}


D
Daniel P. Berrange 已提交
502
/* Parse the XML definition for a disk */
503 504
static struct qemud_vm_disk_def *qemudParseDiskXML(virConnectPtr conn,
                                                   struct qemud_driver *driver ATTRIBUTE_UNUSED,
D
Daniel P. Berrange 已提交
505 506 507 508 509 510 511 512 513 514
                                                   xmlNodePtr node) {
    struct qemud_vm_disk_def *disk = calloc(1, sizeof(struct qemud_vm_disk_def));
    xmlNodePtr cur;
    xmlChar *device = NULL;
    xmlChar *source = NULL;
    xmlChar *target = NULL;
    xmlChar *type = NULL;
    int typ = 0;

    if (!disk) {
515
        qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "disk");
D
Daniel P. Berrange 已提交
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
        return NULL;
    }

    type = xmlGetProp(node, BAD_CAST "type");
    if (type != NULL) {
        if (xmlStrEqual(type, BAD_CAST "file"))
            typ = QEMUD_DISK_FILE;
        else if (xmlStrEqual(type, BAD_CAST "block"))
            typ = QEMUD_DISK_BLOCK;
        else {
            typ = QEMUD_DISK_FILE;
        }
        xmlFree(type);
        type = NULL;
    }

    device = xmlGetProp(node, BAD_CAST "device");
  
    cur = node->children;
    while (cur != NULL) {
        if (cur->type == XML_ELEMENT_NODE) {
            if ((source == NULL) &&
                (xmlStrEqual(cur->name, BAD_CAST "source"))) {
	
                if (typ == QEMUD_DISK_FILE)
                    source = xmlGetProp(cur, BAD_CAST "file");
                else
                    source = xmlGetProp(cur, BAD_CAST "dev");
            } else if ((target == NULL) &&
                       (xmlStrEqual(cur->name, BAD_CAST "target"))) {
                target = xmlGetProp(cur, BAD_CAST "dev");
            } else if (xmlStrEqual(cur->name, BAD_CAST "readonly")) {
                disk->readonly = 1;
            }
        }
        cur = cur->next;
    }

    if (source == NULL) {
555
        qemudReportError(conn, NULL, NULL, VIR_ERR_NO_SOURCE, target ? "%s" : NULL, target);
D
Daniel P. Berrange 已提交
556 557 558
        goto error;
    }
    if (target == NULL) {
559
        qemudReportError(conn, NULL, NULL, VIR_ERR_NO_TARGET, source ? "%s" : NULL, source);
D
Daniel P. Berrange 已提交
560 561 562 563 564 565 566
        goto error;
    }

    if (device &&
        !strcmp((const char *)device, "floppy") &&
        strcmp((const char *)target, "fda") &&
        strcmp((const char *)target, "fdb")) {
567
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "Invalid floppy device name: %s", target);
D
Daniel P. Berrange 已提交
568 569 570 571 572 573
        goto error;
    }
  
    if (device &&
        !strcmp((const char *)device, "cdrom") &&
        strcmp((const char *)target, "hdc")) {
574
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "Invalid cdrom device name: %s", target);
D
Daniel P. Berrange 已提交
575 576 577 578 579 580 581 582 583 584 585 586
        goto error;
    }

    if (device &&
        !strcmp((const char *)device, "cdrom"))
        disk->readonly = 1;

    if ((!device || !strcmp((const char *)device, "disk")) &&
        strcmp((const char *)target, "hda") &&
        strcmp((const char *)target, "hdb") &&
        strcmp((const char *)target, "hdc") &&
        strcmp((const char *)target, "hdd")) {
587
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "Invalid harddisk device name: %s", target);
D
Daniel P. Berrange 已提交
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
        goto error;
    }

    strncpy(disk->src, (const char *)source, NAME_MAX-1);
    disk->src[NAME_MAX-1] = '\0';

    strncpy(disk->dst, (const char *)target, NAME_MAX-1);
    disk->dst[NAME_MAX-1] = '\0';
    disk->type = typ;

    if (!device)
        disk->device = QEMUD_DISK_DISK;
    else if (!strcmp((const char *)device, "disk"))
        disk->device = QEMUD_DISK_DISK;
    else if (!strcmp((const char *)device, "cdrom"))
        disk->device = QEMUD_DISK_CDROM;
    else if (!strcmp((const char *)device, "floppy"))
        disk->device = QEMUD_DISK_FLOPPY;
    else {
607
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "Invalid device type: %s", device);
D
Daniel P. Berrange 已提交
608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
        goto error;
    }

    xmlFree(device);
    xmlFree(target);
    xmlFree(source);

    return disk;

 error:
    if (type)
        xmlFree(type);
    if (target)
        xmlFree(target);
    if (source)
        xmlFree(source);
    if (device)
        xmlFree(device);
    free(disk);
    return NULL;
}

630 631 632 633 634 635 636 637 638
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)));
}

D
Daniel P. Berrange 已提交
639 640

/* Parse the XML definition for a network interface */
641 642
static struct qemud_vm_net_def *qemudParseInterfaceXML(virConnectPtr conn,
                                                       struct qemud_driver *driver ATTRIBUTE_UNUSED,
D
Daniel P. Berrange 已提交
643 644 645 646 647
                                                       xmlNodePtr node) {
    struct qemud_vm_net_def *net = calloc(1, sizeof(struct qemud_vm_net_def));
    xmlNodePtr cur;
    xmlChar *macaddr = NULL;
    xmlChar *type = NULL;
648
    xmlChar *network = NULL;
649 650 651 652 653
    xmlChar *bridge = NULL;
    xmlChar *ifname = NULL;
    xmlChar *script = NULL;
    xmlChar *address = NULL;
    xmlChar *port = NULL;
D
Daniel P. Berrange 已提交
654 655

    if (!net) {
656
        qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "net");
D
Daniel P. Berrange 已提交
657 658 659 660 661 662 663 664 665
        return NULL;
    }

    net->type = QEMUD_NET_USER;

    type = xmlGetProp(node, BAD_CAST "type");
    if (type != NULL) {
        if (xmlStrEqual(type, BAD_CAST "user"))
            net->type = QEMUD_NET_USER;
666 667
        else if (xmlStrEqual(type, BAD_CAST "ethernet"))
            net->type = QEMUD_NET_ETHERNET;
D
Daniel P. Berrange 已提交
668 669 670 671 672 673
        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;
674 675
        else if (xmlStrEqual(type, BAD_CAST "network"))
            net->type = QEMUD_NET_NETWORK;
676 677
        else if (xmlStrEqual(type, BAD_CAST "bridge"))
            net->type = QEMUD_NET_BRIDGE;
D
Daniel P. Berrange 已提交
678 679 680 681 682 683 684 685 686 687 688 689
        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");
690 691 692 693
            } else if ((network == NULL) &&
                       (net->type == QEMUD_NET_NETWORK) &&
                       (xmlStrEqual(cur->name, BAD_CAST "source"))) {
                network = xmlGetProp(cur, BAD_CAST "network");
694 695 696
            } else if ((network == NULL) &&
                       (net->type == QEMUD_NET_BRIDGE) &&
                       (xmlStrEqual(cur->name, BAD_CAST "source"))) {
697
                bridge = xmlGetProp(cur, BAD_CAST "bridge");
698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
            } 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");
            } else if ((script == NULL) &&
                       (net->type == QEMUD_NET_ETHERNET) &&
                       xmlStrEqual(cur->name, BAD_CAST "script")) {
                script = xmlGetProp(cur, BAD_CAST "path");
D
Daniel P. Berrange 已提交
715 716 717 718 719 720
            }
        }
        cur = cur->next;
    }

    if (macaddr) {
721
        unsigned int mac[6];
D
Daniel P. Berrange 已提交
722
        sscanf((const char *)macaddr, "%02x:%02x:%02x:%02x:%02x:%02x",
723 724 725 726 727 728 729 730 731 732 733 734
               (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];
D
Daniel P. Berrange 已提交
735 736

        xmlFree(macaddr);
737 738 739
        macaddr = NULL;
    } else {
        qemudRandomMAC(net);
D
Daniel P. Berrange 已提交
740 741
    }

742 743 744 745
    if (net->type == QEMUD_NET_NETWORK) {
        int len;

        if (network == NULL) {
746
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
747 748
                             "No <source> 'network' attribute specified with <interface type='network'/>");
            goto error;
749
        } else if ((len = xmlStrlen(network)) >= (QEMUD_MAX_NAME_LEN-1)) {
750
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
751 752 753 754 755 756 757
                             "Network name '%s' too long", network);
            goto error;
        } else {
            strncpy(net->dst.network.name, (char *)network, len);
            net->dst.network.name[len] = '\0';
        }

758
        if (network) {
759
            xmlFree(network);
760 761 762 763 764
            network = NULL;
        }

        if (ifname != NULL) {
            if ((len = xmlStrlen(ifname)) >= (BR_IFNAME_MAXLEN-1)) {
765
                qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
766 767 768 769 770 771 772 773 774 775 776
                                 "TAP interface name '%s' is too long", ifname);
                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;
777

778 779
        if (script != NULL) {
            if ((len = xmlStrlen(script)) >= (PATH_MAX-1)) {
780
                qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
781 782 783 784 785 786 787 788 789 790 791
                                 "TAP script path '%s' is too long", script);
                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)) {
792
                qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
793
                                 "TAP interface name '%s' is too long", ifname);
794 795
                goto error;
            } else {
796 797
                strncpy(net->dst.ethernet.ifname, (char *)ifname, len);
                net->dst.ethernet.ifname[len] = '\0';
798
            }
799 800 801 802 803 804
            xmlFree(ifname);
        }
    } else if (net->type == QEMUD_NET_BRIDGE) {
        int len;

        if (bridge == NULL) {
805
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
806 807 808
                             "No <source> 'dev' attribute specified with <interface type='bridge'/>");
            goto error;
        } else if ((len = xmlStrlen(bridge)) >= (BR_IFNAME_MAXLEN-1)) {
809
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
810 811 812 813 814
                             "TAP bridge path '%s' is too long", bridge);
            goto error;
        } else {
            strncpy(net->dst.bridge.brname, (char *)bridge, len);
            net->dst.bridge.brname[len] = '\0';
815
        }
816 817 818 819 820 821

        xmlFree(bridge);
        bridge = NULL;

        if (ifname != NULL) {
            if ((len = xmlStrlen(ifname)) >= (BR_IFNAME_MAXLEN-1)) {
822
                qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
823 824 825 826 827 828 829 830 831 832 833
                                 "TAP interface name '%s' is too long", ifname);
                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) {
834
        int len = 0;
835 836 837
        char *ret;

        if (port == NULL) {
838
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
839 840 841 842 843
                             "No <source> 'port' attribute specified with socket interface");
            goto error;
        }
        if (!(net->dst.socket.port = strtol((char*)port, &ret, 10)) &&
            ret == (char*)port) {
844
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
845 846 847 848 849 850 851 852 853
                             "Cannot parse <source> 'port' attribute with socket interface");
            goto error;
        }
        xmlFree(port);
        port = NULL;

        if (address == NULL) {
            if (net->type == QEMUD_NET_CLIENT ||
                net->type == QEMUD_NET_MCAST) {
854
                qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
855 856 857 858
                                 "No <source> 'address' attribute specified with socket interface");
                goto error;
            }
        } else if ((len = xmlStrlen(address)) >= (BR_INET_ADDR_MAXLEN)) {
859
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
860 861 862 863 864 865 866 867 868 869
                             "IP address '%s' is too long", address);
            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);
870 871
    }

D
Daniel P. Berrange 已提交
872
    return net;
873 874 875 876

 error:
    if (network)
        xmlFree(network);
877 878 879 880 881 882 883 884 885 886
    if (address)
        xmlFree(address);
    if (port)
        xmlFree(port);
    if (ifname)
        xmlFree(ifname);
    if (script)
        xmlFree(script);
    if (bridge)
        xmlFree(bridge);
887 888
    free(net);
    return NULL;
D
Daniel P. Berrange 已提交
889 890 891
}


892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959
/* Parse the XML definition for a network interface */
static struct qemud_vm_input_def *qemudParseInputXML(virConnectPtr conn,
                                                   struct qemud_driver *driver ATTRIBUTE_UNUSED,
                                                   xmlNodePtr node) {
    struct qemud_vm_input_def *input = calloc(1, sizeof(struct qemud_vm_input_def));
    xmlChar *type = NULL;
    xmlChar *bus = NULL;

    if (!input) {
        qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "input");
        return NULL;
    }

    type = xmlGetProp(node, BAD_CAST "type");
    bus = xmlGetProp(node, BAD_CAST "bus");

    if (!type) {
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "no type provide for input device");
        goto error;
    }

    if (!strcmp((const char *)type, "mouse")) {
        input->type = QEMU_INPUT_TYPE_MOUSE;
    } else if (!strcmp((const char *)type, "tablet")) {
        input->type = QEMU_INPUT_TYPE_TABLET;
    } else {
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "unsupported input device type %s", (const char*)type);
        goto error;
    }

    if (bus) {
        if (!strcmp((const char*)bus, "ps2")) { /* Only allow mouse */
            if (input->type == QEMU_INPUT_TYPE_TABLET) {
                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 (!strcmp((const char *)bus, "usb")) { /* Allow mouse & keyboard */
            input->bus = QEMU_INPUT_BUS_USB;
        } else {
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "unsupported input bus %s", (const char*)bus);
            goto error;
        }
    } else {
        if (input->type == QEMU_INPUT_TYPE_MOUSE)
            input->bus = QEMU_INPUT_BUS_PS2;
        else
            input->bus = QEMU_INPUT_BUS_USB;
    }

    if (type)
        xmlFree(type);
    if (bus)
        xmlFree(bus);

    return input;

 error:
    if (type)
        xmlFree(type);
    if (bus)
        xmlFree(bus);

    free(input);
    return NULL;
}


D
Daniel P. Berrange 已提交
960 961 962 963
/*
 * Parses a libvirt XML definition of a guest, and populates the
 * the qemud_vm struct with matching data about the guests config
 */
964 965
static struct qemud_vm_def *qemudParseXML(virConnectPtr conn,
                                          struct qemud_driver *driver,
966
                                          xmlDocPtr xml) {
D
Daniel P. Berrange 已提交
967 968 969 970 971 972
    xmlNodePtr root = NULL;
    xmlChar *prop = NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlXPathObjectPtr obj = NULL;
    char *conv = NULL;
    int i;
973 974 975
    struct qemud_vm_def *def;

    if (!(def = calloc(1, sizeof(struct qemud_vm_def)))) {
976
        qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "xmlXPathContext");
977 978
        return NULL;
    }
D
Daniel P. Berrange 已提交
979 980 981 982

    /* Prepare parser / xpath context */
    root = xmlDocGetRootElement(xml);
    if ((root == NULL) || (!xmlStrEqual(root->name, BAD_CAST "domain"))) {
983
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "incorrect root element");
D
Daniel P. Berrange 已提交
984 985 986 987 988
        goto error;
    }

    ctxt = xmlXPathNewContext(xml);
    if (ctxt == NULL) {
989
        qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "xmlXPathContext");
D
Daniel P. Berrange 已提交
990 991 992 993 994 995
        goto error;
    }


    /* Find out what type of QEMU virtualization to use */
    if (!(prop = xmlGetProp(root, BAD_CAST "type"))) {
996
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "missing domain type attribute");
D
Daniel P. Berrange 已提交
997 998 999 1000
        goto error;
    }

    if (!strcmp((char *)prop, "qemu"))
1001
        def->virtType = QEMUD_VIRT_QEMU;
D
Daniel P. Berrange 已提交
1002
    else if (!strcmp((char *)prop, "kqemu"))
1003
        def->virtType = QEMUD_VIRT_KQEMU;
D
Daniel P. Berrange 已提交
1004
    else if (!strcmp((char *)prop, "kvm"))
1005
        def->virtType = QEMUD_VIRT_KVM;
D
Daniel P. Berrange 已提交
1006
    else {
1007
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "invalid domain type attribute");
D
Daniel P. Berrange 已提交
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
        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)) {
1018
        qemudReportError(conn, NULL, NULL, VIR_ERR_NO_NAME, NULL);
D
Daniel P. Berrange 已提交
1019 1020 1021
        goto error;
    }
    if (strlen((const char *)obj->stringval) >= (QEMUD_MAX_NAME_LEN-1)) {
1022
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "domain name length too long");
D
Daniel P. Berrange 已提交
1023 1024
        goto error;
    }
1025
    strcpy(def->name, (const char *)obj->stringval);
D
Daniel P. Berrange 已提交
1026 1027 1028 1029 1030 1031 1032
    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)) {
1033
        int err;
D
Daniel P. Berrange 已提交
1034
        if ((err = virUUIDGenerate(def->uuid))) {
1035
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1036 1037 1038
                             "Failed to generate UUID: %s", strerror(err));
            goto error;
        }
D
Daniel P. Berrange 已提交
1039
    } else if (virUUIDParse((const char *)obj->stringval, def->uuid) < 0) {
1040
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "malformed uuid element");
D
Daniel P. Berrange 已提交
1041 1042 1043 1044 1045 1046 1047 1048 1049
        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)) {
1050
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "missing memory element");
D
Daniel P. Berrange 已提交
1051 1052 1053
        goto error;
    } else {
        conv = NULL;
1054
        def->maxmem = strtoll((const char*)obj->stringval, &conv, 10);
D
Daniel P. Berrange 已提交
1055
        if (conv == (const char*)obj->stringval) {
1056
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "malformed memory information");
D
Daniel P. Berrange 已提交
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067
            goto error;
        }
    }
    if (obj)
        xmlXPathFreeObject(obj);


    /* 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)) {
1068
        def->memory = def->maxmem;
D
Daniel P. Berrange 已提交
1069 1070
    } else {
        conv = NULL;
1071 1072 1073
        def->memory = strtoll((const char*)obj->stringval, &conv, 10);
        if (def->memory > def->maxmem)
            def->memory = def->maxmem;
D
Daniel P. Berrange 已提交
1074
        if (conv == (const char*)obj->stringval) {
1075
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "malformed memory information");
D
Daniel P. Berrange 已提交
1076 1077 1078 1079 1080 1081 1082 1083 1084 1085
            goto error;
        }
    }
    if (obj)
        xmlXPathFreeObject(obj);

    /* Extract domain vcpu info */
    obj = xmlXPathEval(BAD_CAST "string(/domain/vcpu[1])", ctxt);
    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
1086
        def->vcpus = 1;
D
Daniel P. Berrange 已提交
1087 1088
    } else {
        conv = NULL;
1089
        def->vcpus = strtoll((const char*)obj->stringval, &conv, 10);
D
Daniel P. Berrange 已提交
1090
        if (conv == (const char*)obj->stringval) {
1091
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "malformed vcpu information");
D
Daniel P. Berrange 已提交
1092 1093 1094 1095 1096 1097 1098 1099 1100 1101
            goto error;
        }
    }
    if (obj)
        xmlXPathFreeObject(obj);

    /* 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)) {
1102
        def->features |= QEMUD_FEATURE_ACPI;
D
Daniel P. Berrange 已提交
1103
    }
1104
    xmlXPathFreeObject(obj);
D
Daniel P. Berrange 已提交
1105

D
Daniel P. Berrange 已提交
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120

    /* 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 (!strcmp((char*)obj->stringval, "destroy"))
            def->noReboot = 1;
        else
            def->noReboot = 0;
    }
    if (obj)
        xmlXPathFreeObject(obj);

1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134
    /* 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 (!strcmp((char*)obj->stringval, "localtime"))
            def->localtime = 1;
        else
            def->localtime = 0;
    }
    if (obj)
        xmlXPathFreeObject(obj);

D
Daniel P. Berrange 已提交
1135

D
Daniel P. Berrange 已提交
1136 1137 1138 1139
    /* 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)) {
1140
        qemudReportError(conn, NULL, NULL, VIR_ERR_OS_TYPE, NULL);
D
Daniel P. Berrange 已提交
1141 1142 1143
        goto error;
    }
    if (strcmp((const char *)obj->stringval, "hvm")) {
1144
        qemudReportError(conn, NULL, NULL, VIR_ERR_OS_TYPE, "%s", obj->stringval);
D
Daniel P. Berrange 已提交
1145 1146
        goto error;
    }
1147
    strcpy(def->os.type, (const char *)obj->stringval);
D
Daniel P. Berrange 已提交
1148 1149 1150 1151 1152 1153 1154 1155
    xmlXPathFreeObject(obj);


    obj = xmlXPathEval(BAD_CAST "string(/domain/os/type[1]/@arch)", ctxt);
    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
        const char *defaultArch = qemudDefaultArch();
        if (strlen(defaultArch) >= (QEMUD_OS_TYPE_MAX_LEN-1)) {
1156
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "architecture type too long");
D
Daniel P. Berrange 已提交
1157 1158
            goto error;
        }
1159
        strcpy(def->os.arch, defaultArch);
D
Daniel P. Berrange 已提交
1160 1161
    } else {
        if (strlen((const char *)obj->stringval) >= (QEMUD_OS_TYPE_MAX_LEN-1)) {
1162
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "architecture type too long");
D
Daniel P. Berrange 已提交
1163 1164
            goto error;
        }
1165
        strcpy(def->os.arch, (const char *)obj->stringval);
D
Daniel P. Berrange 已提交
1166 1167 1168 1169 1170 1171 1172
    }
    if (obj)
        xmlXPathFreeObject(obj);

    obj = xmlXPathEval(BAD_CAST "string(/domain/os/type[1]/@machine)", ctxt);
    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
1173
        const char *defaultMachine = qemudDefaultMachineForArch(def->os.arch);
1174 1175 1176 1177
        if (!defaultMachine) {
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "unsupported arch %s", def->os.arch);
            goto error;
        }
D
Daniel P. Berrange 已提交
1178
        if (strlen(defaultMachine) >= (QEMUD_OS_MACHINE_MAX_LEN-1)) {
1179
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "machine type too long");
D
Daniel P. Berrange 已提交
1180 1181
            goto error;
        }
1182
        strcpy(def->os.machine, defaultMachine);
D
Daniel P. Berrange 已提交
1183 1184
    } else {
        if (strlen((const char *)obj->stringval) >= (QEMUD_OS_MACHINE_MAX_LEN-1)) {
1185
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "architecture type too long");
D
Daniel P. Berrange 已提交
1186 1187
            goto error;
        }
1188
        strcpy(def->os.machine, (const char *)obj->stringval);
D
Daniel P. Berrange 已提交
1189 1190 1191 1192 1193 1194 1195 1196 1197
    }
    if (obj)
        xmlXPathFreeObject(obj);


    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)) {
1198
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "kernel path too long");
D
Daniel P. Berrange 已提交
1199 1200
            goto error;
        }
1201
        strcpy(def->os.kernel, (const char *)obj->stringval);
D
Daniel P. Berrange 已提交
1202 1203 1204 1205 1206 1207 1208 1209 1210
    }
    if (obj)
        xmlXPathFreeObject(obj);


    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)) {
1211
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "initrd path too long");
D
Daniel P. Berrange 已提交
1212 1213
            goto error;
        }
1214
        strcpy(def->os.initrd, (const char *)obj->stringval);
D
Daniel P. Berrange 已提交
1215 1216 1217 1218 1219 1220 1221 1222 1223
    }
    if (obj)
        xmlXPathFreeObject(obj);


    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)) {
1224
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "cmdline arguments too long");
D
Daniel P. Berrange 已提交
1225 1226
            goto error;
        }
1227
        strcpy(def->os.cmdline, (const char *)obj->stringval);
D
Daniel P. Berrange 已提交
1228 1229 1230 1231 1232 1233 1234 1235 1236 1237
    }
    if (obj)
        xmlXPathFreeObject(obj);


    /* 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++) {
1238 1239
            if (!(prop = xmlGetProp(obj->nodesetval->nodeTab[i], BAD_CAST "dev")))
                continue;
D
Daniel P. Berrange 已提交
1240
            if (!strcmp((char *)prop, "hd")) {
1241
                def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_DISK;
D
Daniel P. Berrange 已提交
1242
            } else if (!strcmp((char *)prop, "fd")) {
1243
                def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_FLOPPY;
D
Daniel P. Berrange 已提交
1244
            } else if (!strcmp((char *)prop, "cdrom")) {
1245
                def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_CDROM;
1246
            } else if (!strcmp((char *)prop, "network")) {
1247
                def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_NET;
D
Daniel P. Berrange 已提交
1248 1249 1250
            } else {
                goto error;
            }
1251
            xmlFree(prop);
1252
            prop = NULL;
D
Daniel P. Berrange 已提交
1253 1254 1255
        }
    }
    xmlXPathFreeObject(obj);
1256 1257 1258
    if (def->os.nBootDevs == 0) {
        def->os.nBootDevs = 1;
        def->os.bootDevs[0] = QEMUD_BOOT_DISK;
D
Daniel P. Berrange 已提交
1259 1260 1261 1262 1263 1264
    }


    obj = xmlXPathEval(BAD_CAST "string(/domain/devices/emulator[1])", ctxt);
    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
1265
        char *tmp = qemudLocateBinaryForArch(conn, def->virtType, def->os.arch);
D
Daniel P. Berrange 已提交
1266 1267 1268
        if (!tmp) {
            goto error;
        }
1269
        strcpy(def->os.binary, tmp);
D
Daniel P. Berrange 已提交
1270 1271 1272
        free(tmp);
    } else {
        if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) {
1273
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "emulator path too long");
D
Daniel P. Berrange 已提交
1274 1275
            goto error;
        }
1276
        strcpy(def->os.binary, (const char *)obj->stringval);
D
Daniel P. Berrange 已提交
1277 1278 1279 1280 1281 1282 1283
    }
    if (obj)
        xmlXPathFreeObject(obj);

    obj = xmlXPathEval(BAD_CAST "/domain/devices/graphics", ctxt);
    if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
        (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) {
1284
        def->graphicsType = QEMUD_GRAPHICS_NONE;
1285
    } else if ((prop = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "type"))) {
D
Daniel P. Berrange 已提交
1286
        if (!strcmp((char *)prop, "vnc")) {
1287
            xmlChar *vncport, *vnclisten;
1288
            def->graphicsType = QEMUD_GRAPHICS_VNC;
1289 1290
            vncport = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "port");
            if (vncport) {
D
Daniel P. Berrange 已提交
1291
                conv = NULL;
1292
                def->vncPort = strtoll((const char*)vncport, &conv, 10);
D
Daniel P. Berrange 已提交
1293
            } else {
1294
                def->vncPort = -1;
D
Daniel P. Berrange 已提交
1295
            }
1296 1297 1298 1299
            vnclisten = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "listen");
            if (vnclisten && *vnclisten)
                strncpy(def->vncListen, (char *)vnclisten, BR_INET_ADDR_MAXLEN-1);
            else
D
Daniel P. Berrange 已提交
1300
                strcpy(def->vncListen, driver->vncListen);
1301 1302 1303
            def->vncListen[BR_INET_ADDR_MAXLEN-1] = '\0';
            xmlFree(vncport);
            xmlFree(vnclisten);
D
Daniel P. Berrange 已提交
1304
        } else if (!strcmp((char *)prop, "sdl")) {
1305
            def->graphicsType = QEMUD_GRAPHICS_SDL;
D
Daniel P. Berrange 已提交
1306
        } else {
1307
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "Unsupported graphics type %s", prop);
D
Daniel P. Berrange 已提交
1308 1309
            goto error;
        }
1310
        xmlFree(prop);
1311
        prop = NULL;
D
Daniel P. Berrange 已提交
1312
    }
1313
    xmlXPathFreeObject(obj);
D
Daniel P. Berrange 已提交
1314 1315 1316 1317 1318

    /* 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)) {
1319
        struct qemud_vm_disk_def *prev = NULL;
D
Daniel P. Berrange 已提交
1320 1321
        for (i = 0; i < obj->nodesetval->nodeNr; i++) {
            struct qemud_vm_disk_def *disk;
1322
            if (!(disk = qemudParseDiskXML(conn, driver, obj->nodesetval->nodeTab[i]))) {
D
Daniel P. Berrange 已提交
1323 1324
                goto error;
            }
1325
            def->ndisks++;
1326 1327 1328 1329 1330 1331 1332
            disk->next = NULL;
            if (i == 0) {
                def->disks = disk;
            } else {
                prev->next = disk;
            }
            prev = disk;
D
Daniel P. Berrange 已提交
1333 1334 1335 1336 1337 1338 1339 1340 1341
        }
    }
    xmlXPathFreeObject(obj);


    /* analysis of the network devices */
    obj = xmlXPathEval(BAD_CAST "/domain/devices/interface", ctxt);
    if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
        (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) {
1342
        struct qemud_vm_net_def *prev = NULL;
D
Daniel P. Berrange 已提交
1343 1344
        for (i = 0; i < obj->nodesetval->nodeNr; i++) {
            struct qemud_vm_net_def *net;
1345
            if (!(net = qemudParseInterfaceXML(conn, driver, obj->nodesetval->nodeTab[i]))) {
D
Daniel P. Berrange 已提交
1346 1347
                goto error;
            }
1348
            def->nnets++;
1349 1350 1351 1352 1353 1354 1355
            net->next = NULL;
            if (i == 0) {
                def->nets = net;
            } else {
                prev->next = net;
            }
            prev = net;
D
Daniel P. Berrange 已提交
1356 1357 1358
        }
    }
    xmlXPathFreeObject(obj);
1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377

    /* 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;
            if (!(input = qemudParseInputXML(conn, driver, obj->nodesetval->nodeTab[i]))) {
                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;
1378
            if (def->inputs == NULL) {
1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413
                def->inputs = input;
            } else {
                prev->next = input;
            }
            prev = input;
        }
    }
    xmlXPathFreeObject(obj);
    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(struct qemud_vm_input_def));
            if (!input) {
                qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "input");
                goto error;
            }
            input->type = QEMU_INPUT_TYPE_MOUSE;
            input->bus = QEMU_INPUT_BUS_PS2;
            input->next = def->inputs;
            def->inputs = input;
            def->ninputs++;
        }
    }

D
Daniel P. Berrange 已提交
1414 1415
    xmlXPathFreeContext(ctxt);

1416
    return def;
D
Daniel P. Berrange 已提交
1417 1418 1419 1420 1421 1422 1423 1424

 error:
    if (prop)
        free(prop);
    if (obj)
        xmlXPathFreeObject(obj);
    if (ctxt)
        xmlXPathFreeContext(ctxt);
1425 1426
    qemudFreeVMDef(def);
    return NULL;
D
Daniel P. Berrange 已提交
1427 1428 1429
}


1430
static char *
1431 1432
qemudNetworkIfaceConnect(virConnectPtr conn,
                         struct qemud_driver *driver,
1433
                         struct qemud_vm *vm,
1434 1435
                         struct qemud_vm_net_def *net,
                         int vlan)
1436
{
1437 1438 1439
    struct qemud_network *network = NULL;
    char *brname;
    char *ifname;
1440 1441 1442 1443 1444 1445
    char tapfdstr[4+3+32+7];
    char *retval = NULL;
    int err;
    int tapfd = -1;
    int *tapfds;

1446
    if (net->type == QEMUD_NET_NETWORK) {
1447
        if (!(network = qemudFindNetworkByName(driver, net->dst.network.name))) {
1448
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1449 1450 1451
                             "Network '%s' not found", net->dst.network.name);
            goto error;
        } else if (network->bridge[0] == '\0') {
1452
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469
                             "Network '%s' not active", net->dst.network.name);
            goto error;
        }
        brname = network->bridge;
        if (net->dst.network.ifname[0] == '\0' ||
            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' ||
            strchr(net->dst.bridge.ifname, '%')) {
            strcpy(net->dst.bridge.ifname, "vnet%d");
        }
        ifname = net->dst.bridge.ifname;
    } else {
1470
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1471
                         "Network type %d is not supported", net->type);
1472 1473 1474
        goto error;
    }

1475
    if (!driver->brctl && (err = brInit(&driver->brctl))) {
1476
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1477 1478 1479 1480
                         "cannot initialize bridge support: %s", strerror(err));
        goto error;
    }

1481
    if ((err = brAddTap(driver->brctl, brname,
1482
                        ifname, BR_IFNAME_MAXLEN, &tapfd))) {
1483
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1484
                         "Failed to add tap interface '%s' to bridge '%s' : %s",
1485
                         ifname, brname, strerror(err));
1486 1487 1488
        goto error;
    }

1489
    snprintf(tapfdstr, sizeof(tapfdstr), "tap,fd=%d,script=,vlan=%d", tapfd, vlan);
1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503

    if (!(retval = strdup(tapfdstr)))
        goto no_memory;

    if (!(tapfds = realloc(vm->tapfds, sizeof(int) * (vm->ntapfds+2))))
        goto no_memory;

    vm->tapfds = tapfds;
    vm->tapfds[vm->ntapfds++] = tapfd;
    vm->tapfds[vm->ntapfds]   = -1;

    return retval;

 no_memory:
1504
    qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "tapfds");
1505 1506 1507 1508 1509 1510 1511 1512
 error:
    if (retval)
        free(retval);
    if (tapfd != -1)
        close(tapfd);
    return NULL;
}

D
Daniel P. Berrange 已提交
1513 1514 1515 1516
/*
 * Constructs a argv suitable for launching qemu with config defined
 * for a given virtual machine.
 */
1517 1518
int qemudBuildCommandLine(virConnectPtr conn,
                          struct qemud_driver *driver,
D
Daniel P. Berrange 已提交
1519
                          struct qemud_vm *vm,
1520 1521
                          char ***argv) {
    int len, n = -1, i;
D
Daniel P. Berrange 已提交
1522 1523 1524
    char memory[50];
    char vcpus[50];
    char boot[QEMUD_MAX_BOOT_DEVS+1];
1525
    struct stat sb;
1526 1527
    struct qemud_vm_disk_def *disk = vm->def->disks;
    struct qemud_vm_net_def *net = vm->def->nets;
1528
    struct qemud_vm_input_def *input = vm->def->inputs;
1529 1530
    struct utsname ut;
    int disableKQEMU = 0;
D
Daniel P. Berrange 已提交
1531

1532 1533 1534 1535 1536 1537 1538 1539
    /* Make sure the binary we are about to try exec'ing exists.
     * Technically we could catch the exec() failure, but that's
     * in a sub-process so its hard to feed back a useful error
     */
    if (stat(vm->def->os.binary, &sb) < 0) {
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                         "Cannot find QEMU binary %s: %s", vm->def->os.binary,
                         strerror(errno));
1540
        return -1;
1541 1542 1543 1544 1545 1546 1547 1548
    }

    if (vm->qemuVersion == 0) {
        if (qemudExtractVersionInfo(vm->def->os.binary,
                                    &(vm->qemuVersion),
                                    &(vm->qemuCmdFlags)) < 0)
            return -1;
    }
1549

1550 1551
    uname(&ut);

D
Daniel P. Berrange 已提交
1552
    /* Nasty hack make i?86 look like i686 to simplify next comparison */
1553 1554 1555 1556
    if (ut.machine[0] == 'i' &&
        ut.machine[2] == '8' &&
        ut.machine[3] == '6' &&
        !ut.machine[4])
D
Daniel P. Berrange 已提交
1557
        ut.machine[1] = '6';
1558 1559 1560 1561 1562 1563

    /* Need to explicitly disable KQEMU if
     * 1. Arch matches host arch
     * 2. Guest is 'qemu'
     * 3. The qemu binary has the -no-kqemu flag
     */
1564
    if ((vm->qemuCmdFlags & QEMUD_CMD_FLAG_KQEMU) &&
1565 1566 1567 1568
        !strcmp(ut.machine, vm->def->os.arch) &&
        vm->def->virtType == QEMUD_VIRT_QEMU)
        disableKQEMU = 1;

1569
    len = 1 + /* qemu */
D
Daniel P. Berrange 已提交
1570
        2 + /* machine type */
1571
        disableKQEMU + /* Disable kqemu */
1572 1573
        2 * vm->def->ndisks + /* disks*/
        (vm->def->nnets > 0 ? (4 * vm->def->nnets) : 2) + /* networks */
1574 1575
        1 + /* usb */
        2 * vm->def->ninputs + /* input devices */
D
Daniel P. Berrange 已提交
1576 1577 1578 1579
        2 + /* memory*/
        2 + /* cpus */
        2 + /* boot device */
        2 + /* monitor */
1580
        (vm->def->localtime ? 1 : 0) + /* localtime */
1581
        (vm->qemuCmdFlags & QEMUD_CMD_FLAG_NO_REBOOT &&
D
Daniel P. Berrange 已提交
1582
         vm->def->noReboot ? 1 : 0) + /* no-reboot */
1583 1584 1585 1586 1587
        (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 */
        (vm->def->graphicsType == QEMUD_GRAPHICS_VNC ? 2 :
1588 1589
         (vm->def->graphicsType == QEMUD_GRAPHICS_SDL ? 0 : 1)) + /* graphics */
        (vm->migrateFrom[0] ? 3 : 0); /* migrateFrom */
D
Daniel P. Berrange 已提交
1590

1591 1592
    snprintf(memory, sizeof(memory), "%d", vm->def->memory/1024);
    snprintf(vcpus, sizeof(vcpus), "%d", vm->def->vcpus);
D
Daniel P. Berrange 已提交
1593

1594
    if (!(*argv = malloc(sizeof(char *) * (len+1))))
D
Daniel P. Berrange 已提交
1595
        goto no_memory;
1596
    if (!((*argv)[++n] = strdup(vm->def->os.binary)))
D
Daniel P. Berrange 已提交
1597 1598 1599
        goto no_memory;
    if (!((*argv)[++n] = strdup("-M")))
        goto no_memory;
1600
    if (!((*argv)[++n] = strdup(vm->def->os.machine)))
D
Daniel P. Berrange 已提交
1601
        goto no_memory;
1602
    if (disableKQEMU) {
D
Daniel P. Berrange 已提交
1603
        if (!((*argv)[++n] = strdup("-no-kqemu")))
1604
            goto no_memory;
D
Daniel P. Berrange 已提交
1605 1606 1607 1608 1609 1610 1611 1612 1613 1614
    }
    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;

1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626
    /*
     * 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;
    }

D
Daniel P. Berrange 已提交
1627 1628 1629 1630 1631
    if (!((*argv)[++n] = strdup("-monitor")))
        goto no_memory;
    if (!((*argv)[++n] = strdup("pty")))
        goto no_memory;

1632 1633 1634 1635 1636
    if (vm->def->localtime) {
        if (!((*argv)[++n] = strdup("-localtime")))
            goto no_memory;
    }

1637
    if (vm->qemuCmdFlags & QEMUD_CMD_FLAG_NO_REBOOT &&
D
Daniel P. Berrange 已提交
1638 1639 1640 1641 1642
        vm->def->noReboot) {
        if (!((*argv)[++n] = strdup("-no-reboot")))
            goto no_memory;
    }

1643
    if (!(vm->def->features & QEMUD_FEATURE_ACPI)) {
1644 1645
        if (!((*argv)[++n] = strdup("-no-acpi")))
            goto no_memory;
D
Daniel P. Berrange 已提交
1646 1647
    }

1648 1649
    for (i = 0 ; i < vm->def->os.nBootDevs ; i++) {
        switch (vm->def->os.bootDevs[i]) {
D
Daniel P. Berrange 已提交
1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666
        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;
        }
    }
1667
    boot[vm->def->os.nBootDevs] = '\0';
D
Daniel P. Berrange 已提交
1668 1669 1670 1671 1672
    if (!((*argv)[++n] = strdup("-boot")))
        goto no_memory;
    if (!((*argv)[++n] = strdup(boot)))
        goto no_memory;

1673
    if (vm->def->os.kernel[0]) {
D
Daniel P. Berrange 已提交
1674 1675
        if (!((*argv)[++n] = strdup("-kernel")))
            goto no_memory;
1676
        if (!((*argv)[++n] = strdup(vm->def->os.kernel)))
D
Daniel P. Berrange 已提交
1677 1678
            goto no_memory;
    }
1679
    if (vm->def->os.initrd[0]) {
D
Daniel P. Berrange 已提交
1680 1681
        if (!((*argv)[++n] = strdup("-initrd")))
            goto no_memory;
1682
        if (!((*argv)[++n] = strdup(vm->def->os.initrd)))
D
Daniel P. Berrange 已提交
1683 1684
            goto no_memory;
    }
1685
    if (vm->def->os.cmdline[0]) {
D
Daniel P. Berrange 已提交
1686 1687
        if (!((*argv)[++n] = strdup("-append")))
            goto no_memory;
1688
        if (!((*argv)[++n] = strdup(vm->def->os.cmdline)))
D
Daniel P. Berrange 已提交
1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715
            goto no_memory;
    }

    while (disk) {
        char dev[NAME_MAX];
        char file[PATH_MAX];
        if (!strcmp(disk->dst, "hdc") &&
            disk->device == QEMUD_DISK_CDROM)
            snprintf(dev, NAME_MAX, "-%s", "cdrom");
        else
            snprintf(dev, NAME_MAX, "-%s", disk->dst);
        snprintf(file, PATH_MAX, "%s", disk->src);

        if (!((*argv)[++n] = strdup(dev)))
            goto no_memory;
        if (!((*argv)[++n] = strdup(file)))
            goto no_memory;

        disk = disk->next;
    }

    if (!net) {
        if (!((*argv)[++n] = strdup("-net")))
            goto no_memory;
        if (!((*argv)[++n] = strdup("none")))
            goto no_memory;
    } else {
1716
        int vlan = 0;
D
Daniel P. Berrange 已提交
1717
        while (net) {
1718
            char nic[100];
1719

1720 1721 1722 1723 1724 1725
            if (snprintf(nic, sizeof(nic), "nic,macaddr=%02x:%02x:%02x:%02x:%02x:%02x,vlan=%d",
                         net->mac[0], net->mac[1],
                         net->mac[2], net->mac[3],
                         net->mac[4], net->mac[5],
                         vlan) >= sizeof(nic))
                goto error;
D
Daniel P. Berrange 已提交
1726 1727 1728 1729 1730

            if (!((*argv)[++n] = strdup("-net")))
                goto no_memory;
            if (!((*argv)[++n] = strdup(nic)))
                goto no_memory;
1731

D
Daniel P. Berrange 已提交
1732 1733
            if (!((*argv)[++n] = strdup("-net")))
                goto no_memory;
1734

1735 1736 1737
            switch (net->type) {
            case QEMUD_NET_NETWORK:
            case QEMUD_NET_BRIDGE:
1738
                if (!((*argv)[++n] = qemudNetworkIfaceConnect(conn, driver, vm, net, vlan)))
1739
                    goto error;
1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794
                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;
                }
1795
            }
D
Daniel P. Berrange 已提交
1796 1797

            net = net->next;
1798
            vlan++;
D
Daniel P. Berrange 已提交
1799 1800 1801
        }
    }

1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814
    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;
    }

1815
    if (vm->def->graphicsType == QEMUD_GRAPHICS_VNC) {
D
Daniel P. Berrange 已提交
1816
        char vncdisplay[PATH_MAX];
1817
        int ret;
D
Daniel P. Berrange 已提交
1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832

        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",
1833
                           vm->def->vncListen,
D
Daniel P. Berrange 已提交
1834 1835 1836
                           vm->def->vncActivePort - 5900,
                           options);
        } else {
1837 1838
            ret = snprintf(vncdisplay, sizeof(vncdisplay), "%d",
                           vm->def->vncActivePort - 5900);
D
Daniel P. Berrange 已提交
1839
        }
1840
        if (ret < 0 || ret >= (int)sizeof(vncdisplay))
1841 1842
            goto error;

D
Daniel P. Berrange 已提交
1843 1844
        if (!((*argv)[++n] = strdup("-vnc")))
            goto no_memory;
1845
        if (!((*argv)[++n] = strdup(vncdisplay)))
D
Daniel P. Berrange 已提交
1846
            goto no_memory;
1847
    } else if (vm->def->graphicsType == QEMUD_GRAPHICS_NONE) {
1848
        /* Nada - we added -nographic earlier in this function */
D
Daniel P. Berrange 已提交
1849 1850 1851 1852
    } else {
        /* SDL is the default. no args needed */
    }

1853 1854 1855 1856 1857 1858 1859 1860 1861
    if (vm->migrateFrom[0]) {
        if (!((*argv)[++n] = strdup("-S")))
            goto no_memory;
        if (!((*argv)[++n] = strdup("-incoming")))
            goto no_memory;
        if (!((*argv)[++n] = strdup(vm->migrateFrom)))
            goto no_memory;
    }

D
Daniel P. Berrange 已提交
1862 1863 1864 1865 1866
    (*argv)[++n] = NULL;

    return 0;

 no_memory:
1867
    qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "argv");
1868 1869 1870 1871 1872 1873 1874 1875
 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;
    }
D
Daniel P. Berrange 已提交
1876 1877
    if (argv) {
        for (i = 0 ; i < n ; i++)
1878 1879
            free((*argv)[i]);
        free(*argv);
D
Daniel P. Berrange 已提交
1880 1881 1882 1883 1884 1885
    }
    return -1;
}


/* Save a guest's config data into a persistent file */
1886 1887
static int qemudSaveConfig(virConnectPtr conn,
                           struct qemud_driver *driver,
1888 1889
                           struct qemud_vm *vm,
                           struct qemud_vm_def *def) {
D
Daniel P. Berrange 已提交
1890 1891 1892 1893
    char *xml;
    int fd = -1, ret = -1;
    int towrite;

1894
    if (!(xml = qemudGenerateXML(conn, driver, vm, def, 0)))
D
Daniel P. Berrange 已提交
1895 1896 1897 1898 1899
        return -1;

    if ((fd = open(vm->configFile,
                   O_WRONLY | O_CREAT | O_TRUNC,
                   S_IRUSR | S_IWUSR )) < 0) {
1900
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1901 1902
                         "cannot create config file %s: %s",
                         vm->configFile, strerror(errno));
D
Daniel P. Berrange 已提交
1903 1904 1905 1906 1907
        goto cleanup;
    }

    towrite = strlen(xml);
    if (write(fd, xml, towrite) != towrite) {
1908
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1909 1910
                         "cannot write config file %s: %s",
                         vm->configFile, strerror(errno));
D
Daniel P. Berrange 已提交
1911 1912 1913 1914
        goto cleanup;
    }

    if (close(fd) < 0) {
1915
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1916 1917
                         "cannot save config file %s: %s",
                         vm->configFile, strerror(errno));
D
Daniel P. Berrange 已提交
1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931
        goto cleanup;
    }

    ret = 0;

 cleanup:
    if (fd != -1)
        close(fd);

    free(xml);

    return ret;
}

1932
struct qemud_vm_def *
1933 1934
qemudParseVMDef(virConnectPtr conn,
                struct qemud_driver *driver,
1935 1936
                const char *xmlStr,
                const char *displayName) {
D
Daniel P. Berrange 已提交
1937
    xmlDocPtr xml;
1938
    struct qemud_vm_def *def = NULL;
D
Daniel P. Berrange 已提交
1939

1940
    if (!(xml = xmlReadDoc(BAD_CAST xmlStr, displayName ? displayName : "domain.xml", NULL,
D
Daniel P. Berrange 已提交
1941 1942
                           XML_PARSE_NOENT | XML_PARSE_NONET |
                           XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
1943
        qemudReportError(conn, NULL, NULL, VIR_ERR_XML_ERROR, NULL);
D
Daniel P. Berrange 已提交
1944 1945 1946
        return NULL;
    }

1947
    def = qemudParseXML(conn, driver, xml);
1948

D
Daniel P. Berrange 已提交
1949 1950
    xmlFreeDoc(xml);

1951 1952 1953 1954
    return def;
}

struct qemud_vm *
1955 1956
qemudAssignVMDef(virConnectPtr conn,
                 struct qemud_driver *driver,
1957 1958 1959 1960
                 struct qemud_vm_def *def)
{
    struct qemud_vm *vm = NULL;

1961
    if ((vm = qemudFindVMByName(driver, def->name))) {
1962
        if (!qemudIsActiveVM(vm)) {
1963 1964 1965 1966 1967 1968 1969
            qemudFreeVMDef(vm->def);
            vm->def = def;
        } else {
            if (vm->newDef)
                qemudFreeVMDef(vm->newDef);
            vm->newDef = def;
        }
1970 1971 1972
        /* Reset version, because the emulator path might have changed */
        vm->qemuVersion = 0;
        vm->qemuCmdFlags = 0;
1973
        return vm;
D
Daniel P. Berrange 已提交
1974 1975
    }

1976
    if (!(vm = calloc(1, sizeof(struct qemud_vm)))) {
1977
        qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "vm");
1978 1979
        return NULL;
    }
D
Daniel P. Berrange 已提交
1980

1981
    vm->stdin = -1;
1982 1983 1984 1985 1986
    vm->stdout = -1;
    vm->stderr = -1;
    vm->monitor = -1;
    vm->pid = -1;
    vm->id = -1;
1987
    vm->state = VIR_DOMAIN_SHUTOFF;
1988
    vm->def = def;
1989
    vm->next = driver->vms;
1990

1991 1992
    driver->vms = vm;
    driver->ninactivevms++;
1993 1994 1995 1996 1997

    return vm;
}

void
1998
qemudRemoveInactiveVM(struct qemud_driver *driver,
1999 2000 2001 2002
                      struct qemud_vm *vm)
{
    struct qemud_vm *prev = NULL, *curr;

2003
    curr = driver->vms;
2004 2005 2006
    while (curr != vm) {
        prev = curr;
        curr = curr->next;
D
Daniel P. Berrange 已提交
2007 2008
    }

2009 2010 2011 2012
    if (curr) {
        if (prev)
            prev->next = curr->next;
        else
2013
            driver->vms = curr->next;
2014

2015
        driver->ninactivevms--;
2016 2017
    }

2018
    qemudFreeVM(vm);
D
Daniel P. Berrange 已提交
2019 2020
}

2021
int
2022 2023
qemudSaveVMDef(virConnectPtr conn,
               struct qemud_driver *driver,
2024 2025 2026 2027 2028
               struct qemud_vm *vm,
               struct qemud_vm_def *def) {
    if (vm->configFile[0] == '\0') {
        int err;

2029
        if ((err = qemudEnsureDir(driver->configDir))) {
2030
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
2031
                             "cannot create config directory %s: %s",
2032
                             driver->configDir, strerror(err));
2033 2034 2035
            return -1;
        }

2036
        if (qemudMakeConfigPath(driver->configDir, def->name, ".xml",
2037
                                vm->configFile, PATH_MAX) < 0) {
2038
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
2039 2040 2041
                             "cannot construct config file path");
            return -1;
        }
2042

2043
        if (qemudMakeConfigPath(driver->autostartDir, def->name, ".xml",
2044
                                vm->autostartLink, PATH_MAX) < 0) {
2045
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
2046 2047 2048 2049
                             "cannot construct autostart link path");
            vm->configFile[0] = '\0';
            return -1;
        }
2050 2051
    }

2052
    return qemudSaveConfig(conn, driver, vm, def);
2053
}
D
Daniel P. Berrange 已提交
2054

2055 2056
static int qemudSaveNetworkConfig(virConnectPtr conn,
                                  struct qemud_driver *driver,
2057 2058
                                  struct qemud_network *network,
                                  struct qemud_network_def *def) {
2059 2060 2061
    char *xml;
    int fd, ret = -1;
    int towrite;
2062
    int err;
2063

2064
    if (!(xml = qemudGenerateNetworkXML(conn, driver, network, def))) {
2065 2066 2067
        return -1;
    }

2068
    if ((err = qemudEnsureDir(driver->networkConfigDir))) {
2069
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
2070
                         "cannot create config directory %s: %s",
2071
                         driver->networkConfigDir, strerror(err));
2072 2073 2074 2075 2076 2077
        goto cleanup;
    }

    if ((fd = open(network->configFile,
                   O_WRONLY | O_CREAT | O_TRUNC,
                   S_IRUSR | S_IWUSR )) < 0) {
2078
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
2079 2080
                         "cannot create config file %s: %s",
                         network->configFile, strerror(errno));
2081 2082 2083 2084 2085
        goto cleanup;
    }

    towrite = strlen(xml);
    if (write(fd, xml, towrite) != towrite) {
2086
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
2087
                         "cannot write config file %s: %s",
2088
                         network->configFile, strerror(errno));
2089 2090 2091 2092
        goto cleanup;
    }

    if (close(fd) < 0) {
2093
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
2094
                         "cannot save config file %s: %s",
2095
                         network->configFile, strerror(errno));
2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107
        goto cleanup;
    }

    ret = 0;

 cleanup:

    free(xml);

    return ret;
}

2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123
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);
}
2124

2125
static int qemudParseBridgeXML(struct qemud_driver *driver ATTRIBUTE_UNUSED,
2126
                               struct qemud_network_def *def,
2127 2128 2129 2130 2131
                               xmlNodePtr node) {
    xmlChar *name, *stp, *delay;

    name = xmlGetProp(node, BAD_CAST "name");
    if (name != NULL) {
2132 2133
        strncpy(def->bridge, (const char *)name, IF_NAMESIZE-1);
        def->bridge[IF_NAMESIZE-1] = '\0';
2134 2135 2136 2137 2138 2139 2140
        xmlFree(name);
        name = NULL;
    }

    stp = xmlGetProp(node, BAD_CAST "stp");
    if (stp != NULL) {
        if (xmlStrEqual(stp, BAD_CAST "off")) {
2141
            def->disableSTP = 1;
2142 2143 2144 2145 2146 2147 2148
        }
        xmlFree(stp);
        stp = NULL;
    }

    delay = xmlGetProp(node, BAD_CAST "delay");
    if (delay != NULL) {
2149
        def->forwardDelay = strtol((const char *)delay, NULL, 10);
2150 2151 2152 2153 2154 2155 2156
        xmlFree(delay);
        delay = NULL;
    }

    return 1;
}

2157 2158
static int qemudParseDhcpRangesXML(virConnectPtr conn,
                                   struct qemud_driver *driver ATTRIBUTE_UNUSED,
2159
                                   struct qemud_network_def *def,
2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175
                                   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(struct qemud_dhcp_range_def)))) {
2176
            qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "range");
2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189
            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';

2190 2191 2192
            range->next = def->ranges;
            def->ranges = range;
            def->nranges++;
2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206
        } else {
            free(range);
        }

        if (start)
            xmlFree(start);
        if (end)
            xmlFree(end);

        cur = cur->next;
    }

    return 1;
}
2207

2208 2209
static int qemudParseInetXML(virConnectPtr conn,
                             struct qemud_driver *driver ATTRIBUTE_UNUSED,
2210
                             struct qemud_network_def *def,
2211 2212
                             xmlNodePtr node) {
    xmlChar *address, *netmask;
2213
    xmlNodePtr cur;
2214 2215 2216

    address = xmlGetProp(node, BAD_CAST "address");
    if (address != NULL) {
2217 2218
        strncpy(def->ipAddress, (const char *)address, BR_INET_ADDR_MAXLEN-1);
        def->ipAddress[BR_INET_ADDR_MAXLEN-1] = '\0';
2219 2220 2221 2222 2223 2224
        xmlFree(address);
        address = NULL;
    }

    netmask = xmlGetProp(node, BAD_CAST "netmask");
    if (netmask != NULL) {
2225 2226
        strncpy(def->netmask, (const char *)netmask, BR_INET_ADDR_MAXLEN-1);
        def->netmask[BR_INET_ADDR_MAXLEN-1] = '\0';
2227 2228 2229 2230
        xmlFree(netmask);
        netmask = NULL;
    }

2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245
    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);
    }

2246 2247 2248 2249
    cur = node->children;
    while (cur != NULL) {
        if (cur->type == XML_ELEMENT_NODE &&
            xmlStrEqual(cur->name, BAD_CAST "dhcp") &&
2250
            !qemudParseDhcpRangesXML(conn, driver, def, cur))
2251 2252 2253 2254
            return 0;
        cur = cur->next;
    }

2255 2256 2257 2258
    return 1;
}


2259 2260
static struct qemud_network_def *qemudParseNetworkXML(virConnectPtr conn,
                                                      struct qemud_driver *driver,
2261
                                                      xmlDocPtr xml) {
2262 2263
    xmlNodePtr root = NULL;
    xmlXPathContextPtr ctxt = NULL;
2264
    xmlXPathObjectPtr obj = NULL, tmp = NULL;
2265 2266 2267
    struct qemud_network_def *def;

    if (!(def = calloc(1, sizeof(struct qemud_network_def)))) {
2268
        qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "network_def");
2269 2270
        return NULL;
    }
2271 2272 2273 2274

    /* Prepare parser / xpath context */
    root = xmlDocGetRootElement(xml);
    if ((root == NULL) || (!xmlStrEqual(root->name, BAD_CAST "network"))) {
2275
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "incorrect root element");
2276 2277 2278 2279 2280
        goto error;
    }

    ctxt = xmlXPathNewContext(xml);
    if (ctxt == NULL) {
2281
        qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "xmlXPathContext");
2282 2283 2284 2285 2286 2287 2288 2289
        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)) {
2290
        qemudReportError(conn, NULL, NULL, VIR_ERR_NO_NAME, NULL);
2291 2292 2293
        goto error;
    }
    if (strlen((const char *)obj->stringval) >= (QEMUD_MAX_NAME_LEN-1)) {
2294
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "network name length too long");
2295 2296
        goto error;
    }
2297
    strcpy(def->name, (const char *)obj->stringval);
2298 2299 2300 2301 2302 2303 2304
    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)) {
2305
        int err;
D
Daniel P. Berrange 已提交
2306
        if ((err = virUUIDGenerate(def->uuid))) {
2307
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
2308 2309 2310
                             "Failed to generate UUID: %s", strerror(err));
            goto error;
        }
D
Daniel P. Berrange 已提交
2311
    } else if (virUUIDParse((const char *)obj->stringval, def->uuid) < 0) {
2312
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "malformed uuid element");
2313 2314 2315 2316
        goto error;
    }
    xmlXPathFreeObject(obj);

2317 2318 2319 2320
    /* 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)) {
2321
        if (!qemudParseBridgeXML(driver, def, obj->nodesetval->nodeTab[0])) {
2322 2323 2324 2325 2326 2327 2328 2329 2330
            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)) {
2331
        if (!qemudParseInetXML(conn, driver, def, obj->nodesetval->nodeTab[0])) {
2332 2333 2334 2335 2336 2337
            goto error;
        }
    }
    xmlXPathFreeObject(obj);

    /* IPv4 forwarding setup */
2338 2339 2340
    obj = xmlXPathEval(BAD_CAST "count(/network/forward) > 0", ctxt);
    if ((obj != NULL) && (obj->type == XPATH_BOOLEAN) &&
        obj->boolval) {
2341 2342
        if (!def->ipAddress[0] ||
            !def->netmask[0]) {
2343
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
2344 2345 2346 2347
                             "Forwarding requested, but no IPv4 address/netmask provided");
            goto error;
        }

2348 2349 2350 2351 2352 2353
        def->forward = 1;
        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)) {
2354
                qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369
                                 "forward device name '%s' is too long",
                                 (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);

2370 2371
    xmlXPathFreeContext(ctxt);

2372
    return def;
2373 2374 2375 2376 2377 2378

 error:
    /* XXX free all the stuff in the qemud_network struct, or leave it upto
       the caller ? */
    if (obj)
        xmlXPathFreeObject(obj);
2379 2380
    if (tmp)
        xmlXPathFreeObject(tmp);
2381 2382
    if (ctxt)
        xmlXPathFreeContext(ctxt);
2383 2384
    qemudFreeNetworkDef(def);
    return NULL;
2385 2386
}

2387
struct qemud_network_def *
2388 2389
qemudParseNetworkDef(virConnectPtr conn,
                     struct qemud_driver *driver,
2390 2391
                     const char *xmlStr,
                     const char *displayName) {
2392
    xmlDocPtr xml;
2393
    struct qemud_network_def *def;
2394

2395
    if (!(xml = xmlReadDoc(BAD_CAST xmlStr, displayName ? displayName : "network.xml", NULL,
2396 2397
                           XML_PARSE_NOENT | XML_PARSE_NONET |
                           XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
2398
        qemudReportError(conn, NULL, NULL, VIR_ERR_XML_ERROR, NULL);
2399 2400 2401
        return NULL;
    }

2402
    def = qemudParseNetworkXML(conn, driver, xml);
2403

2404 2405
    xmlFreeDoc(xml);

2406 2407 2408 2409
    return def;
}

struct qemud_network *
2410 2411
qemudAssignNetworkDef(virConnectPtr conn,
                      struct qemud_driver *driver,
2412 2413 2414
                      struct qemud_network_def *def) {
    struct qemud_network *network;

2415
    if ((network = qemudFindNetworkByName(driver, def->name))) {
2416
        if (!qemudIsActiveNetwork(network)) {
2417 2418 2419 2420 2421 2422 2423 2424
            qemudFreeNetworkDef(network->def);
            network->def = def;
        } else {
            if (network->newDef)
                qemudFreeNetworkDef(network->newDef);
            network->newDef = def;
        }

2425
        return network;
2426 2427
    }

2428
    if (!(network = calloc(1, sizeof(struct qemud_network)))) {
2429
        qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "network");
2430 2431
        return NULL;
    }
2432

2433
    network->def = def;
2434
    network->next = driver->networks;
2435

2436 2437
    driver->networks = network;
    driver->ninactivenetworks++;
2438 2439 2440 2441 2442

    return network;
}

void
2443
qemudRemoveInactiveNetwork(struct qemud_driver *driver,
2444 2445 2446 2447
                           struct qemud_network *network)
{
    struct qemud_network *prev = NULL, *curr;

2448
    curr = driver->networks;
2449 2450 2451
    while (curr != network) {
        prev = curr;
        curr = curr->next;
2452 2453
    }

2454 2455 2456 2457
    if (curr) {
        if (prev)
            prev->next = curr->next;
        else
2458
            driver->networks = curr->next;
2459

2460
        driver->ninactivenetworks--;
2461 2462
    }

2463
    qemudFreeNetwork(network);
2464 2465
}

2466
int
2467 2468
qemudSaveNetworkDef(virConnectPtr conn,
                    struct qemud_driver *driver,
2469 2470 2471 2472 2473 2474
                    struct qemud_network *network,
                    struct qemud_network_def *def) {

    if (network->configFile[0] == '\0') {
        int err;

2475
        if ((err = qemudEnsureDir(driver->networkConfigDir))) {
2476
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
2477
                             "cannot create config directory %s: %s",
2478
                             driver->networkConfigDir, strerror(err));
2479 2480 2481
            return -1;
        }

2482
        if (qemudMakeConfigPath(driver->networkConfigDir, def->name, ".xml",
2483
                                network->configFile, PATH_MAX) < 0) {
2484
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
2485 2486 2487
                             "cannot construct config file path");
            return -1;
        }
2488

2489
        if (qemudMakeConfigPath(driver->networkAutostartDir, def->name, ".xml",
2490
                                network->autostartLink, PATH_MAX) < 0) {
2491
            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
2492 2493 2494 2495
                             "cannot construct autostart link path");
            network->configFile[0] = '\0';
            return -1;
        }
2496 2497
    }

2498
    return qemudSaveNetworkConfig(conn, driver, network, def);
2499
}
2500

2501 2502 2503 2504
static int
qemudReadFile(const char *path,
              char *buf,
              int maxlen) {
D
Daniel P. Berrange 已提交
2505 2506
    FILE *fh;
    struct stat st;
2507
    int ret = 0;
D
Daniel P. Berrange 已提交
2508

2509 2510 2511 2512
    if (!(fh = fopen(path, "r"))) {
        qemudLog(QEMUD_WARN, "Failed to open file '%s': %s",
                 path, strerror(errno));
        goto error;
D
Daniel P. Berrange 已提交
2513 2514 2515
    }

    if (fstat(fileno(fh), &st) < 0) {
2516 2517 2518
        qemudLog(QEMUD_WARN, "Failed to stat file '%s': %s",
                 path, strerror(errno));
        goto error;
D
Daniel P. Berrange 已提交
2519 2520
    }

2521 2522 2523
    if (S_ISDIR(st.st_mode)) {
        qemudDebug("Ignoring directory '%s' - clearly not a config file", path);
        goto error;
D
Daniel P. Berrange 已提交
2524 2525
    }

2526 2527 2528
    if (st.st_size >= maxlen) {
        qemudLog(QEMUD_WARN, "File '%s' is too large", path);
        goto error;
D
Daniel P. Berrange 已提交
2529 2530
    }

2531 2532 2533 2534
    if ((ret = fread(buf, st.st_size, 1, fh)) != 1) {
        qemudLog(QEMUD_WARN, "Failed to read config file '%s': %s",
                 path, strerror(errno));
        goto error;
D
Daniel P. Berrange 已提交
2535
    }
2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561

    buf[st.st_size] = '\0';

    ret = 1;

 error:
    if (fh)
        fclose(fh);

    return ret;
}

static int
compareFileToNameSuffix(const char *file,
                        const char *name,
                        const char *suffix) {
    int filelen = strlen(file);
    int namelen = strlen(name);
    int suffixlen = strlen(suffix);

    if (filelen == (namelen + suffixlen) &&
        !strncmp(file, name, namelen) &&
        !strncmp(file + namelen, suffix, suffixlen))
        return 1;
    else
        return 0;
D
Daniel P. Berrange 已提交
2562 2563
}

2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576
static int
hasSuffix(const char *str,
          const char *suffix)
{
    int len = strlen(str);
    int suffixlen = strlen(suffix);

    if (len < suffixlen)
        return 0;

    return strcmp(str + len - suffixlen, suffix) == 0;
}

2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669
static int
checkLinkPointsTo(const char *checkLink,
                  const char *checkDest)
{
    char dest[PATH_MAX];
    char real[PATH_MAX];
    char checkReal[PATH_MAX];
    int n;
    int passed = 0;

    /* read the link destination */
    if ((n = readlink(checkLink, dest, PATH_MAX)) < 0) {
        switch (errno) {
        case ENOENT:
        case ENOTDIR:
            break;

        case EINVAL:
            qemudLog(QEMUD_WARN, "Autostart file '%s' is not a symlink",
                     checkLink);
            break;

        default:
            qemudLog(QEMUD_WARN, "Failed to read autostart symlink '%s': %s",
                     checkLink, strerror(errno));
            break;
        }

        goto failed;
    } else if (n >= PATH_MAX) {
        qemudLog(QEMUD_WARN, "Symlink '%s' contents too long to fit in buffer",
                 checkLink);
        goto failed;
    }

    dest[n] = '\0';

    /* make absolute */
    if (dest[0] != '/') {
        char dir[PATH_MAX];
        char tmp[PATH_MAX];
        char *p;

        strncpy(dir, checkLink, PATH_MAX);
        dir[PATH_MAX] = '\0';

        if (!(p = strrchr(dir, '/'))) {
            qemudLog(QEMUD_WARN, "Symlink path '%s' is not absolute", checkLink);
            goto failed;
        }

        if (p == dir) /* handle unlikely root dir case */
            p++;

        *p = '\0';

        if (qemudMakeConfigPath(dir, dest, NULL, tmp, PATH_MAX) < 0) {
            qemudLog(QEMUD_WARN, "Path '%s/%s' is too long", dir, dest);
            goto failed;
        }

        strncpy(dest, tmp, PATH_MAX);
        dest[PATH_MAX] = '\0';
    }

    /* canonicalize both paths */
    if (!realpath(dest, real)) {
        qemudLog(QEMUD_WARN, "Failed to expand path '%s' :%s",
                 dest, strerror(errno));
        strncpy(real, dest, PATH_MAX);
        real[PATH_MAX] = '\0';
    }

    if (!realpath(checkDest, checkReal)) {
        qemudLog(QEMUD_WARN, "Failed to expand path '%s' :%s",
                 checkDest, strerror(errno));
        strncpy(checkReal, checkDest, PATH_MAX);
        checkReal[PATH_MAX] = '\0';
    }

    /* compare */
    if (strcmp(checkReal, real) != 0) {
        qemudLog(QEMUD_WARN, "Autostart link '%s' is not a symlink to '%s', ignoring",
                 checkLink, checkReal);
        goto failed;
    }

    passed = 1;

 failed:
    return passed;
}

2670
static struct qemud_vm *
2671
qemudLoadConfig(struct qemud_driver *driver,
2672 2673
                const char *file,
                const char *path,
2674 2675
                const char *xml,
                const char *autostartLink) {
2676 2677 2678
    struct qemud_vm_def *def;
    struct qemud_vm *vm;

2679
    if (!(def = qemudParseVMDef(NULL, driver, xml, file))) {
2680
        virErrorPtr err = virGetLastError();
2681
        qemudLog(QEMUD_WARN, "Error parsing QEMU guest config '%s' : %s",
2682
                 path, err->message);
2683 2684 2685 2686 2687 2688 2689 2690 2691 2692
        return NULL;
    }

    if (!compareFileToNameSuffix(file, def->name, ".xml")) {
        qemudLog(QEMUD_WARN, "QEMU guest config filename '%s' does not match guest name '%s'",
                 path, def->name);
        qemudFreeVMDef(def);
        return NULL;
    }

2693
    if (!(vm = qemudAssignVMDef(NULL, driver, def))) {
2694 2695 2696 2697 2698 2699 2700 2701
        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';

2702 2703 2704 2705 2706
    strncpy(vm->autostartLink, autostartLink, PATH_MAX);
    vm->autostartLink[PATH_MAX-1] = '\0';

    vm->autostart = checkLinkPointsTo(vm->autostartLink, vm->configFile);

2707 2708 2709 2710
    return vm;
}

static struct qemud_network *
2711
qemudLoadNetworkConfig(struct qemud_driver *driver,
2712 2713
                       const char *file,
                       const char *path,
2714 2715
                       const char *xml,
                       const char *autostartLink) {
2716 2717 2718
    struct qemud_network_def *def;
    struct qemud_network *network;

2719
    if (!(def = qemudParseNetworkDef(NULL, driver, xml, file))) {
2720
        virErrorPtr err = virGetLastError();
2721
        qemudLog(QEMUD_WARN, "Error parsing network config '%s' : %s",
2722
                 path, err->message);
2723 2724 2725 2726 2727 2728 2729 2730 2731 2732
        return NULL;
    }

    if (!compareFileToNameSuffix(file, def->name, ".xml")) {
        qemudLog(QEMUD_WARN, "Network config filename '%s' does not match network name '%s'",
                 path, def->name);
        qemudFreeNetworkDef(def);
        return NULL;
    }

2733
    if (!(network = qemudAssignNetworkDef(NULL, driver, def))) {
2734 2735 2736 2737 2738 2739 2740 2741
        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';

2742 2743 2744 2745 2746
    strncpy(network->autostartLink, autostartLink, PATH_MAX);
    network->autostartLink[PATH_MAX-1] = '\0';

    network->autostart = checkLinkPointsTo(network->autostartLink, network->configFile);

2747 2748
    return network;
}
D
Daniel P. Berrange 已提交
2749

2750
static
2751
int qemudScanConfigDir(struct qemud_driver *driver,
2752
                       const char *configDir,
2753
                       const char *autostartDir,
2754
                       int isGuest) {
D
Daniel P. Berrange 已提交
2755 2756 2757
    DIR *dir;
    struct dirent *entry;

2758
    if (!(dir = opendir(configDir))) {
D
Daniel P. Berrange 已提交
2759 2760
        if (errno == ENOENT)
            return 0;
2761 2762
        qemudLog(QEMUD_ERR, "Failed to open dir '%s': %s",
                 configDir, strerror(errno));
D
Daniel P. Berrange 已提交
2763 2764 2765 2766
        return -1;
    }

    while ((entry = readdir(dir))) {
2767 2768
        char xml[QEMUD_MAX_XML_LEN];
        char path[PATH_MAX];
2769
        char autostartLink[PATH_MAX];
2770

D
Daniel P. Berrange 已提交
2771 2772 2773
        if (entry->d_name[0] == '.')
            continue;

2774 2775 2776
        if (!hasSuffix(entry->d_name, ".xml"))
            continue;

2777 2778 2779 2780 2781 2782
        if (qemudMakeConfigPath(configDir, entry->d_name, NULL, path, PATH_MAX) < 0) {
            qemudLog(QEMUD_WARN, "Config filename '%s/%s' is too long",
                     configDir, entry->d_name);
            continue;
        }

2783 2784 2785 2786 2787 2788
        if (qemudMakeConfigPath(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;
        }

2789
        if (!qemudReadFile(path, xml, QEMUD_MAX_XML_LEN))
D
Daniel P. Berrange 已提交
2790 2791
            continue;

2792
        if (isGuest)
2793
            qemudLoadConfig(driver, entry->d_name, path, xml, autostartLink);
2794
        else
2795
            qemudLoadNetworkConfig(driver, entry->d_name, path, xml, autostartLink);
D
Daniel P. Berrange 已提交
2796 2797 2798 2799 2800 2801 2802
    }

    closedir(dir);
 
    return 0;
}

2803
/* Scan for all guest and network config files */
2804 2805
int qemudScanConfigs(struct qemud_driver *driver) {
    if (qemudScanConfigDir(driver, driver->configDir, driver->autostartDir, 1) < 0)
2806
        return -1;
2807

2808
    if (qemudScanConfigDir(driver, driver->networkConfigDir, driver->networkAutostartDir, 0) < 0)
2809 2810 2811
        return -1;

    return 0;
2812
}
D
Daniel P. Berrange 已提交
2813 2814

/* Generate an XML document describing the guest's configuration */
2815 2816
char *qemudGenerateXML(virConnectPtr conn,
                       struct qemud_driver *driver ATTRIBUTE_UNUSED,
2817 2818 2819
                       struct qemud_vm *vm,
                       struct qemud_vm_def *def,
                       int live) {
D
Daniel P. Berrange 已提交
2820
    virBufferPtr buf = 0;
D
Daniel P. Berrange 已提交
2821
    unsigned char *uuid;
2822
    char uuidstr[VIR_UUID_STRING_BUFLEN];
D
Daniel P. Berrange 已提交
2823 2824
    struct qemud_vm_disk_def *disk;
    struct qemud_vm_net_def *net;
2825
    struct qemud_vm_input_def *input;
D
Daniel P. Berrange 已提交
2826 2827 2828
    const char *type = NULL;
    int n;

D
Daniel P. Berrange 已提交
2829
    buf = virBufferNew (QEMUD_MAX_XML_LEN);
2830
    if (!buf)
2831 2832
        goto no_memory;

2833
    switch (def->virtType) {
D
Daniel P. Berrange 已提交
2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844
    case QEMUD_VIRT_QEMU:
        type = "qemu";
        break;
    case QEMUD_VIRT_KQEMU:
        type = "kqemu";
        break;
    case QEMUD_VIRT_KVM:
        type = "kvm";
        break;
    }
    if (!type) {
2845
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "unexpected domain type %d", def->virtType);
D
Daniel P. Berrange 已提交
2846 2847 2848
        goto cleanup;
    }

2849
    if (qemudIsActiveVM(vm) && live) {
D
Daniel P. Berrange 已提交
2850
        if (virBufferVSprintf(buf, "<domain type='%s' id='%d'>\n", type, vm->id) < 0)
D
Daniel P. Berrange 已提交
2851 2852
            goto no_memory;
    } else {
D
Daniel P. Berrange 已提交
2853
        if (virBufferVSprintf(buf, "<domain type='%s'>\n", type) < 0)
D
Daniel P. Berrange 已提交
2854 2855 2856
            goto no_memory;
    }

D
Daniel P. Berrange 已提交
2857
    if (virBufferVSprintf(buf, "  <name>%s</name>\n", def->name) < 0)
D
Daniel P. Berrange 已提交
2858 2859
        goto no_memory;

2860
    uuid = def->uuid;
2861 2862
    virUUIDFormat(uuid, uuidstr);
    if (virBufferVSprintf(buf, "  <uuid>%s</uuid>\n", uuidstr) < 0)
D
Daniel P. Berrange 已提交
2863
        goto no_memory;
D
Daniel P. Berrange 已提交
2864
    if (virBufferVSprintf(buf, "  <memory>%d</memory>\n", def->maxmem) < 0)
D
Daniel P. Berrange 已提交
2865
        goto no_memory;
D
Daniel P. Berrange 已提交
2866
    if (virBufferVSprintf(buf, "  <currentMemory>%d</currentMemory>\n", def->memory) < 0)
D
Daniel P. Berrange 已提交
2867
        goto no_memory;
D
Daniel P. Berrange 已提交
2868
    if (virBufferVSprintf(buf, "  <vcpu>%d</vcpu>\n", def->vcpus) < 0)
D
Daniel P. Berrange 已提交
2869 2870
        goto no_memory;

D
Daniel P. Berrange 已提交
2871
    if (virBufferAdd(buf, "  <os>\n", -1) < 0)
D
Daniel P. Berrange 已提交
2872 2873
        goto no_memory;

2874
    if (def->virtType == QEMUD_VIRT_QEMU) {
D
Daniel P. Berrange 已提交
2875
        if (virBufferVSprintf(buf, "    <type arch='%s' machine='%s'>%s</type>\n",
2876
                              def->os.arch, def->os.machine, def->os.type) < 0)
D
Daniel P. Berrange 已提交
2877 2878
            goto no_memory;
    } else {
D
Daniel P. Berrange 已提交
2879
        if (virBufferVSprintf(buf, "    <type>%s</type>\n", def->os.type) < 0)
D
Daniel P. Berrange 已提交
2880 2881 2882
            goto no_memory;
    }

2883
    if (def->os.kernel[0])
D
Daniel P. Berrange 已提交
2884
        if (virBufferVSprintf(buf, "    <kernel>%s</kernel>\n", def->os.kernel) < 0)
D
Daniel P. Berrange 已提交
2885
            goto no_memory;
2886
    if (def->os.initrd[0])
D
Daniel P. Berrange 已提交
2887
        if (virBufferVSprintf(buf, "    <initrd>%s</initrd>\n", def->os.initrd) < 0)
D
Daniel P. Berrange 已提交
2888
            goto no_memory;
2889
    if (def->os.cmdline[0])
D
Daniel P. Berrange 已提交
2890
        if (virBufferVSprintf(buf, "    <cmdline>%s</cmdline>\n", def->os.cmdline) < 0)
D
Daniel P. Berrange 已提交
2891 2892
            goto no_memory;

2893
    for (n = 0 ; n < def->os.nBootDevs ; n++) {
D
Daniel P. Berrange 已提交
2894
        const char *boottype = "hd";
2895
        switch (def->os.bootDevs[n]) {
D
Daniel P. Berrange 已提交
2896 2897 2898 2899 2900 2901 2902 2903 2904 2905
        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:
2906
            boottype = "network";
D
Daniel P. Berrange 已提交
2907 2908
            break;
        }
D
Daniel P. Berrange 已提交
2909
        if (virBufferVSprintf(buf, "    <boot dev='%s'/>\n", boottype) < 0)
D
Daniel P. Berrange 已提交
2910 2911 2912
            goto no_memory;
    }

D
Daniel P. Berrange 已提交
2913
    if (virBufferAdd(buf, "  </os>\n", -1) < 0)
D
Daniel P. Berrange 已提交
2914 2915
        goto no_memory;

2916
    if (def->features & QEMUD_FEATURE_ACPI) {
D
Daniel P. Berrange 已提交
2917
        if (virBufferAdd(buf, "  <features>\n", -1) < 0)
2918
            goto no_memory;
D
Daniel P. Berrange 已提交
2919
        if (virBufferAdd(buf, "    <acpi/>\n", -1) < 0)
2920
            goto no_memory;
D
Daniel P. Berrange 已提交
2921
        if (virBufferAdd(buf, "  </features>\n", -1) < 0)
2922 2923 2924
            goto no_memory;
    }

2925 2926
    virBufferVSprintf(buf, "  <clock offset='%s'/>\n", def->localtime ? "localtime" : "utc");

D
Daniel P. Berrange 已提交
2927
    if (virBufferAdd(buf, "  <on_poweroff>destroy</on_poweroff>\n", -1) < 0)
D
Daniel P. Berrange 已提交
2928 2929
        goto no_memory;
    if (def->noReboot) {
D
Daniel P. Berrange 已提交
2930
        if (virBufferAdd(buf, "  <on_reboot>destroy</on_reboot>\n", -1) < 0)
D
Daniel P. Berrange 已提交
2931 2932
            goto no_memory;
    } else {
D
Daniel P. Berrange 已提交
2933
        if (virBufferAdd(buf, "  <on_reboot>restart</on_reboot>\n", -1) < 0)
D
Daniel P. Berrange 已提交
2934 2935
            goto no_memory;
    }
D
Daniel P. Berrange 已提交
2936
    if (virBufferAdd(buf, "  <on_crash>destroy</on_crash>\n", -1) < 0)
D
Daniel P. Berrange 已提交
2937
        goto no_memory;
2938

D
Daniel P. Berrange 已提交
2939
    if (virBufferAdd(buf, "  <devices>\n", -1) < 0)
D
Daniel P. Berrange 已提交
2940 2941
        goto no_memory;

D
Daniel P. Berrange 已提交
2942
    if (virBufferVSprintf(buf, "    <emulator>%s</emulator>\n", def->os.binary) < 0)
D
Daniel P. Berrange 已提交
2943 2944
        goto no_memory;

2945
    disk = def->disks;
D
Daniel P. Berrange 已提交
2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959
    while (disk) {
        const char *types[] = {
            "block",
            "file",
        };
        const char *typeAttrs[] = {
            "dev",
            "file",
        };
        const char *devices[] = {
            "disk",
            "cdrom",
            "floppy",
        };
D
Daniel P. Berrange 已提交
2960
        if (virBufferVSprintf(buf, "    <disk type='%s' device='%s'>\n",
D
Daniel P. Berrange 已提交
2961 2962 2963
                              types[disk->type], devices[disk->device]) < 0)
            goto no_memory;

D
Daniel P. Berrange 已提交
2964
        if (virBufferVSprintf(buf, "      <source %s='%s'/>\n", typeAttrs[disk->type], disk->src) < 0)
D
Daniel P. Berrange 已提交
2965 2966
            goto no_memory;

D
Daniel P. Berrange 已提交
2967
        if (virBufferVSprintf(buf, "      <target dev='%s'/>\n", disk->dst) < 0)
D
Daniel P. Berrange 已提交
2968 2969 2970
            goto no_memory;

        if (disk->readonly)
D
Daniel P. Berrange 已提交
2971
            if (virBufferAdd(buf, "      <readonly/>\n", -1) < 0)
D
Daniel P. Berrange 已提交
2972 2973
                goto no_memory;

D
Daniel P. Berrange 已提交
2974
        if (virBufferVSprintf(buf, "    </disk>\n") < 0)
D
Daniel P. Berrange 已提交
2975 2976 2977 2978 2979
            goto no_memory;

        disk = disk->next;
    }

2980
    net = def->nets;
2981
    while (net) {
D
Daniel P. Berrange 已提交
2982 2983
        const char *types[] = {
            "user",
2984
            "ethernet",
D
Daniel P. Berrange 已提交
2985 2986 2987
            "server",
            "client",
            "mcast",
2988
            "network",
2989
            "bridge",
D
Daniel P. Berrange 已提交
2990
        };
D
Daniel P. Berrange 已提交
2991
        if (virBufferVSprintf(buf, "    <interface type='%s'>\n",
D
Daniel P. Berrange 已提交
2992 2993 2994
                              types[net->type]) < 0)
            goto no_memory;

D
Daniel P. Berrange 已提交
2995
        if (virBufferVSprintf(buf, "      <mac address='%02x:%02x:%02x:%02x:%02x:%02x'/>\n",
D
Daniel P. Berrange 已提交
2996 2997 2998 2999
                              net->mac[0], net->mac[1], net->mac[2],
                              net->mac[3], net->mac[4], net->mac[5]) < 0)
            goto no_memory;

3000 3001
        switch (net->type) {
        case QEMUD_NET_NETWORK:
D
Daniel P. Berrange 已提交
3002
            if (virBufferVSprintf(buf, "      <source network='%s'/>\n", net->dst.network.name) < 0)
3003 3004
                goto no_memory;

3005
            if (net->dst.network.ifname[0] != '\0') {
D
Daniel P. Berrange 已提交
3006
                if (virBufferVSprintf(buf, "      <target dev='%s'/>\n", net->dst.network.ifname) < 0)
3007 3008 3009
                    goto no_memory;
            }
            break;
3010

3011 3012
        case QEMUD_NET_ETHERNET:
            if (net->dst.ethernet.ifname[0] != '\0') {
D
Daniel P. Berrange 已提交
3013
                if (virBufferVSprintf(buf, "      <target dev='%s'/>\n", net->dst.ethernet.ifname) < 0)
3014 3015 3016
                    goto no_memory;
            }
            if (net->dst.ethernet.script[0] != '\0') {
D
Daniel P. Berrange 已提交
3017
                if (virBufferVSprintf(buf, "      <script path='%s'/>\n", net->dst.ethernet.script) < 0)
3018 3019 3020 3021 3022
                    goto no_memory;
            }
            break;

        case QEMUD_NET_BRIDGE:
D
Daniel P. Berrange 已提交
3023
            if (virBufferVSprintf(buf, "      <source bridge='%s'/>\n", net->dst.bridge.brname) < 0)
3024
                goto no_memory;
3025
            if (net->dst.bridge.ifname[0] != '\0') {
D
Daniel P. Berrange 已提交
3026
                if (virBufferVSprintf(buf, "      <target dev='%s'/>\n", net->dst.bridge.ifname) < 0)
3027 3028 3029 3030 3031 3032 3033 3034
                    goto no_memory;
            }
            break;

        case QEMUD_NET_SERVER:
        case QEMUD_NET_CLIENT:
        case QEMUD_NET_MCAST:
            if (net->dst.socket.address[0] != '\0') {
D
Daniel P. Berrange 已提交
3035
                if (virBufferVSprintf(buf, "      <source address='%s' port='%d'/>\n",
3036 3037 3038
                                      net->dst.socket.address, net->dst.socket.port) < 0)
                    goto no_memory;
            } else {
D
Daniel P. Berrange 已提交
3039
                if (virBufferVSprintf(buf, "      <source port='%d'/>\n",
3040 3041 3042
                                      net->dst.socket.port) < 0)
                    goto no_memory;
            }
3043 3044
        }

D
Daniel P. Berrange 已提交
3045
        if (virBufferVSprintf(buf, "    </interface>\n") < 0)
D
Daniel P. Berrange 已提交
3046 3047
            goto no_memory;

3048
        net = net->next;
D
Daniel P. Berrange 已提交
3049 3050
    }

3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063
    input = def->inputs;
    while (input) {
        if (input->bus != QEMU_INPUT_BUS_PS2 &&
            virBufferVSprintf(buf, "    <input type='%s' bus='usb'/>\n",
                              input->type == QEMU_INPUT_TYPE_MOUSE ? "mouse" : "tablet") < 0)
            goto no_memory;
        input = input->next;
    }
    /* If graphics is enable, add implicit mouse */
    if (def->graphicsType != QEMUD_GRAPHICS_NONE)
        if (virBufferAdd(buf, "    <input type='mouse' bus='ps2'/>\n", -1) < 0)
            goto no_memory;

3064 3065
    switch (def->graphicsType) {
    case QEMUD_GRAPHICS_VNC:
D
Daniel P. Berrange 已提交
3066
        if (virBufferAdd(buf, "    <graphics type='vnc'", -1) < 0)
3067 3068 3069
            goto no_memory;

        if (def->vncPort &&
D
Daniel P. Berrange 已提交
3070
            virBufferVSprintf(buf, " port='%d'",
3071
                              qemudIsActiveVM(vm) && live ? def->vncActivePort : def->vncPort) < 0)
3072 3073
            goto no_memory;

3074 3075 3076 3077 3078
        if (def->vncListen[0] &&
            virBufferVSprintf(buf, " listen='%s'",
                              def->vncListen) < 0)
            goto no_memory;

D
Daniel P. Berrange 已提交
3079
        if (virBufferAdd(buf, "/>\n", -1) < 0)
3080 3081 3082 3083
            goto no_memory;
        break;

    case QEMUD_GRAPHICS_SDL:
D
Daniel P. Berrange 已提交
3084
        if (virBufferAdd(buf, "    <graphics type='sdl'/>\n", -1) < 0)
3085 3086 3087 3088 3089 3090 3091 3092
            goto no_memory;
        break;

    case QEMUD_GRAPHICS_NONE:
    default:
        break;
    }

3093
    if (def->graphicsType == QEMUD_GRAPHICS_VNC) {
D
Daniel P. Berrange 已提交
3094 3095
    }

D
Daniel P. Berrange 已提交
3096
    if (virBufferAdd(buf, "  </devices>\n", -1) < 0)
D
Daniel P. Berrange 已提交
3097 3098 3099
        goto no_memory;


D
Daniel P. Berrange 已提交
3100
    if (virBufferAdd(buf, "</domain>\n", -1) < 0)
D
Daniel P. Berrange 已提交
3101 3102
        goto no_memory;

D
Daniel P. Berrange 已提交
3103
    return virBufferContentAndFree (buf);
D
Daniel P. Berrange 已提交
3104 3105

 no_memory:
3106
    qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "xml");
D
Daniel P. Berrange 已提交
3107
 cleanup:
D
Daniel P. Berrange 已提交
3108
    if (buf) virBufferFree (buf);
D
Daniel P. Berrange 已提交
3109 3110 3111 3112
    return NULL;
}


3113 3114
char *qemudGenerateNetworkXML(virConnectPtr conn,
                              struct qemud_driver *driver ATTRIBUTE_UNUSED,
3115
                              struct qemud_network *network,
3116
                              struct qemud_network_def *def) {
D
Daniel P. Berrange 已提交
3117
    virBufferPtr buf = 0;
3118
    unsigned char *uuid;
3119
    char uuidstr[VIR_UUID_STRING_BUFLEN];
3120

D
Daniel P. Berrange 已提交
3121
    buf = virBufferNew (QEMUD_MAX_XML_LEN);
3122
    if (!buf)
3123 3124
        goto no_memory;

D
Daniel P. Berrange 已提交
3125
    if (virBufferVSprintf(buf, "<network>\n") < 0)
3126 3127
        goto no_memory;

D
Daniel P. Berrange 已提交
3128
    if (virBufferVSprintf(buf, "  <name>%s</name>\n", def->name) < 0)
3129 3130
        goto no_memory;

3131
    uuid = def->uuid;
3132 3133
    virUUIDFormat(uuid, uuidstr);
    if (virBufferVSprintf(buf, "  <uuid>%s</uuid>\n", uuidstr) < 0)
3134 3135
        goto no_memory;

3136 3137
    if (def->forward) {
        if (def->forwardDev[0]) {
D
Daniel P. Berrange 已提交
3138
            virBufferVSprintf(buf, "  <forward dev='%s'/>\n",
3139 3140
                              def->forwardDev);
        } else {
D
Daniel P. Berrange 已提交
3141
            virBufferAdd(buf, "  <forward/>\n", -1);
3142 3143 3144
        }
    }

D
Daniel P. Berrange 已提交
3145
    virBufferAdd(buf, "  <bridge", -1);
3146
    if (qemudIsActiveNetwork(network)) {
D
Daniel P. Berrange 已提交
3147
        if (virBufferVSprintf(buf, " name='%s'", network->bridge) < 0)
3148 3149
            goto no_memory;
    } else if (def->bridge[0]) {
D
Daniel P. Berrange 已提交
3150
        if (virBufferVSprintf(buf, " name='%s'", def->bridge) < 0)
3151 3152
            goto no_memory;
    }
D
Daniel P. Berrange 已提交
3153
    if (virBufferVSprintf(buf, " stp='%s' forwardDelay='%d' />\n",
3154 3155
                       def->disableSTP ? "off" : "on",
                       def->forwardDelay) < 0)
3156 3157
        goto no_memory;

3158
    if (def->ipAddress[0] || def->netmask[0]) {
D
Daniel P. Berrange 已提交
3159
        if (virBufferAdd(buf, "  <ip", -1) < 0)
3160 3161
            goto no_memory;

3162
        if (def->ipAddress[0] &&
D
Daniel P. Berrange 已提交
3163
            virBufferVSprintf(buf, " address='%s'", def->ipAddress) < 0)
3164 3165
            goto no_memory;

3166
        if (def->netmask[0] &&
D
Daniel P. Berrange 已提交
3167
            virBufferVSprintf(buf, " netmask='%s'", def->netmask) < 0)
3168 3169
            goto no_memory;

D
Daniel P. Berrange 已提交
3170
        if (virBufferAdd(buf, ">\n", -1) < 0)
3171 3172
            goto no_memory;

3173 3174
        if (def->ranges) {
            struct qemud_dhcp_range_def *range = def->ranges;
D
Daniel P. Berrange 已提交
3175
            if (virBufferAdd(buf, "    <dhcp>\n", -1) < 0)
3176 3177
                goto no_memory;
            while (range) {
D
Daniel P. Berrange 已提交
3178
                if (virBufferVSprintf(buf, "      <range start='%s' end='%s' />\n",
3179 3180 3181 3182
                                      range->start, range->end) < 0)
                    goto no_memory;
                range = range->next;
            }
D
Daniel P. Berrange 已提交
3183
            if (virBufferAdd(buf, "    </dhcp>\n", -1) < 0)
3184 3185 3186
                goto no_memory;
        }

D
Daniel P. Berrange 已提交
3187
        if (virBufferAdd(buf, "  </ip>\n", -1) < 0)
3188
            goto no_memory;
D
Daniel P. Berrange 已提交
3189 3190
    }

D
Daniel P. Berrange 已提交
3191
    if (virBufferAdd(buf, "</network>\n", -1) < 0)
3192 3193
        goto no_memory;

D
Daniel P. Berrange 已提交
3194
    return virBufferContentAndFree (buf);
3195 3196

 no_memory:
3197
    qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "xml");
D
Daniel P. Berrange 已提交
3198
    if (buf) virBufferFree (buf);
3199 3200 3201 3202
    return NULL;
}


3203 3204
int qemudDeleteConfig(virConnectPtr conn,
                      struct qemud_driver *driver ATTRIBUTE_UNUSED,
3205 3206 3207
                      const char *configFile,
                      const char *name) {
    if (!configFile[0]) {
3208
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "no config file for %s", name);
D
Daniel P. Berrange 已提交
3209 3210 3211
        return -1;
    }

3212
    if (unlink(configFile) < 0) {
3213
        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "cannot remove config for %s", name);
3214 3215
        return -1;
    }
3216

D
Daniel P. Berrange 已提交
3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228
    return 0;
}


/*
 * Local variables:
 *  indent-tabs-mode: nil
 *  c-indent-level: 4
 *  c-basic-offset: 4
 *  tab-width: 4
 * End:
 */