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

11
#include <config.h>
12

13
#include "libvirt/libvirt.h"
14 15 16 17 18

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
19
#include <limits.h>
20
#ifdef WITH_XEN
21
#include <xs.h>
22
#endif
23
#include <math.h>               /* for isnan() */
24 25
#include "internal.h"
#include "hash.h"
D
Daniel Veillard 已提交
26
#include "sexpr.h"
27
#include "xml.h"
28
#include "buf.h"
29
#include "util.h"
30
#include "xs_internal.h"        /* for xenStoreDomainGetNetworkID */
31
#include "xen_unified.h"
32

33 34 35 36 37 38 39 40 41
/**
 * virXMLError:
 * @conn: a connection if any
 * @error: the error number
 * @info: information/format string
 * @value: extra integer parameter for the error string
 *
 * Report an error coming from the XML module.
 */
42
static void
43 44
virXMLError(virConnectPtr conn, virErrorNumber error, const char *info,
            int value)
45
{
46
    const char *errmsg;
47

48 49 50 51
    if (error == VIR_ERR_OK)
        return;

    errmsg = __virErrorMsg(error, info);
52
    __virRaiseError(conn, NULL, NULL, VIR_FROM_XML, error, VIR_ERR_ERROR,
53
                    errmsg, info, NULL, value, 0, errmsg, info, value);
54 55
}

56 57 58 59 60
/************************************************************************
 *									*
 * Parser and converter for the CPUset strings used in libvirt		*
 *									*
 ************************************************************************/
61
#if WITH_XEN
62 63 64 65 66 67 68 69 70 71 72
/**
 * parseCpuNumber:
 * @str: pointer to the char pointer used
 * @maxcpu: maximum CPU number allowed
 *
 * Parse a CPU number
 *
 * Returns the CPU number or -1 in case of error. @str will be
 *         updated to skip the number.
 */
static int
73 74
parseCpuNumber(const char **str, int maxcpu)
{
75 76 77 78
    int ret = 0;
    const char *cur = *str;

    if ((*cur < '0') || (*cur > '9'))
79
        return (-1);
80 81 82

    while ((*cur >= '0') && (*cur <= '9')) {
        ret = ret * 10 + (*cur - '0');
83
        if (ret >= maxcpu)
84 85
            return (-1);
        cur++;
86 87
    }
    *str = cur;
88
    return (ret);
89 90 91
}

/**
92
 * virSaveCpuSet:
93 94 95 96 97 98 99 100 101
 * @conn: connection
 * @cpuset: pointer to a char array for the CPU set
 * @maxcpu: number of elements available in @cpuset
 *
 * Serialize the cpuset to a string
 *
 * Returns the new string NULL in case of error. The string need to be
 *         freed by the caller.
 */
102 103
char *
virSaveCpuSet(virConnectPtr conn, char *cpuset, int maxcpu)
104 105 106 107 108 109
{
    virBufferPtr buf;
    char *ret;
    int start, cur;
    int first = 1;

110 111
    if ((cpuset == NULL) || (maxcpu <= 0) || (maxcpu > 100000))
        return (NULL);
112 113 114

    buf = virBufferNew(1000);
    if (buf == NULL) {
115 116
        virXMLError(conn, VIR_ERR_NO_MEMORY, _("allocate buffer"), 1000);
        return (NULL);
117 118 119 120 121
    }
    cur = 0;
    start = -1;
    while (cur < maxcpu) {
        if (cpuset[cur]) {
122 123 124 125
            if (start == -1)
                start = cur;
        } else if (start != -1) {
            if (!first)
126
                virBufferAddLit(buf, ",");
127
            else
128 129 130 131 132 133 134 135
                first = 0;
            if (cur == start + 1)
                virBufferVSprintf(buf, "%d", start);
            else
                virBufferVSprintf(buf, "%d-%d", start, cur - 1);
            start = -1;
        }
        cur++;
136 137
    }
    if (start != -1) {
138
        if (!first)
139
            virBufferAddLit(buf, ",");
140 141 142 143
        if (maxcpu == start + 1)
            virBufferVSprintf(buf, "%d", start);
        else
            virBufferVSprintf(buf, "%d-%d", start, maxcpu - 1);
144 145
    }
    ret = virBufferContentAndFree(buf);
146
    return (ret);
147 148 149 150
}

/**
 * virParseCpuSet:
151
 * @conn: connection
152 153 154 155 156 157 158 159 160 161 162 163 164 165
 * @str: pointer to a CPU set string pointer
 * @sep: potential character used to mark the end of string if not 0
 * @cpuset: pointer to a char array for the CPU set
 * @maxcpu: number of elements available in @cpuset
 *
 * Parse the cpu set, it will set the value for enabled CPUs in the @cpuset
 * to 1, and 0 otherwise. The syntax allows coma separated entries each
 * can be either a CPU number, ^N to unset that CPU or N-M for ranges.
 *
 * Returns the number of CPU found in that set, or -1 in case of error.
 *         @cpuset is modified accordingly to the value parsed.
 *         @str is updated to the end of the part parsed
 */
int
166 167
virParseCpuSet(virConnectPtr conn, const char **str, char sep,
               char *cpuset, int maxcpu)
168 169 170 171 172 173
{
    const char *cur;
    int ret = 0;
    int i, start, last;
    int neg = 0;

174 175 176
    if ((str == NULL) || (cpuset == NULL) || (maxcpu <= 0) ||
        (maxcpu > 100000))
        return (-1);
177 178

    cur = *str;
179
    virSkipSpaces(&cur);
180 181 182 183
    if (*cur == 0)
        goto parse_error;

    /* initialize cpumap to all 0s */
184 185
    for (i = 0; i < maxcpu; i++)
        cpuset[i] = 0;
186 187 188
    ret = 0;

    while ((*cur != 0) && (*cur != sep)) {
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
        /*
         * 3 constructs are allowed:
         *     - N   : a single CPU number
         *     - N-M : a range of CPU numbers with N < M
         *     - ^N  : remove a single CPU number from the current set
         */
        if (*cur == '^') {
            cur++;
            neg = 1;
        }

        if ((*cur < '0') || (*cur > '9'))
            goto parse_error;
        start = parseCpuNumber(&cur, maxcpu);
        if (start < 0)
            goto parse_error;
205
        virSkipSpaces(&cur);
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
        if ((*cur == ',') || (*cur == 0) || (*cur == sep)) {
            if (neg) {
                if (cpuset[start] == 1) {
                    cpuset[start] = 0;
                    ret--;
                }
            } else {
                if (cpuset[start] == 0) {
                    cpuset[start] = 1;
                    ret++;
                }
            }
        } else if (*cur == '-') {
            if (neg)
                goto parse_error;
            cur++;
222
            virSkipSpaces(&cur);
223 224 225 226 227 228 229 230 231
            last = parseCpuNumber(&cur, maxcpu);
            if (last < start)
                goto parse_error;
            for (i = start; i <= last; i++) {
                if (cpuset[i] == 0) {
                    cpuset[i] = 1;
                    ret++;
                }
            }
232
            virSkipSpaces(&cur);
233 234 235
        }
        if (*cur == ',') {
            cur++;
236
            virSkipSpaces(&cur);
237 238 239 240 241
            neg = 0;
        } else if ((*cur == 0) || (*cur == sep)) {
            break;
        } else
            goto parse_error;
242 243
    }
    *str = cur;
244
    return (ret);
245

246
  parse_error:
247
    virXMLError(conn, VIR_ERR_XEN_CALL,
248 249
                _("topology cpuset syntax error"), 0);
    return (-1);
250 251 252
}


253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
/**
 * virConvertCpuSet:
 * @conn: connection
 * @str: pointer to a Xen or user provided CPU set string pointer
 * @maxcpu: number of CPUs on the node, if 0 4096 will be used
 *
 * Parse the given CPU set string and convert it to a range based
 * string.
 *
 * Returns a new string which must be freed by the caller or NULL in
 *         case of error.
 */
char *
virConvertCpuSet(virConnectPtr conn, const char *str, int maxcpu) {
    int ret;
    char *res, *cpuset;
    const char *cur = str;

    if (str == NULL)
        return(NULL);

    if (maxcpu <= 0)
        maxcpu = 4096;

277
    cpuset = calloc(maxcpu, sizeof(*cpuset));
278 279 280 281
    if (cpuset == NULL) {
	virXMLError(conn, VIR_ERR_NO_MEMORY, _("allocate buffer"), 0);
	return(NULL);
    }
282

283 284 285 286 287 288 289 290 291
    ret = virParseCpuSet(conn, &cur, 0, cpuset, maxcpu);
    if (ret < 0) {
        free(cpuset);
	return(NULL);
    }
    res = virSaveCpuSet(conn, cpuset, maxcpu);
    free(cpuset);
    return (res);
}
292
#endif /* WITH_XEN */
293
#ifndef PROXY
294 295 296 297 298 299 300

/************************************************************************
 *									*
 * Wrappers around libxml2 XPath specific functions			*
 *									*
 ************************************************************************/

301 302 303 304 305 306 307 308 309 310 311
/**
 * virXPathString:
 * @xpath: the XPath string to evaluate
 * @ctxt: an XPath context
 *
 * Convenience function to evaluate an XPath string
 *
 * Returns a new string which must be deallocated by the caller or NULL
 *         if the evaluation failed.
 */
char *
312 313
virXPathString(const char *xpath, xmlXPathContextPtr ctxt)
{
314 315 316 317
    xmlXPathObjectPtr obj;
    char *ret;

    if ((ctxt == NULL) || (xpath == NULL)) {
318
        virXMLError(NULL, VIR_ERR_INTERNAL_ERROR,
319
                    _("Invalid parameter to virXPathString()"), 0);
320
        return (NULL);
321 322 323
    }
    obj = xmlXPathEval(BAD_CAST xpath, ctxt);
    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
D
Daniel P. Berrange 已提交
324
        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
325
        xmlXPathFreeObject(obj);
326
        return (NULL);
D
Daniel P. Berrange 已提交
327
    }
328 329 330
    ret = strdup((char *) obj->stringval);
    xmlXPathFreeObject(obj);
    if (ret == NULL) {
331
        virXMLError(NULL, VIR_ERR_NO_MEMORY, _("strdup failed"), 0);
332
    }
333
    return (ret);
334 335 336 337 338 339 340 341 342 343 344 345 346 347
}

/**
 * virXPathNumber:
 * @xpath: the XPath string to evaluate
 * @ctxt: an XPath context
 * @value: the returned double value
 *
 * Convenience function to evaluate an XPath number
 *
 * Returns 0 in case of success in which case @value is set,
 *         or -1 if the evaluation failed.
 */
int
348 349
virXPathNumber(const char *xpath, xmlXPathContextPtr ctxt, double *value)
{
350 351 352
    xmlXPathObjectPtr obj;

    if ((ctxt == NULL) || (xpath == NULL) || (value == NULL)) {
353
        virXMLError(NULL, VIR_ERR_INTERNAL_ERROR,
354
                    _("Invalid parameter to virXPathNumber()"), 0);
355
        return (-1);
356 357 358 359
    }
    obj = xmlXPathEval(BAD_CAST xpath, ctxt);
    if ((obj == NULL) || (obj->type != XPATH_NUMBER) ||
        (isnan(obj->floatval))) {
360 361
        xmlXPathFreeObject(obj);
        return (-1);
362
    }
363

364 365
    *value = obj->floatval;
    xmlXPathFreeObject(obj);
366
    return (0);
367 368 369 370 371 372 373 374 375 376 377
}

/**
 * virXPathLong:
 * @xpath: the XPath string to evaluate
 * @ctxt: an XPath context
 * @value: the returned long value
 *
 * Convenience function to evaluate an XPath number
 *
 * Returns 0 in case of success in which case @value is set,
378 379
 *         or -1 if the XPath evaluation failed or -2 if the
 *         value doesn't have a long format.
380 381
 */
int
382 383
virXPathLong(const char *xpath, xmlXPathContextPtr ctxt, long *value)
{
384 385 386 387
    xmlXPathObjectPtr obj;
    int ret = 0;

    if ((ctxt == NULL) || (xpath == NULL) || (value == NULL)) {
388
        virXMLError(NULL, VIR_ERR_INTERNAL_ERROR,
389
                    _("Invalid parameter to virXPathNumber()"), 0);
390
        return (-1);
391 392 393 394 395
    }
    obj = xmlXPathEval(BAD_CAST xpath, ctxt);
    if ((obj != NULL) && (obj->type == XPATH_STRING) &&
        (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
        char *conv = NULL;
396
        long val;
397

398 399
        val = strtol((const char *) obj->stringval, &conv, 10);
        if (conv == (const char *) obj->stringval) {
400 401
            ret = -2;
        } else {
402 403
            *value = val;
        }
404 405
    } else if ((obj != NULL) && (obj->type == XPATH_NUMBER) &&
               (!(isnan(obj->floatval)))) {
406 407 408 409
        *value = (long) obj->floatval;
        if (*value != obj->floatval) {
            ret = -2;
        }
410
    } else {
411
        ret = -1;
412
    }
413

414
    xmlXPathFreeObject(obj);
415
    return (ret);
416 417 418 419 420 421 422 423 424 425 426 427
}

/**
 * virXPathBoolean:
 * @xpath: the XPath string to evaluate
 * @ctxt: an XPath context
 *
 * Convenience function to evaluate an XPath boolean
 *
 * Returns 0 if false, 1 if true, or -1 if the evaluation failed.
 */
int
428 429
virXPathBoolean(const char *xpath, xmlXPathContextPtr ctxt)
{
430 431 432 433
    xmlXPathObjectPtr obj;
    int ret;

    if ((ctxt == NULL) || (xpath == NULL)) {
434
        virXMLError(NULL, VIR_ERR_INTERNAL_ERROR,
435
                    _("Invalid parameter to virXPathBoolean()"), 0);
436
        return (-1);
437 438 439 440
    }
    obj = xmlXPathEval(BAD_CAST xpath, ctxt);
    if ((obj == NULL) || (obj->type != XPATH_BOOLEAN) ||
        (obj->boolval < 0) || (obj->boolval > 1)) {
441 442
        xmlXPathFreeObject(obj);
        return (-1);
443 444
    }
    ret = obj->boolval;
445

446
    xmlXPathFreeObject(obj);
447
    return (ret);
448 449 450 451 452 453 454 455 456 457 458 459 460
}

/**
 * virXPathNode:
 * @xpath: the XPath string to evaluate
 * @ctxt: an XPath context
 *
 * Convenience function to evaluate an XPath node set and returning
 * only one node, the first one in the set if any
 *
 * Returns a pointer to the node or NULL if the evaluation failed.
 */
xmlNodePtr
461 462
virXPathNode(const char *xpath, xmlXPathContextPtr ctxt)
{
463 464 465 466
    xmlXPathObjectPtr obj;
    xmlNodePtr ret;

    if ((ctxt == NULL) || (xpath == NULL)) {
467
        virXMLError(NULL, VIR_ERR_INTERNAL_ERROR,
468
                    _("Invalid parameter to virXPathNode()"), 0);
469
        return (NULL);
470 471 472 473
    }
    obj = xmlXPathEval(BAD_CAST xpath, ctxt);
    if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
        (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr <= 0) ||
474 475 476
        (obj->nodesetval->nodeTab == NULL)) {
        xmlXPathFreeObject(obj);
        return (NULL);
477
    }
478

479 480
    ret = obj->nodesetval->nodeTab[0];
    xmlXPathFreeObject(obj);
481
    return (ret);
482
}
483

484 485 486 487 488 489 490 491 492 493 494 495
/**
 * virXPathNodeSet:
 * @xpath: the XPath string to evaluate
 * @ctxt: an XPath context
 * @list: the returned list of nodes (or NULL if only count matters)
 *
 * Convenience function to evaluate an XPath node set
 *
 * Returns the number of nodes found in which case @list is set (and
 *         must be freed) or -1 if the evaluation failed.
 */
int
496 497 498
virXPathNodeSet(const char *xpath, xmlXPathContextPtr ctxt,
                xmlNodePtr ** list)
{
499 500 501 502
    xmlXPathObjectPtr obj;
    int ret;

    if ((ctxt == NULL) || (xpath == NULL)) {
503
        virXMLError(NULL, VIR_ERR_INTERNAL_ERROR,
504
                    _("Invalid parameter to virXPathNodeSet()"), 0);
505
        return (-1);
506 507 508 509
    }
    obj = xmlXPathEval(BAD_CAST xpath, ctxt);
    if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
        (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr <= 0) ||
510 511 512 513 514
        (obj->nodesetval->nodeTab == NULL)) {
        xmlXPathFreeObject(obj);
        if (list != NULL)
            *list = NULL;
        return (-1);
515
    }
516

517 518
    ret = obj->nodesetval->nodeNr;
    if (list != NULL) {
519
        *list = malloc(ret * sizeof(**list));
520 521 522
        if (*list == NULL) {
            virXMLError(NULL, VIR_ERR_NO_MEMORY,
                        _("allocate string array"),
523
                        ret * sizeof(**list));
524 525 526 527
        } else {
            memcpy(*list, obj->nodesetval->nodeTab,
                   ret * sizeof(xmlNodePtr));
        }
528 529
    }
    xmlXPathFreeObject(obj);
530
    return (ret);
531 532
}

533 534 535 536 537
/************************************************************************
 *									*
 * Converter functions to go from the XML tree to an S-Expr for Xen	*
 *									*
 ************************************************************************/
538
#if WITH_XEN
539
/**
540
 * virtDomainParseXMLGraphicsDescImage:
541
 * @conn: pointer to the hypervisor connection
542 543
 * @node: node containing graphics description
 * @buf: a buffer for the result S-Expr
544
 * @xendConfigVersion: xend configuration file format
545
 *
546 547 548
 * Parse the graphics 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
549 550 551 552
 * valid over time.
 *
 * Returns 0 in case of success, -1 in case of error
 */
553 554 555 556
static int
virDomainParseXMLGraphicsDescImage(virConnectPtr conn ATTRIBUTE_UNUSED,
                                   xmlNodePtr node, virBufferPtr buf,
                                   int xendConfigVersion)
557 558 559 560 561 562
{
    xmlChar *graphics_type = NULL;

    graphics_type = xmlGetProp(node, BAD_CAST "type");
    if (graphics_type != NULL) {
        if (xmlStrEqual(graphics_type, BAD_CAST "sdl")) {
563
            virBufferAddLit(buf, "(sdl 1)");
564 565 566
            /* TODO:
             * Need to understand sdl options
             *
567 568
             *virBufferAddLit(buf, "(display localhost:10.0)");
             *virBufferAddLit(buf, "(xauthority /root/.Xauthority)");
569
             */
570
        } else if (xmlStrEqual(graphics_type, BAD_CAST "vnc")) {
571
            virBufferAddLit(buf, "(vnc 1)");
572
            if (xendConfigVersion >= 2) {
573
                xmlChar *vncport = xmlGetProp(node, BAD_CAST "port");
574 575
                xmlChar *vnclisten = xmlGetProp(node, BAD_CAST "listen");
                xmlChar *vncpasswd = xmlGetProp(node, BAD_CAST "passwd");
576
                xmlChar *keymap = xmlGetProp(node, BAD_CAST "keymap");
577

578
                if (vncport != NULL) {
579 580
                    long port = strtol((const char *) vncport, NULL, 10);

581
                    if (port == -1)
582
                        virBufferAddLit(buf, "(vncunused 1)");
583
                    else if (port >= 5900)
584 585
                        virBufferVSprintf(buf, "(vncdisplay %ld)",
                                          port - 5900);
586
                    xmlFree(vncport);
587
                }
588 589 590 591 592 593 594 595
                if (vnclisten != NULL) {
                    virBufferVSprintf(buf, "(vnclisten %s)", vnclisten);
                    xmlFree(vnclisten);
                }
                if (vncpasswd != NULL) {
                    virBufferVSprintf(buf, "(vncpasswd %s)", vncpasswd);
                    xmlFree(vncpasswd);
                }
596 597 598 599
                if (keymap != NULL) {
                    virBufferVSprintf(buf, "(keymap %s)", keymap);
                    xmlFree(keymap);
                }
600 601
            }
        }
602 603 604 605 606 607
        xmlFree(graphics_type);
    }
    return 0;
}


608 609
/**
 * virtDomainParseXMLGraphicsDescVFB:
610
 * @conn: pointer to the hypervisor connection
611 612 613
 * @node: node containing graphics description
 * @buf: a buffer for the result S-Expr
 *
614 615 616
 * Parse the graphics 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
617 618 619 620
 * valid over time.
 *
 * Returns 0 in case of success, -1 in case of error
 */
621 622 623
static int
virDomainParseXMLGraphicsDescVFB(virConnectPtr conn ATTRIBUTE_UNUSED,
                                 xmlNodePtr node, virBufferPtr buf)
624 625 626 627 628
{
    xmlChar *graphics_type = NULL;

    graphics_type = xmlGetProp(node, BAD_CAST "type");
    if (graphics_type != NULL) {
629 630
        virBufferAddLit(buf, "(device (vkbd))");
        virBufferAddLit(buf, "(device (vfb ");
631
        if (xmlStrEqual(graphics_type, BAD_CAST "sdl")) {
632
            virBufferAddLit(buf, "(type sdl)");
633 634 635
            /* TODO:
             * Need to understand sdl options
             *
636 637
             *virBufferAddLit(buf, "(display localhost:10.0)");
             *virBufferAddLit(buf, "(xauthority /root/.Xauthority)");
638
             */
639
        } else if (xmlStrEqual(graphics_type, BAD_CAST "vnc")) {
640
            virBufferAddLit(buf, "(type vnc)");
641 642 643
            xmlChar *vncport = xmlGetProp(node, BAD_CAST "port");
            xmlChar *vnclisten = xmlGetProp(node, BAD_CAST "listen");
            xmlChar *vncpasswd = xmlGetProp(node, BAD_CAST "passwd");
644
            xmlChar *keymap = xmlGetProp(node, BAD_CAST "keymap");
645

646
            if (vncport != NULL) {
647 648
                long port = strtol((const char *) vncport, NULL, 10);

649
                if (port == -1)
650
                    virBufferAddLit(buf, "(vncunused 1)");
651
                else if (port >= 5900)
652 653
                    virBufferVSprintf(buf, "(vncdisplay %ld)",
                                      port - 5900);
654 655 656 657 658 659 660 661 662 663
                xmlFree(vncport);
            }
            if (vnclisten != NULL) {
                virBufferVSprintf(buf, "(vnclisten %s)", vnclisten);
                xmlFree(vnclisten);
            }
            if (vncpasswd != NULL) {
                virBufferVSprintf(buf, "(vncpasswd %s)", vncpasswd);
                xmlFree(vncpasswd);
            }
664 665 666 667
            if (keymap != NULL) {
                virBufferVSprintf(buf, "(keymap %s)", keymap);
                xmlFree(keymap);
            }
668
        }
669
        virBufferAddLit(buf, "))");
670 671 672 673 674 675
        xmlFree(graphics_type);
    }
    return 0;
}


676
/**
677
 * virDomainParseXMLOSDescHVM:
678
 * @conn: pointer to the hypervisor connection
679
 * @node: node containing HVM OS description
680
 * @buf: a buffer for the result S-Expr
681
 * @ctxt: a path context representing the XML description
682
 * @vcpus: number of virtual CPUs to configure
683
 * @xendConfigVersion: xend configuration file format
684
 * @hasKernel: whether the domain is booting from a kernel
685
 *
686 687
 * Parse the OS part of the XML description for a HVM domain
 * and add it to the S-Expr in buf.
688 689 690 691
 *
 * Returns 0 in case of success, -1 in case of error.
 */
static int
692 693
virDomainParseXMLOSDescHVM(virConnectPtr conn, xmlNodePtr node,
                           virBufferPtr buf, xmlXPathContextPtr ctxt,
694 695
                           int vcpus, int xendConfigVersion,
                           int hasKernel)
696 697
{
    xmlNodePtr cur, txt;
698
    xmlNodePtr *nodes = NULL;
699
    xmlChar *loader = NULL;
700 701
    char bootorder[5];
    int nbootorder = 0;
702
    int res, nb_nodes;
703
    char *str;
704 705 706 707

    cur = node->children;
    while (cur != NULL) {
        if (cur->type == XML_ELEMENT_NODE) {
708 709
            if ((loader == NULL) &&
                (xmlStrEqual(cur->name, BAD_CAST "loader"))) {
710 711
                txt = cur->children;
                if ((txt != NULL) && (txt->type == XML_TEXT_NODE) &&
712
                    (txt->next == NULL))
713
                    loader = txt->content;
714 715
            } else if ((xmlStrEqual(cur->name, BAD_CAST "boot"))) {
                xmlChar *boot_dev = xmlGetProp(cur, BAD_CAST "dev");
716 717 718 719

                if (nbootorder ==
                    ((sizeof(bootorder) / sizeof(bootorder[0])) - 1)) {
                    virXMLError(conn, VIR_ERR_XML_ERROR,
720
                                _("too many boot devices"), 0);
721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737
                    return (-1);
                }
                if (xmlStrEqual(boot_dev, BAD_CAST "fd")) {
                    bootorder[nbootorder++] = 'a';
                } else if (xmlStrEqual(boot_dev, BAD_CAST "cdrom")) {
                    bootorder[nbootorder++] = 'd';
                } else if (xmlStrEqual(boot_dev, BAD_CAST "network")) {
                    bootorder[nbootorder++] = 'n';
                } else if (xmlStrEqual(boot_dev, BAD_CAST "hd")) {
                    bootorder[nbootorder++] = 'c';
                } else {
                    xmlFree(boot_dev);
                    /* Any other type of boot dev is unsupported right now */
                    virXMLError(conn, VIR_ERR_XML_ERROR, NULL, 0);
                    return (-1);
                }
                xmlFree(boot_dev);
738 739 740 741
            }
        }
        cur = cur->next;
    }
742 743 744 745 746 747 748
    /*
     * XenD always needs boot order defined for HVM, even if
     * booting off a kernel + initrd, so force to 'c' if nothing
     * else is specified
     */
    if (nbootorder == 0)
        bootorder[nbootorder++] = 'c';
749
    bootorder[nbootorder] = '\0';
750

751
    if (loader == NULL) {
752
        virXMLError(conn, VIR_ERR_INTERNAL_ERROR, _("no HVM domain loader"), 0);
753
        return -1;
754 755
    }

756
    /*
757 758 759 760 761 762 763 764 765
     * Originally XenD abused the 'kernel' parameter for the HVM
     * firmware. New XenD allows HVM guests to boot from a kernel
     * and if this is enabled, the HVM firmware must use the new
     * 'loader' parameter
     */
    if (hasKernel) {
        virBufferVSprintf(buf, "(loader '%s')", (const char *) loader);
    } else {
        virBufferVSprintf(buf, "(kernel '%s')", (const char *) loader);
766 767
    }

768 769
    virBufferVSprintf(buf, "(vcpus %d)", vcpus);

770 771
    if (nbootorder)
        virBufferVSprintf(buf, "(boot %s)", bootorder);
772

773
    /* get the 1st floppy device file */
774 775 776 777
    cur = virXPathNode(
         "/domain/devices/disk[@device='floppy' and target/@dev='fda']/source",
         ctxt);
    if (cur != NULL) {
778
        xmlChar *fdfile;
779

780
        fdfile = xmlGetProp(cur, BAD_CAST "file");
781
        if (fdfile != NULL) {
782 783
            virBufferVSprintf(buf, "(fda '%s')", fdfile);
            free(fdfile);
784
        }
785
    }
786

787
    /* get the 2nd floppy device file */
788 789 790 791
    cur = virXPathNode(
         "/domain/devices/disk[@device='floppy' and target/@dev='fdb']/source",
         ctxt);
    if (cur != NULL) {
792
        xmlChar *fdfile;
793

794
        fdfile = xmlGetProp(cur, BAD_CAST "file");
795
        if (fdfile != NULL) {
796 797
            virBufferVSprintf(buf, "(fdb '%s')", fdfile);
            free(fdfile);
798
        }
799
    }
800 801


802 803 804
    /* get the cdrom device file */
    /* Only XenD <= 3.0.2 wants cdrom config here */
    if (xendConfigVersion == 1) {
805 806 807 808
        cur = virXPathNode(
	  "/domain/devices/disk[@device='cdrom' and target/@dev='hdc']/source",
             ctxt);
        if (cur != NULL) {
809 810 811 812 813
            xmlChar *cdfile;

            cdfile = xmlGetProp(cur, BAD_CAST "file");
            if (cdfile != NULL) {
                virBufferVSprintf(buf, "(cdrom '%s')",
814
                                  (const char *) cdfile);
815
                xmlFree(cdfile);
816 817
            }
        }
818 819
    }

820
    if (virXPathNode("/domain/features/acpi", ctxt) != NULL)
821
        virBufferAddLit(buf, "(acpi 1)");
822
    if (virXPathNode("/domain/features/apic", ctxt) != NULL)
823
        virBufferAddLit(buf, "(apic 1)");
824
    if (virXPathNode("/domain/features/pae", ctxt) != NULL)
825
        virBufferAddLit(buf, "(pae 1)");
826

827
    virBufferAddLit(buf, "(usb 1)");
828 829 830
    nb_nodes = virXPathNodeSet("/domain/devices/input", ctxt, &nodes);
    if (nb_nodes > 0) {
        int i;
831

832 833 834 835
        for (i = 0; i < nb_nodes; i++) {
            xmlChar *itype = NULL, *bus = NULL;
            int isMouse = 1;

836
            itype = xmlGetProp(nodes[i], (xmlChar *) "type");
837 838 839 840

            if (!itype) {
                goto error;
            }
841
            if (!strcmp((const char *) itype, "tablet"))
842
                isMouse = 0;
843
            else if (strcmp((const char *) itype, "mouse")) {
844
                xmlFree(itype);
845 846
                virXMLError(conn, VIR_ERR_XML_ERROR,
                            _("invalid input device"), 0);
847 848 849 850
                goto error;
            }
            xmlFree(itype);

851
            bus = xmlGetProp(nodes[i], (xmlChar *) "bus");
852 853 854 855
            if (!bus) {
                if (!isMouse) {
                    /* Nothing - implicit ps2 */
                } else {
856
                    virBufferAddLit(buf, "(usbdevice tablet)");
857 858
                }
            } else {
859
                if (!strcmp((const char *) bus, "ps2")) {
860 861
                    if (!isMouse) {
                        xmlFree(bus);
862 863
                        virXMLError(conn, VIR_ERR_XML_ERROR,
                                    _("invalid input device"), 0);
864 865 866
                        goto error;
                    }
                    /* Nothing - implicit ps2 */
867
                } else if (!strcmp((const char *) bus, "usb")) {
868
                    if (isMouse)
869
                        virBufferAddLit(buf, "(usbdevice mouse)");
870
                    else
871
                        virBufferAddLit(buf, "(usbdevice tablet)");
872 873 874 875 876 877 878 879 880
                }
            }
            xmlFree(bus);
        }
        free(nodes);
        nodes = NULL;
    }


881 882
    res = virXPathBoolean("count(domain/devices/console) > 0", ctxt);
    if (res < 0) {
883
        virXMLError(conn, VIR_ERR_XML_ERROR, NULL, 0);
884
        goto error;
885
    }
886
    if (res) {
887
        virBufferAddLit(buf, "(serial pty)");
888
    }
889

890 891
    str = virXPathString("string(/domain/clock/@offset)", ctxt);
    if (str != NULL && !strcmp(str, "localtime")) {
892
        virBufferAddLit(buf, "(localtime 1)");
893
    }
894
    free(str);
895

896
    return (0);
897

898
  error:
899
    free(nodes);
900
    return (-1);
901 902
}

903

904
/**
905
 * virDomainParseXMLOSDescKernel:
906
 * @conn: pointer to the hypervisor connection
907 908 909
 * @node: node containing PV OS description
 * @buf: a buffer for the result S-Expr
 *
910 911
 * Parse the OS part of the XML description for a domain using a direct
 * kernel and initrd to boot.
912 913 914 915
 *
 * Returns 0 in case of success, -1 in case of error.
 */
static int
916 917 918
virDomainParseXMLOSDescKernel(virConnectPtr conn ATTRIBUTE_UNUSED,
                              xmlNodePtr node,
                              virBufferPtr buf)
919
{
920 921 922 923 924 925 926 927 928
    xmlNodePtr cur, txt;
    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) {
929 930
            if ((kernel == NULL) &&
                (xmlStrEqual(cur->name, BAD_CAST "kernel"))) {
931
                txt = cur->children;
932
                if ((txt != NULL) && (txt->type == XML_TEXT_NODE) &&
933
                    (txt->next == NULL))
934 935 936 937
                    kernel = txt->content;
            } else if ((root == NULL) &&
                       (xmlStrEqual(cur->name, BAD_CAST "root"))) {
                txt = cur->children;
938
                if ((txt != NULL) && (txt->type == XML_TEXT_NODE) &&
939
                    (txt->next == NULL))
940 941 942 943
                    root = txt->content;
            } else if ((initrd == NULL) &&
                       (xmlStrEqual(cur->name, BAD_CAST "initrd"))) {
                txt = cur->children;
944
                if ((txt != NULL) && (txt->type == XML_TEXT_NODE) &&
945
                    (txt->next == NULL))
946 947 948 949
                    initrd = txt->content;
            } else if ((cmdline == NULL) &&
                       (xmlStrEqual(cur->name, BAD_CAST "cmdline"))) {
                txt = cur->children;
950
                if ((txt != NULL) && (txt->type == XML_TEXT_NODE) &&
951
                    (txt->next == NULL))
952 953 954
                    cmdline = txt->content;
            }
        }
955 956
        cur = cur->next;
    }
957

958 959
    virBufferVSprintf(buf, "(kernel '%s')", (const char *) kernel);

960
    if (initrd != NULL)
961
        virBufferVSprintf(buf, "(ramdisk '%s')", (const char *) initrd);
962
    if (root != NULL)
963
        virBufferVSprintf(buf, "(root '%s')", (const char *) root);
964
    if (cmdline != NULL)
965
        virBufferVSprintf(buf, "(args '%s')", (const char *) cmdline);
966

967
    return (0);
968 969
}

970 971 972 973 974 975 976 977 978 979
/**
 * virCatchXMLParseError:
 * @ctx: the context
 * @msg: the error message
 * @...: extra arguments
 *
 * SAX callback on parsing errors, act as a gate for libvirt own
 * error reporting.
 */
static void
980 981
virCatchXMLParseError(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...)
{
982 983
    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;

984
    if ((ctxt != NULL) &&
985
        (ctxt->lastError.level == XML_ERR_FATAL) &&
986
        (ctxt->lastError.message != NULL)) {
987
        virXMLError(NULL, VIR_ERR_XML_DETAIL, ctxt->lastError.message,
988
                    ctxt->lastError.line);
989 990 991
    }
}

992 993
/**
 * virDomainParseXMLDiskDesc:
994
 * @node: node containing disk description
995
 * @conn: pointer to the hypervisor connection
996
 * @buf: a buffer for the result S-Expr
997
 * @xendConfigVersion: xend configuration file format
998 999 1000 1001 1002 1003 1004 1005 1006
 *
 * 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
1007 1008
virDomainParseXMLDiskDesc(virConnectPtr conn, xmlNodePtr node,
                          virBufferPtr buf, int hvm, int xendConfigVersion)
1009
{
1010 1011
    xmlNodePtr cur;
    xmlChar *type = NULL;
1012
    xmlChar *device = NULL;
1013 1014
    xmlChar *source = NULL;
    xmlChar *target = NULL;
1015 1016
    xmlChar *drvName = NULL;
    xmlChar *drvType = NULL;
1017
    int ro = 0;
1018
    int shareable = 0;
1019
    int typ = 0;
1020
    int cdrom = 0;
1021
    int isNoSrcCdrom = 0;
1022
    int ret = 0;
1023 1024 1025

    type = xmlGetProp(node, BAD_CAST "type");
    if (type != NULL) {
1026 1027 1028 1029 1030
        if (xmlStrEqual(type, BAD_CAST "file"))
            typ = 0;
        else if (xmlStrEqual(type, BAD_CAST "block"))
            typ = 1;
        xmlFree(type);
1031
    }
1032
    device = xmlGetProp(node, BAD_CAST "device");
1033

1034 1035 1036
    cur = node->children;
    while (cur != NULL) {
        if (cur->type == XML_ELEMENT_NODE) {
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
            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");
1047 1048 1049
            } else if ((drvName == NULL) &&
                       (xmlStrEqual(cur->name, BAD_CAST "driver"))) {
                drvName = xmlGetProp(cur, BAD_CAST "name");
1050
                if (drvName && !strcmp((const char *) drvName, "tap"))
1051
                    drvType = xmlGetProp(cur, BAD_CAST "type");
1052 1053
            } else if (xmlStrEqual(cur->name, BAD_CAST "readonly")) {
                ro = 1;
1054
            } else if (xmlStrEqual(cur->name, BAD_CAST "shareable")) {
1055
                shareable = 1;
1056 1057
            }
        }
1058 1059 1060 1061
        cur = cur->next;
    }

    if (source == NULL) {
1062 1063 1064
        /* There is a case without the source
         * to the CD-ROM device
         */
1065
        if (hvm && device && !strcmp((const char *) device, "cdrom")) {
1066 1067 1068 1069
            isNoSrcCdrom = 1;
        }
        if (!isNoSrcCdrom) {
            virXMLError(conn, VIR_ERR_NO_SOURCE, (const char *) target, 0);
1070 1071
            ret = -1;
            goto cleanup;
1072
        }
1073 1074
    }
    if (target == NULL) {
1075
        virXMLError(conn, VIR_ERR_NO_TARGET, (const char *) source, 0);
1076 1077
        ret = -1;
        goto cleanup;
1078
    }
1079

1080 1081
    /* Xend (all versions) put the floppy device config
     * under the hvm (image (os)) block
1082
     */
1083
    if (hvm && device && !strcmp((const char *) device, "floppy")) {
1084
        goto cleanup;
1085 1086 1087
    }

    /* Xend <= 3.0.2 doesn't include cdrom config here */
1088
    if (hvm && device && !strcmp((const char *) device, "cdrom")) {
1089
        if (xendConfigVersion == 1)
1090
            goto cleanup;
1091 1092
        else
            cdrom = 1;
1093 1094 1095
    }


1096
    virBufferAddLit(buf, "(device ");
1097
    /* Normally disks are in a (device (vbd ...)) block
1098 1099 1100
     * but blktap disks ended up in a differently named
     * (device (tap ....)) block.... */
    if (drvName && !strcmp((const char *) drvName, "tap")) {
1101
        virBufferAddLit(buf, "(tap ");
1102
    } else {
1103
        virBufferAddLit(buf, "(vbd ");
1104
    }
1105

1106
    if (hvm) {
1107 1108
        char *tmp = (char *) target;

1109
        /* Just in case user mistakenly still puts ioemu: in their XML */
1110 1111
        if (!strncmp((const char *) tmp, "ioemu:", 6))
            tmp += 6;
1112 1113 1114

        /* Xend <= 3.0.2 wants a ioemu: prefix on devices for HVM */
        if (xendConfigVersion == 1)
1115 1116 1117 1118
            virBufferVSprintf(buf, "(dev 'ioemu:%s')", (const char *) tmp);
        else                    /* But newer does not */
            virBufferVSprintf(buf, "(dev '%s%s')", (const char *) tmp,
                              cdrom ? ":cdrom" : ":disk");
1119
    } else
1120
        virBufferVSprintf(buf, "(dev '%s')", (const char *) target);
1121

1122
    if (drvName && !isNoSrcCdrom) {
1123
        if (!strcmp((const char *) drvName, "tap")) {
1124
            virBufferVSprintf(buf, "(uname '%s:%s:%s')",
1125 1126 1127
                              (const char *) drvName,
                              (drvType ? (const char *) drvType : "aio"),
                              (const char *) source);
1128 1129
        } else {
            virBufferVSprintf(buf, "(uname '%s:%s')",
1130 1131
                              (const char *) drvName,
                              (const char *) source);
1132
        }
1133
    } else if (!isNoSrcCdrom) {
1134 1135 1136 1137 1138 1139 1140 1141
        if (typ == 0)
            virBufferVSprintf(buf, "(uname 'file:%s')", source);
        else if (typ == 1) {
            if (source[0] == '/')
                virBufferVSprintf(buf, "(uname 'phy:%s')", source);
            else
                virBufferVSprintf(buf, "(uname 'phy:/dev/%s')", source);
        }
1142
    }
1143
    if (ro == 1)
1144
        virBufferAddLit(buf, "(mode 'r')");
1145
    else if (shareable == 1)
1146
        virBufferAddLit(buf, "(mode 'w!')");
1147
    else
1148
        virBufferAddLit(buf, "(mode 'w')");
1149

1150 1151
    virBufferAddLit(buf, ")");
    virBufferAddLit(buf, ")");
1152

1153 1154
  cleanup:
    if (drvType)
1155
        xmlFree(drvType);
1156
    if (drvName)
1157
        xmlFree(drvName);
1158
    if (device)
1159
        xmlFree(device);
1160
    if (target)
1161
        xmlFree(target);
1162
    if (source)
1163 1164
        xmlFree(source);
    return (ret);
1165 1166 1167 1168
}

/**
 * virDomainParseXMLIfDesc:
1169
 * @conn: pointer to the hypervisor connection
1170
 * @node: node containing the interface description
1171
 * @buf: a buffer for the result S-Expr
1172
 * @xendConfigVersion: xend configuration file format
1173 1174 1175 1176 1177 1178 1179 1180 1181
 *
 * 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
1182 1183 1184
virDomainParseXMLIfDesc(virConnectPtr conn ATTRIBUTE_UNUSED,
                        xmlNodePtr node, virBufferPtr buf, int hvm,
                        int xendConfigVersion)
1185
{
1186 1187 1188 1189 1190
    xmlNodePtr cur;
    xmlChar *type = NULL;
    xmlChar *source = NULL;
    xmlChar *mac = NULL;
    xmlChar *script = NULL;
1191
    xmlChar *ip = NULL;
1192
    int typ = 0;
1193
    int ret = -1;
1194 1195 1196

    type = xmlGetProp(node, BAD_CAST "type");
    if (type != NULL) {
1197 1198 1199 1200
        if (xmlStrEqual(type, BAD_CAST "bridge"))
            typ = 0;
        else if (xmlStrEqual(type, BAD_CAST "ethernet"))
            typ = 1;
1201 1202
        else if (xmlStrEqual(type, BAD_CAST "network"))
            typ = 2;
1203
        xmlFree(type);
1204 1205 1206 1207
    }
    cur = node->children;
    while (cur != NULL) {
        if (cur->type == XML_ELEMENT_NODE) {
1208 1209 1210 1211
            if ((source == NULL) &&
                (xmlStrEqual(cur->name, BAD_CAST "source"))) {
                if (typ == 0)
                    source = xmlGetProp(cur, BAD_CAST "bridge");
1212
                else if (typ == 1)
1213
                    source = xmlGetProp(cur, BAD_CAST "dev");
1214 1215
                else
                    source = xmlGetProp(cur, BAD_CAST "network");
1216 1217 1218 1219 1220 1221
            } 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");
1222 1223 1224
            } else if ((ip == NULL) &&
                       (xmlStrEqual(cur->name, BAD_CAST "ip"))) {
                /* XXX in future expect to need to have > 1 ip
1225 1226 1227
                 * address element - eg ipv4 & ipv6. For now
                 * xen only supports a single address though
                 * so lets ignore that complication */
1228
                ip = xmlGetProp(cur, BAD_CAST "address");
1229 1230
            }
        }
1231 1232 1233
        cur = cur->next;
    }

1234
    virBufferAddLit(buf, "(vif ");
1235
    if (mac != NULL) {
1236 1237
        unsigned char addr[6];
        if (virParseMacAddr((const char*) mac, addr) == -1) {
1238 1239 1240
            virXMLError(conn, VIR_ERR_INVALID_MAC, (const char *) mac, 0);
            goto error;
        }
1241
        virBufferVSprintf(buf, "(mac '%s')", (const char *) mac);
1242
    }
1243
    if (source != NULL) {
1244 1245
        if (typ == 0)
            virBufferVSprintf(buf, "(bridge '%s')", (const char *) source);
1246
        else if (typ == 1)      /* TODO does that work like that ? */
1247
            virBufferVSprintf(buf, "(dev '%s')", (const char *) source);
1248
        else {
1249 1250
            virNetworkPtr network =
                virNetworkLookupByName(conn, (const char *) source);
1251
            char *bridge;
1252

1253
            if (!network || !(bridge = virNetworkGetBridgeName(network))) {
1254 1255
                if (network)
                    virNetworkFree(network);
1256 1257
                virXMLError(conn, VIR_ERR_NO_SOURCE, (const char *) source,
                            0);
1258 1259
                goto error;
            }
1260
            virNetworkFree(network);
1261 1262 1263
            virBufferVSprintf(buf, "(bridge '%s')", bridge);
            free(bridge);
        }
1264 1265 1266
    }
    if (script != NULL)
        virBufferVSprintf(buf, "(script '%s')", script);
1267 1268
    if (ip != NULL)
        virBufferVSprintf(buf, "(ip '%s')", ip);
1269 1270 1271 1272 1273
    /*
     * apparently (type ioemu) breaks paravirt drivers on HVM so skip this
     * from Xen 3.1.0
     */
    if ((hvm) && (xendConfigVersion < 4))
1274
        virBufferAddLit(buf, "(type ioemu)");
1275

1276
    virBufferAddLit(buf, ")");
1277
    ret = 0;
1278
  error:
1279
    if (mac != NULL)
1280
        xmlFree(mac);
1281
    if (source != NULL)
1282
        xmlFree(source);
1283
    if (script != NULL)
1284
        xmlFree(script);
1285 1286
    if (ip != NULL)
        xmlFree(ip);
1287
    return (ret);
1288 1289 1290 1291
}

/**
 * virDomainParseXMLDesc:
1292
 * @conn: pointer to the hypervisor connection
1293
 * @xmldesc: string with the XML description
1294
 * @xendConfigVersion: xend configuration file format
1295 1296
 *
 * Parse the XML description and turn it into the xend sexp needed to
D
Daniel Veillard 已提交
1297
 * create the domain. This is a temporary interface as the S-Expr interface
1298 1299 1300 1301 1302 1303 1304
 * 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 *
1305 1306
virDomainParseXMLDesc(virConnectPtr conn, const char *xmldesc, char **name,
                      int xendConfigVersion)
1307
{
1308 1309
    xmlDocPtr xml = NULL;
    xmlNodePtr node;
1310
    char *nam = NULL;
1311 1312
    virBuffer buf;
    xmlChar *prop;
1313
    xmlParserCtxtPtr pctxt;
1314 1315
    xmlXPathContextPtr ctxt = NULL;
    int i, res;
1316
    int bootloader = 0;
1317
    int hvm = 0;
1318
    unsigned int vcpus = 1;
1319
    unsigned long mem = 0, max_mem = 0;
1320 1321 1322 1323
    char *str;
    double f;
    xmlNodePtr *nodes;
    int nb_nodes;
1324 1325

    if (name != NULL)
1326
        *name = NULL;
1327 1328
    buf.content = malloc(1000);
    if (buf.content == NULL)
1329
        return (NULL);
1330 1331 1332
    buf.size = 1000;
    buf.use = 0;

1333 1334 1335 1336 1337
    pctxt = xmlNewParserCtxt();
    if ((pctxt == NULL) || (pctxt->sax == NULL)) {
        goto error;
    }

1338 1339 1340
    /* TODO pass the connection point to the error handler:
     *   pctxt->userData = virConnectPtr;
     */
1341 1342
    pctxt->sax->error = virCatchXMLParseError;

1343 1344
    xml = xmlCtxtReadDoc(pctxt, (const xmlChar *) xmldesc, "domain.xml",
                         NULL, XML_PARSE_NOENT | XML_PARSE_NONET |
1345
                         XML_PARSE_NOWARNING);
1346 1347 1348 1349 1350 1351 1352 1353 1354 1355
    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")) {
1356 1357 1358 1359
            xmlFree(prop);
            goto error;
        }
        xmlFree(prop);
1360
    }
1361
    virBufferAddLit(&buf, "(vm ");
1362 1363 1364 1365 1366
    ctxt = xmlXPathNewContext(xml);
    if (ctxt == NULL) {
        goto error;
    }
    /*
1367
     * extract some of the basics, name, memory, cpus ...
1368
     */
1369
    nam = virXPathString("string(/domain/name[1])", ctxt);
1370
    if (nam == NULL) {
1371
        virXMLError(conn, VIR_ERR_NO_NAME, xmldesc, 0);
1372
        goto error;
1373
    }
1374
    virBufferVSprintf(&buf, "(name '%s')", nam);
1375

1376 1377
    if ((virXPathNumber("number(/domain/memory[1])", ctxt, &f) < 0) ||
        (f < MIN_XEN_GUEST_SIZE * 1024)) {
1378
        max_mem = 128;
1379
    } else {
1380
        max_mem = (f / 1024);
1381
    }
1382

1383 1384
    if ((virXPathNumber("number(/domain/currentMemory[1])", ctxt, &f) < 0)
        || (f < MIN_XEN_GUEST_SIZE * 1024)) {
1385 1386
        mem = max_mem;
    } else {
1387
        mem = (f / 1024);
1388 1389 1390
        if (mem > max_mem) {
            max_mem = mem;
        }
1391
    }
1392
    virBufferVSprintf(&buf, "(memory %lu)(maxmem %lu)", mem, max_mem);
1393

1394 1395 1396
    if ((virXPathNumber("number(/domain/vcpu[1])", ctxt, &f) == 0) &&
        (f > 0)) {
        vcpus = (unsigned int) f;
1397
    }
1398
    virBufferVSprintf(&buf, "(vcpus %u)", vcpus);
1399

1400 1401 1402
    str = virXPathString("string(/domain/vcpu/@cpuset)", ctxt);
    if (str != NULL) {
        int maxcpu = xenNbCpus(conn);
1403 1404 1405 1406 1407 1408 1409 1410 1411
        char *cpuset = NULL;
        char *ranges = NULL;
        const char *cur = str;

        /*
         * Parse the CPUset attribute given in libvirt format and reserialize
         * it in a range format guaranteed to be understood by Xen.
         */
        if (maxcpu > 0) {
1412
            cpuset = malloc(maxcpu * sizeof(*cpuset));
1413 1414 1415
            if (cpuset != NULL) {
                res = virParseCpuSet(conn, &cur, 0, cpuset, maxcpu);
                if (res > 0) {
1416
                    ranges = virSaveCpuSet(conn, cpuset, maxcpu);
1417 1418 1419 1420 1421 1422
                    if (ranges != NULL) {
                        virBufferVSprintf(&buf, "(cpus '%s')", ranges);
                        free(ranges);
                    }
                }
                free(cpuset);
1423
                if (res < 0)
1424
                    goto error;
1425 1426 1427 1428
            } else {
                virXMLError(conn, VIR_ERR_NO_MEMORY, xmldesc, 0);
            }
        }
1429 1430 1431
        free(str);
    }

1432 1433 1434
    str = virXPathString("string(/domain/uuid[1])", ctxt);
    if (str != NULL) {
        virBufferVSprintf(&buf, "(uuid '%s')", str);
1435
        free(str);
1436 1437
    }

1438 1439 1440
    str = virXPathString("string(/domain/bootloader[1])", ctxt);
    if (str != NULL) {
        virBufferVSprintf(&buf, "(bootloader '%s')", str);
1441
        /*
1442
         * if using a bootloader, the kernel and initrd strings are not
1443 1444
         * significant and should be discarded
         */
1445
        bootloader = 1;
1446 1447 1448
        free(str);
    } else if (virXPathNumber("count(/domain/bootloader)", ctxt, &f) == 0
               && (f > 0)) {
1449
        virBufferAddLit(&buf, "(bootloader)");
D
Daniel P. Berrange 已提交
1450 1451 1452 1453
        /*
         * if using a bootloader, the kernel and initrd strings are not
         * significant and should be discarded
         */
1454
        bootloader = 1;
1455 1456 1457 1458 1459 1460 1461 1462
    }

    str = virXPathString("string(/domain/bootloader_args[1])", ctxt);
    if (str != NULL && bootloader) {
        /*
         * ignore the bootloader_args value unless a bootloader was specified
         */
        virBufferVSprintf(&buf, "(bootloader_args '%s')", str);
1463
        free(str);
1464 1465
    }

1466 1467 1468
    str = virXPathString("string(/domain/on_poweroff[1])", ctxt);
    if (str != NULL) {
        virBufferVSprintf(&buf, "(on_poweroff '%s')", str);
1469
        free(str);
1470 1471
    }

1472 1473 1474
    str = virXPathString("string(/domain/on_reboot[1])", ctxt);
    if (str != NULL) {
        virBufferVSprintf(&buf, "(on_reboot '%s')", str);
1475
        free(str);
1476 1477
    }

1478 1479 1480
    str = virXPathString("string(/domain/on_crash[1])", ctxt);
    if (str != NULL) {
        virBufferVSprintf(&buf, "(on_crash '%s')", str);
1481
        free(str);
1482 1483
    }

1484
    if (!bootloader) {
1485
        if ((node = virXPathNode("/domain/os[1]", ctxt)) != NULL) {
1486 1487
            int has_kernel = 0;

1488
            /* Analyze of the os description, based on HVM or PV. */
1489
            str = virXPathString("string(/domain/os/type[1])", ctxt);
1490
            if ((str != NULL) && STREQ(str, "hvm"))
1491
                hvm = 1;
1492 1493
            xmlFree(str);
            str = NULL;
1494

1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505
            if (hvm)
                virBufferAddLit(&buf, "(image (hvm ");
            else
                virBufferAddLit(&buf, "(image (linux ");

            if (virXPathBoolean("count(/domain/os/kernel) > 0", ctxt)) {
                if (virDomainParseXMLOSDescKernel(conn, node,
                                                  &buf) != 0)
                    goto error;
                has_kernel = 1;
            }
1506

1507 1508 1509 1510 1511
            if (hvm &&
                virDomainParseXMLOSDescHVM(conn, node,
                                           &buf, ctxt, vcpus,
                                           xendConfigVersion,
                                           has_kernel) != 0)
1512
                goto error;
1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533

            /* get the device emulation model */
            str = virXPathString("string(/domain/devices/emulator[1])", ctxt);
            if (str != NULL) {
                virBufferVSprintf(&buf, "(device_model '%s')", str);
                xmlFree(str);
                str = NULL;
            }

            /* PV graphics for xen <= 3.0.4, or HVM graphics for xen <= 3.1.0 */
            if ((!hvm && xendConfigVersion < 3) ||
                (hvm && xendConfigVersion < 4)) {
                xmlNodePtr cur;
                cur = virXPathNode("/domain/devices/graphics[1]", ctxt);
                if (cur != NULL &&
                    virDomainParseXMLGraphicsDescImage(conn, cur, &buf,
                                                       xendConfigVersion) != 0)
                    goto error;
            }

            virBufferAddLit(&buf, "))");
1534
        } else {
1535
            virXMLError(conn, VIR_ERR_NO_OS, nam, 0);
1536 1537
            goto error;
        }
1538 1539 1540
    }

    /* analyze of the devices */
1541 1542 1543 1544
    nb_nodes = virXPathNodeSet("/domain/devices/disk", ctxt, &nodes);
    if (nb_nodes > 0) {
        for (i = 0; i < nb_nodes; i++) {
            res = virDomainParseXMLDiskDesc(conn, nodes[i], &buf,
1545
                                            hvm, xendConfigVersion);
1546
            if (res != 0) {
1547
                free(nodes);
1548 1549 1550
                goto error;
            }
        }
1551
        free(nodes);
1552
    }
1553

1554 1555 1556
    nb_nodes = virXPathNodeSet("/domain/devices/interface", ctxt, &nodes);
    if (nb_nodes > 0) {
        for (i = 0; i < nb_nodes; i++) {
1557
            virBufferAddLit(&buf, "(device ");
1558 1559 1560
            res =
                virDomainParseXMLIfDesc(conn, nodes[i], &buf, hvm,
                                        xendConfigVersion);
1561
            if (res != 0) {
1562
                free(nodes);
1563 1564
                goto error;
            }
1565
            virBufferAddLit(&buf, ")");
1566
        }
1567
        free(nodes);
1568 1569
    }

1570 1571 1572 1573
    /* New style PV graphics config xen >= 3.0.4,
     * or HVM graphics config xen >= 3.0.5 */
    if ((xendConfigVersion >= 3 && !hvm) ||
        (xendConfigVersion >= 4 && hvm)) {
1574
        nb_nodes = virXPathNodeSet("/domain/devices/graphics", ctxt, &nodes);
1575
        if (nb_nodes > 0) {
1576 1577
            for (i = 0; i < nb_nodes; i++) {
                res = virDomainParseXMLGraphicsDescVFB(conn, nodes[i], &buf);
1578
                if (res != 0) {
1579
                    free(nodes);
1580 1581 1582
                    goto error;
                }
            }
1583
            free(nodes);
1584 1585 1586
        }
    }

1587

1588
    virBufferAddLit(&buf, ")"); /* closes (vm */
1589 1590 1591 1592
    buf.content[buf.use] = 0;

    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(xml);
1593
    xmlFreeParserCtxt(pctxt);
1594 1595

    if (name != NULL)
1596
        *name = nam;
1597 1598
    else
        free(nam);
1599

1600
    return (buf.content);
1601

1602
  error:
1603
    free(nam);
1604
    if (name != NULL)
1605
        *name = NULL;
1606
    xmlXPathFreeContext(ctxt);
1607 1608
    if (xml != NULL)
        xmlFreeDoc(xml);
1609 1610
    if (pctxt != NULL)
        xmlFreeParserCtxt(pctxt);
1611
    free(buf.content);
1612
    return (NULL);
1613
}
1614

1615 1616
/**
 * virParseXMLDevice:
1617
 * @conn: pointer to the hypervisor connection
1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630
 * @xmldesc: string with the XML description
 * @hvm: 1 for fully virtualized guest, 0 for paravirtualized
 * @xendConfigVersion: xend configuration file format
 *
 * Parse the XML description and turn it into the xend sexp needed to
 * create the device. 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-terminated S-Expr string, or NULL in case of error.
 *         the caller must free() the returned value.
 */
char *
1631 1632
virParseXMLDevice(virConnectPtr conn, const char *xmldesc, int hvm,
                  int xendConfigVersion)
1633 1634 1635 1636 1637 1638 1639 1640 1641 1642
{
    xmlDocPtr xml = NULL;
    xmlNodePtr node;
    virBuffer buf;

    buf.content = malloc(1000);
    if (buf.content == NULL)
        return (NULL);
    buf.size = 1000;
    buf.use = 0;
1643
    buf.content[0] = 0;
1644
    xml = xmlReadDoc((const xmlChar *) xmldesc, "device.xml", NULL,
1645 1646
                     XML_PARSE_NOENT | XML_PARSE_NONET |
                     XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
1647 1648
    if (xml == NULL) {
        virXMLError(conn, VIR_ERR_XML_ERROR, NULL, 0);
1649
        goto error;
1650
    }
1651 1652 1653 1654
    node = xmlDocGetRootElement(xml);
    if (node == NULL)
        goto error;
    if (xmlStrEqual(node->name, BAD_CAST "disk")) {
1655 1656
        if (virDomainParseXMLDiskDesc(conn, node, &buf, hvm,
	                              xendConfigVersion) != 0)
1657
            goto error;
1658
        /* SXP is not created when device is "floppy". */
1659 1660 1661 1662 1663
        else if (buf.use == 0)
            goto error;
    } else if (xmlStrEqual(node->name, BAD_CAST "interface")) {
        if (virDomainParseXMLIfDesc(conn, node, &buf, hvm,
	                            xendConfigVersion) != 0)
1664
            goto error;
1665 1666
    } else {
        virXMLError(conn, VIR_ERR_XML_ERROR, (const char *) node->name, 0);
1667
        goto error;
1668
    }
1669
  cleanup:
1670 1671 1672
    if (xml != NULL)
        xmlFreeDoc(xml);
    return buf.content;
1673
  error:
1674 1675 1676 1677 1678
    free(buf.content);
    buf.content = NULL;
    goto cleanup;
}

1679

1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694
/**
 * virDomainXMLDevID:
 * @domain: pointer to domain object
 * @xmldesc: string with the XML description
 * @class: Xen device class "vbd" or "vif" (OUT)
 * @ref: Xen device reference (OUT)
 *
 * Set class according to XML root, and:
 *  - if disk, copy in ref the target name from description
 *  - if network, get MAC address from description, scan XenStore and
 *    copy in ref the corresponding vif number.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
1695 1696
virDomainXMLDevID(virDomainPtr domain, const char *xmldesc, char *class,
                  char *ref, int ref_len)
1697 1698 1699 1700
{
    xmlDocPtr xml = NULL;
    xmlNodePtr node, cur;
    xmlChar *attr = NULL;
1701

1702
    char *xref;
1703
    int ret = 0;
1704

1705
    xml = xmlReadDoc((const xmlChar *) xmldesc, "device.xml", NULL,
1706 1707
                     XML_PARSE_NOENT | XML_PARSE_NONET |
                     XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
1708 1709
    if (xml == NULL) {
        virXMLError(NULL, VIR_ERR_XML_ERROR, NULL, 0);
1710
        goto error;
1711
    }
1712 1713 1714 1715 1716 1717 1718
    node = xmlDocGetRootElement(xml);
    if (node == NULL)
        goto error;
    if (xmlStrEqual(node->name, BAD_CAST "disk")) {
        strcpy(class, "vbd");
        for (cur = node->children; cur != NULL; cur = cur->next) {
            if ((cur->type != XML_ELEMENT_NODE) ||
1719 1720
                (!xmlStrEqual(cur->name, BAD_CAST "target")))
                continue;
1721 1722 1723
            attr = xmlGetProp(cur, BAD_CAST "dev");
            if (attr == NULL)
                goto error;
1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736
            xref = xenStoreDomainGetDiskID(domain->conn, domain->id,
                                              (char *) attr);
            if (xref != NULL) {
                strncpy(ref, xref, ref_len);
                free(xref);
                ref[ref_len - 1] = '\0';
                goto cleanup;
            }
            /* hack to avoid the warning that domain is unused */
            if (domain->id < 0)
                ret = -1;

            goto error;
1737
        }
1738
    } else if (xmlStrEqual(node->name, BAD_CAST "interface")) {
1739 1740 1741
        strcpy(class, "vif");
        for (cur = node->children; cur != NULL; cur = cur->next) {
            if ((cur->type != XML_ELEMENT_NODE) ||
1742 1743
                (!xmlStrEqual(cur->name, BAD_CAST "mac")))
                continue;
1744 1745 1746 1747
            attr = xmlGetProp(cur, BAD_CAST "address");
            if (attr == NULL)
                goto error;

1748
            xref = xenStoreDomainGetNetworkID(domain->conn, domain->id,
1749 1750
                                              (char *) attr);
            if (xref != NULL) {
1751
                strncpy(ref, xref, ref_len);
1752
                free(xref);
1753
                ref[ref_len - 1] = '\0';
1754 1755
                goto cleanup;
            }
1756 1757
            /* hack to avoid the warning that domain is unused */
            if (domain->id < 0)
1758
                ret = -1;
1759

1760 1761
            goto error;
        }
1762 1763
    } else {
        virXMLError(NULL, VIR_ERR_XML_ERROR, (const char *) node->name, 0);
1764
    }
1765
  error:
1766
    ret = -1;
1767
  cleanup:
1768 1769 1770 1771 1772 1773
    if (xml != NULL)
        xmlFreeDoc(xml);
    if (attr != NULL)
        xmlFree(attr);
    return ret;
}
1774
#endif /* WITH_XEN */
1775
#endif /* !PROXY */