qemu_conf.c 89.2 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"
48

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

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
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);
}

69 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 132
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) {
        if (!memcmp(vm->def->uuid, uuid, QEMUD_UUID_RAW_LEN))
            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) {
        if (!memcmp(network->def->uuid, uuid, QEMUD_UUID_RAW_LEN))
            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;
}

133

134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
/* 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;

    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);
    }
149
    free(def);
150 151 152 153 154 155 156 157
}

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

159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
/* 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;
}

178
int
179 180 181 182 183 184
qemudEnsureDir(const char *path)
{
    struct stat st;
    char parent[PATH_MAX];
    char *p;
    int err;
185

186 187
    if (stat(path, &st) >= 0)
        return 0;
188

189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
    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;
205 206 207 208

    return 0;
}

D
Daniel P. Berrange 已提交
209 210 211
/* 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[] = {
212
    "pc", "isapc", NULL
D
Daniel P. Berrange 已提交
213 214
};
static const char *arch_info_mips_machines[] = {
215
    "mips", NULL
D
Daniel P. Berrange 已提交
216 217
};
static const char *arch_info_sparc_machines[] = {
218
    "sun4m", NULL
D
Daniel P. Berrange 已提交
219 220
};
static const char *arch_info_ppc_machines[] = {
221
    "g3bw", "mac99", "prep", NULL
D
Daniel P. Berrange 已提交
222 223 224
};

/* The archicture tables for supported QEMU archs */
225 226 227 228 229 230 231 232 233 234
struct qemu_arch_info qemudArchs[] = { 
    /* i686 must be in position 0 */
    {  "i686", 32, arch_info_x86_machines, "qemu" },
    /* x86_64 must be in position 1 */
    {  "x86_64", 64, arch_info_x86_machines, "qemu-system-x86_64" },
    {  "mips", 32, arch_info_mips_machines, "qemu-system-mips" },
    {  "mipsel", 32, arch_info_mips_machines, "qemu-system-mipsel" },
    {  "sparc", 32, arch_info_sparc_machines, "qemu-system-sparc" },
    {  "ppc", 32, arch_info_ppc_machines, "qemu-system-ppc" },
    { NULL, -1, NULL, NULL }
D
Daniel P. Berrange 已提交
235 236 237 238
};

/* Return the default architecture if none is explicitly requested*/
static const char *qemudDefaultArch(void) {
239
    return qemudArchs[0].arch;
D
Daniel P. Berrange 已提交
240 241 242 243 244 245
}

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

246 247 248
    for (i = 0; qemudArchs[i].arch; i++) {
        if (!strcmp(qemudArchs[i].arch, arch)) {
            return qemudArchs[i].machines[0];
D
Daniel P. Berrange 已提交
249 250 251 252 253 254 255 256 257 258
        }
    }

    return NULL;
}

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

259 260 261
    for (i = 0 ; qemudArchs[i].arch; i++) {
        if (!strcmp(qemudArchs[i].arch, arch)) {
            return qemudArchs[i].binary;
D
Daniel P. Berrange 已提交
262 263 264 265 266 267 268
        }
    }

    return NULL;
}

/* Find the fully qualified path to the binary for an architecture */
269
static char *qemudLocateBinaryForArch(struct qemud_driver *driver ATTRIBUTE_UNUSED,
D
Daniel P. Berrange 已提交
270 271 272 273 274 275 276 277 278 279
                                      int virtType, const char *arch) {
    const char *name;
    char *path;

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

    if (!name) {
280
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "cannot determin binary for architecture %s", arch);
D
Daniel P. Berrange 已提交
281 282 283 284 285 286
        return NULL;
    }

    /* XXX lame. should actually use $PATH ... */
    path = malloc(strlen(name) + strlen("/usr/bin/") + 1);
    if (!path) {
287
        qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_MEMORY, "path");
D
Daniel P. Berrange 已提交
288 289 290 291 292 293 294
        return NULL;
    }
    strcpy(path, "/usr/bin/");
    strcat(path, name);
    return path;
}

295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329

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 已提交
330
        char help[8192]; /* Ought to be enough to hold QEMU help screen */
331
        int got = 0, ret = -1;
332 333 334 335 336
        int major, minor, micro;

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

D
Daniel P. Berrange 已提交
337 338 339 340 341 342 343 344 345 346
        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;
347 348 349 350 351 352 353 354 355 356
        }
        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 已提交
357 358
        if (strstr(help, "-no-reboot"))
            *flags |= QEMUD_CMD_FLAG_NO_REBOOT;
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
        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;
    }
}

389
int qemudExtractVersion(struct qemud_driver *driver) {
390
    char *binary = NULL;
391
    struct stat sb;
392

393
    if (driver->qemuVersion > 0)
394 395
        return 0;

396
    if (!(binary = qemudLocateBinaryForArch(driver, QEMUD_VIRT_QEMU, "i686")))
397 398
        return -1;

399
    if (stat(binary, &sb) < 0) {
400
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
401 402 403 404 405 406
                         "Cannot find QEMU binary %s: %s", binary,
                         strerror(errno));
        free(binary);
        return -1;
    }

407
    if (qemudExtractVersionInfo(binary, &driver->qemuVersion, &driver->qemuCmdFlags) < 0) {
408 409 410 411 412 413 414 415 416
        free(binary);
        return -1;
    }

    free(binary);
    return 0;
}


D
Daniel P. Berrange 已提交
417
/* Parse the XML definition for a disk */
418
static struct qemud_vm_disk_def *qemudParseDiskXML(struct qemud_driver *driver ATTRIBUTE_UNUSED,
D
Daniel P. Berrange 已提交
419 420 421 422 423 424 425 426 427 428
                                                   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) {
429
        qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_MEMORY, "disk");
D
Daniel P. Berrange 已提交
430 431 432 433 434 435 436 437 438 439 440 441 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
        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) {
469
        qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_SOURCE, target ? "%s" : NULL, target);
D
Daniel P. Berrange 已提交
470 471 472
        goto error;
    }
    if (target == NULL) {
473
        qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_TARGET, source ? "%s" : NULL, source);
D
Daniel P. Berrange 已提交
474 475 476 477 478 479 480
        goto error;
    }

    if (device &&
        !strcmp((const char *)device, "floppy") &&
        strcmp((const char *)target, "fda") &&
        strcmp((const char *)target, "fdb")) {
481
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "Invalid floppy device name: %s", target);
D
Daniel P. Berrange 已提交
482 483 484 485 486 487
        goto error;
    }
  
    if (device &&
        !strcmp((const char *)device, "cdrom") &&
        strcmp((const char *)target, "hdc")) {
488
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "Invalid cdrom device name: %s", target);
D
Daniel P. Berrange 已提交
489 490 491 492 493 494 495 496 497 498 499 500
        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")) {
501
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "Invalid harddisk device name: %s", target);
D
Daniel P. Berrange 已提交
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
        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 {
521
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "Invalid device type: %s", device);
D
Daniel P. Berrange 已提交
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
        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;
}

544 545 546 547 548 549 550 551 552
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 已提交
553 554

/* Parse the XML definition for a network interface */
555
static struct qemud_vm_net_def *qemudParseInterfaceXML(struct qemud_driver *driver ATTRIBUTE_UNUSED,
D
Daniel P. Berrange 已提交
556 557 558 559 560
                                                       xmlNodePtr node) {
    struct qemud_vm_net_def *net = calloc(1, sizeof(struct qemud_vm_net_def));
    xmlNodePtr cur;
    xmlChar *macaddr = NULL;
    xmlChar *type = NULL;
561
    xmlChar *network = NULL;
562 563 564 565 566
    xmlChar *bridge = NULL;
    xmlChar *ifname = NULL;
    xmlChar *script = NULL;
    xmlChar *address = NULL;
    xmlChar *port = NULL;
D
Daniel P. Berrange 已提交
567 568

    if (!net) {
569
        qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_MEMORY, "net");
D
Daniel P. Berrange 已提交
570 571 572 573 574 575 576 577 578
        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;
579 580
        else if (xmlStrEqual(type, BAD_CAST "ethernet"))
            net->type = QEMUD_NET_ETHERNET;
D
Daniel P. Berrange 已提交
581 582 583 584 585 586
        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;
587 588
        else if (xmlStrEqual(type, BAD_CAST "network"))
            net->type = QEMUD_NET_NETWORK;
589 590
        else if (xmlStrEqual(type, BAD_CAST "bridge"))
            net->type = QEMUD_NET_BRIDGE;
D
Daniel P. Berrange 已提交
591 592 593 594 595 596 597 598 599 600 601 602
        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");
603 604 605 606
            } else if ((network == NULL) &&
                       (net->type == QEMUD_NET_NETWORK) &&
                       (xmlStrEqual(cur->name, BAD_CAST "source"))) {
                network = xmlGetProp(cur, BAD_CAST "network");
607 608 609
            } else if ((network == NULL) &&
                       (net->type == QEMUD_NET_BRIDGE) &&
                       (xmlStrEqual(cur->name, BAD_CAST "source"))) {
610
                bridge = xmlGetProp(cur, BAD_CAST "bridge");
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
            } 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 已提交
628 629 630 631 632 633
            }
        }
        cur = cur->next;
    }

    if (macaddr) {
634
        unsigned int mac[6];
D
Daniel P. Berrange 已提交
635
        sscanf((const char *)macaddr, "%02x:%02x:%02x:%02x:%02x:%02x",
636 637 638 639 640 641 642 643 644 645 646 647
               (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 已提交
648 649

        xmlFree(macaddr);
650 651 652
        macaddr = NULL;
    } else {
        qemudRandomMAC(net);
D
Daniel P. Berrange 已提交
653 654
    }

655 656 657 658
    if (net->type == QEMUD_NET_NETWORK) {
        int len;

        if (network == NULL) {
659
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
660 661
                             "No <source> 'network' attribute specified with <interface type='network'/>");
            goto error;
662
        } else if ((len = xmlStrlen(network)) >= (QEMUD_MAX_NAME_LEN-1)) {
663
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
664 665 666 667 668 669 670
                             "Network name '%s' too long", network);
            goto error;
        } else {
            strncpy(net->dst.network.name, (char *)network, len);
            net->dst.network.name[len] = '\0';
        }

671
        if (network) {
672
            xmlFree(network);
673 674 675 676 677
            network = NULL;
        }

        if (ifname != NULL) {
            if ((len = xmlStrlen(ifname)) >= (BR_IFNAME_MAXLEN-1)) {
678
                qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
679 680 681 682 683 684 685 686 687 688 689
                                 "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;
690

691 692
        if (script != NULL) {
            if ((len = xmlStrlen(script)) >= (PATH_MAX-1)) {
693
                qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
694 695 696 697 698 699 700 701 702 703 704
                                 "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)) {
705
                qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
706
                                 "TAP interface name '%s' is too long", ifname);
707 708
                goto error;
            } else {
709 710
                strncpy(net->dst.ethernet.ifname, (char *)ifname, len);
                net->dst.ethernet.ifname[len] = '\0';
711
            }
712 713 714 715 716 717
            xmlFree(ifname);
        }
    } else if (net->type == QEMUD_NET_BRIDGE) {
        int len;

        if (bridge == NULL) {
718
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
719 720 721
                             "No <source> 'dev' attribute specified with <interface type='bridge'/>");
            goto error;
        } else if ((len = xmlStrlen(bridge)) >= (BR_IFNAME_MAXLEN-1)) {
722
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
723 724 725 726 727
                             "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';
728
        }
729 730 731 732 733 734

        xmlFree(bridge);
        bridge = NULL;

        if (ifname != NULL) {
            if ((len = xmlStrlen(ifname)) >= (BR_IFNAME_MAXLEN-1)) {
735
                qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
                                 "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) {
        int len;
        char *ret;

        if (port == NULL) {
751
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
752 753 754 755 756
                             "No <source> 'port' attribute specified with socket interface");
            goto error;
        }
        if (!(net->dst.socket.port = strtol((char*)port, &ret, 10)) &&
            ret == (char*)port) {
757
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
758 759 760 761 762 763 764 765 766
                             "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) {
767
                qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
768 769 770 771
                                 "No <source> 'address' attribute specified with socket interface");
                goto error;
            }
        } else if ((len = xmlStrlen(address)) >= (BR_INET_ADDR_MAXLEN)) {
772
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
773 774 775 776 777 778 779 780 781 782
                             "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);
783 784
    }

D
Daniel P. Berrange 已提交
785
    return net;
786 787 788 789

 error:
    if (network)
        xmlFree(network);
790 791 792 793 794 795 796 797 798 799
    if (address)
        xmlFree(address);
    if (port)
        xmlFree(port);
    if (ifname)
        xmlFree(ifname);
    if (script)
        xmlFree(script);
    if (bridge)
        xmlFree(bridge);
800 801
    free(net);
    return NULL;
D
Daniel P. Berrange 已提交
802 803 804 805 806 807 808
}


/*
 * Parses a libvirt XML definition of a guest, and populates the
 * the qemud_vm struct with matching data about the guests config
 */
809
static struct qemud_vm_def *qemudParseXML(struct qemud_driver *driver,
810
                                          xmlDocPtr xml) {
D
Daniel P. Berrange 已提交
811 812 813 814 815 816
    xmlNodePtr root = NULL;
    xmlChar *prop = NULL;
    xmlXPathContextPtr ctxt = NULL;
    xmlXPathObjectPtr obj = NULL;
    char *conv = NULL;
    int i;
817 818 819
    struct qemud_vm_def *def;

    if (!(def = calloc(1, sizeof(struct qemud_vm_def)))) {
820
        qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_MEMORY, "xmlXPathContext");
821 822
        return NULL;
    }
D
Daniel P. Berrange 已提交
823 824 825 826

    /* Prepare parser / xpath context */
    root = xmlDocGetRootElement(xml);
    if ((root == NULL) || (!xmlStrEqual(root->name, BAD_CAST "domain"))) {
827
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "incorrect root element");
D
Daniel P. Berrange 已提交
828 829 830 831 832
        goto error;
    }

    ctxt = xmlXPathNewContext(xml);
    if (ctxt == NULL) {
833
        qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_MEMORY, "xmlXPathContext");
D
Daniel P. Berrange 已提交
834 835 836 837 838 839
        goto error;
    }


    /* Find out what type of QEMU virtualization to use */
    if (!(prop = xmlGetProp(root, BAD_CAST "type"))) {
840
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "missing domain type attribute");
D
Daniel P. Berrange 已提交
841 842 843 844
        goto error;
    }

    if (!strcmp((char *)prop, "qemu"))
845
        def->virtType = QEMUD_VIRT_QEMU;
D
Daniel P. Berrange 已提交
846
    else if (!strcmp((char *)prop, "kqemu"))
847
        def->virtType = QEMUD_VIRT_KQEMU;
D
Daniel P. Berrange 已提交
848
    else if (!strcmp((char *)prop, "kvm"))
849
        def->virtType = QEMUD_VIRT_KVM;
D
Daniel P. Berrange 已提交
850
    else {
851
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "invalid domain type attribute");
D
Daniel P. Berrange 已提交
852 853 854 855 856 857 858 859 860 861
        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)) {
862
        qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_NAME, NULL);
D
Daniel P. Berrange 已提交
863 864 865
        goto error;
    }
    if (strlen((const char *)obj->stringval) >= (QEMUD_MAX_NAME_LEN-1)) {
866
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "domain name length too long");
D
Daniel P. Berrange 已提交
867 868
        goto error;
    }
869
    strcpy(def->name, (const char *)obj->stringval);
D
Daniel P. Berrange 已提交
870 871 872 873 874 875 876
    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)) {
877
        int err;
D
Daniel P. Berrange 已提交
878
        if ((err = virUUIDGenerate(def->uuid))) {
879
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
880 881 882
                             "Failed to generate UUID: %s", strerror(err));
            goto error;
        }
D
Daniel P. Berrange 已提交
883
    } else if (virUUIDParse((const char *)obj->stringval, def->uuid) < 0) {
884
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "malformed uuid element");
D
Daniel P. Berrange 已提交
885 886 887 888 889 890 891 892 893
        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)) {
894
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "missing memory element");
D
Daniel P. Berrange 已提交
895 896 897
        goto error;
    } else {
        conv = NULL;
898
        def->maxmem = strtoll((const char*)obj->stringval, &conv, 10);
D
Daniel P. Berrange 已提交
899
        if (conv == (const char*)obj->stringval) {
900
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "malformed memory information");
D
Daniel P. Berrange 已提交
901 902 903 904 905 906 907 908 909 910 911
            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)) {
912
        def->memory = def->maxmem;
D
Daniel P. Berrange 已提交
913 914
    } else {
        conv = NULL;
915 916 917
        def->memory = strtoll((const char*)obj->stringval, &conv, 10);
        if (def->memory > def->maxmem)
            def->memory = def->maxmem;
D
Daniel P. Berrange 已提交
918
        if (conv == (const char*)obj->stringval) {
919
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "malformed memory information");
D
Daniel P. Berrange 已提交
920 921 922 923 924 925 926 927 928 929
            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)) {
930
        def->vcpus = 1;
D
Daniel P. Berrange 已提交
931 932
    } else {
        conv = NULL;
933
        def->vcpus = strtoll((const char*)obj->stringval, &conv, 10);
D
Daniel P. Berrange 已提交
934
        if (conv == (const char*)obj->stringval) {
935
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "malformed vcpu information");
D
Daniel P. Berrange 已提交
936 937 938 939 940 941 942 943 944 945
            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)) {
946
        def->features |= QEMUD_FEATURE_ACPI;
D
Daniel P. Berrange 已提交
947
    }
948
    xmlXPathFreeObject(obj);
D
Daniel P. Berrange 已提交
949

D
Daniel P. Berrange 已提交
950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965

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


D
Daniel P. Berrange 已提交
966 967 968 969
    /* 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)) {
970
        qemudReportError(NULL, NULL, NULL, VIR_ERR_OS_TYPE, NULL);
D
Daniel P. Berrange 已提交
971 972 973
        goto error;
    }
    if (strcmp((const char *)obj->stringval, "hvm")) {
974
        qemudReportError(NULL, NULL, NULL, VIR_ERR_OS_TYPE, "%s", obj->stringval);
D
Daniel P. Berrange 已提交
975 976
        goto error;
    }
977
    strcpy(def->os.type, (const char *)obj->stringval);
D
Daniel P. Berrange 已提交
978 979 980 981 982 983 984 985
    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)) {
986
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "architecture type too long");
D
Daniel P. Berrange 已提交
987 988
            goto error;
        }
989
        strcpy(def->os.arch, defaultArch);
D
Daniel P. Berrange 已提交
990 991
    } else {
        if (strlen((const char *)obj->stringval) >= (QEMUD_OS_TYPE_MAX_LEN-1)) {
992
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "architecture type too long");
D
Daniel P. Berrange 已提交
993 994
            goto error;
        }
995
        strcpy(def->os.arch, (const char *)obj->stringval);
D
Daniel P. Berrange 已提交
996 997 998 999 1000 1001 1002
    }
    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)) {
1003
        const char *defaultMachine = qemudDefaultMachineForArch(def->os.arch);
D
Daniel P. Berrange 已提交
1004
        if (strlen(defaultMachine) >= (QEMUD_OS_MACHINE_MAX_LEN-1)) {
1005
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "machine type too long");
D
Daniel P. Berrange 已提交
1006 1007
            goto error;
        }
1008
        strcpy(def->os.machine, defaultMachine);
D
Daniel P. Berrange 已提交
1009 1010
    } else {
        if (strlen((const char *)obj->stringval) >= (QEMUD_OS_MACHINE_MAX_LEN-1)) {
1011
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "architecture type too long");
D
Daniel P. Berrange 已提交
1012 1013
            goto error;
        }
1014
        strcpy(def->os.machine, (const char *)obj->stringval);
D
Daniel P. Berrange 已提交
1015 1016 1017 1018 1019 1020 1021 1022 1023
    }
    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)) {
1024
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "kernel path too long");
D
Daniel P. Berrange 已提交
1025 1026
            goto error;
        }
1027
        strcpy(def->os.kernel, (const char *)obj->stringval);
D
Daniel P. Berrange 已提交
1028 1029 1030 1031 1032 1033 1034 1035 1036
    }
    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)) {
1037
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "initrd path too long");
D
Daniel P. Berrange 已提交
1038 1039
            goto error;
        }
1040
        strcpy(def->os.initrd, (const char *)obj->stringval);
D
Daniel P. Berrange 已提交
1041 1042 1043 1044 1045 1046 1047 1048 1049
    }
    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)) {
1050
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "cmdline arguments too long");
D
Daniel P. Berrange 已提交
1051 1052
            goto error;
        }
1053
        strcpy(def->os.cmdline, (const char *)obj->stringval);
D
Daniel P. Berrange 已提交
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
    }
    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++) {
1064 1065
            if (!(prop = xmlGetProp(obj->nodesetval->nodeTab[i], BAD_CAST "dev")))
                continue;
D
Daniel P. Berrange 已提交
1066
            if (!strcmp((char *)prop, "hd")) {
1067
                def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_DISK;
D
Daniel P. Berrange 已提交
1068
            } else if (!strcmp((char *)prop, "fd")) {
1069
                def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_FLOPPY;
D
Daniel P. Berrange 已提交
1070
            } else if (!strcmp((char *)prop, "cdrom")) {
1071
                def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_CDROM;
D
Daniel P. Berrange 已提交
1072
            } else if (!strcmp((char *)prop, "net")) {
1073
                def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_NET;
D
Daniel P. Berrange 已提交
1074
            } else {
1075
                xmlFree(prop);
D
Daniel P. Berrange 已提交
1076 1077
                goto error;
            }
1078
            xmlFree(prop);
1079
            prop = NULL;
D
Daniel P. Berrange 已提交
1080 1081 1082
        }
    }
    xmlXPathFreeObject(obj);
1083 1084 1085
    if (def->os.nBootDevs == 0) {
        def->os.nBootDevs = 1;
        def->os.bootDevs[0] = QEMUD_BOOT_DISK;
D
Daniel P. Berrange 已提交
1086 1087 1088 1089 1090 1091
    }


    obj = xmlXPathEval(BAD_CAST "string(/domain/devices/emulator[1])", ctxt);
    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
1092
        char *tmp = qemudLocateBinaryForArch(driver, def->virtType, def->os.arch);
D
Daniel P. Berrange 已提交
1093 1094 1095
        if (!tmp) {
            goto error;
        }
1096
        strcpy(def->os.binary, tmp);
D
Daniel P. Berrange 已提交
1097 1098 1099
        free(tmp);
    } else {
        if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) {
1100
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "emulator path too long");
D
Daniel P. Berrange 已提交
1101 1102
            goto error;
        }
1103
        strcpy(def->os.binary, (const char *)obj->stringval);
D
Daniel P. Berrange 已提交
1104 1105 1106 1107 1108 1109 1110
    }
    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)) {
1111
        def->graphicsType = QEMUD_GRAPHICS_NONE;
1112
    } else if ((prop = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "type"))) {
D
Daniel P. Berrange 已提交
1113
        if (!strcmp((char *)prop, "vnc")) {
1114
            def->graphicsType = QEMUD_GRAPHICS_VNC;
D
Daniel P. Berrange 已提交
1115 1116 1117
            prop = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "port");
            if (prop) {
                conv = NULL;
1118
                def->vncPort = strtoll((const char*)prop, &conv, 10);
D
Daniel P. Berrange 已提交
1119
            } else {
1120
                def->vncPort = -1;
D
Daniel P. Berrange 已提交
1121 1122
            }
        } else if (!strcmp((char *)prop, "sdl")) {
1123
            def->graphicsType = QEMUD_GRAPHICS_SDL;
D
Daniel P. Berrange 已提交
1124
        } else {
1125
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "Unsupported graphics type %s", prop);
D
Daniel P. Berrange 已提交
1126 1127
            goto error;
        }
1128
        xmlFree(prop);
1129
        prop = NULL;
D
Daniel P. Berrange 已提交
1130
    }
1131
    xmlXPathFreeObject(obj);
D
Daniel P. Berrange 已提交
1132 1133 1134 1135 1136

    /* 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)) {
1137
        struct qemud_vm_disk_def *prev = NULL;
D
Daniel P. Berrange 已提交
1138 1139
        for (i = 0; i < obj->nodesetval->nodeNr; i++) {
            struct qemud_vm_disk_def *disk;
1140
            if (!(disk = qemudParseDiskXML(driver, obj->nodesetval->nodeTab[i]))) {
D
Daniel P. Berrange 已提交
1141 1142
                goto error;
            }
1143
            def->ndisks++;
1144 1145 1146 1147 1148 1149 1150
            disk->next = NULL;
            if (i == 0) {
                def->disks = disk;
            } else {
                prev->next = disk;
            }
            prev = disk;
D
Daniel P. Berrange 已提交
1151 1152 1153 1154 1155 1156 1157 1158 1159
        }
    }
    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)) {
1160
        struct qemud_vm_net_def *prev = NULL;
D
Daniel P. Berrange 已提交
1161 1162
        for (i = 0; i < obj->nodesetval->nodeNr; i++) {
            struct qemud_vm_net_def *net;
1163
            if (!(net = qemudParseInterfaceXML(driver, obj->nodesetval->nodeTab[i]))) {
D
Daniel P. Berrange 已提交
1164 1165
                goto error;
            }
1166
            def->nnets++;
1167 1168 1169 1170 1171 1172 1173
            net->next = NULL;
            if (i == 0) {
                def->nets = net;
            } else {
                prev->next = net;
            }
            prev = net;
D
Daniel P. Berrange 已提交
1174 1175 1176 1177 1178
        }
    }
    xmlXPathFreeObject(obj);
    xmlXPathFreeContext(ctxt);

1179
    return def;
D
Daniel P. Berrange 已提交
1180 1181 1182 1183 1184 1185 1186 1187

 error:
    if (prop)
        free(prop);
    if (obj)
        xmlXPathFreeObject(obj);
    if (ctxt)
        xmlXPathFreeContext(ctxt);
1188 1189
    qemudFreeVMDef(def);
    return NULL;
D
Daniel P. Berrange 已提交
1190 1191 1192
}


1193
static char *
1194
qemudNetworkIfaceConnect(struct qemud_driver *driver,
1195
                         struct qemud_vm *vm,
1196 1197
                         struct qemud_vm_net_def *net,
                         int vlan)
1198
{
1199 1200 1201
    struct qemud_network *network = NULL;
    char *brname;
    char *ifname;
1202 1203 1204 1205 1206 1207
    char tapfdstr[4+3+32+7];
    char *retval = NULL;
    int err;
    int tapfd = -1;
    int *tapfds;

1208
    if (net->type == QEMUD_NET_NETWORK) {
1209
        if (!(network = qemudFindNetworkByName(driver, net->dst.network.name))) {
1210
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1211 1212 1213
                             "Network '%s' not found", net->dst.network.name);
            goto error;
        } else if (network->bridge[0] == '\0') {
1214
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231
                             "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 {
1232
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1233
                         "Network type %d is not supported", net->type);
1234 1235 1236
        goto error;
    }

1237
    if (!driver->brctl && (err = brInit(&driver->brctl))) {
1238
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1239 1240 1241 1242
                         "cannot initialize bridge support: %s", strerror(err));
        goto error;
    }

1243
    if ((err = brAddTap(driver->brctl, brname,
1244
                        ifname, BR_IFNAME_MAXLEN, &tapfd))) {
1245
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1246
                         "Failed to add tap interface '%s' to bridge '%s' : %s",
1247
                         ifname, brname, strerror(err));
1248 1249 1250
        goto error;
    }

1251
    snprintf(tapfdstr, sizeof(tapfdstr), "tap,fd=%d,script=,vlan=%d", tapfd, vlan);
1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265

    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:
1266
    qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_MEMORY, "tapfds");
1267 1268 1269 1270 1271 1272 1273 1274
 error:
    if (retval)
        free(retval);
    if (tapfd != -1)
        close(tapfd);
    return NULL;
}

D
Daniel P. Berrange 已提交
1275 1276 1277 1278
/*
 * Constructs a argv suitable for launching qemu with config defined
 * for a given virtual machine.
 */
1279
int qemudBuildCommandLine(struct qemud_driver *driver,
D
Daniel P. Berrange 已提交
1280
                          struct qemud_vm *vm,
1281 1282
                          char ***argv) {
    int len, n = -1, i;
D
Daniel P. Berrange 已提交
1283 1284 1285
    char memory[50];
    char vcpus[50];
    char boot[QEMUD_MAX_BOOT_DEVS+1];
1286
    struct stat sb;
1287 1288
    struct qemud_vm_disk_def *disk = vm->def->disks;
    struct qemud_vm_net_def *net = vm->def->nets;
1289 1290
    struct utsname ut;
    int disableKQEMU = 0;
D
Daniel P. Berrange 已提交
1291

1292
    if (qemudExtractVersion(driver) < 0)
1293 1294
        return -1;

1295 1296
    uname(&ut);

D
Daniel P. Berrange 已提交
1297
    /* Nasty hack make i?86 look like i686 to simplify next comparison */
1298 1299 1300 1301
    if (ut.machine[0] == 'i' &&
        ut.machine[2] == '8' &&
        ut.machine[3] == '6' &&
        !ut.machine[4])
D
Daniel P. Berrange 已提交
1302
        ut.machine[1] = '6';
1303 1304 1305 1306 1307 1308

    /* Need to explicitly disable KQEMU if
     * 1. Arch matches host arch
     * 2. Guest is 'qemu'
     * 3. The qemu binary has the -no-kqemu flag
     */
1309
    if ((driver->qemuCmdFlags & QEMUD_CMD_FLAG_KQEMU) &&
1310 1311 1312 1313 1314 1315 1316 1317 1318
        !strcmp(ut.machine, vm->def->os.arch) &&
        vm->def->virtType == QEMUD_VIRT_QEMU)
        disableKQEMU = 1;

    /* 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) {
1319
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1320 1321 1322 1323 1324
                         "Cannot find QEMU binary %s: %s", vm->def->os.binary,
                         strerror(errno));
        return -1;
    }

1325
    len = 1 + /* qemu */
D
Daniel P. Berrange 已提交
1326
        2 + /* machine type */
1327
        disableKQEMU + /* Disable kqemu */
1328 1329
        2 * vm->def->ndisks + /* disks*/
        (vm->def->nnets > 0 ? (4 * vm->def->nnets) : 2) + /* networks */
D
Daniel P. Berrange 已提交
1330 1331 1332 1333
        2 + /* memory*/
        2 + /* cpus */
        2 + /* boot device */
        2 + /* monitor */
1334
        (driver->qemuCmdFlags & QEMUD_CMD_FLAG_NO_REBOOT &&
D
Daniel P. Berrange 已提交
1335
         vm->def->noReboot ? 1 : 0) + /* no-reboot */
1336 1337 1338 1339 1340 1341
        (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 :
         (vm->def->graphicsType == QEMUD_GRAPHICS_SDL ? 0 : 1)); /* graphics */
D
Daniel P. Berrange 已提交
1342

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

1346
    if (!(*argv = malloc(sizeof(char *) * (len+1))))
D
Daniel P. Berrange 已提交
1347
        goto no_memory;
1348
    if (!((*argv)[++n] = strdup(vm->def->os.binary)))
D
Daniel P. Berrange 已提交
1349 1350 1351
        goto no_memory;
    if (!((*argv)[++n] = strdup("-M")))
        goto no_memory;
1352
    if (!((*argv)[++n] = strdup(vm->def->os.machine)))
D
Daniel P. Berrange 已提交
1353
        goto no_memory;
1354
    if (disableKQEMU) {
D
Daniel P. Berrange 已提交
1355
        if (!((*argv)[++n] = strdup("-no-kqemu")))
1356
            goto no_memory;
D
Daniel P. Berrange 已提交
1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371
    }
    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;

    if (!((*argv)[++n] = strdup("-monitor")))
        goto no_memory;
    if (!((*argv)[++n] = strdup("pty")))
        goto no_memory;

1372
    if (driver->qemuCmdFlags & QEMUD_CMD_FLAG_NO_REBOOT &&
D
Daniel P. Berrange 已提交
1373 1374 1375 1376 1377
        vm->def->noReboot) {
        if (!((*argv)[++n] = strdup("-no-reboot")))
            goto no_memory;
    }

1378
    if (!(vm->def->features & QEMUD_FEATURE_ACPI)) {
1379 1380
        if (!((*argv)[++n] = strdup("-no-acpi")))
            goto no_memory;
D
Daniel P. Berrange 已提交
1381 1382
    }

1383 1384
    for (i = 0 ; i < vm->def->os.nBootDevs ; i++) {
        switch (vm->def->os.bootDevs[i]) {
D
Daniel P. Berrange 已提交
1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401
        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;
        }
    }
1402
    boot[vm->def->os.nBootDevs] = '\0';
D
Daniel P. Berrange 已提交
1403 1404 1405 1406 1407
    if (!((*argv)[++n] = strdup("-boot")))
        goto no_memory;
    if (!((*argv)[++n] = strdup(boot)))
        goto no_memory;

1408
    if (vm->def->os.kernel[0]) {
D
Daniel P. Berrange 已提交
1409 1410
        if (!((*argv)[++n] = strdup("-kernel")))
            goto no_memory;
1411
        if (!((*argv)[++n] = strdup(vm->def->os.kernel)))
D
Daniel P. Berrange 已提交
1412 1413
            goto no_memory;
    }
1414
    if (vm->def->os.initrd[0]) {
D
Daniel P. Berrange 已提交
1415 1416
        if (!((*argv)[++n] = strdup("-initrd")))
            goto no_memory;
1417
        if (!((*argv)[++n] = strdup(vm->def->os.initrd)))
D
Daniel P. Berrange 已提交
1418 1419
            goto no_memory;
    }
1420
    if (vm->def->os.cmdline[0]) {
D
Daniel P. Berrange 已提交
1421 1422
        if (!((*argv)[++n] = strdup("-append")))
            goto no_memory;
1423
        if (!((*argv)[++n] = strdup(vm->def->os.cmdline)))
D
Daniel P. Berrange 已提交
1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450
            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 {
1451
        int vlan = 0;
D
Daniel P. Berrange 已提交
1452
        while (net) {
1453
            char nic[100];
1454

1455 1456 1457 1458 1459 1460
            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 已提交
1461 1462 1463 1464 1465

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

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

1470 1471 1472
            switch (net->type) {
            case QEMUD_NET_NETWORK:
            case QEMUD_NET_BRIDGE:
1473
                if (!((*argv)[++n] = qemudNetworkIfaceConnect(driver, vm, net, vlan)))
1474
                    goto error;
1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529
                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;
                }
1530
            }
D
Daniel P. Berrange 已提交
1531 1532

            net = net->next;
1533
            vlan++;
D
Daniel P. Berrange 已提交
1534 1535 1536
        }
    }

1537
    if (vm->def->graphicsType == QEMUD_GRAPHICS_VNC) {
D
Daniel P. Berrange 已提交
1538
        char port[10];
1539 1540
        int ret;
        ret = snprintf(port, sizeof(port),
1541
                       ((driver->qemuCmdFlags & QEMUD_CMD_FLAG_VNC_COLON) ?
1542 1543 1544 1545 1546
                        ":%d" : "%d"),
                       vm->def->vncActivePort - 5900);
        if (ret < 0 || ret >= (int)sizeof(port))
            goto error;

D
Daniel P. Berrange 已提交
1547 1548 1549 1550
        if (!((*argv)[++n] = strdup("-vnc")))
            goto no_memory;
        if (!((*argv)[++n] = strdup(port)))
            goto no_memory;
1551
    } else if (vm->def->graphicsType == QEMUD_GRAPHICS_NONE) {
D
Daniel P. Berrange 已提交
1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562
        if (!((*argv)[++n] = strdup("-nographic")))
            goto no_memory;
    } else {
        /* SDL is the default. no args needed */
    }

    (*argv)[++n] = NULL;

    return 0;

 no_memory:
1563
    qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_MEMORY, "argv");
1564 1565 1566 1567 1568 1569 1570 1571
 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 已提交
1572 1573
    if (argv) {
        for (i = 0 ; i < n ; i++)
1574 1575
            free((*argv)[i]);
        free(*argv);
D
Daniel P. Berrange 已提交
1576 1577 1578 1579 1580 1581
    }
    return -1;
}


/* Save a guest's config data into a persistent file */
1582
static int qemudSaveConfig(struct qemud_driver *driver,
1583 1584
                           struct qemud_vm *vm,
                           struct qemud_vm_def *def) {
D
Daniel P. Berrange 已提交
1585 1586 1587 1588
    char *xml;
    int fd = -1, ret = -1;
    int towrite;

1589
    if (!(xml = qemudGenerateXML(driver, vm, def, 0)))
D
Daniel P. Berrange 已提交
1590 1591 1592 1593 1594
        return -1;

    if ((fd = open(vm->configFile,
                   O_WRONLY | O_CREAT | O_TRUNC,
                   S_IRUSR | S_IWUSR )) < 0) {
1595
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1596 1597
                         "cannot create config file %s: %s",
                         vm->configFile, strerror(errno));
D
Daniel P. Berrange 已提交
1598 1599 1600 1601 1602
        goto cleanup;
    }

    towrite = strlen(xml);
    if (write(fd, xml, towrite) != towrite) {
1603
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1604 1605
                         "cannot write config file %s: %s",
                         vm->configFile, strerror(errno));
D
Daniel P. Berrange 已提交
1606 1607 1608 1609
        goto cleanup;
    }

    if (close(fd) < 0) {
1610
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1611 1612
                         "cannot save config file %s: %s",
                         vm->configFile, strerror(errno));
D
Daniel P. Berrange 已提交
1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626
        goto cleanup;
    }

    ret = 0;

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

    free(xml);

    return ret;
}

1627
struct qemud_vm_def *
1628
qemudParseVMDef(struct qemud_driver *driver,
1629 1630
                const char *xmlStr,
                const char *displayName) {
D
Daniel P. Berrange 已提交
1631
    xmlDocPtr xml;
1632
    struct qemud_vm_def *def = NULL;
D
Daniel P. Berrange 已提交
1633

1634
    if (!(xml = xmlReadDoc(BAD_CAST xmlStr, displayName ? displayName : "domain.xml", NULL,
D
Daniel P. Berrange 已提交
1635 1636
                           XML_PARSE_NOENT | XML_PARSE_NONET |
                           XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
1637
        qemudReportError(NULL, NULL, NULL, VIR_ERR_XML_ERROR, NULL);
D
Daniel P. Berrange 已提交
1638 1639 1640
        return NULL;
    }

1641
    def = qemudParseXML(driver, xml);
1642

D
Daniel P. Berrange 已提交
1643 1644
    xmlFreeDoc(xml);

1645 1646 1647 1648
    return def;
}

struct qemud_vm *
1649
qemudAssignVMDef(struct qemud_driver *driver,
1650 1651 1652 1653
                 struct qemud_vm_def *def)
{
    struct qemud_vm *vm = NULL;

1654
    if ((vm = qemudFindVMByName(driver, def->name))) {
1655
        if (!qemudIsActiveVM(vm)) {
1656 1657 1658 1659 1660 1661 1662
            qemudFreeVMDef(vm->def);
            vm->def = def;
        } else {
            if (vm->newDef)
                qemudFreeVMDef(vm->newDef);
            vm->newDef = def;
        }
D
Daniel P. Berrange 已提交
1663

1664
        return vm;
D
Daniel P. Berrange 已提交
1665 1666
    }

1667
    if (!(vm = calloc(1, sizeof(struct qemud_vm)))) {
1668
        qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_MEMORY, "vm");
1669 1670
        return NULL;
    }
D
Daniel P. Berrange 已提交
1671

1672 1673 1674 1675 1676
    vm->stdout = -1;
    vm->stderr = -1;
    vm->monitor = -1;
    vm->pid = -1;
    vm->id = -1;
1677
    vm->state = VIR_DOMAIN_SHUTOFF;
1678
    vm->def = def;
1679
    vm->next = driver->vms;
1680

1681 1682
    driver->vms = vm;
    driver->ninactivevms++;
1683 1684 1685 1686 1687

    return vm;
}

void
1688
qemudRemoveInactiveVM(struct qemud_driver *driver,
1689 1690 1691 1692
                      struct qemud_vm *vm)
{
    struct qemud_vm *prev = NULL, *curr;

1693
    curr = driver->vms;
1694 1695 1696
    while (curr != vm) {
        prev = curr;
        curr = curr->next;
D
Daniel P. Berrange 已提交
1697 1698
    }

1699 1700 1701 1702
    if (curr) {
        if (prev)
            prev->next = curr->next;
        else
1703
            driver->vms = curr->next;
1704

1705
        driver->ninactivevms--;
1706 1707
    }

1708
    qemudFreeVM(vm);
D
Daniel P. Berrange 已提交
1709 1710
}

1711
int
1712
qemudSaveVMDef(struct qemud_driver *driver,
1713 1714 1715 1716 1717
               struct qemud_vm *vm,
               struct qemud_vm_def *def) {
    if (vm->configFile[0] == '\0') {
        int err;

1718
        if ((err = qemudEnsureDir(driver->configDir))) {
1719
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1720
                             "cannot create config directory %s: %s",
1721
                             driver->configDir, strerror(err));
1722 1723 1724
            return -1;
        }

1725
        if (qemudMakeConfigPath(driver->configDir, def->name, ".xml",
1726
                                vm->configFile, PATH_MAX) < 0) {
1727
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1728 1729 1730
                             "cannot construct config file path");
            return -1;
        }
1731

1732
        if (qemudMakeConfigPath(driver->autostartDir, def->name, ".xml",
1733
                                vm->autostartLink, PATH_MAX) < 0) {
1734
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1735 1736 1737 1738
                             "cannot construct autostart link path");
            vm->configFile[0] = '\0';
            return -1;
        }
1739 1740
    }

1741
    return qemudSaveConfig(driver, vm, def);
1742
}
D
Daniel P. Berrange 已提交
1743

1744
static int qemudSaveNetworkConfig(struct qemud_driver *driver,
1745 1746
                                  struct qemud_network *network,
                                  struct qemud_network_def *def) {
1747 1748 1749
    char *xml;
    int fd, ret = -1;
    int towrite;
1750
    int err;
1751

1752
    if (!(xml = qemudGenerateNetworkXML(driver, network, def))) {
1753 1754 1755
        return -1;
    }

1756
    if ((err = qemudEnsureDir(driver->networkConfigDir))) {
1757
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1758
                         "cannot create config directory %s: %s",
1759
                         driver->networkConfigDir, strerror(err));
1760 1761 1762 1763 1764 1765
        goto cleanup;
    }

    if ((fd = open(network->configFile,
                   O_WRONLY | O_CREAT | O_TRUNC,
                   S_IRUSR | S_IWUSR )) < 0) {
1766
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1767 1768
                         "cannot create config file %s: %s",
                         network->configFile, strerror(errno));
1769 1770 1771 1772 1773
        goto cleanup;
    }

    towrite = strlen(xml);
    if (write(fd, xml, towrite) != towrite) {
1774
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1775
                         "cannot write config file %s: %s",
1776
                         network->configFile, strerror(errno));
1777 1778 1779 1780
        goto cleanup;
    }

    if (close(fd) < 0) {
1781
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1782
                         "cannot save config file %s: %s",
1783
                         network->configFile, strerror(errno));
1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795
        goto cleanup;
    }

    ret = 0;

 cleanup:

    free(xml);

    return ret;
}

1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811
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);
}
1812

1813
static int qemudParseBridgeXML(struct qemud_driver *driver ATTRIBUTE_UNUSED,
1814
                               struct qemud_network_def *def,
1815 1816 1817 1818 1819
                               xmlNodePtr node) {
    xmlChar *name, *stp, *delay;

    name = xmlGetProp(node, BAD_CAST "name");
    if (name != NULL) {
1820 1821
        strncpy(def->bridge, (const char *)name, IF_NAMESIZE-1);
        def->bridge[IF_NAMESIZE-1] = '\0';
1822 1823 1824 1825 1826 1827 1828
        xmlFree(name);
        name = NULL;
    }

    stp = xmlGetProp(node, BAD_CAST "stp");
    if (stp != NULL) {
        if (xmlStrEqual(stp, BAD_CAST "off")) {
1829
            def->disableSTP = 1;
1830 1831 1832 1833 1834 1835 1836
        }
        xmlFree(stp);
        stp = NULL;
    }

    delay = xmlGetProp(node, BAD_CAST "delay");
    if (delay != NULL) {
1837
        def->forwardDelay = strtol((const char *)delay, NULL, 10);
1838 1839 1840 1841 1842 1843 1844
        xmlFree(delay);
        delay = NULL;
    }

    return 1;
}

1845
static int qemudParseDhcpRangesXML(struct qemud_driver *driver ATTRIBUTE_UNUSED,
1846
                                   struct qemud_network_def *def,
1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862
                                   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)))) {
1863
            qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_MEMORY, "range");
1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876
            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';

1877 1878 1879
            range->next = def->ranges;
            def->ranges = range;
            def->nranges++;
1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893
        } else {
            free(range);
        }

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

        cur = cur->next;
    }

    return 1;
}
1894

1895
static int qemudParseInetXML(struct qemud_driver *driver ATTRIBUTE_UNUSED,
1896
                             struct qemud_network_def *def,
1897 1898
                             xmlNodePtr node) {
    xmlChar *address, *netmask;
1899
    xmlNodePtr cur;
1900 1901 1902

    address = xmlGetProp(node, BAD_CAST "address");
    if (address != NULL) {
1903 1904
        strncpy(def->ipAddress, (const char *)address, BR_INET_ADDR_MAXLEN-1);
        def->ipAddress[BR_INET_ADDR_MAXLEN-1] = '\0';
1905 1906 1907 1908 1909 1910
        xmlFree(address);
        address = NULL;
    }

    netmask = xmlGetProp(node, BAD_CAST "netmask");
    if (netmask != NULL) {
1911 1912
        strncpy(def->netmask, (const char *)netmask, BR_INET_ADDR_MAXLEN-1);
        def->netmask[BR_INET_ADDR_MAXLEN-1] = '\0';
1913 1914 1915 1916
        xmlFree(netmask);
        netmask = NULL;
    }

1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931
    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);
    }

1932 1933 1934 1935
    cur = node->children;
    while (cur != NULL) {
        if (cur->type == XML_ELEMENT_NODE &&
            xmlStrEqual(cur->name, BAD_CAST "dhcp") &&
1936
            !qemudParseDhcpRangesXML(driver, def, cur))
1937 1938 1939 1940
            return 0;
        cur = cur->next;
    }

1941 1942 1943 1944
    return 1;
}


1945
static struct qemud_network_def *qemudParseNetworkXML(struct qemud_driver *driver,
1946
                                                      xmlDocPtr xml) {
1947 1948
    xmlNodePtr root = NULL;
    xmlXPathContextPtr ctxt = NULL;
1949
    xmlXPathObjectPtr obj = NULL, tmp = NULL;
1950 1951 1952
    struct qemud_network_def *def;

    if (!(def = calloc(1, sizeof(struct qemud_network_def)))) {
1953
        qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_MEMORY, "network_def");
1954 1955
        return NULL;
    }
1956 1957 1958 1959

    /* Prepare parser / xpath context */
    root = xmlDocGetRootElement(xml);
    if ((root == NULL) || (!xmlStrEqual(root->name, BAD_CAST "network"))) {
1960
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "incorrect root element");
1961 1962 1963 1964 1965
        goto error;
    }

    ctxt = xmlXPathNewContext(xml);
    if (ctxt == NULL) {
1966
        qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_MEMORY, "xmlXPathContext");
1967 1968 1969 1970 1971 1972 1973 1974
        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)) {
1975
        qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_NAME, NULL);
1976 1977 1978
        goto error;
    }
    if (strlen((const char *)obj->stringval) >= (QEMUD_MAX_NAME_LEN-1)) {
1979
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "network name length too long");
1980 1981
        goto error;
    }
1982
    strcpy(def->name, (const char *)obj->stringval);
1983 1984 1985 1986 1987 1988 1989
    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)) {
1990
        int err;
D
Daniel P. Berrange 已提交
1991
        if ((err = virUUIDGenerate(def->uuid))) {
1992
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
1993 1994 1995
                             "Failed to generate UUID: %s", strerror(err));
            goto error;
        }
D
Daniel P. Berrange 已提交
1996
    } else if (virUUIDParse((const char *)obj->stringval, def->uuid) < 0) {
1997
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", "malformed uuid element");
1998 1999 2000 2001
        goto error;
    }
    xmlXPathFreeObject(obj);

2002 2003 2004 2005
    /* 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)) {
2006
        if (!qemudParseBridgeXML(driver, def, obj->nodesetval->nodeTab[0])) {
2007 2008 2009 2010 2011 2012 2013 2014 2015
            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)) {
2016
        if (!qemudParseInetXML(driver, def, obj->nodesetval->nodeTab[0])) {
2017 2018 2019 2020 2021 2022
            goto error;
        }
    }
    xmlXPathFreeObject(obj);

    /* IPv4 forwarding setup */
2023 2024 2025
    obj = xmlXPathEval(BAD_CAST "count(/network/forward) > 0", ctxt);
    if ((obj != NULL) && (obj->type == XPATH_BOOLEAN) &&
        obj->boolval) {
2026 2027
        if (!def->ipAddress[0] ||
            !def->netmask[0]) {
2028
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
2029 2030 2031 2032
                             "Forwarding requested, but no IPv4 address/netmask provided");
            goto error;
        }

2033 2034 2035 2036 2037 2038
        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)) {
2039
                qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054
                                 "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);

2055 2056
    xmlXPathFreeContext(ctxt);

2057
    return def;
2058 2059 2060 2061 2062 2063

 error:
    /* XXX free all the stuff in the qemud_network struct, or leave it upto
       the caller ? */
    if (obj)
        xmlXPathFreeObject(obj);
2064 2065
    if (tmp)
        xmlXPathFreeObject(tmp);
2066 2067
    if (ctxt)
        xmlXPathFreeContext(ctxt);
2068 2069
    qemudFreeNetworkDef(def);
    return NULL;
2070 2071
}

2072
struct qemud_network_def *
2073
qemudParseNetworkDef(struct qemud_driver *driver,
2074 2075
                     const char *xmlStr,
                     const char *displayName) {
2076
    xmlDocPtr xml;
2077
    struct qemud_network_def *def;
2078

2079
    if (!(xml = xmlReadDoc(BAD_CAST xmlStr, displayName ? displayName : "network.xml", NULL,
2080 2081
                           XML_PARSE_NOENT | XML_PARSE_NONET |
                           XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
2082
        qemudReportError(NULL, NULL, NULL, VIR_ERR_XML_ERROR, NULL);
2083 2084 2085
        return NULL;
    }

2086
    def = qemudParseNetworkXML(driver, xml);
2087

2088 2089
    xmlFreeDoc(xml);

2090 2091 2092 2093
    return def;
}

struct qemud_network *
2094
qemudAssignNetworkDef(struct qemud_driver *driver,
2095 2096 2097
                      struct qemud_network_def *def) {
    struct qemud_network *network;

2098
    if ((network = qemudFindNetworkByName(driver, def->name))) {
2099
        if (!qemudIsActiveNetwork(network)) {
2100 2101 2102 2103 2104 2105 2106 2107
            qemudFreeNetworkDef(network->def);
            network->def = def;
        } else {
            if (network->newDef)
                qemudFreeNetworkDef(network->newDef);
            network->newDef = def;
        }

2108
        return network;
2109 2110
    }

2111
    if (!(network = calloc(1, sizeof(struct qemud_network)))) {
2112
        qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_MEMORY, "network");
2113 2114
        return NULL;
    }
2115

2116
    network->def = def;
2117
    network->next = driver->networks;
2118

2119 2120
    driver->networks = network;
    driver->ninactivenetworks++;
2121 2122 2123 2124 2125

    return network;
}

void
2126
qemudRemoveInactiveNetwork(struct qemud_driver *driver,
2127 2128 2129 2130
                           struct qemud_network *network)
{
    struct qemud_network *prev = NULL, *curr;

2131
    curr = driver->networks;
2132 2133 2134
    while (curr != network) {
        prev = curr;
        curr = curr->next;
2135 2136
    }

2137 2138 2139 2140
    if (curr) {
        if (prev)
            prev->next = curr->next;
        else
2141
            driver->networks = curr->next;
2142

2143
        driver->ninactivenetworks--;
2144 2145
    }

2146
    qemudFreeNetwork(network);
2147 2148
}

2149
int
2150
qemudSaveNetworkDef(struct qemud_driver *driver,
2151 2152 2153 2154 2155 2156
                    struct qemud_network *network,
                    struct qemud_network_def *def) {

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

2157
        if ((err = qemudEnsureDir(driver->networkConfigDir))) {
2158
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
2159
                             "cannot create config directory %s: %s",
2160
                             driver->networkConfigDir, strerror(err));
2161 2162 2163
            return -1;
        }

2164
        if (qemudMakeConfigPath(driver->networkConfigDir, def->name, ".xml",
2165
                                network->configFile, PATH_MAX) < 0) {
2166
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
2167 2168 2169
                             "cannot construct config file path");
            return -1;
        }
2170

2171
        if (qemudMakeConfigPath(driver->networkAutostartDir, def->name, ".xml",
2172
                                network->autostartLink, PATH_MAX) < 0) {
2173
            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
2174 2175 2176 2177
                             "cannot construct autostart link path");
            network->configFile[0] = '\0';
            return -1;
        }
2178 2179
    }

2180
    return qemudSaveNetworkConfig(driver, network, def);
2181
}
2182

2183 2184 2185 2186
static int
qemudReadFile(const char *path,
              char *buf,
              int maxlen) {
D
Daniel P. Berrange 已提交
2187 2188
    FILE *fh;
    struct stat st;
2189
    int ret = 0;
D
Daniel P. Berrange 已提交
2190

2191 2192 2193 2194
    if (!(fh = fopen(path, "r"))) {
        qemudLog(QEMUD_WARN, "Failed to open file '%s': %s",
                 path, strerror(errno));
        goto error;
D
Daniel P. Berrange 已提交
2195 2196 2197
    }

    if (fstat(fileno(fh), &st) < 0) {
2198 2199 2200
        qemudLog(QEMUD_WARN, "Failed to stat file '%s': %s",
                 path, strerror(errno));
        goto error;
D
Daniel P. Berrange 已提交
2201 2202
    }

2203 2204 2205
    if (S_ISDIR(st.st_mode)) {
        qemudDebug("Ignoring directory '%s' - clearly not a config file", path);
        goto error;
D
Daniel P. Berrange 已提交
2206 2207
    }

2208 2209 2210
    if (st.st_size >= maxlen) {
        qemudLog(QEMUD_WARN, "File '%s' is too large", path);
        goto error;
D
Daniel P. Berrange 已提交
2211 2212
    }

2213 2214 2215 2216
    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 已提交
2217
    }
2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243

    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 已提交
2244 2245
}

2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258
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;
}

2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351
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;
}

2352
static struct qemud_vm *
2353
qemudLoadConfig(struct qemud_driver *driver,
2354 2355
                const char *file,
                const char *path,
2356 2357
                const char *xml,
                const char *autostartLink) {
2358 2359 2360
    struct qemud_vm_def *def;
    struct qemud_vm *vm;

2361
    if (!(def = qemudParseVMDef(driver, xml, file))) {
2362
        virErrorPtr err = virGetLastError();
2363
        qemudLog(QEMUD_WARN, "Error parsing QEMU guest config '%s' : %s",
2364
                 path, err->message);
2365 2366 2367 2368 2369 2370 2371 2372 2373 2374
        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;
    }

2375
    if (!(vm = qemudAssignVMDef(driver, def))) {
2376 2377 2378 2379 2380 2381 2382 2383
        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';

2384 2385 2386 2387 2388
    strncpy(vm->autostartLink, autostartLink, PATH_MAX);
    vm->autostartLink[PATH_MAX-1] = '\0';

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

2389 2390 2391 2392
    return vm;
}

static struct qemud_network *
2393
qemudLoadNetworkConfig(struct qemud_driver *driver,
2394 2395
                       const char *file,
                       const char *path,
2396 2397
                       const char *xml,
                       const char *autostartLink) {
2398 2399 2400
    struct qemud_network_def *def;
    struct qemud_network *network;

2401
    if (!(def = qemudParseNetworkDef(driver, xml, file))) {
2402
        virErrorPtr err = virGetLastError();
2403
        qemudLog(QEMUD_WARN, "Error parsing network config '%s' : %s",
2404
                 path, err->message);
2405 2406 2407 2408 2409 2410 2411 2412 2413 2414
        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;
    }

2415
    if (!(network = qemudAssignNetworkDef(driver, def))) {
2416 2417 2418 2419 2420 2421 2422 2423
        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';

2424 2425 2426 2427 2428
    strncpy(network->autostartLink, autostartLink, PATH_MAX);
    network->autostartLink[PATH_MAX-1] = '\0';

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

2429 2430
    return network;
}
D
Daniel P. Berrange 已提交
2431

2432
static
2433
int qemudScanConfigDir(struct qemud_driver *driver,
2434
                       const char *configDir,
2435
                       const char *autostartDir,
2436
                       int isGuest) {
D
Daniel P. Berrange 已提交
2437 2438 2439
    DIR *dir;
    struct dirent *entry;

2440
    if (!(dir = opendir(configDir))) {
D
Daniel P. Berrange 已提交
2441 2442
        if (errno == ENOENT)
            return 0;
2443 2444
        qemudLog(QEMUD_ERR, "Failed to open dir '%s': %s",
                 configDir, strerror(errno));
D
Daniel P. Berrange 已提交
2445 2446 2447 2448
        return -1;
    }

    while ((entry = readdir(dir))) {
2449 2450
        char xml[QEMUD_MAX_XML_LEN];
        char path[PATH_MAX];
2451
        char autostartLink[PATH_MAX];
2452

D
Daniel P. Berrange 已提交
2453 2454 2455
        if (entry->d_name[0] == '.')
            continue;

2456 2457 2458
        if (!hasSuffix(entry->d_name, ".xml"))
            continue;

2459 2460 2461 2462 2463 2464
        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;
        }

2465 2466 2467 2468 2469 2470
        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;
        }

2471
        if (!qemudReadFile(path, xml, QEMUD_MAX_XML_LEN))
D
Daniel P. Berrange 已提交
2472 2473
            continue;

2474
        if (isGuest)
2475
            qemudLoadConfig(driver, entry->d_name, path, xml, autostartLink);
2476
        else
2477
            qemudLoadNetworkConfig(driver, entry->d_name, path, xml, autostartLink);
D
Daniel P. Berrange 已提交
2478 2479 2480 2481 2482 2483 2484
    }

    closedir(dir);
 
    return 0;
}

2485
/* Scan for all guest and network config files */
2486 2487
int qemudScanConfigs(struct qemud_driver *driver) {
    if (qemudScanConfigDir(driver, driver->configDir, driver->autostartDir, 1) < 0)
2488
        return -1;
2489

2490
    if (qemudScanConfigDir(driver, driver->networkConfigDir, driver->networkAutostartDir, 0) < 0)
2491 2492 2493
        return -1;

    return 0;
2494
}
D
Daniel P. Berrange 已提交
2495 2496

/* Generate an XML document describing the guest's configuration */
2497
char *qemudGenerateXML(struct qemud_driver *driver ATTRIBUTE_UNUSED,
2498 2499 2500
                       struct qemud_vm *vm,
                       struct qemud_vm_def *def,
                       int live) {
D
Daniel P. Berrange 已提交
2501
    virBufferPtr buf = 0;
D
Daniel P. Berrange 已提交
2502 2503 2504 2505 2506 2507
    unsigned char *uuid;
    struct qemud_vm_disk_def *disk;
    struct qemud_vm_net_def *net;
    const char *type = NULL;
    int n;

D
Daniel P. Berrange 已提交
2508
    buf = virBufferNew (QEMUD_MAX_XML_LEN);
2509
    if (!buf)
2510 2511
        goto no_memory;

2512
    switch (def->virtType) {
D
Daniel P. Berrange 已提交
2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523
    case QEMUD_VIRT_QEMU:
        type = "qemu";
        break;
    case QEMUD_VIRT_KQEMU:
        type = "kqemu";
        break;
    case QEMUD_VIRT_KVM:
        type = "kvm";
        break;
    }
    if (!type) {
2524
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "unexpected domain type %d", def->virtType);
D
Daniel P. Berrange 已提交
2525 2526 2527
        goto cleanup;
    }

2528
    if (qemudIsActiveVM(vm) && live) {
D
Daniel P. Berrange 已提交
2529
        if (virBufferVSprintf(buf, "<domain type='%s' id='%d'>\n", type, vm->id) < 0)
D
Daniel P. Berrange 已提交
2530 2531
            goto no_memory;
    } else {
D
Daniel P. Berrange 已提交
2532
        if (virBufferVSprintf(buf, "<domain type='%s'>\n", type) < 0)
D
Daniel P. Berrange 已提交
2533 2534 2535
            goto no_memory;
    }

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

2539
    uuid = def->uuid;
D
Daniel P. Berrange 已提交
2540
    if (virBufferVSprintf(buf, "  <uuid>%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x</uuid>\n",
D
Daniel P. Berrange 已提交
2541 2542 2543 2544 2545
                          uuid[0], uuid[1], uuid[2], uuid[3],
                          uuid[4], uuid[5], uuid[6], uuid[7],
                          uuid[8], uuid[9], uuid[10], uuid[11],
                          uuid[12], uuid[13], uuid[14], uuid[15]) < 0)
        goto no_memory;
D
Daniel P. Berrange 已提交
2546
    if (virBufferVSprintf(buf, "  <memory>%d</memory>\n", def->maxmem) < 0)
D
Daniel P. Berrange 已提交
2547
        goto no_memory;
D
Daniel P. Berrange 已提交
2548
    if (virBufferVSprintf(buf, "  <currentMemory>%d</currentMemory>\n", def->memory) < 0)
D
Daniel P. Berrange 已提交
2549
        goto no_memory;
D
Daniel P. Berrange 已提交
2550
    if (virBufferVSprintf(buf, "  <vcpu>%d</vcpu>\n", def->vcpus) < 0)
D
Daniel P. Berrange 已提交
2551 2552
        goto no_memory;

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

2556
    if (def->virtType == QEMUD_VIRT_QEMU) {
D
Daniel P. Berrange 已提交
2557
        if (virBufferVSprintf(buf, "    <type arch='%s' machine='%s'>%s</type>\n",
2558
                              def->os.arch, def->os.machine, def->os.type) < 0)
D
Daniel P. Berrange 已提交
2559 2560
            goto no_memory;
    } else {
D
Daniel P. Berrange 已提交
2561
        if (virBufferVSprintf(buf, "    <type>%s</type>\n", def->os.type) < 0)
D
Daniel P. Berrange 已提交
2562 2563 2564
            goto no_memory;
    }

2565
    if (def->os.kernel[0])
D
Daniel P. Berrange 已提交
2566
        if (virBufferVSprintf(buf, "    <kernel>%s</kernel>\n", def->os.kernel) < 0)
D
Daniel P. Berrange 已提交
2567
            goto no_memory;
2568
    if (def->os.initrd[0])
D
Daniel P. Berrange 已提交
2569
        if (virBufferVSprintf(buf, "    <initrd>%s</initrd>\n", def->os.initrd) < 0)
D
Daniel P. Berrange 已提交
2570
            goto no_memory;
2571
    if (def->os.cmdline[0])
D
Daniel P. Berrange 已提交
2572
        if (virBufferVSprintf(buf, "    <cmdline>%s</cmdline>\n", def->os.cmdline) < 0)
D
Daniel P. Berrange 已提交
2573 2574
            goto no_memory;

2575
    for (n = 0 ; n < def->os.nBootDevs ; n++) {
D
Daniel P. Berrange 已提交
2576
        const char *boottype = "hd";
2577
        switch (def->os.bootDevs[n]) {
D
Daniel P. Berrange 已提交
2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590
        case QEMUD_BOOT_FLOPPY:
            boottype = "fd";
            break;
        case QEMUD_BOOT_DISK:
            boottype = "hd";
            break;
        case QEMUD_BOOT_CDROM:
            boottype = "cdrom";
            break;
        case QEMUD_BOOT_NET:
            boottype = "net";
            break;
        }
D
Daniel P. Berrange 已提交
2591
        if (virBufferVSprintf(buf, "    <boot dev='%s'/>\n", boottype) < 0)
D
Daniel P. Berrange 已提交
2592 2593 2594
            goto no_memory;
    }

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

2598
    if (def->features & QEMUD_FEATURE_ACPI) {
D
Daniel P. Berrange 已提交
2599
        if (virBufferAdd(buf, "  <features>\n", -1) < 0)
2600
            goto no_memory;
D
Daniel P. Berrange 已提交
2601
        if (virBufferAdd(buf, "    <acpi/>\n", -1) < 0)
2602
            goto no_memory;
D
Daniel P. Berrange 已提交
2603
        if (virBufferAdd(buf, "  </features>\n", -1) < 0)
2604 2605 2606
            goto no_memory;
    }

D
Daniel P. Berrange 已提交
2607
    if (virBufferAdd(buf, "  <on_poweroff>destroy</on_poweroff>\n", -1) < 0)
D
Daniel P. Berrange 已提交
2608 2609
        goto no_memory;
    if (def->noReboot) {
D
Daniel P. Berrange 已提交
2610
        if (virBufferAdd(buf, "  <on_reboot>destroy</on_reboot>\n", -1) < 0)
D
Daniel P. Berrange 已提交
2611 2612
            goto no_memory;
    } else {
D
Daniel P. Berrange 已提交
2613
        if (virBufferAdd(buf, "  <on_reboot>restart</on_reboot>\n", -1) < 0)
D
Daniel P. Berrange 已提交
2614 2615
            goto no_memory;
    }
D
Daniel P. Berrange 已提交
2616
    if (virBufferAdd(buf, "  <on_crash>destroy</on_crash>\n", -1) < 0)
D
Daniel P. Berrange 已提交
2617
        goto no_memory;
2618

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

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

2625
    disk = def->disks;
D
Daniel P. Berrange 已提交
2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639
    while (disk) {
        const char *types[] = {
            "block",
            "file",
        };
        const char *typeAttrs[] = {
            "dev",
            "file",
        };
        const char *devices[] = {
            "disk",
            "cdrom",
            "floppy",
        };
D
Daniel P. Berrange 已提交
2640
        if (virBufferVSprintf(buf, "    <disk type='%s' device='%s'>\n",
D
Daniel P. Berrange 已提交
2641 2642 2643
                              types[disk->type], devices[disk->device]) < 0)
            goto no_memory;

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

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

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

D
Daniel P. Berrange 已提交
2654
        if (virBufferVSprintf(buf, "    </disk>\n") < 0)
D
Daniel P. Berrange 已提交
2655 2656 2657 2658 2659
            goto no_memory;

        disk = disk->next;
    }

2660
    net = def->nets;
2661
    while (net) {
D
Daniel P. Berrange 已提交
2662 2663
        const char *types[] = {
            "user",
2664
            "ethernet",
D
Daniel P. Berrange 已提交
2665 2666 2667
            "server",
            "client",
            "mcast",
2668
            "network",
2669
            "bridge",
D
Daniel P. Berrange 已提交
2670
        };
D
Daniel P. Berrange 已提交
2671
        if (virBufferVSprintf(buf, "    <interface type='%s'>\n",
D
Daniel P. Berrange 已提交
2672 2673 2674
                              types[net->type]) < 0)
            goto no_memory;

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

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

2685
            if (net->dst.network.ifname[0] != '\0') {
D
Daniel P. Berrange 已提交
2686
                if (virBufferVSprintf(buf, "      <target dev='%s'/>\n", net->dst.network.ifname) < 0)
2687 2688 2689
                    goto no_memory;
            }
            break;
2690

2691 2692
        case QEMUD_NET_ETHERNET:
            if (net->dst.ethernet.ifname[0] != '\0') {
D
Daniel P. Berrange 已提交
2693
                if (virBufferVSprintf(buf, "      <target dev='%s'/>\n", net->dst.ethernet.ifname) < 0)
2694 2695 2696
                    goto no_memory;
            }
            if (net->dst.ethernet.script[0] != '\0') {
D
Daniel P. Berrange 已提交
2697
                if (virBufferVSprintf(buf, "      <script path='%s'/>\n", net->dst.ethernet.script) < 0)
2698 2699 2700 2701 2702
                    goto no_memory;
            }
            break;

        case QEMUD_NET_BRIDGE:
D
Daniel P. Berrange 已提交
2703
            if (virBufferVSprintf(buf, "      <source bridge='%s'/>\n", net->dst.bridge.brname) < 0)
2704
                goto no_memory;
2705
            if (net->dst.bridge.ifname[0] != '\0') {
D
Daniel P. Berrange 已提交
2706
                if (virBufferVSprintf(buf, "      <target dev='%s'/>\n", net->dst.bridge.ifname) < 0)
2707 2708 2709 2710 2711 2712 2713 2714
                    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 已提交
2715
                if (virBufferVSprintf(buf, "      <source address='%s' port='%d'/>\n",
2716 2717 2718
                                      net->dst.socket.address, net->dst.socket.port) < 0)
                    goto no_memory;
            } else {
D
Daniel P. Berrange 已提交
2719
                if (virBufferVSprintf(buf, "      <source port='%d'/>\n",
2720 2721 2722
                                      net->dst.socket.port) < 0)
                    goto no_memory;
            }
2723 2724
        }

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

2728
        net = net->next;
D
Daniel P. Berrange 已提交
2729 2730
    }

2731 2732
    switch (def->graphicsType) {
    case QEMUD_GRAPHICS_VNC:
D
Daniel P. Berrange 已提交
2733
        if (virBufferAdd(buf, "    <graphics type='vnc'", -1) < 0)
2734 2735 2736
            goto no_memory;

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

D
Daniel P. Berrange 已提交
2741
        if (virBufferAdd(buf, "/>\n", -1) < 0)
2742 2743 2744 2745
            goto no_memory;
        break;

    case QEMUD_GRAPHICS_SDL:
D
Daniel P. Berrange 已提交
2746
        if (virBufferAdd(buf, "    <graphics type='sdl'/>\n", -1) < 0)
2747 2748 2749 2750 2751 2752 2753 2754
            goto no_memory;
        break;

    case QEMUD_GRAPHICS_NONE:
    default:
        break;
    }

2755
    if (def->graphicsType == QEMUD_GRAPHICS_VNC) {
D
Daniel P. Berrange 已提交
2756 2757
    }

D
Daniel P. Berrange 已提交
2758
    if (virBufferAdd(buf, "  </devices>\n", -1) < 0)
D
Daniel P. Berrange 已提交
2759 2760 2761
        goto no_memory;


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

D
Daniel P. Berrange 已提交
2765
    return virBufferContentAndFree (buf);
D
Daniel P. Berrange 已提交
2766 2767

 no_memory:
2768
    qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_MEMORY, "xml");
D
Daniel P. Berrange 已提交
2769
 cleanup:
D
Daniel P. Berrange 已提交
2770
    if (buf) virBufferFree (buf);
D
Daniel P. Berrange 已提交
2771 2772 2773 2774
    return NULL;
}


2775
char *qemudGenerateNetworkXML(struct qemud_driver *driver ATTRIBUTE_UNUSED,
2776
                              struct qemud_network *network,
2777
                              struct qemud_network_def *def) {
D
Daniel P. Berrange 已提交
2778
    virBufferPtr buf = 0;
2779 2780
    unsigned char *uuid;

D
Daniel P. Berrange 已提交
2781
    buf = virBufferNew (QEMUD_MAX_XML_LEN);
2782
    if (!buf)
2783 2784
        goto no_memory;

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

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

2791
    uuid = def->uuid;
D
Daniel P. Berrange 已提交
2792
    if (virBufferVSprintf(buf, "  <uuid>%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x</uuid>\n",
2793 2794 2795 2796 2797 2798
                          uuid[0], uuid[1], uuid[2], uuid[3],
                          uuid[4], uuid[5], uuid[6], uuid[7],
                          uuid[8], uuid[9], uuid[10], uuid[11],
                          uuid[12], uuid[13], uuid[14], uuid[15]) < 0)
        goto no_memory;

2799 2800
    if (def->forward) {
        if (def->forwardDev[0]) {
D
Daniel P. Berrange 已提交
2801
            virBufferVSprintf(buf, "  <forward dev='%s'/>\n",
2802 2803
                              def->forwardDev);
        } else {
D
Daniel P. Berrange 已提交
2804
            virBufferAdd(buf, "  <forward/>\n", -1);
2805 2806 2807
        }
    }

D
Daniel P. Berrange 已提交
2808
    virBufferAdd(buf, "  <bridge", -1);
2809
    if (qemudIsActiveNetwork(network)) {
D
Daniel P. Berrange 已提交
2810
        if (virBufferVSprintf(buf, " name='%s'", network->bridge) < 0)
2811 2812
            goto no_memory;
    } else if (def->bridge[0]) {
D
Daniel P. Berrange 已提交
2813
        if (virBufferVSprintf(buf, " name='%s'", def->bridge) < 0)
2814 2815
            goto no_memory;
    }
D
Daniel P. Berrange 已提交
2816
    if (virBufferVSprintf(buf, " stp='%s' forwardDelay='%d' />\n",
2817 2818
                       def->disableSTP ? "off" : "on",
                       def->forwardDelay) < 0)
2819 2820
        goto no_memory;

2821
    if (def->ipAddress[0] || def->netmask[0]) {
D
Daniel P. Berrange 已提交
2822
        if (virBufferAdd(buf, "  <ip", -1) < 0)
2823 2824
            goto no_memory;

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

2829
        if (def->netmask[0] &&
D
Daniel P. Berrange 已提交
2830
            virBufferVSprintf(buf, " netmask='%s'", def->netmask) < 0)
2831 2832
            goto no_memory;

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

2836 2837
        if (def->ranges) {
            struct qemud_dhcp_range_def *range = def->ranges;
D
Daniel P. Berrange 已提交
2838
            if (virBufferAdd(buf, "    <dhcp>\n", -1) < 0)
2839 2840
                goto no_memory;
            while (range) {
D
Daniel P. Berrange 已提交
2841
                if (virBufferVSprintf(buf, "      <range start='%s' end='%s' />\n",
2842 2843 2844 2845
                                      range->start, range->end) < 0)
                    goto no_memory;
                range = range->next;
            }
D
Daniel P. Berrange 已提交
2846
            if (virBufferAdd(buf, "    </dhcp>\n", -1) < 0)
2847 2848 2849
                goto no_memory;
        }

D
Daniel P. Berrange 已提交
2850
        if (virBufferAdd(buf, "  </ip>\n", -1) < 0)
2851
            goto no_memory;
D
Daniel P. Berrange 已提交
2852 2853
    }

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

D
Daniel P. Berrange 已提交
2857
    return virBufferContentAndFree (buf);
2858 2859

 no_memory:
2860
    qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_MEMORY, "xml");
D
Daniel P. Berrange 已提交
2861
    if (buf) virBufferFree (buf);
2862 2863 2864 2865
    return NULL;
}


2866
int qemudDeleteConfig(struct qemud_driver *driver ATTRIBUTE_UNUSED,
2867 2868 2869
                      const char *configFile,
                      const char *name) {
    if (!configFile[0]) {
2870
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "no config file for %s", name);
D
Daniel P. Berrange 已提交
2871 2872 2873
        return -1;
    }

2874
    if (unlink(configFile) < 0) {
2875
        qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "cannot remove config for %s", name);
2876 2877
        return -1;
    }
2878

D
Daniel P. Berrange 已提交
2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890
    return 0;
}


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