2
* uml_conf.c: UML driver configuration
4
* Copyright (C) 2006-2009 Red Hat, Inc.
5
* Copyright (C) 2006 Daniel P. Berrange
7
* This library is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU Lesser General Public
9
* License as published by the Free Software Foundation; either
10
* version 2.1 of the License, or (at your option) any later version.
12
* This library is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* Lesser General Public License for more details.
17
* You should have received a copy of the GNU Lesser General Public
18
* License along with this library; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
* Author: Daniel P. Berrange <berrange@redhat.com>
29
#include <sys/types.h>
36
#include <arpa/inet.h>
37
#include <sys/utsname.h>
50
#define VIR_FROM_THIS VIR_FROM_UML
52
#define umlLog(level, msg, ...) \
53
virLogMessage(__FILE__, level, 0, msg, __VA_ARGS__)
55
virCapsPtr umlCapsInit(void) {
56
struct utsname utsname;
58
virCapsGuestPtr guest;
60
/* Really, this never fails - look at the man-page. */
63
if ((caps = virCapabilitiesNew(utsname.machine,
67
/* Some machines have problematic NUMA toplogy causing
68
* unexpected failures. We don't want to break the QEMU
69
* driver in this scenario, so log errors & carry on
71
if (nodeCapsInitNUMA(caps) < 0) {
72
virCapabilitiesFreeNUMAInfo(caps);
73
VIR_WARN0("Failed to query host NUMA topology, disabling NUMA capabilities");
76
if (virGetHostUUID(caps->host.host_uuid)) {
77
umlReportError(VIR_ERR_INTERNAL_ERROR,
78
"%s", _("cannot get the host uuid"));
82
if ((guest = virCapabilitiesAddGuest(caps,
85
STREQ(utsname.machine, "x86_64") ? 64 : 32,
92
if (virCapabilitiesAddGuestDomain(guest,
100
caps->defaultConsoleTargetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_UML;
105
virCapabilitiesFree(caps);
111
umlConnectTapDevice(virDomainNetDefPtr net,
114
brControl *brctl = NULL;
116
int template_ifname = 0;
118
unsigned char tapmac[VIR_MAC_BUFLEN];
120
if ((err = brInit(&brctl))) {
121
virReportSystemError(err, "%s",
122
_("cannot initialize bridge support"));
127
STRPREFIX(net->ifname, "vnet") ||
128
strchr(net->ifname, '%')) {
129
VIR_FREE(net->ifname);
130
if (!(net->ifname = strdup("vnet%d")))
132
/* avoid exposing vnet%d in dumpxml or error outputs */
136
memcpy(tapmac, net->mac, VIR_MAC_BUFLEN);
137
tapmac[0] = 0xFE; /* Discourage bridge from using TAP dev MAC */
138
if ((err = brAddTap(brctl,
144
if (errno == ENOTSUP) {
145
/* In this particular case, give a better diagnostic. */
146
umlReportError(VIR_ERR_INTERNAL_ERROR,
147
_("Failed to add tap interface to bridge. "
148
"%s is not a bridge device"), bridge);
149
} else if (template_ifname) {
150
virReportSystemError(err,
151
_("Failed to add tap interface to bridge '%s'"),
154
virReportSystemError(err,
155
_("Failed to add tap interface '%s' to bridge '%s'"),
156
net->ifname, bridge);
159
VIR_FREE(net->ifname);
176
umlBuildCommandLineNet(virConnectPtr conn,
177
virDomainNetDefPtr def,
180
virBuffer buf = VIR_BUFFER_INITIALIZER;
182
/* General format: ethNN=type,options */
184
virBufferVSprintf(&buf, "eth%d=", idx);
187
case VIR_DOMAIN_NET_TYPE_USER:
188
/* ethNNN=slirp,macaddr */
189
virBufferAddLit(&buf, "slirp");
192
case VIR_DOMAIN_NET_TYPE_ETHERNET:
193
/* ethNNN=tuntap,tapname,macaddr,gateway */
194
virBufferAddLit(&buf, "tuntap");
195
if (def->data.ethernet.ipaddr) {
196
umlReportError(VIR_ERR_INTERNAL_ERROR, "%s",
197
_("IP address not supported for ethernet inteface"));
200
if (def->data.ethernet.script) {
201
umlReportError(VIR_ERR_INTERNAL_ERROR, "%s",
202
_("script execution not supported for ethernet inteface"));
207
case VIR_DOMAIN_NET_TYPE_SERVER:
208
umlReportError(VIR_ERR_INTERNAL_ERROR, "%s",
209
_("TCP server networking type not supported"));
212
case VIR_DOMAIN_NET_TYPE_CLIENT:
213
umlReportError(VIR_ERR_INTERNAL_ERROR, "%s",
214
_("TCP client networking type not supported"));
217
case VIR_DOMAIN_NET_TYPE_MCAST:
218
/* ethNNN=tuntap,macaddr,ipaddr,port */
219
virBufferAddLit(&buf, "mcast");
222
case VIR_DOMAIN_NET_TYPE_NETWORK:
225
virNetworkPtr network = virNetworkLookupByName(conn,
226
def->data.network.name);
228
umlReportError(VIR_ERR_INTERNAL_ERROR,
229
_("Network '%s' not found"),
230
def->data.network.name);
233
bridge = virNetworkGetBridgeName(network);
234
virNetworkFree(network);
235
if (bridge == NULL) {
239
if (umlConnectTapDevice(def, bridge) < 0) {
244
/* ethNNN=tuntap,tapname,macaddr,gateway */
245
virBufferVSprintf(&buf, "tuntap,%s", def->ifname);
249
case VIR_DOMAIN_NET_TYPE_BRIDGE:
250
if (umlConnectTapDevice(def, def->data.bridge.brname) < 0)
253
/* ethNNN=tuntap,tapname,macaddr,gateway */
254
virBufferVSprintf(&buf, "tuntap,%s", def->ifname);
257
case VIR_DOMAIN_NET_TYPE_INTERNAL:
258
umlReportError(VIR_ERR_INTERNAL_ERROR, "%s",
259
_("internal networking type not supported"));
262
case VIR_DOMAIN_NET_TYPE_DIRECT:
263
umlReportError(VIR_ERR_INTERNAL_ERROR, "%s",
264
_("direct networking type not supported"));
267
case VIR_DOMAIN_NET_TYPE_LAST:
271
virBufferVSprintf(&buf, ",%02x:%02x:%02x:%02x:%02x:%02x",
272
def->mac[0], def->mac[1], def->mac[2],
273
def->mac[3], def->mac[4], def->mac[5]);
275
if (def->type == VIR_DOMAIN_NET_TYPE_MCAST) {
276
virBufferVSprintf(&buf, ",%s,%d",
277
def->data.socket.address,
278
def->data.socket.port);
281
if (virBufferError(&buf)) {
286
return virBufferContentAndReset(&buf);
289
virBufferFreeAndReset(&buf);
294
umlBuildCommandLineChr(virDomainChrDefPtr def,
300
case VIR_DOMAIN_CHR_TYPE_NULL:
301
if (virAsprintf(&ret, "%s%d=null", dev, def->target.port) < 0) {
307
case VIR_DOMAIN_CHR_TYPE_PTY:
308
if (virAsprintf(&ret, "%s%d=pts", dev, def->target.port) < 0) {
314
case VIR_DOMAIN_CHR_TYPE_DEV:
315
if (virAsprintf(&ret, "%s%d=tty:%s", dev, def->target.port,
316
def->data.file.path) < 0) {
322
case VIR_DOMAIN_CHR_TYPE_STDIO:
323
if (virAsprintf(&ret, "%s%d=fd:0,fd:1", dev, def->target.port) < 0) {
329
case VIR_DOMAIN_CHR_TYPE_TCP:
330
if (def->data.tcp.listen != 1) {
331
umlReportError(VIR_ERR_INTERNAL_ERROR, "%s",
332
_("only TCP listen is supported for chr device"));
336
if (virAsprintf(&ret, "%s%d=port:%s", dev, def->target.port,
337
def->data.tcp.service) < 0) {
343
case VIR_DOMAIN_CHR_TYPE_FILE:
344
case VIR_DOMAIN_CHR_TYPE_PIPE:
345
/* XXX could open the file/pipe & just pass the FDs */
347
case VIR_DOMAIN_CHR_TYPE_VC:
348
case VIR_DOMAIN_CHR_TYPE_UDP:
349
case VIR_DOMAIN_CHR_TYPE_UNIX:
351
umlReportError(VIR_ERR_INTERNAL_ERROR,
352
_("unsupported chr device type %d"), def->type);
360
* Null-terminate the current argument and return a pointer to the next.
361
* This should follow the same rules as the Linux kernel: arguments are
362
* separated by spaces; arguments can be quoted with double quotes; double
363
* quotes can't be escaped.
365
static char *umlNextArg(char *args)
369
for (; *args; args++) {
370
if (*args == ' ' && !in_quote) {
375
in_quote = !in_quote;
385
* Constructs a argv suitable for launching uml with config defined
386
* for a given virtual machine.
388
int umlBuildCommandLine(virConnectPtr conn,
389
struct uml_driver *driver ATTRIBUTE_UNUSED,
391
const char ***retargv,
392
const char ***retenv)
397
int qargc = 0, qarga = 0;
398
const char **qargv = NULL;
399
int qenvc = 0, qenva = 0;
400
const char **qenv = NULL;
401
char *cmdline = NULL;
405
#define ADD_ARG_SPACE \
407
if (qargc == qarga) { \
409
if (VIR_REALLOC_N(qargv, qarga) < 0) \
414
#define ADD_ARG(thisarg) \
417
qargv[qargc++] = thisarg; \
420
#define ADD_ARG_LIT(thisarg) \
423
if ((qargv[qargc++] = strdup(thisarg)) == NULL) \
427
#define ADD_ARG_PAIR(key,val) \
431
if (virAsprintf(&arg, "%s=%s", key, val) < 0) \
433
qargv[qargc++] = arg; \
437
#define ADD_ENV_SPACE \
439
if (qenvc == qenva) { \
441
if (VIR_REALLOC_N(qenv, qenva) < 0) \
446
#define ADD_ENV(thisarg) \
449
qenv[qenvc++] = thisarg; \
452
#define ADD_ENV_LIT(thisarg) \
455
if ((qenv[qenvc++] = strdup(thisarg)) == NULL) \
459
#define ADD_ENV_COPY(envname) \
461
char *val = getenv(envname); \
465
if (virAsprintf(&envval, "%s=%s", envname, val) < 0) \
467
qenv[qenvc++] = envval; \
471
snprintf(memory, sizeof(memory), "%luK", vm->def->memory);
473
ADD_ENV_LIT("LC_ALL=C");
475
ADD_ENV_COPY("LD_PRELOAD");
476
ADD_ENV_COPY("LD_LIBRARY_PATH");
477
ADD_ENV_COPY("PATH");
478
ADD_ENV_COPY("HOME");
479
ADD_ENV_COPY("USER");
480
ADD_ENV_COPY("LOGNAME");
481
ADD_ENV_COPY("TMPDIR");
483
ADD_ARG_LIT(vm->def->os.kernel);
484
//ADD_ARG_PAIR("con0", "fd:0,fd:1");
485
ADD_ARG_PAIR("mem", memory);
486
ADD_ARG_PAIR("umid", vm->def->name);
488
if (vm->def->os.root)
489
ADD_ARG_PAIR("root", vm->def->os.root);
491
for (i = 0 ; i < vm->def->ndisks ; i++) {
492
virDomainDiskDefPtr disk = vm->def->disks[i];
494
if (!STRPREFIX(disk->dst, "ubd")) {
495
umlReportError(VIR_ERR_INTERNAL_ERROR,
496
_("unsupported disk type '%s'"), disk->dst);
500
ADD_ARG_PAIR(disk->dst, disk->src);
503
for (i = 0 ; i < vm->def->nnets ; i++) {
504
char *ret = umlBuildCommandLineNet(conn, vm->def->nets[i], i);
510
for (i = 0 ; i < UML_MAX_CHAR_DEVICE ; i++) {
512
if (i == 0 && vm->def->console)
513
ret = umlBuildCommandLineChr(vm->def->console, "con");
515
if (virAsprintf(&ret, "con%d=none", i) < 0)
520
for (i = 0 ; i < UML_MAX_CHAR_DEVICE ; i++) {
521
virDomainChrDefPtr chr = NULL;
523
for (j = 0 ; j < vm->def->nserials ; j++)
524
if (vm->def->serials[j]->target.port == i)
525
chr = vm->def->serials[j];
527
ret = umlBuildCommandLineChr(chr, "ssl");
529
if (virAsprintf(&ret, "ssl%d=none", i) < 0)
534
if (vm->def->os.cmdline) {
535
char *args, *next_arg;
536
if ((cmdline = strdup(vm->def->os.cmdline)) == NULL)
544
next_arg = umlNextArg(args);
562
for (i = 0 ; i < qargc ; i++)
563
VIR_FREE((qargv)[i]);
567
for (i = 0 ; i < qenvc ; i++)