diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2015-11-22 20:55:19 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2015-11-22 20:55:19 +0000 |
commit | 6f0b9075f4beea2770c23e044037335ac54f6a64 (patch) | |
tree | 321b055a3461820f3663ecac990d993d008a8583 /usr.sbin | |
parent | fa0f3767a64aa01ea0879ce5a586c06a5fda7ca1 (diff) |
Add initial parser to support a vmm.conf(5) configuration file format
for vmm and virtual machines. Additionally, add a matching vmmctl
command line grammar that replaces (most of the) getopt arguments.
The goal is to provide a sane way to configure vmm(4) and vmd(8).
"There is still a lot to be done, and fixed" in this as well.
OK mlarkin@
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/vmmctl/Makefile | 4 | ||||
-rw-r--r-- | usr.sbin/vmmctl/main.c | 323 | ||||
-rw-r--r-- | usr.sbin/vmmctl/parse.y | 673 | ||||
-rw-r--r-- | usr.sbin/vmmctl/parser.c | 317 | ||||
-rw-r--r-- | usr.sbin/vmmctl/parser.h | 81 | ||||
-rw-r--r-- | usr.sbin/vmmctl/vmm.conf.5 | 166 | ||||
-rw-r--r-- | usr.sbin/vmmctl/vmmctl.8 | 117 | ||||
-rw-r--r-- | usr.sbin/vmmctl/vmmctl.c | 368 |
8 files changed, 1636 insertions, 413 deletions
diff --git a/usr.sbin/vmmctl/Makefile b/usr.sbin/vmmctl/Makefile index 2c0c1e878f1..858a4b27473 100644 --- a/usr.sbin/vmmctl/Makefile +++ b/usr.sbin/vmmctl/Makefile @@ -2,7 +2,7 @@ .if ${MACHINE} == "amd64" PROG= vmmctl -SRCS= vmmctl.c +SRCS= vmmctl.c parser.c main.c parse.y CFLAGS+= -Wall CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes CFLAGS+= -Wmissing-declarations @@ -18,7 +18,7 @@ NOPROG= yes .endif -MAN= vmmctl.8 +MAN= vmmctl.8 vmm.conf.5 MANSUBDIR=${MACHINE} .include <bsd.prog.mk> diff --git a/usr.sbin/vmmctl/main.c b/usr.sbin/vmmctl/main.c new file mode 100644 index 00000000000..7711d6eaa5b --- /dev/null +++ b/usr.sbin/vmmctl/main.c @@ -0,0 +1,323 @@ +/* $OpenBSD: main.c,v 1.1 2015/11/22 20:55:18 reyk Exp $ */ + +/* + * 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. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <sys/un.h> +#include <sys/cdefs.h> + +#include <machine/vmmvar.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <limits.h> +#include <string.h> +#include <unistd.h> +#include <imsg.h> + +#include "vmd.h" +#include "parser.h" + +static const char *socket_name = SOCKET_NAME; + +__dead void usage(void); +int vmm_action(struct parse_result *); + +__dead void +usage(void) +{ + extern char *__progname; + + fprintf(stderr, "usage: %s [-q] [-s socket] command [arg ...]\n", + __progname); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + struct parse_result *res; + int ch; + int ret; + const char *paths[2]; + + while ((ch = getopt(argc, argv, "s:")) != -1) { + switch (ch) { + case 's': + socket_name = optarg; + break; + default: + usage(); + /* NOTREACHED */ + } + } + argc -= optind; + argv += optind; + + /* parse command line options */ + if ((res = parse(argc, argv)) == NULL) + exit(1); + + switch (res->action) { + case NONE: + usage(); + break; + case CMD_CREATE: + paths[0] = res->path; + paths[1] = NULL; + if (pledge("stdio rpath wpath cpath", paths) == -1) + err(1, "pledge"); + if (res->size < 1) + errx(1, "specified image size too small"); + + ret = create_imagefile(res->path, res->size); + if (ret != 0) { + errno = ret; + err(1, "create imagefile operation failed"); + } else + warnx("imagefile created"); + return (0); + case CMD_LOAD: + if (pledge("stdio rpath unix", NULL) == -1) + err(1, "pledge"); + + /* parse configuration file options */ + if (res->path == NULL) + ret = parse_config(VMM_CONF); + else + ret = parse_config(res->path); + break; + default: + if (pledge("stdio unix", NULL) == -1) + err(1, "pledge"); + ret = vmmaction(res); + break; + } + + return (ret); +} + +int +vmmaction(struct parse_result *res) +{ + struct sockaddr_un sun; + struct imsg imsg; + int done = 0; + int n; + int ret; + int ctl_sock; + + /* + * Connect to vmd control socket. + * XXX vmd currently only accepts one request per connection, + * XXX so we have to open the control socket each time this + * XXX function is called. This should be changed later. + */ + if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + err(1, "socket"); + + bzero(&sun, sizeof(sun)); + sun.sun_family = AF_UNIX; + strlcpy(sun.sun_path, socket_name, sizeof(sun.sun_path)); + + if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) + err(1, "connect: %s", socket_name); + + if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) + err(1, "malloc"); + imsg_init(ibuf, ctl_sock); + + switch (res->action) { + case CMD_ENABLE: + enable_vmm(); + break; + case CMD_DISABLE: + disable_vmm(); + break; + case CMD_START: + /* XXX validation should be done in start_vm() */ + if (res->size < 1) + errx(1, "specified memory size too small"); + if (res->path == NULL) + errx(1, "no kernel specified"); + if (res->ndisks > VMM_MAX_DISKS_PER_VM) + errx(1, "too many disks"); + else if (res->ndisks == 0) + warnx("starting without disks"); + + ret = start_vm(res->name, res->size, res->nifs, + res->ndisks, res->disks, res->path); + if (ret) { + errno = ret; + err(1, "start VM operation failed"); + } + break; + case CMD_TERMINATE: + terminate_vm(res->id); + break; + case CMD_INFO: + get_info_vm(res->id); + break; + case CMD_CREATE: + case CMD_LOAD: + case NONE: + break; + } + + while (ibuf->w.queued) + if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) + err(1, "write error"); + + while (!done) { + if ((n = imsg_read(ibuf)) == -1) + errx(1, "imsg_read error"); + if (n == 0) + errx(1, "pipe closed"); + + while (!done) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + errx(1, "imsg_get error"); + if (n == 0) + break; + + ret = 0; + switch (res->action) { + case CMD_DISABLE: + done = disable_vmm_complete(&imsg, &ret); + break; + case CMD_ENABLE: + done = enable_vmm_complete(&imsg, &ret); + break; + case CMD_START: + done = start_vm_complete(&imsg, &ret); + break; + case CMD_TERMINATE: + done = terminate_vm_complete(&imsg, &ret); + break; + case CMD_INFO: + done = add_info(&imsg, &ret); + break; + default: + done = 1; + break; + } + + imsg_free(&imsg); + } + } + + close(ibuf->fd); + free(ibuf); + + return (0); +} + +int +parse_ifs(struct parse_result *res, char *word, int val) +{ + const char *error; + + if (word != NULL) { + val = strtonum(word, 0, INT_MAX, &error); + if (error != NULL) { + warnx("invalid count \"%s\": %s", word, error); + return (-1); + } + } + res->nifs = val; + return (0); +} + +int +parse_size(struct parse_result *res, char *word, + long long val) +{ + char *s; + + if (word != NULL) { + val = strtol(word, &s, 10); + if (errno == ERANGE && + (val == LLONG_MIN || val == LLONG_MAX)) { + warnx("out of range: %s", word); + return (-1); + } + } + + /* Convert to megabytes */ + if (*s == '\0') + res->size = val / 1024 / 1024; + else if (strcmp(s, "K") == 0) + res->size = val / 1024; + else if (strcmp(s, "M") == 0) + res->size = val; + else if (strcmp(s, "G") == 0) + res->size = val * 1024; + else { + warnx("invalid unit: %s", s); + return (-1); + } + if (res->size > LLONG_MAX) { + warnx("size too large: %s", word); + return (-1); + } + + return (0); +} + +int +parse_disk(struct parse_result *res, char *word) +{ + char **disks; + char *s; + + if ((disks = reallocarray(res->disks, res->ndisks + 1, + sizeof(char *))) == NULL) { + warn("reallocarray"); + return (-1); + } + if ((s = strdup(word)) == NULL) { + warn("strdup"); + return (-1); + } + disks[res->ndisks] = s; + res->disks = disks; + res->ndisks++; + + return (0); +} + +int +parse_vmid(struct parse_result *res, char *word, uint32_t id) +{ + const char *error; + + if (word != NULL) { + id = strtonum(word, 0, UINT32_MAX, &error); + if (error != NULL) { + warnx("invalid id: %s", error); + return (-1); + } + } + res->id = id; + + return (0); +} diff --git a/usr.sbin/vmmctl/parse.y b/usr.sbin/vmmctl/parse.y new file mode 100644 index 00000000000..8461ff396f1 --- /dev/null +++ b/usr.sbin/vmmctl/parse.y @@ -0,0 +1,673 @@ +/* $OpenBSD: parse.y,v 1.1 2015/11/22 20:55:18 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 <err.h> + +#include "vmd.h" +#include "parser.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 *); + +struct parse_result res; +static int errors = 0; + +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 VMM 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) + errx(1, "cannot store variable"); + free($1); + free($3); + } + ; + +main : VMM disable { + memset(&res, 0, sizeof(res)); + res.action = $2 ? CMD_DISABLE : CMD_ENABLE; + if (vmmaction(&res) != 0) + errx(1, "vmmaction"); + } + | VM STRING { + memset(&res, 0, sizeof(res)); + res.name = $2; + } '{' optnl vm_opts_l '}' { + if (res.disable) { + yyerror("vm \"%s\" disabled", res.name); + YYACCEPT; + } + + res.action = CMD_START; + if (vmmaction(&res) != 0) + errx(1, "vmmaction"); + } + ; + +vm_opts_l : vm_opts_l vm_opts nl + | vm_opts optnl + ; + +vm_opts : disable { + res.disable = $1; + } + | DISK STRING { + if (parse_disk(&res, $2) != 0) { + yyerror("failed to parse disks: %s", $2); + free($2); + YYERROR; + } + } + | KERNEL STRING { + if (res.path != NULL) { + yyerror("argument specified more than once"); + YYERROR; + } + res.path = $2; + } + | NIFS NUMBER { + if (res.nifs) { + yyerror("argument specified more than once"); + YYERROR; + } + if (parse_ifs(&res, NULL, $2) != 0) { + yyerror("failed to parse interfaces: %lld", $2); + YYERROR; + } + } + | MEMORY NUMBER { + if (res.size != 0) { + yyerror("argument specified more than once"); + YYERROR; + } + if (parse_size(&res, NULL, $2) != 0) { + yyerror("failed to parse size: %lld", $2); + YYERROR; + } + } + | MEMORY STRING { + if (res.size != 0) { + yyerror("argument specified more than once"); + YYERROR; + } + if (parse_size(&res, $2, 0) != 0) { + yyerror("failed to parse size: %s", $2); + free($2); + YYERROR; + } + } + ; + +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) + errx(1, "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 }, + { "vmm", VMM } + }; + 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) + err(1, "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) + err(1, "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) { + warn("malloc"); + return (NULL); + } + if ((nfile->name = strdup(name)) == NULL) { + warn("malloc"); + free(nfile); + return (NULL); + } + if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { + warn("%s", nfile->name); + 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) { + return (-1); + } + 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 = strlen(s) - strlen(val) + 1; + if ((sym = malloc(len)) == NULL) + errx(1, "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); +} diff --git a/usr.sbin/vmmctl/parser.c b/usr.sbin/vmmctl/parser.c new file mode 100644 index 00000000000..b095067b3b1 --- /dev/null +++ b/usr.sbin/vmmctl/parser.c @@ -0,0 +1,317 @@ +/* $OpenBSD: parser.c,v 1.1 2015/11/22 20:55:18 reyk Exp $ */ + +/* + * Copyright (c) 2010-2015 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2004 Esben Norby <norby@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@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. + */ + +#include <sys/types.h> +#include <sys/uio.h> + +#include <machine/vmmvar.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <err.h> + +#include "vmd.h" +#include "parser.h" + +enum token_type { + NOTOKEN, + ENDTOKEN, + KEYWORD, + DISK, + NAME, + NIFS, + PATH, + SIZE, + VMID +}; + +struct token { + enum token_type type; + const char *keyword; + int value; + const struct token *next; +}; + +static const struct token t_main[]; +static const struct token t_show[]; +static const struct token t_create[]; +static const struct token t_imgsize[]; +static const struct token t_imgsize_val[]; +static const struct token t_start[]; +static const struct token t_start_name[]; +static const struct token t_disk[]; +static const struct token t_kernel[]; +static const struct token t_memory[]; +static const struct token t_ifs[]; +static const struct token t_vm[]; +static const struct token t_opt_id[]; +static const struct token t_id[]; +static const struct token t_opt_path[]; + +static const struct token t_main[] = { + { KEYWORD, "enable", CMD_ENABLE, NULL }, + { KEYWORD, "create", CMD_CREATE, t_create }, + { KEYWORD, "disable", CMD_DISABLE, NULL }, + { KEYWORD, "load", CMD_LOAD, t_opt_path }, + { KEYWORD, "show", NONE, t_show }, + { KEYWORD, "start", CMD_START, t_start_name }, + { KEYWORD, "terminate", CMD_TERMINATE, t_id }, + { ENDTOKEN, "", NONE, NULL } +}; + +static const struct token t_create[] = { + { PATH, "", NONE, t_imgsize }, + { ENDTOKEN, "", NONE, NULL } +}; + +static const struct token t_imgsize[] = { + { KEYWORD, "size", NONE, t_imgsize_val }, + { ENDTOKEN, "", NONE, NULL } +}; + +static const struct token t_imgsize_val[] = { + { SIZE, "", NONE, NULL }, + { ENDTOKEN, "", NONE, NULL } +}; + +static const struct token t_start[] = { + { NOTOKEN, "", NONE, NULL }, + { KEYWORD, "disk", NONE, t_disk }, + { KEYWORD, "interfaces", NONE, t_ifs }, + { KEYWORD, "kernel", NONE, t_kernel }, + { KEYWORD, "memory", NONE, t_memory }, + { ENDTOKEN, "", NONE, NULL } +}; + +static const struct token t_ifs[] = { + { NIFS, "", NONE, t_start }, + { ENDTOKEN, "", NONE, NULL } +}; + +static const struct token t_disk[] = { + { DISK, "", NONE, t_start }, + { ENDTOKEN, "", NONE, NULL } +}; + +static const struct token t_memory[] = { + { SIZE, "", NONE, t_start }, + { ENDTOKEN, "", NONE, NULL } +}; + +static const struct token t_kernel[] = { + { PATH, "", NONE, t_start }, + { ENDTOKEN, "", NONE, NULL } +}; + +static const struct token t_start_name[] = { + { NAME, "", NONE, t_start }, + { ENDTOKEN, "", NONE, NULL } +}; + +static const struct token t_show[] = { + { KEYWORD, "info", CMD_INFO, t_opt_id }, + { ENDTOKEN, "", NONE, NULL } +}; + +static const struct token t_opt_id[] = { + { NOTOKEN, "", NONE, NULL }, + { VMID, "", NONE, NULL }, + { ENDTOKEN, "", NONE, NULL } +}; + +static const struct token t_id[] = { + { VMID, "", NONE, NULL }, + { ENDTOKEN, "", NONE, NULL } +}; + +static const struct token t_opt_path[] = { + { NOTOKEN, "", NONE, NULL }, + { PATH, "", NONE, NULL }, + { ENDTOKEN, "", NONE, NULL } +}; + +static struct parse_result res; + +const struct token *match_token(char *, const struct token []); +void show_valid_args(const struct token []); +int parse_addr(const char *); + +struct parse_result * +parse(int argc, char *argv[]) +{ + const struct token *table = t_main; + const struct token *match; + + bzero(&res, sizeof(res)); + + while (argc >= 0) { + if ((match = match_token(argv[0], table)) == NULL) { + fprintf(stderr, "valid commands/args:\n"); + show_valid_args(table); + return (NULL); + } + + argc--; + argv++; + + if (match->type == NOTOKEN || match->next == NULL) + break; + + table = match->next; + } + + if (argc > 0) { + fprintf(stderr, "superfluous argument: %s\n", argv[0]); + return (NULL); + } + + return (&res); +} + +const struct token * +match_token(char *word, const struct token table[]) +{ + unsigned int i, match = 0; + const struct token *t = NULL; + + for (i = 0; table[i].type != ENDTOKEN; i++) { + switch (table[i].type) { + case NOTOKEN: + if (word == NULL || strlen(word) == 0) { + match++; + t = &table[i]; + } + break; + case KEYWORD: + if (word != NULL && strncmp(word, table[i].keyword, + strlen(word)) == 0) { + match++; + t = &table[i]; + if (t->value) + res.action = t->value; + } + break; + case PATH: + if (!match && word != NULL && strlen(word) > 0) { + res.path = strdup(word); + match++; + t = &table[i]; + } + break; + case NAME: + if (!match && word != NULL && strlen(word) > 0) { + res.name = strdup(word); + match++; + t = &table[i]; + } + break; + case NIFS: + if (match || word == NULL || *word == '\0') + break; + + if (parse_ifs(&res, word, 0) == -1) + return (NULL); + + match++; + t = &table[i]; + break; + case SIZE: + if (match || word == NULL || *word == '\0') + break; + + if (parse_size(&res, word, 0) == -1) + return (NULL); + + match++; + t = &table[i]; + break; + case VMID: + if (match || word == NULL || *word == '\0') + break; + + if (parse_vmid(&res, word, 0) == -1) + return (NULL); + + match++; + t = &table[i]; + break; + case DISK: + if (match || word == NULL || *word == '\0') + break; + + if (parse_disk(&res, word) == -1) + return (NULL); + + match++; + t = &table[i]; + break; + case ENDTOKEN: + break; + } + } + + if (match != 1) { + if (word == NULL) + fprintf(stderr, "missing argument:\n"); + else if (match > 1) + fprintf(stderr, "ambiguous argument: %s\n", word); + else if (match < 1) + fprintf(stderr, "unknown argument: %s\n", word); + return (NULL); + } + + return (t); +} + +void +show_valid_args(const struct token table[]) +{ + int i; + + for (i = 0; table[i].type != ENDTOKEN; i++) { + switch (table[i].type) { + case NOTOKEN: + fprintf(stderr, " <cr>\n"); + break; + case KEYWORD: + fprintf(stderr, " %s\n", table[i].keyword); + break; + case NAME: + fprintf(stderr, " <name>\n"); + break; + case PATH: + case DISK: + fprintf(stderr, " <path>\n"); + break; + case NIFS: + fprintf(stderr, " <count>\n"); + break; + case VMID: + fprintf(stderr, " <id>\n"); + break; + case SIZE: + fprintf(stderr, " <size>(K|M|G)\n"); + break; + case ENDTOKEN: + break; + } + } +} diff --git a/usr.sbin/vmmctl/parser.h b/usr.sbin/vmmctl/parser.h new file mode 100644 index 00000000000..66b86b7bc53 --- /dev/null +++ b/usr.sbin/vmmctl/parser.h @@ -0,0 +1,81 @@ +/* $OpenBSD: parser.h,v 1.1 2015/11/22 20:55:18 reyk Exp $ */ + +/* + * 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. + */ + +#include <sys/queue.h> +#include <imsg.h> + +#ifndef VMMCTL_PARSER_H +#define VMMCTL_PARSER_H + +#define VMM_CONF "/etc/vmm.conf" + +enum actions { + NONE, + CMD_CREATE, + CMD_ENABLE, + CMD_DISABLE, + CMD_START, + CMD_TERMINATE, + CMD_INFO, + CMD_LOAD +}; + +struct parse_result { + enum actions action; + uint32_t id; + char *name; + char *path; + size_t size; + size_t nifs; + size_t ndisks; + char **disks; + int disable; +}; + +struct imsgbuf *ibuf; + +/* main.c */ +int vmmaction(struct parse_result *); +int parse_ifs(struct parse_result *, char *, int); +int parse_size(struct parse_result *, char *, long long); +int parse_disk(struct parse_result *, char *); +int parse_vmid(struct parse_result *, char *, uint32_t); + +/* parser.c */ +struct parse_result * + parse(int, char *[]); + +/* parse.y */ +int parse_config(const char *); +int cmdline_symset(char *); + +/* vmmctl.c */ +int create_imagefile(char *, long); +int start_vm(const char *, int, int, int, char **, char *); +int start_vm_complete(struct imsg *, int *); +void enable_vmm(void); +int enable_vmm_complete(struct imsg *, int *); +void disable_vmm(void); +int disable_vmm_complete(struct imsg *, int *); +void terminate_vm(uint32_t); +int terminate_vm_complete(struct imsg *, int *); +void get_info_vm(uint32_t); +int add_info(struct imsg *, int *); +void print_vm_info(struct vm_info_result *, size_t); + +#endif /* VMMCTL_PARSER_H */ diff --git a/usr.sbin/vmmctl/vmm.conf.5 b/usr.sbin/vmmctl/vmm.conf.5 new file mode 100644 index 00000000000..cacefa96a0d --- /dev/null +++ b/usr.sbin/vmmctl/vmm.conf.5 @@ -0,0 +1,166 @@ +.\" $OpenBSD: vmm.conf.5,v 1.1 2015/11/22 20:55:18 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: November 22 2015 $ +.Dt VMM.CONF 5 +.Os +.Sh NAME +.Nm vmm.conf +.Nd virtual machine configuration +.Sh DESCRIPTION +.Nm +is the configuration file to configure the 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 VMM Configuration +Global settings for the +.Xr vmm 4 +subsystem. +.It Sy VM Configuration +Configuration for each individual virtual machine. +.El +.Pp +Withint the sections, +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 VMM CONFIGURATION +The options are as follows: +.Bl -tag -width Ds +.It Cm vmm disable +Disable the VMM subsystem. +Virtual machines running on the host should be terminated first. +.It Cm vmm enable +Enable the VMM subsystem. +The VMM subsystem must be enabled before VMs can be managed on the host. +.El +.Pp +Generally, the +.Nm +utility is run with +.Cm enable +option during system startup to enable the VMM subsystem on boot. +This can be automated via the +.Xr rc 8 +and +.Xr rc.conf 8 +facilities used during system startup. +.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 size +Memory size of the VM, in bytes. +Optionally, the units 'K', 'M', or 'G', for kilo-, mega-, and gigabytes +can be used. +.It Cm interfaces Ar count +Number of network interfaces to add to the VM. +.El +.Sh EXAMPLES +Enable the VMM subsystem: +.Bd -literal -offset indent +vmm enable +.Ed +.Pp +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 +.Pp +.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 Mike Larkin Aq Mt mlarkin@openbsd.org +and +.An Reyk Floeter Aq Mt reyk@openbsd.org . diff --git a/usr.sbin/vmmctl/vmmctl.8 b/usr.sbin/vmmctl/vmmctl.8 index 3ece162b75d..cc06350929e 100644 --- a/usr.sbin/vmmctl/vmmctl.8 +++ b/usr.sbin/vmmctl/vmmctl.8 @@ -19,24 +19,10 @@ .Nm vmmctl .Nd control VMM subsystem .Sh SYNOPSIS -.Nm -.Op Fl de .Nm -.Op Fl C -.Op Fl i Ar imagefile path -.Op Fl s Ar size in MB -.Nm -.Op Fl S -.Op Fl m Ar memory size -.Op Fl n Ar nr nics -.Op Fl b Ar diskfile -.Op Fl k Ar kernel -.Nm -.Op Fl T -.Op id -.Nm -.Op Fl I -.Op id +.Op Fl s Ar socket +.Ar command +.Op Ar arg ... .Sh DESCRIPTION The .Nm @@ -45,48 +31,60 @@ A VMM manages virtual machines (VMs) on a .Ar host . The VMM subsystem is responsible for creating, destroying, and executing VMs. - .Sh VMM OPERATIONS The options are as follows: -.Bl -tag -width Dsssigfile -.It Fl d -Disable the VMM subsystem. Virtual machines running on the host should be -terminated first. -.It Fl e +.Bl -tag -width Ds +.It Cm enable Enable the VMM subsystem. The VMM subsystem must be enabled before VMs can be managed on the host. +.It Cm disable +Disable the VMM subsystem. Virtual machines running on the host should be +terminated first. +.It Cm load Op Ar path +Load the +.Xr vmm.conf 5 +configuration file from +.Pa /etc/vmm.conf +or the specified +.Ar path. .El .Pp -Generally, the +Generally, the .Nm -utility is run with -e option during system startup to enable the VMM -subsystem on boot. This can be automated via the +utility is run with +.Cm enable +option during system startup to enable the VMM subsystem on boot. +This can be automated via the .Xr rc 8 and .Xr rc.conf 8 facilities used during system startup. .Sh VM OPERATIONS The options are as follows: -.Bl -tag -width Dsssignature -.It Fl C +.Bl -tag -width Ds +.It Cm create Ar path Cm size Ar number Creates a VM disk image file with the specified pathname and size in MB. -.It Fl S -Starts a VM defined by the specified parameters. -.It Fl m -Memory size (in MB) of the VM -.It Fl n -Number of network interfaces to add to the VM -.It Fl b +.It Cm start Ar name Op Ar parameters +Starts a VM defined by the specified name and parameters: +.Bl -tag -width "memory size" +.It Cm disk Ar path Disk image file (may be specified multiple times to add multiple disk images). -.It Fl k -Kernel to load when booting the VM -.It Fl T -Terminates (stops) a VM defined by the specified VM ID. -.It Fl I -Lists VMs running on the host, optionally listing just the selected VM ID. +.It Cm kernel Ar path +Kernel to load when booting the VM. +.It Cm memory Ar size +Memory size of the VM, in bytes. +Optionally, the units 'K', 'M', or 'G', for kilo-, mega-, and gigabytes +can be used. +.It Cm interfaces Ar count +Number of network interfaces to add to the VM. +.El +.It Cm terminate Ar id +Terminates (stops) a VM defined by the specified VM +.Ar id . +.It Cm show info Op Ar id +Lists VMs running on the host, optionally listing just the selected VM +.Ar id . .El - -.Bl -tag -width Dsssignature .Sh EXIT STATUS .Ex -std vmmctl .Nm @@ -94,26 +92,39 @@ may fail due to one of the following reasons: .Pp .Bl -bullet -compact .It -The VMM subsystem could not be enabled or disabled as requested +The VMM subsystem could not be enabled or disabled as requested. .It -A requested VM-based operation could not be completed +A requested VM-based operation could not be completed. .El .Sh EXAMPLES -Enable the VMM subsystem -.Dl $ vmmctl -e +Enable the VMM subsystem: +.Dl $ vmmctl enable +.Pp +Disable the VMM subsystem: +.Dl $ vmmctl disable .Pp -Disable the VMM subsystem -.Dl $ vmmctl -d +Create a new disk image ('disk.img') of 4 gigabyte size: +.Dl $ vmmctl create disk.img size 4G .Pp Create a new VM with 512MB memory, 1 network interface, one disk image -('disk.img') and boot from kernel '/bsd'. -.Dl $ vmmctl -S -m 512 -n 1 -b disk.img -k /bsd +('disk.img') and boot from kernel '/bsd': +.Dl $ vmmctl start "myvm" memory 512M interfaces 1 disk disk.img kernel /bsd .Pp Terminate VM number 1 -.Dl $ vmmctl -T1 +.Dl $ vmmctl terminate 1 .Pp +.Sh FILES +.Bl -tag -width "/etc/var/run/vmd.sockXX" -compact +.It Pa /etc/vmm.conf +Default configuration file. +.It Pa /var/run/vmd.sock +.Ux Ns -domain +socket used for communication with +.Xr vmd 8 . +.El .Sh SEE ALSO .Xr vmm 4 , +.Xr vmm.conf 5 , .Xr vmd 8 , .Xr rc.conf 8 .Sh HISTORY @@ -123,3 +134,5 @@ command first appeared in .Ox 5.9 . .Sh AUTHORS .An Mike Larkin Aq Mt mlarkin@openbsd.org +and +.An Reyk Floeter Aq Mt reyk@openbsd.org . diff --git a/usr.sbin/vmmctl/vmmctl.c b/usr.sbin/vmmctl/vmmctl.c index 967641a425f..0a61b6af853 100644 --- a/usr.sbin/vmmctl/vmmctl.c +++ b/usr.sbin/vmmctl/vmmctl.c @@ -38,367 +38,12 @@ #include <unistd.h> #include "vmd.h" +#include "parser.h" -__dead void usage(void); -int main(int, char **); -int create_imagefile(char *, long); -void enable_vmm(void); -int enable_vmm_complete(struct imsg *, int *); -void disable_vmm(void); -int disable_vmm_complete(struct imsg *, int *); -int start_vm(int, int, int, char **, char *); -int start_vm_complete(struct imsg *, int *); -void terminate_vm(uint32_t); -int terminate_vm_complete(struct imsg *, int *); -void get_info_vm(void); -int add_info(struct imsg *, int *); -void print_vm_info(struct vm_info_result *, size_t); - -#define CMD_ENABLE 0x1 -#define CMD_DISABLE 0x2 -#define CMD_CREATE 0x4 -#define CMD_START 0x8 -#define CMD_TERMINATE 0x10 -#define CMD_INFO 0x20 - -struct imsgbuf *ibuf; extern char *__progname; uint32_t info_id; /* - * usage - * - * print program usage. does not return - */ -__dead void -usage(void) -{ - fprintf(stderr, "usage: %s [-de]\n" - " %s [-C] [-i imagefile path] [-s size in MB]\n" - " %s [-S] [-m memory size][-n nr nics][-b diskfile]" - "[-k kernel]\n" - " %s [-T[id]]\n" - " %s [-I[id]]\n", - __progname, __progname, __progname, __progname, - __progname); - exit(1); -} - -int -main(int argc, char **argv) -{ - int ch, command, ret, fd, processed, n, memsize, nnics, ndisks, i; - const char *errstr; - char *imgfile, *sockname; - char *disks[VMM_MAX_DISKS_PER_VM]; - char *kernel; - long imgsize; - struct sockaddr_un sun; - struct imsg imsg; - uint32_t terminate_id; - - command = 0; - imgfile = NULL; - imgsize = 0; - info_id = 0; - terminate_id = 0; - memsize = 0; - nnics = 0; - ndisks = 0; - kernel = NULL; - - for (i = 0 ; i < VMM_MAX_DISKS_PER_VM; i++) { - disks[i] = malloc(VMM_MAX_PATH_DISK); - if (disks[i] == NULL) { - fprintf(stderr, "memory allocation error\n"); - exit(1); - } - - bzero(disks[i], VMM_MAX_PATH_DISK); - } - - - while ((ch = getopt(argc, argv, "CST::I::dei:s:m:n:b:k:")) != -1) { - switch(ch) { - case 'C': - /* Create imagefile command */ - if (command) - usage(); - - command = CMD_CREATE; - break; - case 'S': - /* Start command */ - if (command) - usage(); - - command = CMD_START; - break; - case 'T': - /* Terminate command */ - if (command) - usage(); - - command = CMD_TERMINATE; - if (optarg != NULL) { - terminate_id = strtonum(optarg, 1, UINT_MAX, - &errstr); - if (errstr) { - fprintf(stderr, - "%s: invalid VM ID (%s) specified: " - "%s\n", __progname, optarg, errstr); - usage(); - } - } else { - fprintf(stderr, "%s: missing VM ID parameter\n", - __progname); - usage(); - } - - if (terminate_id == 0) { - fprintf(stderr, "%s: invalid vm ID supplied\n", - __progname); - usage(); - } - break; - case 'I': - /* Info command */ - if (command) - usage(); - - command = CMD_INFO; - if (optarg != NULL) { - info_id = strtonum(optarg, 1, UINT_MAX, - &errstr); - if (errstr) { - fprintf(stderr, - "%s: invalid VM ID (%s) specified: " - "%s\n", __progname, optarg, errstr); - usage(); - } - } - break; - case 'd': - /* Disable VMM mode */ - if (command) - usage(); - - command = CMD_DISABLE; - break; - case 'e': - /* Enable VMM mode */ - if (command) - usage(); - - command = CMD_ENABLE; - break; - case 'i': - /* Imagefile name parameter */ - if (imgfile) - usage(); - - imgfile = strdup(optarg); - break; - case 's': - /* Imagefile size parameter */ - if (imgsize != 0) - usage(); - - imgsize = strtonum(optarg, 1, LONG_MAX, &errstr); - if (errstr) { - fprintf(stderr, - "%s: invalid image size (%s) specified: " - "%s\n", __progname, optarg, errstr); - usage(); - } - break; - case 'm': - /* VM memory parameter */ - if (memsize !=0) - usage(); - - memsize = strtonum(optarg, 1, VMM_MAX_VM_MEM_SIZE, - &errstr); - - if (errstr) { - fprintf(stderr," %s: invalid memory size (%s) " - "specified: %s\n", __progname, optarg, - errstr); - } - break; - case 'n': - /* VM num nics parameter */ - if (nnics !=0) - usage(); - - nnics = strtonum(optarg, 1, VMM_MAX_VM_MEM_SIZE, - &errstr); - - if (errstr) { - fprintf(stderr," %s: invalid number of nics " - " (%s) specified: %s\n", __progname, - optarg, errstr); - } - break; - case 'b': - /* VM disk parameter */ - if (ndisks < VMM_MAX_DISKS_PER_VM) { - strlcpy(disks[ndisks], optarg, - VMM_MAX_PATH_DISK); - ndisks++; - } else { - fprintf(stderr, "%s: maximum number of disks " - "reached, ignoring disk %s\n", __progname, - optarg); - } - break; - case 'k': - /* VM kernel parameter */ - if (kernel != NULL) - usage(); - - kernel = malloc(VMM_MAX_KERNEL_PATH); - if (kernel == NULL) { - fprintf(stderr, "memory allocation error\n"); - exit(1); - } - - strlcpy(kernel, optarg, VMM_MAX_KERNEL_PATH); - break; - default: - usage(); - } - } - - if (!command) - usage(); - - /* Set up comms via imsg with vmd, unless CMD_CREATE (imgfile) */ - if (command != CMD_CREATE) { - sockname = SOCKET_NAME; - if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) - err(1, "socket init failed"); - - bzero(&sun, sizeof(sun)); - sun.sun_family = AF_UNIX; - if (strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path)) >= - sizeof(sun.sun_path)) - errx(1, "socket name too long"); - - if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) - err(1, "connect failed to vmd control socket"); - - if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) - err(1, NULL); - imsg_init(ibuf, fd); - } - - if (command == CMD_DISABLE) - disable_vmm(); - - if (command == CMD_ENABLE) - enable_vmm(); - - if (command == CMD_CREATE) { - if (imgfile == NULL) { - fprintf(stderr, "%s: missing -i (imagefile path) " - "argument\n", __progname); - exit(1); - } - - if (imgsize <= 0) { - fprintf(stderr, "%s: missing/invalid -s (imgfile size) " - "argument\n", __progname); - exit(1); - } - - ret = create_imagefile(imgfile, imgsize); - if (ret) { - fprintf(stderr, "%s: create imagefile operation failed " - "(%s)\n", __progname, strerror(ret)); - exit(ret); - } else { - fprintf(stdout, "%s: imagefile created\n", __progname); - exit(0); - } - } - - if (command == CMD_START) { - if (memsize == 0) { - fprintf(stderr, "%s: missing -m (memory size) " - "argument\n", __progname); - exit(1); - } - - ret = start_vm(memsize, nnics, ndisks, disks, kernel); - if (ret) { - fprintf(stderr, "%s: start VM operation failed " - "(%s)\n", __progname, strerror(ret)); - exit(ret); - } - } - - if (command == CMD_TERMINATE) - terminate_vm(terminate_id); - - if (command == CMD_INFO) - get_info_vm(); - - /* Send request message */ - while (ibuf->w.queued) - if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) - err(1, "write error"); - - /* - * We expect vmd to send us one reply message, and that the reply - * message we receive will be of the proper _REPLY type. - * - * The exception to this is the -I (get info) option, where vmd - * may send us arbitrary number of _REPLY messages followed by - * and _END message to indicate no more messages are forthcoming. - */ - processed = 0; - while (!processed) { - if ((n = imsg_read(ibuf)) == -1) - err(1, "imsg_read error"); - if (n == 0) - errx(1, "pipe closed"); - - while (!processed) { - if ((n = imsg_get(ibuf, &imsg)) == -1) - err(1, "imsg_get error"); - if (n == 0) - break; - - switch (command) { - case CMD_DISABLE: - processed = disable_vmm_complete(&imsg, - &ret); - break; - case CMD_ENABLE: - processed = enable_vmm_complete(&imsg, - &ret); - break; - case CMD_START: - processed = start_vm_complete(&imsg, - &ret); - break; - case CMD_TERMINATE: - processed = terminate_vm_complete(&imsg, - &ret); - break; - case CMD_INFO: - processed = add_info(&imsg, &ret); - break; - } - imsg_free(&imsg); - } - } - - return (ret); -} - -/* * enable_vmm * * Request vmd to enable VMM mode on the machine. This will result in the @@ -518,6 +163,7 @@ disable_vmm_complete(struct imsg *imsg, int *ret) * Request vmd to start the VM defined by the supplied parameters * * Parameters: + * name: optional name of the VM * memsize: memory size (MB) of the VM to create * nnics: number of vionet network interfaces to create * ndisks: number of disk images @@ -529,7 +175,8 @@ disable_vmm_complete(struct imsg *imsg, int *ret) * ENOMEM if a memory allocation failure occurred. */ int -start_vm(int memsize, int nnics, int ndisks, char **disks, char *kernel) +start_vm(const char *name, int memsize, int nnics, int ndisks, char **disks, + char *kernel) { struct vm_create_params *vcp; int i; @@ -547,7 +194,9 @@ start_vm(int memsize, int nnics, int ndisks, char **disks, char *kernel) for (i = 0 ; i < ndisks; i++) strlcpy(vcp->vcp_disks[i], disks[i], VMM_MAX_PATH_DISK); - strlcpy(vcp->vcp_kernel, kernel, VMM_MAX_KERNEL_PATH); + if (name != NULL) + strlcpy(vcp->vcp_name, name, VMM_MAX_NAME_LEN); + strlcpy(vcp->vcp_kernel, kernel, VMM_MAX_KERNEL_PATH); vcp->vcp_nnics = nnics; imsg_compose(ibuf, IMSG_VMDOP_START_VM_REQUEST, 0, 0, -1, @@ -673,8 +322,9 @@ terminate_vm_complete(struct imsg *imsg, int *ret) * Request a list of running VMs from vmd */ void -get_info_vm(void) +get_info_vm(uint32_t id) { + info_id = id; imsg_compose(ibuf, IMSG_VMDOP_GET_INFO_VM_REQUEST, 0, 0, -1, NULL, 0); } |