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

51
#define VIR_FROM_THIS VIR_FROM_UML
52

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

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)
66
        goto error;
67

68 69 70 71 72 73 74 75
    /* 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");
    }
76

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

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

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

101 102
    caps->defaultConsoleTargetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_UML;

103 104
    return caps;

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


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

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

127 128 129 130 131 132 133 134 135 136
    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;
    }

137 138 139 140 141 142 143
    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,
144
                        NULL))) {
D
Doug Goldstein 已提交
145
        if (err == ENOTSUP) {
146
            /* In this particular case, give a better diagnostic. */
147
            umlReportError(VIR_ERR_INTERNAL_ERROR,
148 149
                           _("Failed to add tap interface to bridge. "
                             "%s is not a bridge device"), bridge);
150 151 152 153 154
        } 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."));
155
        } else if (template_ifname) {
156
            virReportSystemError(err,
157 158
                                 _("Failed to add tap interface to bridge '%s'"),
                                 bridge);
159
        } else {
160
            virReportSystemError(err,
161 162
                                 _("Failed to add tap interface '%s' to bridge '%s'"),
                                 net->ifname, bridge);
163
        }
164 165
        if (template_ifname)
            VIR_FREE(net->ifname);
166 167 168
        goto error;
    }

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

177 178 179 180 181
    brShutdown(brctl);

    return 0;

no_memory:
182
    virReportOOMError();
183 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
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) {
209
            umlReportError(VIR_ERR_INTERNAL_ERROR, "%s",
210 211 212 213
                           _("IP address not supported for ethernet inteface"));
            goto error;
        }
        if (def->data.ethernet.script) {
214
            umlReportError(VIR_ERR_INTERNAL_ERROR, "%s",
215 216 217 218 219 220
                           _("script execution not supported for ethernet inteface"));
            goto error;
        }
        break;

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

    case VIR_DOMAIN_NET_TYPE_CLIENT:
226
        umlReportError(VIR_ERR_INTERNAL_ERROR, "%s",
227 228 229 230 231 232 233 234 235 236 237 238 239 240
                       _("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) {
241
            umlReportError(VIR_ERR_INTERNAL_ERROR,
242 243 244 245 246 247 248 249 250 251
                           _("Network '%s' not found"),
                           def->data.network.name);
            goto error;
        }
        bridge = virNetworkGetBridgeName(network);
        virNetworkFree(network);
        if (bridge == NULL) {
            goto error;
        }

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

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

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

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

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

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

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

    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)) {
295
        virReportOOMError();
296 297 298 299 300 301
        return NULL;
    }

    return virBufferContentAndReset(&buf);

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

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

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

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

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

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

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

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

    case VIR_DOMAIN_CHR_TYPE_FILE:
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
         {
            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();
                close(fd_out);
                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. */
379 380 381 382 383

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

    return ret;
}

392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
/*
 * 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;
}

417 418 419 420 421
/*
 * Constructs a argv suitable for launching uml with config defined
 * for a given virtual machine.
 */
int umlBuildCommandLine(virConnectPtr conn,
422
                        struct uml_driver *driver,
423
                        virDomainObjPtr vm,
424
                        fd_set *keepfd,
425
                        const char ***retargv,
426 427
                        const char ***retenv)
{
428 429 430 431 432 433 434
    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;
435
    char *cmdline = NULL;
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464

    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;                                                  \
465
        if (virAsprintf(&arg, "%s=%s", key, val) < 0)                   \
466
            goto no_memory;                                             \
467
        qargv[qargc++] = arg;                                           \
468 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
    } 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) {                                              \
499
            if (virAsprintf(&envval, "%s=%s", envname, val) < 0)        \
500 501 502 503 504
                goto no_memory;                                         \
            qenv[qenvc++] = envval;                                     \
        }                                                               \
    } while (0)

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

    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);
520
    ADD_ARG_PAIR("uml_dir", driver->monitorDir);
521 522 523 524 525 526 527 528

    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")) {
529
            umlReportError(VIR_ERR_INTERNAL_ERROR,
530 531 532 533 534 535 536
                           _("unsupported disk type '%s'"), disk->dst);
            goto error;
        }

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

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

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

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

568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583
    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;
        }
    }

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

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

 no_memory:
592
    virReportOOMError();
593
 error:
594

595 596 597 598 599 600 601 602 603 604
    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);
    }
605
    VIR_FREE(cmdline);
606 607 608 609 610 611 612 613 614 615 616
    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
}