uml_conf.c 19.3 KB
Newer Older
1 2 3
/*
 * uml_conf.c: UML driver configuration
 *
4
 * Copyright (C) 2006-2010 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
 * Copyright (C) 2006 Daniel P. Berrange
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 * Author: Daniel P. Berrange <berrange@redhat.com>
 */

#include <config.h>

#include <dirent.h>
#include <string.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <sys/utsname.h>

#include "uml_conf.h"
#include "uuid.h"
#include "buf.h"
#include "conf.h"
#include "util.h"
#include "memory.h"
45
#include "nodeinfo.h"
46
#include "verify.h"
47
#include "bridge.h"
48
#include "logging.h"
49
#include "domain_nwfilter.h"
50
#include "files.h"
51

52
#define VIR_FROM_THIS VIR_FROM_UML
53

54 55
#define umlLog(level, msg, ...)                                     \
        virLogMessage(__FILE__, level, 0, msg, __VA_ARGS__)
56 57 58 59 60 61 62 63 64 65 66

virCapsPtr umlCapsInit(void) {
    struct utsname utsname;
    virCapsPtr caps;
    virCapsGuestPtr guest;

    /* Really, this never fails - look at the man-page. */
    uname (&utsname);

    if ((caps = virCapabilitiesNew(utsname.machine,
                                   0, 0)) == NULL)
67
        goto error;
68

69 70 71 72 73 74 75 76
    /* Some machines have problematic NUMA toplogy causing
     * unexpected failures. We don't want to break the QEMU
     * driver in this scenario, so log errors & carry on
     */
    if (nodeCapsInitNUMA(caps) < 0) {
        virCapabilitiesFreeNUMAInfo(caps);
        VIR_WARN0("Failed to query host NUMA topology, disabling NUMA capabilities");
    }
77

78 79 80 81 82 83
    if (virGetHostUUID(caps->host.host_uuid)) {
        umlReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("cannot get the host uuid"));
        goto error;
    }

84 85 86 87 88 89 90 91
    if ((guest = virCapabilitiesAddGuest(caps,
                                         "uml",
                                         utsname.machine,
                                         STREQ(utsname.machine, "x86_64") ? 64 : 32,
                                         NULL,
                                         NULL,
                                         0,
                                         NULL)) == NULL)
92
        goto error;
93 94 95 96 97 98 99

    if (virCapabilitiesAddGuestDomain(guest,
                                      "uml",
                                      NULL,
                                      NULL,
                                      0,
                                      NULL) == NULL)
100
        goto error;
101

102 103
    caps->defaultConsoleTargetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_UML;

104 105
    return caps;

106
 error:
107 108 109 110 111
    virCapabilitiesFree(caps);
    return NULL;
}


112
static int
113 114
umlConnectTapDevice(virConnectPtr conn,
                    virDomainNetDefPtr net,
115 116
                    const char *bridge)
{
117 118
    brControl *brctl = NULL;
    int template_ifname = 0;
119
    int err;
120
    unsigned char tapmac[VIR_MAC_BUFLEN];
121 122

    if ((err = brInit(&brctl))) {
123
        virReportSystemError(err, "%s",
124
                             _("cannot initialize bridge support"));
125 126 127
        goto error;
    }

128 129 130 131 132 133 134 135 136 137
    if (!net->ifname ||
        STRPREFIX(net->ifname, "vnet") ||
        strchr(net->ifname, '%')) {
        VIR_FREE(net->ifname);
        if (!(net->ifname = strdup("vnet%d")))
            goto no_memory;
        /* avoid exposing vnet%d in dumpxml or error outputs */
        template_ifname = 1;
    }

138 139 140 141 142 143 144
    memcpy(tapmac, net->mac, VIR_MAC_BUFLEN);
    tapmac[0] = 0xFE; /* Discourage bridge from using TAP dev MAC */
    if ((err = brAddTap(brctl,
                        bridge,
                        &net->ifname,
                        tapmac,
                        0,
145
                        NULL))) {
D
Doug Goldstein 已提交
146
        if (err == ENOTSUP) {
147
            /* In this particular case, give a better diagnostic. */
148
            umlReportError(VIR_ERR_INTERNAL_ERROR,
149 150
                           _("Failed to add tap interface to bridge. "
                             "%s is not a bridge device"), bridge);
151 152 153 154 155
        } else if (err == ENOENT) {
            virReportSystemError(err, "%s",
                    _("Failed to add tap interface to bridge. Your kernel "
                      "is missing the 'tun' module or CONFIG_TUN, or you need "
                      "to add the /dev/net/tun device node."));
156
        } else if (template_ifname) {
157
            virReportSystemError(err,
158 159
                                 _("Failed to add tap interface to bridge '%s'"),
                                 bridge);
160
        } else {
161
            virReportSystemError(err,
162 163
                                 _("Failed to add tap interface '%s' to bridge '%s'"),
                                 net->ifname, bridge);
164
        }
165 166
        if (template_ifname)
            VIR_FREE(net->ifname);
167 168 169
        goto error;
    }

170 171 172 173 174 175 176 177
    if (net->filter) {
        if (virDomainConfNWFilterInstantiate(conn, net)) {
            if (template_ifname)
                VIR_FREE(net->ifname);
            goto error;
        }
    }

178 179 180 181 182
    brShutdown(brctl);

    return 0;

no_memory:
183
    virReportOOMError();
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
error:
    brShutdown(brctl);
    return -1;
}

static char *
umlBuildCommandLineNet(virConnectPtr conn,
                       virDomainNetDefPtr def,
                       int idx)
{
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    /* General format:  ethNN=type,options */

    virBufferVSprintf(&buf, "eth%d=", idx);

    switch (def->type) {
    case VIR_DOMAIN_NET_TYPE_USER:
        /* ethNNN=slirp,macaddr */
        virBufferAddLit(&buf, "slirp");
        break;

    case VIR_DOMAIN_NET_TYPE_ETHERNET:
        /* ethNNN=tuntap,tapname,macaddr,gateway */
        virBufferAddLit(&buf, "tuntap");
        if (def->data.ethernet.ipaddr) {
210
            umlReportError(VIR_ERR_INTERNAL_ERROR, "%s",
211 212 213 214
                           _("IP address not supported for ethernet inteface"));
            goto error;
        }
        if (def->data.ethernet.script) {
215
            umlReportError(VIR_ERR_INTERNAL_ERROR, "%s",
216 217 218 219 220 221
                           _("script execution not supported for ethernet inteface"));
            goto error;
        }
        break;

    case VIR_DOMAIN_NET_TYPE_SERVER:
222
        umlReportError(VIR_ERR_INTERNAL_ERROR, "%s",
223 224 225 226
                       _("TCP server networking type not supported"));
        goto error;

    case VIR_DOMAIN_NET_TYPE_CLIENT:
227
        umlReportError(VIR_ERR_INTERNAL_ERROR, "%s",
228 229 230 231 232 233 234 235 236 237 238 239 240 241
                       _("TCP client networking type not supported"));
        goto error;

    case VIR_DOMAIN_NET_TYPE_MCAST:
        /* ethNNN=tuntap,macaddr,ipaddr,port */
        virBufferAddLit(&buf, "mcast");
        break;

    case VIR_DOMAIN_NET_TYPE_NETWORK:
    {
        char *bridge;
        virNetworkPtr network = virNetworkLookupByName(conn,
                                                       def->data.network.name);
        if (!network) {
242
            umlReportError(VIR_ERR_INTERNAL_ERROR,
243 244 245 246 247 248 249 250 251 252
                           _("Network '%s' not found"),
                           def->data.network.name);
            goto error;
        }
        bridge = virNetworkGetBridgeName(network);
        virNetworkFree(network);
        if (bridge == NULL) {
            goto error;
        }

253
        if (umlConnectTapDevice(conn, def, bridge) < 0) {
254 255 256 257 258 259 260 261 262 263
            VIR_FREE(bridge);
            goto error;
        }

        /* ethNNN=tuntap,tapname,macaddr,gateway */
        virBufferVSprintf(&buf, "tuntap,%s", def->ifname);
        break;
    }

    case VIR_DOMAIN_NET_TYPE_BRIDGE:
264
        if (umlConnectTapDevice(conn, def, def->data.bridge.brname) < 0)
265 266 267 268 269 270 271
            goto error;

        /* ethNNN=tuntap,tapname,macaddr,gateway */
        virBufferVSprintf(&buf, "tuntap,%s", def->ifname);
        break;

    case VIR_DOMAIN_NET_TYPE_INTERNAL:
272
        umlReportError(VIR_ERR_INTERNAL_ERROR, "%s",
273 274
                       _("internal networking type not supported"));
        goto error;
S
Stefan Berger 已提交
275 276

    case VIR_DOMAIN_NET_TYPE_DIRECT:
277
        umlReportError(VIR_ERR_INTERNAL_ERROR, "%s",
S
Stefan Berger 已提交
278 279 280 281 282
                       _("direct networking type not supported"));
        goto error;

    case VIR_DOMAIN_NET_TYPE_LAST:
        break;
283 284 285 286 287 288 289 290 291 292 293 294 295
    }

    virBufferVSprintf(&buf, ",%02x:%02x:%02x:%02x:%02x:%02x",
                      def->mac[0], def->mac[1], def->mac[2],
                      def->mac[3], def->mac[4], def->mac[5]);

    if (def->type == VIR_DOMAIN_NET_TYPE_MCAST) {
        virBufferVSprintf(&buf, ",%s,%d",
                          def->data.socket.address,
                          def->data.socket.port);
    }

    if (virBufferError(&buf)) {
296
        virReportOOMError();
297 298 299 300 301 302
        return NULL;
    }

    return virBufferContentAndReset(&buf);

error:
303
    virBufferFreeAndReset(&buf);
304 305 306
    return NULL;
}

307
static char *
308
umlBuildCommandLineChr(virDomainChrDefPtr def,
309 310
                       const char *dev,
                       fd_set *keepfd)
311
{
312
    char *ret = NULL;
313 314 315

    switch (def->type) {
    case VIR_DOMAIN_CHR_TYPE_NULL:
316
        if (virAsprintf(&ret, "%s%d=null", dev, def->target.port) < 0) {
317
            virReportOOMError();
318 319 320 321 322
            return NULL;
        }
        break;

    case VIR_DOMAIN_CHR_TYPE_PTY:
323
        if (virAsprintf(&ret, "%s%d=pts", dev, def->target.port) < 0) {
324
            virReportOOMError();
325 326 327 328 329
            return NULL;
        }
        break;

    case VIR_DOMAIN_CHR_TYPE_DEV:
330
        if (virAsprintf(&ret, "%s%d=tty:%s", dev, def->target.port,
331
                        def->data.file.path) < 0) {
332
            virReportOOMError();
333 334 335 336 337
            return NULL;
        }
        break;

    case VIR_DOMAIN_CHR_TYPE_STDIO:
338
        if (virAsprintf(&ret, "%s%d=fd:0,fd:1", dev, def->target.port) < 0) {
339
            virReportOOMError();
340 341 342 343 344 345
            return NULL;
        }
        break;

    case VIR_DOMAIN_CHR_TYPE_TCP:
        if (def->data.tcp.listen != 1) {
346 347
            umlReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("only TCP listen is supported for chr device"));
348 349 350
            return NULL;
        }

351
        if (virAsprintf(&ret, "%s%d=port:%s", dev, def->target.port,
352
                        def->data.tcp.service) < 0) {
353
            virReportOOMError();
354 355 356 357 358
            return NULL;
        }
        break;

    case VIR_DOMAIN_CHR_TYPE_FILE:
359 360 361 362 363 364 365 366 367 368 369 370
         {
            int fd_out;

            if ((fd_out = open(def->data.file.path,
                               O_WRONLY | O_APPEND | O_CREAT, 0660)) < 0) {
                virReportSystemError(errno,
                                     _("failed to open chardev file: %s"),
                                     def->data.file.path);
                return NULL;
            }
            if (virAsprintf(&ret, "%s%d=null,fd:%d", dev, def->target.port, fd_out) < 0) {
                virReportOOMError();
371
                VIR_FORCE_CLOSE(fd_out);
372 373 374 375 376 377 378 379
                return NULL;
            }
            FD_SET(fd_out, keepfd);
        }
        break;
   case VIR_DOMAIN_CHR_TYPE_PIPE:
        /* XXX could open the pipe & just pass the FDs. Be wary of
         * the effects of blocking I/O, though. */
380 381 382 383 384

    case VIR_DOMAIN_CHR_TYPE_VC:
    case VIR_DOMAIN_CHR_TYPE_UDP:
    case VIR_DOMAIN_CHR_TYPE_UNIX:
    default:
385
        umlReportError(VIR_ERR_INTERNAL_ERROR,
386 387 388 389 390 391 392
                       _("unsupported chr device type %d"), def->type);
        break;
    }

    return ret;
}

393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
/*
 * Null-terminate the current argument and return a pointer to the next.
 * This should follow the same rules as the Linux kernel: arguments are
 * separated by spaces; arguments can be quoted with double quotes; double
 * quotes can't be escaped.
 */
static char *umlNextArg(char *args)
{
    int in_quote = 0;

    for (; *args; args++) {
        if (*args == ' ' && !in_quote) {
            *args++ = '\0';
            break;
        }
        if (*args == '"')
            in_quote = !in_quote;
    }

    while (*args == ' ')
        args++;

    return args;
}

418 419 420 421 422
/*
 * Constructs a argv suitable for launching uml with config defined
 * for a given virtual machine.
 */
int umlBuildCommandLine(virConnectPtr conn,
423
                        struct uml_driver *driver,
424
                        virDomainObjPtr vm,
425
                        fd_set *keepfd,
426
                        const char ***retargv,
427 428
                        const char ***retenv)
{
429 430 431 432 433 434 435
    int i, j;
    char memory[50];
    struct utsname ut;
    int qargc = 0, qarga = 0;
    const char **qargv = NULL;
    int qenvc = 0, qenva = 0;
    const char **qenv = NULL;
436
    char *cmdline = NULL;
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465

    uname(&ut);

#define ADD_ARG_SPACE                                                   \
    do {                                                                \
        if (qargc == qarga) {                                           \
            qarga += 10;                                                \
            if (VIR_REALLOC_N(qargv, qarga) < 0)                        \
                goto no_memory;                                         \
        }                                                               \
    } while (0)

#define ADD_ARG(thisarg)                                                \
    do {                                                                \
        ADD_ARG_SPACE;                                                  \
        qargv[qargc++] = thisarg;                                       \
    } while (0)

#define ADD_ARG_LIT(thisarg)                                            \
    do {                                                                \
        ADD_ARG_SPACE;                                                  \
        if ((qargv[qargc++] = strdup(thisarg)) == NULL)                 \
            goto no_memory;                                             \
    } while (0)

#define ADD_ARG_PAIR(key,val)                                           \
    do {                                                                \
        char *arg;                                                      \
        ADD_ARG_SPACE;                                                  \
466
        if (virAsprintf(&arg, "%s=%s", key, val) < 0)                   \
467
            goto no_memory;                                             \
468
        qargv[qargc++] = arg;                                           \
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
    } while (0)


#define ADD_ENV_SPACE                                                   \
    do {                                                                \
        if (qenvc == qenva) {                                           \
            qenva += 10;                                                \
            if (VIR_REALLOC_N(qenv, qenva) < 0)                         \
                goto no_memory;                                         \
        }                                                               \
    } while (0)

#define ADD_ENV(thisarg)                                                \
    do {                                                                \
        ADD_ENV_SPACE;                                                  \
        qenv[qenvc++] = thisarg;                                        \
    } while (0)

#define ADD_ENV_LIT(thisarg)                                            \
    do {                                                                \
        ADD_ENV_SPACE;                                                  \
        if ((qenv[qenvc++] = strdup(thisarg)) == NULL)                  \
            goto no_memory;                                             \
    } while (0)

#define ADD_ENV_COPY(envname)                                           \
    do {                                                                \
        char *val = getenv(envname);                                    \
        char *envval;                                                   \
        ADD_ENV_SPACE;                                                  \
        if (val != NULL) {                                              \
500
            if (virAsprintf(&envval, "%s=%s", envname, val) < 0)        \
501 502 503 504 505
                goto no_memory;                                         \
            qenv[qenvc++] = envval;                                     \
        }                                                               \
    } while (0)

506
    snprintf(memory, sizeof(memory), "%luK", vm->def->mem.cur_balloon);
507 508 509 510 511 512 513 514 515 516 517 518 519 520

    ADD_ENV_LIT("LC_ALL=C");

    ADD_ENV_COPY("LD_PRELOAD");
    ADD_ENV_COPY("LD_LIBRARY_PATH");
    ADD_ENV_COPY("PATH");
    ADD_ENV_COPY("USER");
    ADD_ENV_COPY("LOGNAME");
    ADD_ENV_COPY("TMPDIR");

    ADD_ARG_LIT(vm->def->os.kernel);
    //ADD_ARG_PAIR("con0", "fd:0,fd:1");
    ADD_ARG_PAIR("mem", memory);
    ADD_ARG_PAIR("umid", vm->def->name);
521
    ADD_ARG_PAIR("uml_dir", driver->monitorDir);
522 523 524 525 526 527 528 529

    if (vm->def->os.root)
        ADD_ARG_PAIR("root", vm->def->os.root);

    for (i = 0 ; i < vm->def->ndisks ; i++) {
        virDomainDiskDefPtr disk = vm->def->disks[i];

        if (!STRPREFIX(disk->dst, "ubd")) {
530
            umlReportError(VIR_ERR_INTERNAL_ERROR,
531 532 533 534 535 536 537
                           _("unsupported disk type '%s'"), disk->dst);
            goto error;
        }

        ADD_ARG_PAIR(disk->dst, disk->src);
    }

538 539 540 541 542 543 544
    for (i = 0 ; i < vm->def->nnets ; i++) {
        char *ret = umlBuildCommandLineNet(conn, vm->def->nets[i], i);
        if (!ret)
            goto error;
        ADD_ARG(ret);
    }

545
    for (i = 0 ; i < UML_MAX_CHAR_DEVICE ; i++) {
546
        char *ret = NULL;
547
        if (i == 0 && vm->def->console)
548
            ret = umlBuildCommandLineChr(vm->def->console, "con", keepfd);
549
        if (!ret)
550
            if (virAsprintf(&ret, "con%d=none", i) < 0)
551 552 553 554 555 556
                goto no_memory;
        ADD_ARG(ret);
    }

    for (i = 0 ; i < UML_MAX_CHAR_DEVICE ; i++) {
        virDomainChrDefPtr chr = NULL;
557
        char *ret = NULL;
558
        for (j = 0 ; j < vm->def->nserials ; j++)
559
            if (vm->def->serials[j]->target.port == i)
560 561
                chr = vm->def->serials[j];
        if (chr)
562
            ret = umlBuildCommandLineChr(chr, "ssl", keepfd);
563
        if (!ret)
564
            if (virAsprintf(&ret, "ssl%d=none", i) < 0)
565 566 567 568
                goto no_memory;
        ADD_ARG(ret);
    }

569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
    if (vm->def->os.cmdline) {
        char *args, *next_arg;
        if ((cmdline = strdup(vm->def->os.cmdline)) == NULL)
            goto no_memory;

        args = cmdline;
        while (*args == ' ')
            args++;

        while (*args) {
            next_arg = umlNextArg(args);
            ADD_ARG_LIT(args);
            args = next_arg;
        }
    }

585 586 587 588 589 590 591 592
    ADD_ARG(NULL);
    ADD_ENV(NULL);

    *retargv = qargv;
    *retenv = qenv;
    return 0;

 no_memory:
593
    virReportOOMError();
594
 error:
595

596 597 598 599 600 601 602 603 604 605
    if (qargv) {
        for (i = 0 ; i < qargc ; i++)
            VIR_FREE((qargv)[i]);
        VIR_FREE(qargv);
    }
    if (qenv) {
        for (i = 0 ; i < qenvc ; i++)
            VIR_FREE((qenv)[i]);
        VIR_FREE(qenv);
    }
606
    VIR_FREE(cmdline);
607 608 609 610 611 612 613 614 615 616 617
    return -1;

#undef ADD_ARG
#undef ADD_ARG_LIT
#undef ADD_ARG_SPACE
#undef ADD_USBDISK
#undef ADD_ENV
#undef ADD_ENV_COPY
#undef ADD_ENV_LIT
#undef ADD_ENV_SPACE
}