summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorreyk <reyk@openbsd.org>2015-12-03 16:11:32 +0000
committerreyk <reyk@openbsd.org>2015-12-03 16:11:32 +0000
commitf01317bc90e73b75e5415e2c1ca2129cad7a710f (patch)
tree99305d155593c8bf8718da9e6cf11b98bcf63065
parentpledge(pf) needs to allow DIOCKILLSRCNODES, used in relayd. (diff)
downloadwireguard-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/Makefile28
-rw-r--r--usr.sbin/vmd/config.c4
-rw-r--r--usr.sbin/vmd/parse.y749
-rw-r--r--usr.sbin/vmd/vm.conf.5140
-rw-r--r--usr.sbin/vmd/vmd.c66
-rw-r--r--usr.sbin/vmd/vmd.h11
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 */