xml.c 27.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/*
 * xml.c: XML based interfaces for the libvir library
 *
 * Copyright (C) 2005 Red Hat, Inc.
 *
 * See COPYING.LIB for the License of this software
 *
 * Daniel Veillard <veillard@redhat.com>
 */

11
#include "libvirt.h"
12 13 14 15 16 17

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <xs.h>
18 19 20
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
21 22
#include "internal.h"
#include "hash.h"
D
Daniel Veillard 已提交
23
#include "sexpr.h"
24
#include "xml.h"
25

26 27 28 29 30 31 32 33 34
/**
 * virXenError:
 * @conn: the connection if available
 * @error: the error number
 * @info: extra information string
 *
 * Handle an error at the xend daemon interface
 */
static void
35 36
virXMLError(virErrorNumber error, const char *info, int value)
{
37
    const char *errmsg;
38

39 40 41 42 43
    if (error == VIR_ERR_OK)
        return;

    errmsg = __virErrorMsg(error, info);
    __virRaiseError(NULL, NULL, VIR_FROM_XML, error, VIR_ERR_ERROR,
44
                    errmsg, info, NULL, value, 0, errmsg, info, value);
45 46
}

47 48 49 50 51 52 53 54 55 56
/**
 * virBufferGrow:
 * @buf:  the buffer
 * @len:  the minimum free size to allocate
 *
 * Grow the available space of an XML buffer.
 *
 * Returns the new available space or -1 in case of error
 */
static int
57 58
virBufferGrow(virBufferPtr buf, unsigned int len)
{
59 60 61
    int size;
    char *newbuf;

62 63 64 65
    if (buf == NULL)
        return (-1);
    if (len + buf->use < buf->size)
        return (0);
66 67 68 69 70

    size = buf->use + len + 1000;

    newbuf = (char *) realloc(buf->content, size);
    if (newbuf == NULL) {
71
        virXMLError(VIR_ERR_NO_MEMORY, "growing buffer", size);
72
        return (-1);
73 74 75
    }
    buf->content = newbuf;
    buf->size = size;
76
    return (buf->size - buf->use);
77 78 79 80 81 82 83 84 85 86 87 88 89
}

/**
 * virBufferAdd:
 * @buf:  the buffer to dump
 * @str:  the string
 * @len:  the number of bytes to add
 *
 * Add a string range to an XML buffer. if len == -1, the length of
 * str is recomputed to the full string.
 *
 * Returns 0 successful, -1 in case of internal or API error.
 */
D
Daniel Veillard 已提交
90
int
91 92
virBufferAdd(virBufferPtr buf, const char *str, int len)
{
93 94 95
    unsigned int needSize;

    if ((str == NULL) || (buf == NULL)) {
96
        return -1;
97
    }
98 99
    if (len == 0)
        return 0;
100 101 102 103 104

    if (len < 0)
        len = strlen(str);

    needSize = buf->use + len + 2;
105 106 107
    if (needSize > buf->size) {
        if (!virBufferGrow(buf, needSize)) {
            return (-1);
108 109 110 111 112 113
        }
    }

    memmove(&buf->content[buf->use], str, len);
    buf->use += len;
    buf->content[buf->use] = 0;
114
    return (0);
115 116
}

K
Karel Zak 已提交
117 118 119 120 121 122 123 124 125 126
void
virBufferFree(virBufferPtr buf)
{
    if (buf) {
       if (buf->content)
	       free(buf->content);
       free(buf);
    }
}

127 128 129 130 131 132 133 134 135 136
/**
 * virBufferVSprintf:
 * @buf:  the buffer to dump
 * @format:  the format
 * @argptr:  the variable list of arguments
 *
 * Do a formatted print to an XML buffer.
 *
 * Returns 0 successful, -1 in case of internal or API error.
 */
D
Daniel Veillard 已提交
137
int
138 139
virBufferVSprintf(virBufferPtr buf, const char *format, ...)
{
140 141 142 143
    int size, count;
    va_list locarg, argptr;

    if ((format == NULL) || (buf == NULL)) {
144
        return (-1);
145 146 147 148 149 150
    }
    size = buf->size - buf->use - 1;
    va_start(argptr, format);
    va_copy(locarg, argptr);
    while (((count = vsnprintf(&buf->content[buf->use], size, format,
                               locarg)) < 0) || (count >= size - 1)) {
151 152 153 154 155 156 157
        buf->content[buf->use] = 0;
        va_end(locarg);
        if (virBufferGrow(buf, 1000) < 0) {
            return (-1);
        }
        size = buf->size - buf->use - 1;
        va_copy(locarg, argptr);
158 159 160 161
    }
    va_end(locarg);
    buf->use += count;
    buf->content[buf->use] = 0;
162
    return (0);
163 164
}

D
Daniel Veillard 已提交
165
#if 0
166

D
Daniel Veillard 已提交
167 168 169 170 171
/*
 * This block of function are now implemented by a xend poll in
 * xend_internal.c instead of querying the Xen store, code is kept
 * for reference of in case Xend may not be available in the future ...
 */
172

173 174 175 176 177 178 179 180 181 182 183 184
/**
 * virDomainGetXMLDevice:
 * @domain: a domain object
 * @sub: the xenstore subsection 'vbd', 'vif', ...
 * @dev: the xenstrore internal device number
 * @name: the value's name
 *
 * Extract one information the device used by the domain from xensttore
 *
 * Returns the new string or NULL in case of error
 */
static char *
185 186 187
virDomainGetXMLDeviceInfo(virDomainPtr domain, const char *sub,
                          long dev, const char *name)
{
188 189 190 191 192 193 194
    char s[256];
    unsigned int len = 0;

    snprintf(s, 255, "/local/domain/0/backend/%s/%d/%ld/%s",
             sub, domain->handle, dev, name);
    s[255] = 0;

195
    return xs_read(domain->conn->xshandle, 0, &s[0], &len);
196 197 198 199 200 201 202 203 204 205 206 207 208
}

/**
 * virDomainGetXMLDevice:
 * @domain: a domain object
 * @buf: the output buffer object
 * @dev: the xenstrore internal device number
 *
 * Extract and dump in the buffer informations on the device used by the domain
 *
 * Returns 0 in case of success, -1 in case of failure
 */
static int
209 210
virDomainGetXMLDevice(virDomainPtr domain, virBufferPtr buf, long dev)
{
211 212 213 214
    char *type, *val;

    type = virDomainGetXMLDeviceInfo(domain, "vbd", dev, "type");
    if (type == NULL)
215
        return (-1);
216
    if (!strcmp(type, "file")) {
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
        virBufferVSprintf(buf, "    <disk type='file'>\n");
        val = virDomainGetXMLDeviceInfo(domain, "vbd", dev, "params");
        if (val != NULL) {
            virBufferVSprintf(buf, "      <source file='%s'/>\n", val);
            free(val);
        }
        val = virDomainGetXMLDeviceInfo(domain, "vbd", dev, "dev");
        if (val != NULL) {
            virBufferVSprintf(buf, "      <target dev='%s'/>\n", val);
            free(val);
        }
        val = virDomainGetXMLDeviceInfo(domain, "vbd", dev, "read-only");
        if (val != NULL) {
            virBufferVSprintf(buf, "      <readonly/>\n", val);
            free(val);
        }
        virBufferAdd(buf, "    </disk>\n", 12);
234
    } else if (!strcmp(type, "phy")) {
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
        virBufferVSprintf(buf, "    <disk type='device'>\n");
        val = virDomainGetXMLDeviceInfo(domain, "vbd", dev, "params");
        if (val != NULL) {
            virBufferVSprintf(buf, "      <source device='%s'/>\n", val);
            free(val);
        }
        val = virDomainGetXMLDeviceInfo(domain, "vbd", dev, "dev");
        if (val != NULL) {
            virBufferVSprintf(buf, "      <target dev='%s'/>\n", val);
            free(val);
        }
        val = virDomainGetXMLDeviceInfo(domain, "vbd", dev, "read-only");
        if (val != NULL) {
            virBufferVSprintf(buf, "      <readonly/>\n", val);
            free(val);
        }
        virBufferAdd(buf, "    </disk>\n", 12);
252
    } else {
253 254
        TODO fprintf(stderr, "Don't know how to handle device type %s\n",
                     type);
255 256 257
    }
    free(type);

258
    return (0);
259 260 261 262 263 264 265 266 267 268 269 270
}

/**
 * virDomainGetXMLDevices:
 * @domain: a domain object
 * @buf: the output buffer object
 *
 * Extract the devices used by the domain and dumps then in the buffer
 *
 * Returns 0 in case of success, -1 in case of failure
 */
static int
271 272
virDomainGetXMLDevices(virDomainPtr domain, virBufferPtr buf)
{
273 274 275 276 277 278 279
    int ret = -1;
    unsigned int num, i;
    long id;
    char **list = NULL, *endptr;
    char backend[200];
    virConnectPtr conn;

K
Karel Zak 已提交
280
    if (!VIR_IS_CONNECTED_DOMAIN(domain))
281 282
        return (-1);

283 284
    conn = domain->conn;

285
    snprintf(backend, 199, "/local/domain/0/backend/vbd/%d",
286 287
             virDomainGetID(domain));
    backend[199] = 0;
288
    list = xs_directory(conn->xshandle, 0, backend, &num);
289 290 291 292
    ret = 0;
    if (list == NULL)
        goto done;

293
    for (i = 0; i < num; i++) {
294
        id = strtol(list[i], &endptr, 10);
295 296 297 298 299
        if ((endptr == list[i]) || (*endptr != 0)) {
            ret = -1;
            goto done;
        }
        virDomainGetXMLDevice(domain, buf, id);
300 301
    }

302
  done:
303 304 305
    if (list != NULL)
        free(list);

306
    return (ret);
307 308 309 310 311 312 313 314 315 316 317 318 319 320
}

/**
 * virDomainGetXMLInterface:
 * @domain: a domain object
 * @buf: the output buffer object
 * @dev: the xenstrore internal device number
 *
 * Extract and dump in the buffer informations on the interface used by
 * the domain
 *
 * Returns 0 in case of success, -1 in case of failure
 */
static int
321 322
virDomainGetXMLInterface(virDomainPtr domain, virBufferPtr buf, long dev)
{
323 324 325 326
    char *type, *val;

    type = virDomainGetXMLDeviceInfo(domain, "vif", dev, "bridge");
    if (type == NULL) {
327 328 329 330 331 332 333 334 335 336 337 338
        virBufferVSprintf(buf, "    <interface type='default'>\n");
        val = virDomainGetXMLDeviceInfo(domain, "vif", dev, "mac");
        if (val != NULL) {
            virBufferVSprintf(buf, "      <mac address='%s'/>\n", val);
            free(val);
        }
        val = virDomainGetXMLDeviceInfo(domain, "vif", dev, "script");
        if (val != NULL) {
            virBufferVSprintf(buf, "      <script path='%s'/>\n", val);
            free(val);
        }
        virBufferAdd(buf, "    </interface>\n", 17);
339
    } else {
340 341 342 343 344 345 346 347 348 349 350 351 352
        virBufferVSprintf(buf, "    <interface type='bridge'>\n");
        virBufferVSprintf(buf, "      <source bridge='%s'/>\n", type);
        val = virDomainGetXMLDeviceInfo(domain, "vif", dev, "mac");
        if (val != NULL) {
            virBufferVSprintf(buf, "      <mac address='%s'/>\n", val);
            free(val);
        }
        val = virDomainGetXMLDeviceInfo(domain, "vif", dev, "script");
        if (val != NULL) {
            virBufferVSprintf(buf, "      <script path='%s'/>\n", val);
            free(val);
        }
        virBufferAdd(buf, "    </interface>\n", 17);
353 354 355
    }
    free(type);

356
    return (0);
357 358 359 360 361 362 363 364 365 366 367 368
}

/**
 * virDomainGetXMLInterfaces:
 * @domain: a domain object
 * @buf: the output buffer object
 *
 * Extract the interfaces used by the domain and dumps then in the buffer
 *
 * Returns 0 in case of success, -1 in case of failure
 */
static int
369 370
virDomainGetXMLInterfaces(virDomainPtr domain, virBufferPtr buf)
{
371 372 373 374 375 376 377
    int ret = -1;
    unsigned int num, i;
    long id;
    char **list = NULL, *endptr;
    char backend[200];
    virConnectPtr conn;

K
Karel Zak 已提交
378
    if (!VIR_IS_CONNECTED_DOMAIN(domain))
379 380
        return (-1);

381 382
    conn = domain->conn;

383
    snprintf(backend, 199, "/local/domain/0/backend/vif/%d",
384 385
             virDomainGetID(domain));
    backend[199] = 0;
386
    list = xs_directory(conn->xshandle, 0, backend, &num);
387 388 389 390
    ret = 0;
    if (list == NULL)
        goto done;

391
    for (i = 0; i < num; i++) {
392
        id = strtol(list[i], &endptr, 10);
393 394 395 396 397
        if ((endptr == list[i]) || (*endptr != 0)) {
            ret = -1;
            goto done;
        }
        virDomainGetXMLInterface(domain, buf, id);
398 399
    }

400
  done:
401 402 403
    if (list != NULL)
        free(list);

404
    return (ret);
405 406
}

407 408 409



410 411 412 413 414 415 416 417 418 419
/**
 * virDomainGetXMLBoot:
 * @domain: a domain object
 * @buf: the output buffer object
 *
 * Extract the boot informations used to start that domain
 *
 * Returns 0 in case of success, -1 in case of failure
 */
static int
420 421
virDomainGetXMLBoot(virDomainPtr domain, virBufferPtr buf)
{
422 423
    char *vm, *str;

K
Karel Zak 已提交
424
    if (!VIR_IS_DOMAIN(domain))
425 426
        return (-1);

427
    vm = virDomainGetVM(domain);
428
    if (vm == NULL)
429
        return (-1);
430 431 432 433 434 435 436 437 438 439 440 441 442 443

    virBufferAdd(buf, "  <os>\n", 7);
    str = virDomainGetVMInfo(domain, vm, "image/ostype");
    if (str != NULL) {
        virBufferVSprintf(buf, "    <type>%s</type>\n", str);
        free(str);
    }
    str = virDomainGetVMInfo(domain, vm, "image/kernel");
    if (str != NULL) {
        virBufferVSprintf(buf, "    <kernel>%s</kernel>\n", str);
        free(str);
    }
    str = virDomainGetVMInfo(domain, vm, "image/ramdisk");
    if (str != NULL) {
444 445
        if (str[0] != 0)
            virBufferVSprintf(buf, "    <initrd>%s</initrd>\n", str);
446 447 448 449
        free(str);
    }
    str = virDomainGetVMInfo(domain, vm, "image/cmdline");
    if (str != NULL) {
450 451
        if (str[0] != 0)
            virBufferVSprintf(buf, "    <cmdline>%s</cmdline>\n", str);
452 453 454 455 456
        free(str);
    }
    virBufferAdd(buf, "  </os>\n", 8);

    free(vm);
457
    return (0);
458 459
}

460 461 462 463 464 465 466 467 468 469 470 471
/**
 * virDomainGetXMLDesc:
 * @domain: a domain object
 * @flags: and OR'ed set of extraction flags, not used yet
 *
 * Provide an XML description of the domain. NOTE: this API is subject
 * to changes.
 *
 * Returns a 0 terminated UTF-8 encoded XML instance, or NULL in case of error.
 *         the caller must free() the returned value.
 */
char *
472 473
virDomainGetXMLDesc(virDomainPtr domain, int flags)
{
474
    char *ret = NULL;
475
    unsigned char uuid[16];
476 477 478
    virBuffer buf;
    virDomainInfo info;

K
Karel Zak 已提交
479
    if (!VIR_IS_DOMAIN(domain))
480
        return (NULL);
K
Karel Zak 已提交
481
    if (flags != 0)
482
        return (NULL);
483
    if (virDomainGetInfo(domain, &info) < 0)
484
        return (NULL);
485 486 487

    ret = malloc(1000);
    if (ret == NULL)
488
        return (NULL);
489 490 491 492 493 494
    buf.content = ret;
    buf.size = 1000;
    buf.use = 0;

    virBufferVSprintf(&buf, "<domain type='xen' id='%d'>\n",
                      virDomainGetID(domain));
495 496
    virBufferVSprintf(&buf, "  <name>%s</name>\n",
                      virDomainGetName(domain));
497 498 499 500 501 502 503 504
    if (virDomainGetUUID(domain, &uuid[0]) == 0) {
    virBufferVSprintf(&buf,
"  <uuid>%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x</uuid>\n",
                      uuid[0], uuid[1], uuid[2], uuid[3],
                      uuid[4], uuid[5], uuid[6], uuid[7],
                      uuid[8], uuid[9], uuid[10], uuid[11],
                      uuid[12], uuid[13], uuid[14], uuid[15]);
    }
505
    virDomainGetXMLBoot(domain, &buf);
506 507 508 509 510 511
    virBufferVSprintf(&buf, "  <memory>%lu</memory>\n", info.maxMem);
    virBufferVSprintf(&buf, "  <vcpu>%d</vcpu>\n", (int) info.nrVirtCpu);
    virBufferAdd(&buf, "  <devices>\n", 12);
    virDomainGetXMLDevices(domain, &buf);
    virDomainGetXMLInterfaces(domain, &buf);
    virBufferAdd(&buf, "  </devices>\n", 13);
512
    virBufferAdd(&buf, "</domain>\n", 10);
513

514
    buf.content[buf.use] = 0;
515
    return (ret);
516
}
517

D
Daniel Veillard 已提交
518 519
#endif

520 521 522 523
/**
 * virDomainParseXMLOSDesc:
 * @xmldesc: string with the XML description
 * @buf: a buffer for the result S-Expr
524
 * @bootloader: indocate if a bootloader script was provided
525 526 527 528 529 530 531 532 533
 *
 * Parse the OS part of the XML description and add it to the S-Expr in buf
 * This is a temporary interface as the S-Expr interface
 * will be replaced by XML-RPC in the future. However the XML format should
 * stay valid over time.
 *
 * Returns 0 in case of success, -1 in case of error.
 */
static int
534
virDomainParseXMLOSDesc(xmlNodePtr node, virBufferPtr buf, int bootloader)
535
{
536 537 538 539 540 541 542 543 544 545
    xmlNodePtr cur, txt;
    const xmlChar *type = NULL;
    const xmlChar *root = NULL;
    const xmlChar *kernel = NULL;
    const xmlChar *initrd = NULL;
    const xmlChar *cmdline = NULL;

    cur = node->children;
    while (cur != NULL) {
        if (cur->type == XML_ELEMENT_NODE) {
546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572
            if ((type == NULL)
                && (xmlStrEqual(cur->name, BAD_CAST "type"))) {
                txt = cur->children;
                if ((txt->type == XML_TEXT_NODE) && (txt->next == NULL))
                    type = txt->content;
            } else if ((kernel == NULL) &&
                       (xmlStrEqual(cur->name, BAD_CAST "kernel"))) {
                txt = cur->children;
                if ((txt->type == XML_TEXT_NODE) && (txt->next == NULL))
                    kernel = txt->content;
            } else if ((root == NULL) &&
                       (xmlStrEqual(cur->name, BAD_CAST "root"))) {
                txt = cur->children;
                if ((txt->type == XML_TEXT_NODE) && (txt->next == NULL))
                    root = txt->content;
            } else if ((initrd == NULL) &&
                       (xmlStrEqual(cur->name, BAD_CAST "initrd"))) {
                txt = cur->children;
                if ((txt->type == XML_TEXT_NODE) && (txt->next == NULL))
                    initrd = txt->content;
            } else if ((cmdline == NULL) &&
                       (xmlStrEqual(cur->name, BAD_CAST "cmdline"))) {
                txt = cur->children;
                if ((txt->type == XML_TEXT_NODE) && (txt->next == NULL))
                    cmdline = txt->content;
            }
        }
573 574 575 576
        cur = cur->next;
    }
    if ((type != NULL) && (!xmlStrEqual(type, BAD_CAST "linux"))) {
        /* VIR_ERR_OS_TYPE */
577 578
        virXMLError(VIR_ERR_OS_TYPE, (const char *) type, 0);
        return (-1);
579 580 581
    }
    virBufferAdd(buf, "(linux ", 7);
    if (kernel == NULL) {
582 583 584 585 586 587
	if (bootloader == 0) {
	    virXMLError(VIR_ERR_NO_KERNEL, NULL, 0);
	    return (-1);
	}
    } else {
	virBufferVSprintf(buf, "(kernel '%s')", (const char *) kernel);
588 589
    }
    if (initrd != NULL)
590
        virBufferVSprintf(buf, "(ramdisk '%s')", (const char *) initrd);
591
    if (root != NULL)
592
        virBufferVSprintf(buf, "(root '%s')", (const char *) root);
593
    if (cmdline != NULL)
594
        virBufferVSprintf(buf, "(args '%s')", (const char *) cmdline);
595
    virBufferAdd(buf, ")", 1);
596
    return (0);
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
}

/**
 * virDomainParseXMLDiskDesc:
 * @xmldesc: string with the XML description
 * @buf: a buffer for the result S-Expr
 *
 * Parse the one disk in the XML description and add it to the S-Expr in buf
 * This is a temporary interface as the S-Expr interface
 * will be replaced by XML-RPC in the future. However the XML format should
 * stay valid over time.
 *
 * Returns 0 in case of success, -1 in case of error.
 */
static int
612 613
virDomainParseXMLDiskDesc(xmlNodePtr node, virBufferPtr buf)
{
614 615 616 617 618 619 620 621 622
    xmlNodePtr cur;
    xmlChar *type = NULL;
    xmlChar *source = NULL;
    xmlChar *target = NULL;
    int ro = 0;
    int typ = 0;

    type = xmlGetProp(node, BAD_CAST "type");
    if (type != NULL) {
623 624 625 626 627
        if (xmlStrEqual(type, BAD_CAST "file"))
            typ = 0;
        else if (xmlStrEqual(type, BAD_CAST "block"))
            typ = 1;
        xmlFree(type);
628 629 630 631
    }
    cur = node->children;
    while (cur != NULL) {
        if (cur->type == XML_ELEMENT_NODE) {
632 633 634 635 636 637 638 639 640 641 642 643 644 645
            if ((source == NULL) &&
                (xmlStrEqual(cur->name, BAD_CAST "source"))) {

                if (typ == 0)
                    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")) {
                ro = 1;
            }
        }
646 647 648 649
        cur = cur->next;
    }

    if (source == NULL) {
650 651 652 653 654
        virXMLError(VIR_ERR_NO_SOURCE, (const char *) target, 0);

        if (target != NULL)
            xmlFree(target);
        return (-1);
655 656
    }
    if (target == NULL) {
657 658 659 660
        virXMLError(VIR_ERR_NO_TARGET, (const char *) source, 0);
        if (source != NULL)
            xmlFree(source);
        return (-1);
661 662 663
    }
    virBufferAdd(buf, "(vbd ", 5);
    if (target[0] == '/')
664
        virBufferVSprintf(buf, "(dev '%s')", (const char *) target);
665
    else
666
        virBufferVSprintf(buf, "(dev '/dev/%s')", (const char *) target);
667 668 669 670
    if (typ == 0)
        virBufferVSprintf(buf, "(uname 'file:%s')", source);
    else if (typ == 1) {
        if (source[0] == '/')
671 672 673
            virBufferVSprintf(buf, "(uname 'phy:%s')", source);
        else
            virBufferVSprintf(buf, "(uname 'phy:/dev/%s')", source);
674 675 676 677 678 679 680 681 682
    }
    if (ro == 0)
        virBufferVSprintf(buf, "(mode 'w')");
    else if (ro == 1)
        virBufferVSprintf(buf, "(mode 'r')");

    virBufferAdd(buf, ")", 1);
    xmlFree(target);
    xmlFree(source);
683
    return (0);
684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
}

/**
 * virDomainParseXMLIfDesc:
 * @xmldesc: string with the XML description
 * @buf: a buffer for the result S-Expr
 *
 * Parse the one interface the XML description and add it to the S-Expr in buf
 * This is a temporary interface as the S-Expr interface
 * will be replaced by XML-RPC in the future. However the XML format should
 * stay valid over time.
 *
 * Returns 0 in case of success, -1 in case of error.
 */
static int
699 700
virDomainParseXMLIfDesc(xmlNodePtr node, virBufferPtr buf)
{
701 702 703 704 705 706 707 708 709
    xmlNodePtr cur;
    xmlChar *type = NULL;
    xmlChar *source = NULL;
    xmlChar *mac = NULL;
    xmlChar *script = NULL;
    int typ = 0;

    type = xmlGetProp(node, BAD_CAST "type");
    if (type != NULL) {
710 711 712 713 714
        if (xmlStrEqual(type, BAD_CAST "bridge"))
            typ = 0;
        else if (xmlStrEqual(type, BAD_CAST "ethernet"))
            typ = 1;
        xmlFree(type);
715 716 717 718
    }
    cur = node->children;
    while (cur != NULL) {
        if (cur->type == XML_ELEMENT_NODE) {
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
            if ((source == NULL) &&
                (xmlStrEqual(cur->name, BAD_CAST "source"))) {

                if (typ == 0)
                    source = xmlGetProp(cur, BAD_CAST "bridge");
                else
                    source = xmlGetProp(cur, BAD_CAST "dev");
            } else if ((mac == NULL) &&
                       (xmlStrEqual(cur->name, BAD_CAST "mac"))) {
                mac = xmlGetProp(cur, BAD_CAST "address");
            } else if ((script == NULL) &&
                       (xmlStrEqual(cur->name, BAD_CAST "script"))) {
                script = xmlGetProp(cur, BAD_CAST "path");
            }
        }
734 735 736 737 738
        cur = cur->next;
    }

    virBufferAdd(buf, "(vif ", 5);
    if (mac != NULL)
739
        virBufferVSprintf(buf, "(mac '%s')", (const char *) mac);
740
    if (source != NULL) {
741 742 743 744
        if (typ == 0)
            virBufferVSprintf(buf, "(bridge '%s')", (const char *) source);
        else                    /* TODO does that work like that ? */
            virBufferVSprintf(buf, "(dev '%s')", (const char *) source);
745 746 747 748 749 750
    }
    if (script != NULL)
        virBufferVSprintf(buf, "(script '%s')", script);

    virBufferAdd(buf, ")", 1);
    if (mac != NULL)
751
        xmlFree(mac);
752
    if (source != NULL)
753
        xmlFree(source);
754
    if (script != NULL)
755 756
        xmlFree(script);
    return (0);
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771
}

/**
 * virDomainParseXMLDesc:
 * @xmldesc: string with the XML description
 *
 * Parse the XML description and turn it into the xend sexp needed to
 * create the comain. This is a temporary interface as the S-Expr interface
 * will be replaced by XML-RPC in the future. However the XML format should
 * stay valid over time.
 *
 * Returns the 0 terminatedi S-Expr string or NULL in case of error.
 *         the caller must free() the returned value.
 */
char *
772 773
virDomainParseXMLDesc(const char *xmldesc, char **name)
{
774 775
    xmlDocPtr xml = NULL;
    xmlNodePtr node;
776
    char *ret = NULL, *nam = NULL;
777 778 779 780 781
    virBuffer buf;
    xmlChar *prop;
    xmlXPathObjectPtr obj = NULL;
    xmlXPathContextPtr ctxt = NULL;
    int i, res;
782
    int bootloader = 0;
783 784

    if (name != NULL)
785
        *name = NULL;
786 787
    ret = malloc(1000);
    if (ret == NULL)
788
        return (NULL);
789 790 791 792 793
    buf.content = ret;
    buf.size = 1000;
    buf.use = 0;

    xml = xmlReadDoc((const xmlChar *) xmldesc, "domain.xml", NULL,
794 795
                     XML_PARSE_NOENT | XML_PARSE_NONET |
                     XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
796 797 798 799 800 801 802 803 804 805
    if (xml == NULL) {
        goto error;
    }
    node = xmlDocGetRootElement(xml);
    if ((node == NULL) || (!xmlStrEqual(node->name, BAD_CAST "domain")))
        goto error;

    prop = xmlGetProp(node, BAD_CAST "type");
    if (prop != NULL) {
        if (!xmlStrEqual(prop, BAD_CAST "xen")) {
806 807 808 809
            xmlFree(prop);
            goto error;
        }
        xmlFree(prop);
810 811 812 813 814 815 816 817 818 819
    }
    virBufferAdd(&buf, "(vm ", 4);
    ctxt = xmlXPathNewContext(xml);
    if (ctxt == NULL) {
        goto error;
    }
    /*
     * extract soem of the basics, name, memory, cpus ...
     */
    obj = xmlXPathEval(BAD_CAST "string(/domain/name[1])", ctxt);
820
    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
821
        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
822
        virXMLError(VIR_ERR_NO_NAME, xmldesc, 0);
823 824 825
        goto error;
    }
    virBufferVSprintf(&buf, "(name '%s')", obj->stringval);
826 827
    nam = strdup((const char *) obj->stringval);
    if (nam == NULL) {
828 829
        virXMLError(VIR_ERR_NO_MEMORY, "copying name", 0);
        goto error;
830
    }
831 832 833 834 835
    xmlXPathFreeObject(obj);

    obj = xmlXPathEval(BAD_CAST "number(/domain/memory[1])", ctxt);
    if ((obj == NULL) || (obj->type != XPATH_NUMBER) ||
        (obj->floatval < 64000)) {
836
        virBufferVSprintf(&buf, "(memory 128)(maxmem 128)");
837 838
    } else {
        unsigned long mem = (obj->floatval / 1024);
839

840 841 842 843 844 845 846
        virBufferVSprintf(&buf, "(memory %lu)(maxmem %lu)", mem, mem);
    }
    xmlXPathFreeObject(obj);

    obj = xmlXPathEval(BAD_CAST "number(/domain/vcpu[1])", ctxt);
    if ((obj == NULL) || (obj->type != XPATH_NUMBER) ||
        (obj->floatval <= 0)) {
847
        virBufferVSprintf(&buf, "(vcpus 1)");
848 849
    } else {
        unsigned int cpu = (unsigned int) obj->floatval;
850

851 852 853 854
        virBufferVSprintf(&buf, "(vcpus %u)", cpu);
    }
    xmlXPathFreeObject(obj);

855 856 857 858 859 860 861 862
    obj = xmlXPathEval(BAD_CAST "string(/domain/bootloader[1])", ctxt);
    if ((obj != NULL) && (obj->type == XPATH_STRING) &&
        (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
	virBufferVSprintf(&buf, "(bootloader '%s')", obj->stringval);
	bootloader = 1;
    }
    xmlXPathFreeObject(obj);

863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886
    obj = xmlXPathEval(BAD_CAST "string(/domain/on_poweroff[1])", ctxt);
    if ((obj != NULL) && (obj->type == XPATH_STRING) &&
        (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
	virBufferVSprintf(&buf, "(on_poweroff '%s')", obj->stringval);
	bootloader = 1;
    }
    xmlXPathFreeObject(obj);

    obj = xmlXPathEval(BAD_CAST "string(/domain/on_reboot[1])", ctxt);
    if ((obj != NULL) && (obj->type == XPATH_STRING) &&
        (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
	virBufferVSprintf(&buf, "(on_reboot '%s')", obj->stringval);
	bootloader = 1;
    }
    xmlXPathFreeObject(obj);

    obj = xmlXPathEval(BAD_CAST "string(/domain/on_crash[1])", ctxt);
    if ((obj != NULL) && (obj->type == XPATH_STRING) &&
        (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
	virBufferVSprintf(&buf, "(on_crash '%s')", obj->stringval);
	bootloader = 1;
    }
    xmlXPathFreeObject(obj);

887 888 889 890
    /* analyze of the os description */
    virBufferAdd(&buf, "(image ", 7);
    obj = xmlXPathEval(BAD_CAST "/domain/os[1]", ctxt);
    if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
891 892
        (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr != 1)) {
        virXMLError(VIR_ERR_NO_OS, nam, 0);
893 894
        goto error;
    }
895 896
    res = virDomainParseXMLOSDesc(obj->nodesetval->nodeTab[0], &buf,
                                  bootloader);
897 898 899 900 901 902 903 904 905
    if (res != 0) {
        goto error;
    }
    xmlXPathFreeObject(obj);
    virBufferAdd(&buf, ")", 1);

    /* analyze of the devices */
    obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt);
    if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
906 907
        (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr < 1)) {
        virXMLError(VIR_ERR_NO_DEVICE, nam, 0);
908 909
        goto error;
    }
910 911 912 913 914 915 916
    for (i = 0; i < obj->nodesetval->nodeNr; i++) {
        virBufferAdd(&buf, "(device ", 8);
        res = virDomainParseXMLDiskDesc(obj->nodesetval->nodeTab[i], &buf);
        if (res != 0) {
            goto error;
        }
        virBufferAdd(&buf, ")", 1);
917 918 919 920 921
    }
    xmlXPathFreeObject(obj);
    obj = xmlXPathEval(BAD_CAST "/domain/devices/interface", ctxt);
    if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
        (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) {
922 923 924 925 926 927 928 929 930
        for (i = 0; i < obj->nodesetval->nodeNr; i++) {
            virBufferAdd(&buf, "(device ", 8);
            res =
                virDomainParseXMLIfDesc(obj->nodesetval->nodeTab[i], &buf);
            if (res != 0) {
                goto error;
            }
            virBufferAdd(&buf, ")", 1);
        }
931 932 933 934
    }
    xmlXPathFreeObject(obj);


D
Daniel Veillard 已提交
935
    virBufferAdd(&buf, ")", 1); /* closes (vm */
936 937 938 939
    buf.content[buf.use] = 0;

    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(xml);
940 941

    if (name != NULL)
942
        *name = nam;
943

944 945 946
    return (ret);

  error:
947
    if (nam != NULL)
948
        free(nam);
949
    if (name != NULL)
950
        *name = NULL;
951 952 953 954 955 956 957 958
    if (obj != NULL)
        xmlXPathFreeObject(obj);
    if (ctxt != NULL)
        xmlXPathFreeContext(ctxt);
    if (xml != NULL)
        xmlFreeDoc(xml);
    if (ret != NULL)
        free(ret);
959
    return (NULL);
960
}