diff options
author | 2015-12-03 16:11:32 +0000 | |
---|---|---|
committer | 2015-12-03 16:11:32 +0000 | |
commit | f01317bc90e73b75e5415e2c1ca2129cad7a710f (patch) | |
tree | 99305d155593c8bf8718da9e6cf11b98bcf63065 | |
parent | pledge(pf) needs to allow DIOCKILLSRCNODES, used in relayd. (diff) | |
download | wireguard-openbsd-f01317bc90e73b75e5415e2c1ca2129cad7a710f.tar.xz wireguard-openbsd-f01317bc90e73b75e5415e2c1ca2129cad7a710f.zip |
Add support for an optional vm.conf(5) file in vmd. This will replace
vmm.conf(5) in vmmctl. For a short time, both vmd and vmmctl will
support a configuration file, but vmmctl will be changed to send
"load" requests to vmd instead of loading and parsing the file
directly.
-rw-r--r-- | usr.sbin/vmd/Makefile | 28 | ||||
-rw-r--r-- | usr.sbin/vmd/config.c | 4 | ||||
-rw-r--r-- | usr.sbin/vmd/parse.y | 749 | ||||
-rw-r--r-- | usr.sbin/vmd/vm.conf.5 | 140 | ||||
-rw-r--r-- | usr.sbin/vmd/vmd.c | 66 | ||||
-rw-r--r-- | usr.sbin/vmd/vmd.h | 11 |
6 files changed, 963 insertions, 35 deletions
diff --git a/usr.sbin/vmd/Makefile b/usr.sbin/vmd/Makefile index 7962b65dc5d..b24189c9022 100644 --- a/usr.sbin/vmd/Makefile +++ b/usr.sbin/vmd/Makefile @@ -1,16 +1,22 @@ +# $OpenBSD: Makefile,v 1.7 2015/12/03 16:11:32 reyk Exp $ .if ${MACHINE} == "amd64" -PROG= vmd -SRCS= vmm.c loadfile_elf.c pci.c virtio.c -SRCS+= vmd.c control.c log.c proc.c config.c -CFLAGS+= -Wall -I${.CURDIR} -CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes -CFLAGS+= -Wmissing-declarations -CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual -CFLAGS+= -Wsign-compare -LDADD+= -lutil -lpthread -levent -DPADD+= ${LIBUTIL} ${LIBEVENT} +PROG= vmd +SRCS= vmm.c loadfile_elf.c pci.c virtio.c +SRCS+= vmd.c control.c log.c proc.c config.c +SRCS+= parse.y + +CFLAGS+= -Wall -I${.CURDIR} +CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes +CFLAGS+= -Wmissing-declarations +CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual +CFLAGS+= -Wsign-compare + +LDADD+= -lutil -lpthread -levent +DPADD+= ${LIBUTIL} ${LIBEVENT} + +CLEANFILES+= y.tab.h .else @@ -18,6 +24,6 @@ NOPROG= yes .endif -MAN= vmd.8 +MAN= vmd.8 vm.conf.5 .include <bsd.prog.mk> diff --git a/usr.sbin/vmd/config.c b/usr.sbin/vmd/config.c index 480cadf0a3f..d835ed88f6d 100644 --- a/usr.sbin/vmd/config.c +++ b/usr.sbin/vmd/config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.2 2015/12/03 13:27:14 reyk Exp $ */ +/* $OpenBSD: config.c,v 1.3 2015/12/03 16:11:32 reyk Exp $ */ /* * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org> @@ -190,8 +190,6 @@ config_getvm(struct privsep *ps, struct vm_create_params *vcp, } close(ttys_fd); - log_info("%s console: %s", vcp->vcp_name, vm->vm_ttyname); - proc_compose_imsg(ps, PROC_VMM, -1, IMSG_VMDOP_START_VM_END, vm->vm_vmid, fd, vcp, sizeof(*vcp)); diff --git a/usr.sbin/vmd/parse.y b/usr.sbin/vmd/parse.y new file mode 100644 index 00000000000..165f23a2a4f --- /dev/null +++ b/usr.sbin/vmd/parse.y @@ -0,0 +1,749 @@ +/* $OpenBSD: parse.y,v 1.1 2015/12/03 16:11:32 reyk Exp $ */ + +/* + * Copyright (c) 2007-2015 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> + * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> + * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. + * Copyright (c) 2001 Theo de Raadt. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +%{ +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/uio.h> + +#include <machine/vmmvar.h> + +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> +#include <netdb.h> +#include <util.h> +#include <err.h> + +#include "proc.h" +#include "vmd.h" + +TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); +static struct file { + TAILQ_ENTRY(file) entry; + FILE *stream; + char *name; + int lineno; + int errors; +} *file, *topfile; +struct file *pushfile(const char *, int); +int popfile(void); +int yyparse(void); +int yylex(void); +int yyerror(const char *, ...) + __attribute__((__format__ (printf, 1, 2))) + __attribute__((__nonnull__ (1))); +int kw_cmp(const void *, const void *); +int lookup(char *); +int lgetc(int); +int lungetc(int); +int findeol(void); + +TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); +struct sym { + TAILQ_ENTRY(sym) entry; + int used; + int persist; + char *nam; + char *val; +}; +int symset(const char *, const char *, int); +char *symget(const char *); + +ssize_t parse_size(char *, int64_t); +int parse_disk(char *); + +static struct vm_create_params vcp; +static int vcp_disable = 0; +static int errors = 0; + +extern struct vmd *env; + +typedef struct { + union { + int64_t number; + char *string; + } v; + int lineno; +} YYSTYPE; + +%} + + +%token INCLUDE ERROR +%token DISK NIFS PATH SIZE VMID +%token ENABLE DISABLE VM KERNEL MEMORY +%token <v.string> STRING +%token <v.number> NUMBER +%type <v.number> disable + +%% + +grammar : /* empty */ + | grammar include '\n' + | grammar '\n' + | grammar varset '\n' + | grammar main '\n' + | grammar error '\n' { file->errors++; } + ; + +include : INCLUDE STRING { + struct file *nfile; + + if ((nfile = pushfile($2, 0)) == NULL) { + yyerror("failed to include file %s", $2); + free($2); + YYERROR; + } + free($2); + + file = nfile; + lungetc('\n'); + } + ; + +varset : STRING '=' STRING { + if (symset($1, $3, 0) == -1) + fatalx("cannot store variable"); + free($1); + free($3); + } + ; + +main : VM STRING { + memset(&vcp, 0, sizeof(vcp)); + vcp_disable = 0; + if (strlcpy(vcp.vcp_name, $2, sizeof(vcp.vcp_name)) >= + sizeof(vcp.vcp_name)) { + yyerror("vm name too long"); + YYERROR; + } + } '{' optnl vm_opts_l '}' { + if (vcp_disable) { + log_debug("%s:%d: vm \"%s\" disabled (skipped)", + file->name, yylval.lineno, vcp.vcp_name); + } else if (!env->vmd_noaction) { + /* + * XXX Start the vm right away - + * XXX this should be done after parsing + * XXX the configuration. + */ + if (config_getvm(&env->vmd_ps, &vcp, + -1, -1) == -1) { + log_warnx("%s:%d: vm \"%s\" failed", + file->name, yylval.lineno, + vcp.vcp_name); + YYERROR; + } + log_debug("%s:%d: vm \"%s\" enabled", + file->name, yylval.lineno, vcp.vcp_name); + } + } + ; + +vm_opts_l : vm_opts_l vm_opts nl + | vm_opts optnl + ; + +vm_opts : disable { + vcp_disable = $1; + } + | DISK STRING { + if (parse_disk($2) != 0) { + yyerror("failed to parse disks: %s", $2); + free($2); + YYERROR; + } + free($2); + } + | KERNEL STRING { + if (vcp.vcp_kernel[0] != '\0') { + yyerror("kernel specified more than once"); + free($2); + YYERROR; + } + if (strlcpy(vcp.vcp_kernel, $2, + sizeof(vcp.vcp_kernel)) >= sizeof(vcp.vcp_kernel)) { + yyerror("kernel name too long"); + free($2); + YYERROR; + } + free($2); + } + | NIFS NUMBER { + if (vcp.vcp_nnics != 0) { + yyerror("interfaces specified more than once"); + YYERROR; + } + if ($2 < 0 || $2 > VMM_MAX_NICS_PER_VM) { + yyerror("too many interfaces: %lld", $2); + YYERROR; + } + vcp.vcp_nnics = (size_t)$2; + } + | MEMORY NUMBER { + ssize_t res; + if (vcp.vcp_memory_size != 0) { + yyerror("memory specified more than once"); + YYERROR; + } + if ((res = parse_size(NULL, $2)) == -1) { + yyerror("failed to parse size: %lld", $2); + YYERROR; + } + vcp.vcp_memory_size = (size_t)res; + } + | MEMORY STRING { + ssize_t res; + if (vcp.vcp_memory_size != 0) { + yyerror("argument specified more than once"); + free($2); + YYERROR; + } + if ((res = parse_size($2, 0)) == -1) { + yyerror("failed to parse size: %s", $2); + free($2); + YYERROR; + } + vcp.vcp_memory_size = (size_t)res; + } + ; + +disable : ENABLE { $$ = 0; } + | DISABLE { $$ = 1; } + ; + +optnl : '\n' optnl + | + ; + +nl : '\n' optnl + ; + +%% + +struct keywords { + const char *k_name; + int k_val; +}; + +int +yyerror(const char *fmt, ...) +{ + va_list ap; + char *msg; + + file->errors++; + va_start(ap, fmt); + if (vasprintf(&msg, fmt, ap) == -1) + fatal("yyerror vasprintf"); + va_end(ap); + warnx("%s:%d: %s", file->name, yylval.lineno, msg); + free(msg); + return (0); +} + +int +kw_cmp(const void *k, const void *e) +{ + return (strcmp(k, ((const struct keywords *)e)->k_name)); +} + +int +lookup(char *s) +{ + /* this has to be sorted always */ + static const struct keywords keywords[] = { + { "disable", DISABLE }, + { "disk", DISK }, + { "enable", ENABLE }, + { "id", VMID }, + { "include", INCLUDE }, + { "interfaces", NIFS }, + { "kernel", KERNEL }, + { "memory", MEMORY }, + { "size", SIZE }, + { "vm", VM } + }; + const struct keywords *p; + + p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), + sizeof(keywords[0]), kw_cmp); + + if (p) + return (p->k_val); + else + return (STRING); +} + +#define MAXPUSHBACK 128 + +u_char *parsebuf; +int parseindex; +u_char pushback_buffer[MAXPUSHBACK]; +int pushback_index = 0; + +int +lgetc(int quotec) +{ + int c, next; + + if (parsebuf) { + /* Read character from the parsebuffer instead of input. */ + if (parseindex >= 0) { + c = parsebuf[parseindex++]; + if (c != '\0') + return (c); + parsebuf = NULL; + } else + parseindex++; + } + + if (pushback_index) + return (pushback_buffer[--pushback_index]); + + if (quotec) { + if ((c = getc(file->stream)) == EOF) { + yyerror("reached end of file while parsing " + "quoted string"); + if (file == topfile || popfile() == EOF) + return (EOF); + return (quotec); + } + return (c); + } + + while ((c = getc(file->stream)) == '\\') { + next = getc(file->stream); + if (next != '\n') { + c = next; + break; + } + yylval.lineno = file->lineno; + file->lineno++; + } + if (c == '\t' || c == ' ') { + /* Compress blanks to a single space. */ + do { + c = getc(file->stream); + } while (c == '\t' || c == ' '); + ungetc(c, file->stream); + c = ' '; + } + + while (c == EOF) { + if (file == topfile || popfile() == EOF) + return (EOF); + c = getc(file->stream); + } + return (c); +} + +int +lungetc(int c) +{ + if (c == EOF) + return (EOF); + if (parsebuf) { + parseindex--; + if (parseindex >= 0) + return (c); + } + if (pushback_index < MAXPUSHBACK-1) + return (pushback_buffer[pushback_index++] = c); + else + return (EOF); +} + +int +findeol(void) +{ + int c; + + parsebuf = NULL; + + /* skip to either EOF or the first real EOL */ + while (1) { + if (pushback_index) + c = pushback_buffer[--pushback_index]; + else + c = lgetc(0); + if (c == '\n') { + file->lineno++; + break; + } + if (c == EOF) + break; + } + return (ERROR); +} + +int +yylex(void) +{ + u_char buf[8096]; + u_char *p, *val; + int quotec, next, c; + int token; + +top: + p = buf; + while ((c = lgetc(0)) == ' ' || c == '\t') + ; /* nothing */ + + yylval.lineno = file->lineno; + if (c == '#') + while ((c = lgetc(0)) != '\n' && c != EOF) + ; /* nothing */ + if (c == '$' && parsebuf == NULL) { + while (1) { + if ((c = lgetc(0)) == EOF) + return (0); + + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + if (isalnum(c) || c == '_') { + *p++ = c; + continue; + } + *p = '\0'; + lungetc(c); + break; + } + val = symget(buf); + if (val == NULL) { + yyerror("macro '%s' not defined", buf); + return (findeol()); + } + parsebuf = val; + parseindex = 0; + goto top; + } + + switch (c) { + case '\'': + case '"': + quotec = c; + while (1) { + if ((c = lgetc(quotec)) == EOF) + return (0); + if (c == '\n') { + file->lineno++; + continue; + } else if (c == '\\') { + if ((next = lgetc(quotec)) == EOF) + return (0); + if (next == quotec || c == ' ' || c == '\t') + c = next; + else if (next == '\n') { + file->lineno++; + continue; + } else + lungetc(next); + } else if (c == quotec) { + *p = '\0'; + break; + } else if (c == '\0') { + yyerror("syntax error"); + return (findeol()); + } + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + *p++ = c; + } + yylval.v.string = strdup(buf); + if (yylval.v.string == NULL) + fatal("yylex: strdup"); + return (STRING); + } + +#define allowed_to_end_number(x) \ + (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') + + if (c == '-' || isdigit(c)) { + do { + *p++ = c; + if ((unsigned)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && isdigit(c)); + lungetc(c); + if (p == buf + 1 && buf[0] == '-') + goto nodigits; + if (c == EOF || allowed_to_end_number(c)) { + const char *errstr = NULL; + + *p = '\0'; + yylval.v.number = strtonum(buf, LLONG_MIN, + LLONG_MAX, &errstr); + if (errstr) { + yyerror("\"%s\" invalid number: %s", + buf, errstr); + return (findeol()); + } + return (NUMBER); + } else { +nodigits: + while (p > buf + 1) + lungetc(*--p); + c = *--p; + if (c == '-') + return (c); + } + } + +#define allowed_in_string(x) \ + (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ + x != '{' && x != '}' && \ + x != '!' && x != '=' && x != '#' && \ + x != ',')) + + if (isalnum(c) || c == ':' || c == '_') { + do { + *p++ = c; + if ((unsigned)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); + lungetc(c); + *p = '\0'; + if ((token = lookup(buf)) == STRING) + if ((yylval.v.string = strdup(buf)) == NULL) + fatal("yylex: strdup"); + return (token); + } + if (c == '\n') { + yylval.lineno = file->lineno; + file->lineno++; + } + if (c == EOF) + return (0); + return (c); +} + +struct file * +pushfile(const char *name, int secret) +{ + struct file *nfile; + + if ((nfile = calloc(1, sizeof(struct file))) == NULL) { + log_warn("malloc"); + return (NULL); + } + if ((nfile->name = strdup(name)) == NULL) { + log_warn("malloc"); + free(nfile); + return (NULL); + } + if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { + free(nfile->name); + free(nfile); + return (NULL); + } + nfile->lineno = 1; + TAILQ_INSERT_TAIL(&files, nfile, entry); + return (nfile); +} + +int +popfile(void) +{ + struct file *prev; + + if ((prev = TAILQ_PREV(file, files, entry)) != NULL) + prev->errors += file->errors; + + TAILQ_REMOVE(&files, file, entry); + fclose(file->stream); + free(file->name); + free(file); + file = prev; + return (file ? 0 : EOF); +} + +int +parse_config(const char *filename) +{ + struct sym *sym, *next; + + if ((file = pushfile(filename, 0)) == NULL) { + log_warn("failed to open %s", filename); + return (0); + } + topfile = file; + setservent(1); + + yyparse(); + errors = file->errors; + popfile(); + + endservent(); + + /* Free macros and check which have not been used. */ + for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) { + next = TAILQ_NEXT(sym, entry); + if (!sym->used) + fprintf(stderr, "warning: macro '%s' not " + "used\n", sym->nam); + if (!sym->persist) { + free(sym->nam); + free(sym->val); + TAILQ_REMOVE(&symhead, sym, entry); + free(sym); + } + } + + if (errors) + return (-1); + + return (0); +} + +int +symset(const char *nam, const char *val, int persist) +{ + struct sym *sym; + + for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); + sym = TAILQ_NEXT(sym, entry)) + ; /* nothing */ + + if (sym != NULL) { + if (sym->persist == 1) + return (0); + else { + free(sym->nam); + free(sym->val); + TAILQ_REMOVE(&symhead, sym, entry); + free(sym); + } + } + if ((sym = calloc(1, sizeof(*sym))) == NULL) + return (-1); + + sym->nam = strdup(nam); + if (sym->nam == NULL) { + free(sym); + return (-1); + } + sym->val = strdup(val); + if (sym->val == NULL) { + free(sym->nam); + free(sym); + return (-1); + } + sym->used = 0; + sym->persist = persist; + TAILQ_INSERT_TAIL(&symhead, sym, entry); + return (0); +} + +int +cmdline_symset(char *s) +{ + char *sym, *val; + int ret; + size_t len; + + if ((val = strrchr(s, '=')) == NULL) + return (-1); + + len = (val - s) + 1; + if ((sym = malloc(len)) == NULL) + fatal("cmdline_symset: malloc"); + + (void)strlcpy(sym, s, len); + + ret = symset(sym, val + 1, 1); + free(sym); + + return (ret); +} + +char * +symget(const char *nam) +{ + struct sym *sym; + + TAILQ_FOREACH(sym, &symhead, entry) + if (strcmp(nam, sym->nam) == 0) { + sym->used = 1; + return (sym->val); + } + return (NULL); +} + +ssize_t +parse_size(char *word, int64_t val) +{ + ssize_t size; + long long res; + + if (word != NULL) { + if (scan_scaled(word, &res) != 0) { + log_warn("invalid size: %s", word); + return (-1); + } + val = (int64_t)res; + } + + if (val < (1024 * 1024)) { + log_warnx("size must be at least one megabyte"); + return (-1); + } else + size = val / 1024 / 1024; + + if ((size * 1024 * 1024) != val) + log_warnx("size rounded to %zd megabytes", size); + + return ((ssize_t)size); +} + +int +parse_disk(char *word) +{ + if (vcp.vcp_ndisks >= VMM_MAX_DISKS_PER_VM) { + log_warnx("too many disks"); + return (-1); + } + + if (strlcpy(vcp.vcp_disks[vcp.vcp_ndisks], word, + VMM_MAX_PATH_DISK) >= VMM_MAX_PATH_DISK) { + log_warnx("disk path too long"); + return (-1); + } + + vcp.vcp_ndisks++; + + return (0); +} diff --git a/usr.sbin/vmd/vm.conf.5 b/usr.sbin/vmd/vm.conf.5 new file mode 100644 index 00000000000..355b50ed68f --- /dev/null +++ b/usr.sbin/vmd/vm.conf.5 @@ -0,0 +1,140 @@ +.\" $OpenBSD: vm.conf.5,v 1.1 2015/12/03 16:11:32 reyk Exp $ +.\" +.\" Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org> +.\" Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: December 3 2015 $ +.Dt VM.CONF 5 +.Os +.Sh NAME +.Nm vm.conf +.Nd virtual machine configuration +.Sh DESCRIPTION +.Nm +is the configuration file to configure the virtual machine monitor +(VMM) subsystem. +A VMM manages virtual machines (VMs) on a +.Ar host . +The VMM subsystem is responsible for creating, destroying, and +executing VMs. +.Sh SECTIONS +.Nm +is divided into three main sections: +.Bl -tag -width xxxx +.It Sy Macros +User-defined variables may be defined and used later, simplifying the +configuration file. +.It Sy VM Configuration +Configuration for each individual virtual machine. +.El +.Pp +Within the sections, +the +.Ar bytes +argument can be specified with a human-readable scale, +using the format described in +.Xr scan_scaled 3 . +.Pp +The current line can be extended over multiple lines using a backslash +.Pq Sq \e . +Comments can be put anywhere in the file using a hash mark +.Pq Sq # , +and extend to the end of the current line. +Care should be taken when commenting out multi-line text: +the comment is effective until the end of the entire block. +.Pp +Argument names not beginning with a letter, digit, or underscore +must be quoted. +.Pp +Additional configuration files can be included with the +.Ic include +keyword, for example: +.Bd -literal -offset indent +include "/etc/vm1.example.com.conf" +.Ed +.Sh MACROS +Macros can be defined that will later be expanded in context. +Macro names must start with a letter, digit, or underscore, +and may contain any of those characters. +Macro names may not be reserved words (for example, +.Ic vm , +.Ic memory , +or +.Ic disk ) . +Macros are not expanded inside quotes. +.Pp +For example: +.Bd -literal -offset indent +ramdisk="/bsd.rd" +vm "vm1.example.com" { + memory 512M + kernel $ramdisk +} +.Ed +.Sh VM CONFIGURATION +Each +.Ic vm +section starts with a declaration of the virtual machine +.Ar name : +.Bl -tag -width Ds +.It Ic vm Ar name Brq ... +This name can be any string, and is typically a hostname. +.El +.Pp +Followed by a block of parameters that is enclosed in curly brackets: +.Bl -tag -width Ds +.It Cm enable +Automatically start the VM. +This is the default if neither +.Cm enable +nor +.Cm disable +is specified. +.It Cm disable +Do not start this VM. +.It Cm disk Ar path +Disk image file (may be specified multiple times to add multiple disk images). +.It Cm kernel Ar path +Kernel to load when booting the VM. +.It Cm memory Ar bytes +Memory size of the VM, in bytes, rounded to megabytes. +.It Cm interfaces Ar count +Number of network interfaces to add to the VM. +.El +.Sh EXAMPLES +Create a new VM with 512MB memory, 1 network interface, one disk image +('disk.img') and boot from kernel '/bsd': +.Bd -literal -offset indent +vm "vm2.example.com" { + memory 512M + interfaces 1 + disk "/var/vmm/vm2-disk.img" + kernel "/bsd" +} +.Ed +.Sh SEE ALSO +.Xr vmm 4 , +.Xr vmd 8 , +.Xr vmmctl 8 +.Sh HISTORY +The +.Nm +file format first appeared in +.Ox 5.9 . +.Sh AUTHORS +.An -nosplit +.An Mike Larkin Aq Mt mlarkin@openbsd.org +and +.An Reyk Floeter Aq Mt reyk@openbsd.org . diff --git a/usr.sbin/vmd/vmd.c b/usr.sbin/vmd/vmd.c index f15a4368fad..7529a445f88 100644 --- a/usr.sbin/vmd/vmd.c +++ b/usr.sbin/vmd/vmd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vmd.c,v 1.12 2015/12/03 13:27:14 reyk Exp $ */ +/* $OpenBSD: vmd.c,v 1.13 2015/12/03 16:11:32 reyk Exp $ */ /* * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org> @@ -99,6 +99,20 @@ vmd_dispatch_vmm(int fd, struct privsep_proc *p, struct imsg *imsg) memcpy(&res, imsg->data, sizeof(res)); if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) fatalx("%s: invalid vm response", __func__); + if (res) { + errno = res; + log_warn("%s: failed to start vm", + vm->vm_params.vcp_name); + } else { + log_info("%s: started vm successfully, tty %s", + vm->vm_params.vcp_name, vm->vm_ttyname); + } + /* + * If the peerid is -1, the request originated from + * the parent, not the control socket. + */ + if (vm->vm_peerid == (uint32_t)-1) + break; vmr.vmr_result = res; strlcpy(vmr.vmr_ttyname, vm->vm_ttyname, sizeof(vmr.vmr_ttyname)); @@ -210,15 +224,19 @@ main(int argc, char **argv) { struct privsep *ps; int ch; + const char *conffile = VMD_CONF; if ((env = calloc(1, sizeof(*env))) == NULL) fatal("calloc: env"); - while ((ch = getopt(argc, argv, "dvn")) != -1) { + while ((ch = getopt(argc, argv, "df:vn")) != -1) { switch (ch) { case 'd': env->vmd_debug = 2; break; + case 'f': + conffile = optarg; + break; case 'v': env->vmd_verbose++; break; @@ -230,6 +248,12 @@ main(int argc, char **argv) } } + if (env->vmd_noaction && !env->vmd_debug) + env->vmd_debug = 1; + + /* log to stderr until daemonized */ + log_init(env->vmd_debug ? env->vmd_debug : 1, LOG_DAEMON); + /* check for root privileges */ if (geteuid()) fatalx("need root privileges"); @@ -252,8 +276,11 @@ main(int argc, char **argv) if (env->vmd_fd == -1) fatal("can't open vmm device node %s", VMM_NODE); - /* log to stderr until daemonized */ - log_init(env->vmd_debug ? env->vmd_debug : 1, LOG_DAEMON); + /* Configuration will be parsed after forking the children */ + env->vmd_conffile = VMD_CONF; + + log_init(env->vmd_debug, LOG_DAEMON); + log_verbose(env->vmd_verbose); if (!env->vmd_debug && daemon(0, 0) == -1) fatal("can't daemonize"); @@ -262,7 +289,9 @@ main(int argc, char **argv) log_procinit("parent"); ps->ps_ninstances = 1; - proc_init(ps, procs, nitems(procs)); + + if (!env->vmd_noaction) + proc_init(ps, procs, nitems(procs)); event_init(); @@ -280,7 +309,8 @@ main(int argc, char **argv) signal_add(&ps->ps_evsigpipe, NULL); signal_add(&ps->ps_evsigusr1, NULL); - proc_listen(ps, procs, nitems(procs)); + if (!env->vmd_noaction) + proc_listen(ps, procs, nitems(procs)); if (vmd_configure() == -1) fatalx("configuration failed"); @@ -295,19 +325,6 @@ main(int argc, char **argv) int vmd_configure(void) { -#if 0 - if (parse_config(env->sc_conffile, env) == -1) { - proc_kill(&env->sc_ps); - exit(1); - } -#endif - - if (env->vmd_noaction) { - fprintf(stderr, "configuration OK\n"); - proc_kill(&env->vmd_ps); - exit(0); - } - /* * pledge in the parent process: * stdio - for malloc and basic I/O including events. @@ -320,6 +337,17 @@ vmd_configure(void) if (pledge("stdio rpath wpath proc tty sendfd", NULL) == -1) fatal("pledge"); + if (parse_config(env->vmd_conffile) == -1) { + proc_kill(&env->vmd_ps); + exit(1); + } + + if (env->vmd_noaction) { + fprintf(stderr, "configuration OK\n"); + proc_kill(&env->vmd_ps); + exit(0); + } + return (0); } diff --git a/usr.sbin/vmd/vmd.h b/usr.sbin/vmd/vmd.h index 6817392cc7e..77c18902596 100644 --- a/usr.sbin/vmd/vmd.h +++ b/usr.sbin/vmd/vmd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: vmd.h,v 1.8 2015/12/03 13:27:14 reyk Exp $ */ +/* $OpenBSD: vmd.h,v 1.9 2015/12/03 16:11:32 reyk Exp $ */ /* * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org> @@ -29,6 +29,7 @@ #define VMD_H #define VMD_USER "_vmd" +#define VMD_CONF "/etc/vm.conf" #define SOCKET_NAME "/var/run/vmd.sock" #define VMM_NODE "/dev/vmm" #define VM_NAME_MAX 64 @@ -81,7 +82,7 @@ TAILQ_HEAD(vmlist, vmd_vm); struct vmd { struct privsep vmd_ps; - int vmd_fd; + const char *vmd_conffile; int vmd_debug; int vmd_verbose; @@ -90,6 +91,8 @@ struct vmd { uint32_t vmd_nvm; struct vmlist *vmd_vms; + + int vmd_fd; }; /* vmd.c */ @@ -112,4 +115,8 @@ int config_getvm(struct privsep *, struct vm_create_params *, int config_getdisk(struct privsep *, struct imsg *); int config_getif(struct privsep *, struct imsg *); +/* parse.y */ +int parse_config(const char *); +int cmdline_symset(char *); + #endif /* VMD_H */ |