1*0a9d031fSclaudio /* $OpenBSD: main.c,v 1.84 2024/11/21 13:39:34 claudio Exp $ */ 2eb8d1da1Sreyk 3eb8d1da1Sreyk /* 4eb8d1da1Sreyk * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org> 5eb8d1da1Sreyk * 6eb8d1da1Sreyk * Permission to use, copy, modify, and distribute this software for any 7eb8d1da1Sreyk * purpose with or without fee is hereby granted, provided that the above 8eb8d1da1Sreyk * copyright notice and this permission notice appear in all copies. 9eb8d1da1Sreyk * 10eb8d1da1Sreyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11eb8d1da1Sreyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12eb8d1da1Sreyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13eb8d1da1Sreyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14eb8d1da1Sreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15eb8d1da1Sreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16eb8d1da1Sreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17eb8d1da1Sreyk */ 18eb8d1da1Sreyk 19eb8d1da1Sreyk #include <sys/types.h> 20eb8d1da1Sreyk #include <sys/socket.h> 21eb8d1da1Sreyk #include <sys/queue.h> 22eb8d1da1Sreyk #include <sys/un.h> 23eb8d1da1Sreyk 24eb8d1da1Sreyk #include <err.h> 25eb8d1da1Sreyk #include <errno.h> 26eb8d1da1Sreyk #include <stdio.h> 27eb8d1da1Sreyk #include <stdlib.h> 28eb8d1da1Sreyk #include <stdint.h> 29eb8d1da1Sreyk #include <limits.h> 30eb8d1da1Sreyk #include <string.h> 314d2a1fb2Sreyk #include <syslog.h> 32eb8d1da1Sreyk #include <unistd.h> 33e2ceadc1Sreyk #include <fcntl.h> 34eb8d1da1Sreyk #include <util.h> 35eb8d1da1Sreyk #include <imsg.h> 36eb8d1da1Sreyk 37eb8d1da1Sreyk #include "vmd.h" 384d2a1fb2Sreyk #include "virtio.h" 398213a70aSreyk #include "proc.h" 40eb8d1da1Sreyk #include "vmctl.h" 41eb8d1da1Sreyk 42e2ceadc1Sreyk #define RAW_FMT "raw" 43e2ceadc1Sreyk #define QCOW2_FMT "qcow2" 44e2ceadc1Sreyk 45eb8d1da1Sreyk static const char *socket_name = SOCKET_NAME; 46eb8d1da1Sreyk static int ctl_sock = -1; 47fe102934Sreyk static int tty_autoconnect = 0; 48986b002cSmlarkin int stat_rflag; 49eb8d1da1Sreyk 50eb8d1da1Sreyk __dead void usage(void); 51eb8d1da1Sreyk __dead void ctl_usage(struct ctl_command *); 52eb8d1da1Sreyk 5395009bb4Sreyk int ctl_console(struct parse_result *, int, char *[]); 544d2a1fb2Sreyk int ctl_convert(const char *, const char *, int, size_t); 55eb8d1da1Sreyk int ctl_create(struct parse_result *, int, char *[]); 56eb8d1da1Sreyk int ctl_load(struct parse_result *, int, char *[]); 573afb90b0Sreyk int ctl_log(struct parse_result *, int, char *[]); 581f7fe034Sreyk int ctl_reload(struct parse_result *, int, char *[]); 591f7fe034Sreyk int ctl_reset(struct parse_result *, int, char *[]); 60eb8d1da1Sreyk int ctl_start(struct parse_result *, int, char *[]); 61eb8d1da1Sreyk int ctl_status(struct parse_result *, int, char *[]); 62eb8d1da1Sreyk int ctl_stop(struct parse_result *, int, char *[]); 63583f6618Sclaudio int ctl_waitfor(struct parse_result *, int, char *[]); 6452e954a3Spd int ctl_pause(struct parse_result *, int, char *[]); 6552e954a3Spd int ctl_unpause(struct parse_result *, int, char *[]); 66eed20f3bSpd int ctl_send(struct parse_result *, int, char *[]); 67eed20f3bSpd int ctl_receive(struct parse_result *, int, char *[]); 68eb8d1da1Sreyk 69eb8d1da1Sreyk struct ctl_command ctl_commands[] = { 707ad0becaSreyk { "console", CMD_CONSOLE, ctl_console, "id" }, 7173613953Sreyk { "create", CMD_CREATE, ctl_create, 72bd4b76f8Sreyk "[-b base | -i disk] [-s size] disk", 1 }, 73cb84e044Sjmc { "load", CMD_LOAD, ctl_load, "filename" }, 74cb84e044Sjmc { "log", CMD_LOG, ctl_log, "[brief | verbose]" }, 75cb84e044Sjmc { "pause", CMD_PAUSE, ctl_pause, "id" }, 76cb84e044Sjmc { "receive", CMD_RECEIVE, ctl_receive, "name" , 1}, 771f7fe034Sreyk { "reload", CMD_RELOAD, ctl_reload, "" }, 78cb84e044Sjmc { "reset", CMD_RESET, ctl_reset, "[all | switches | vms]" }, 79cb84e044Sjmc { "send", CMD_SEND, ctl_send, "id", 1}, 808c44bfcaSphessler { "show", CMD_STATUS, ctl_status, "[id]" }, 81bd4b76f8Sreyk { "start", CMD_START, ctl_start, 82cb84e044Sjmc "[-cL] [-B device] [-b path] [-d disk] [-i count]\n" 83b848b186Sdv "\t\t[-m size] [-n switch] [-r path] [-t name] id | name", 1}, 84986b002cSmlarkin { "status", CMD_STATUS, ctl_status, "[-r] [id]" }, 85bd4b76f8Sreyk { "stop", CMD_STOP, ctl_stop, "[-fw] [id | -a]" }, 8652e954a3Spd { "unpause", CMD_UNPAUSE, ctl_unpause, "id" }, 87cb84e044Sjmc { "wait", CMD_WAITFOR, ctl_waitfor, "id" }, 88eb8d1da1Sreyk { NULL } 89eb8d1da1Sreyk }; 90eb8d1da1Sreyk 91eb8d1da1Sreyk __dead void 92eb8d1da1Sreyk usage(void) 93eb8d1da1Sreyk { 94eb8d1da1Sreyk extern char *__progname; 95eb8d1da1Sreyk 962a9bad78Stb fprintf(stderr, "usage:\t%s [-v] command [arg ...]\n", __progname); 97409e257aSjmc 98eb8d1da1Sreyk exit(1); 99eb8d1da1Sreyk } 100eb8d1da1Sreyk 101eb8d1da1Sreyk __dead void 102eb8d1da1Sreyk ctl_usage(struct ctl_command *ctl) 103eb8d1da1Sreyk { 104eb8d1da1Sreyk extern char *__progname; 105eb8d1da1Sreyk 1064d2a1fb2Sreyk fprintf(stderr, "usage:\t%s [-v] %s %s\n", __progname, 107eb8d1da1Sreyk ctl->name, ctl->usage); 108eb8d1da1Sreyk exit(1); 109eb8d1da1Sreyk } 110eb8d1da1Sreyk 111eb8d1da1Sreyk int 112eb8d1da1Sreyk main(int argc, char *argv[]) 113eb8d1da1Sreyk { 1144d2a1fb2Sreyk int ch, verbose = 1; 115eb8d1da1Sreyk 1164d2a1fb2Sreyk while ((ch = getopt(argc, argv, "v")) != -1) { 117eb8d1da1Sreyk switch (ch) { 1184d2a1fb2Sreyk case 'v': 1194d2a1fb2Sreyk verbose = 2; 1204d2a1fb2Sreyk break; 121eb8d1da1Sreyk default: 122eb8d1da1Sreyk usage(); 123eb8d1da1Sreyk /* NOTREACHED */ 124eb8d1da1Sreyk } 125eb8d1da1Sreyk } 126eb8d1da1Sreyk argc -= optind; 127eb8d1da1Sreyk argv += optind; 128eb8d1da1Sreyk optreset = 1; 1298ff019a1Sreyk optind = 1; 130eb8d1da1Sreyk 131eb8d1da1Sreyk if (argc < 1) 132eb8d1da1Sreyk usage(); 133eb8d1da1Sreyk 1344d2a1fb2Sreyk log_init(verbose, LOG_DAEMON); 1354d2a1fb2Sreyk 136eb8d1da1Sreyk return (parse(argc, argv)); 137eb8d1da1Sreyk } 138eb8d1da1Sreyk 139eb8d1da1Sreyk int 140eb8d1da1Sreyk parse(int argc, char *argv[]) 141eb8d1da1Sreyk { 142eb8d1da1Sreyk struct ctl_command *ctl = NULL; 143eb8d1da1Sreyk struct parse_result res; 144eb8d1da1Sreyk int i; 145eb8d1da1Sreyk 146eb8d1da1Sreyk memset(&res, 0, sizeof(res)); 147eb8d1da1Sreyk res.nifs = -1; 148eb8d1da1Sreyk 149eb8d1da1Sreyk for (i = 0; ctl_commands[i].name != NULL; i++) { 150eb8d1da1Sreyk if (strncmp(ctl_commands[i].name, 151eb8d1da1Sreyk argv[0], strlen(argv[0])) == 0) { 152eb8d1da1Sreyk if (ctl != NULL) { 153eb8d1da1Sreyk fprintf(stderr, 154eb8d1da1Sreyk "ambiguous argument: %s\n", argv[0]); 155eb8d1da1Sreyk usage(); 156eb8d1da1Sreyk } 157eb8d1da1Sreyk ctl = &ctl_commands[i]; 158eb8d1da1Sreyk } 159eb8d1da1Sreyk } 160eb8d1da1Sreyk 161eb8d1da1Sreyk if (ctl == NULL) { 162eb8d1da1Sreyk fprintf(stderr, "unknown argument: %s\n", argv[0]); 163eb8d1da1Sreyk usage(); 164eb8d1da1Sreyk } 165eb8d1da1Sreyk 166eb8d1da1Sreyk res.action = ctl->action; 167eb8d1da1Sreyk res.ctl = ctl; 168eb8d1da1Sreyk 169eb8d1da1Sreyk if (!ctl->has_pledge) { 170eb8d1da1Sreyk /* pledge(2) default if command doesn't have its own pledge */ 1716955bc4dSccardenas if (pledge("stdio rpath exec unix getpw unveil", NULL) == -1) 172eb8d1da1Sreyk err(1, "pledge"); 173eb8d1da1Sreyk } 174eb8d1da1Sreyk if (ctl->main(&res, argc, argv) != 0) 175583f6618Sclaudio exit(1); 176eb8d1da1Sreyk 177eb8d1da1Sreyk if (ctl_sock != -1) { 178eb8d1da1Sreyk close(ibuf->fd); 179eb8d1da1Sreyk free(ibuf); 180eb8d1da1Sreyk } 181eb8d1da1Sreyk 182eb8d1da1Sreyk return (0); 183eb8d1da1Sreyk } 184eb8d1da1Sreyk 185eb8d1da1Sreyk int 186eb8d1da1Sreyk vmmaction(struct parse_result *res) 187eb8d1da1Sreyk { 188eb8d1da1Sreyk struct sockaddr_un sun; 189eb8d1da1Sreyk struct imsg imsg; 190eb8d1da1Sreyk int done = 0; 191eb8d1da1Sreyk int n; 192fe102934Sreyk int ret, action; 1933be9785fSreyk unsigned int flags; 194eb8d1da1Sreyk 195eb8d1da1Sreyk if (ctl_sock == -1) { 1962d650259Sbenno if (unveil(SOCKET_NAME, "w") == -1) 197bc5a8259Sbeck err(1, "unveil %s", SOCKET_NAME); 198fe102934Sreyk if ((ctl_sock = socket(AF_UNIX, 199fe102934Sreyk SOCK_STREAM|SOCK_CLOEXEC, 0)) == -1) 200eb8d1da1Sreyk err(1, "socket"); 201eb8d1da1Sreyk 2025eaf1adbSguenther memset(&sun, 0, sizeof(sun)); 203eb8d1da1Sreyk sun.sun_family = AF_UNIX; 204eb8d1da1Sreyk strlcpy(sun.sun_path, socket_name, sizeof(sun.sun_path)); 205eb8d1da1Sreyk 206eb8d1da1Sreyk if (connect(ctl_sock, 207eb8d1da1Sreyk (struct sockaddr *)&sun, sizeof(sun)) == -1) 208eb8d1da1Sreyk err(1, "connect: %s", socket_name); 209eb8d1da1Sreyk 210eb8d1da1Sreyk if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) 211eb8d1da1Sreyk err(1, "malloc"); 212*0a9d031fSclaudio if (imsgbuf_init(ibuf, ctl_sock) == -1) 213*0a9d031fSclaudio err(1, "imsgbuf_init"); 214*0a9d031fSclaudio imsgbuf_allow_fdpass(ibuf); 215eb8d1da1Sreyk } 216eb8d1da1Sreyk 217eb8d1da1Sreyk switch (res->action) { 218eb8d1da1Sreyk case CMD_START: 219eb1cd41dSreyk ret = vm_start(res->id, res->name, res->size, res->nifs, 220f224f92aSccardenas res->nets, res->ndisks, res->disks, res->disktypes, 221917458a3Sclaudio res->path, res->isopath, res->instance, res->bootdevice); 222eb8d1da1Sreyk if (ret) { 223eb8d1da1Sreyk errno = ret; 224eb8d1da1Sreyk err(1, "start VM operation failed"); 225eb8d1da1Sreyk } 226eb8d1da1Sreyk break; 227eb8d1da1Sreyk case CMD_STOP: 2283be9785fSreyk terminate_vm(res->id, res->name, res->flags); 229eb8d1da1Sreyk break; 230eb8d1da1Sreyk case CMD_STATUS: 23195009bb4Sreyk case CMD_CONSOLE: 232e0b12962Sreyk case CMD_STOPALL: 233e0b12962Sreyk get_info_vm(res->id, res->name, res->action, res->flags); 234eb8d1da1Sreyk break; 235eb8d1da1Sreyk case CMD_LOAD: 236008065a5Sreyk imsg_compose(ibuf, IMSG_VMDOP_LOAD, 0, 0, -1, 2371f7fe034Sreyk res->path, strlen(res->path) + 1); 2381f7fe034Sreyk break; 2393afb90b0Sreyk case CMD_LOG: 2403afb90b0Sreyk imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1, 2413afb90b0Sreyk &res->verbose, sizeof(res->verbose)); 2423afb90b0Sreyk break; 2431f7fe034Sreyk case CMD_RELOAD: 2441f7fe034Sreyk imsg_compose(ibuf, IMSG_VMDOP_RELOAD, 0, 0, -1, NULL, 0); 2451f7fe034Sreyk break; 2461f7fe034Sreyk case CMD_RESET: 2471f7fe034Sreyk imsg_compose(ibuf, IMSG_CTL_RESET, 0, 0, -1, 2481f7fe034Sreyk &res->mode, sizeof(res->mode)); 249008065a5Sreyk break; 250583f6618Sclaudio case CMD_WAITFOR: 251583f6618Sclaudio waitfor_vm(res->id, res->name); 252583f6618Sclaudio break; 25352e954a3Spd case CMD_PAUSE: 25452e954a3Spd pause_vm(res->id, res->name); 25552e954a3Spd break; 25652e954a3Spd case CMD_UNPAUSE: 25752e954a3Spd unpause_vm(res->id, res->name); 25852e954a3Spd break; 259eed20f3bSpd case CMD_SEND: 260eed20f3bSpd send_vm(res->id, res->name); 261eed20f3bSpd done = 1; 2620410d4edSpd ret = 0; 263eed20f3bSpd break; 264eed20f3bSpd case CMD_RECEIVE: 265eed20f3bSpd vm_receive(res->id, res->name); 266eed20f3bSpd break; 267008065a5Sreyk case CMD_CREATE: 268eb8d1da1Sreyk case NONE: 2694d2a1fb2Sreyk /* The action is not expected here */ 2704d2a1fb2Sreyk errx(1, "invalid action %u", res->action); 271eb8d1da1Sreyk break; 272eb8d1da1Sreyk } 273eb8d1da1Sreyk 274fe102934Sreyk action = res->action; 2753be9785fSreyk flags = res->flags; 276fe102934Sreyk parse_free(res); 277fe102934Sreyk 278dd7efffeSclaudio if (imsgbuf_flush(ibuf) == -1) 279eb8d1da1Sreyk err(1, "write error"); 280eb8d1da1Sreyk 281eb8d1da1Sreyk while (!done) { 282668e5ba9Sclaudio if ((n = imsgbuf_read(ibuf)) == -1) 283ef2e27a1Sclaudio err(1, "read error"); 284eb8d1da1Sreyk if (n == 0) 285eb8d1da1Sreyk errx(1, "pipe closed"); 286eb8d1da1Sreyk 287eb8d1da1Sreyk while (!done) { 288eb8d1da1Sreyk if ((n = imsg_get(ibuf, &imsg)) == -1) 289eb8d1da1Sreyk errx(1, "imsg_get error"); 290eb8d1da1Sreyk if (n == 0) 291eb8d1da1Sreyk break; 292eb8d1da1Sreyk 293eb8d1da1Sreyk if (imsg.hdr.type == IMSG_CTL_FAIL) { 294d1866b5cSreyk if (IMSG_DATA_SIZE(&imsg) == sizeof(ret)) 295d1866b5cSreyk memcpy(&ret, imsg.data, sizeof(ret)); 296d1866b5cSreyk else 297d1866b5cSreyk ret = 0; 298d1866b5cSreyk if (ret != 0) { 299eb8d1da1Sreyk errno = ret; 300b55cbc1dSreyk err(1, "command failed"); 301b55cbc1dSreyk } else 302b55cbc1dSreyk errx(1, "command failed"); 303eb8d1da1Sreyk } 304eb8d1da1Sreyk 305eb8d1da1Sreyk ret = 0; 306fe102934Sreyk switch (action) { 307eb8d1da1Sreyk case CMD_START: 30876031b44Sreyk done = vm_start_complete(&imsg, &ret, 309fe102934Sreyk tty_autoconnect); 310eb8d1da1Sreyk break; 311583f6618Sclaudio case CMD_WAITFOR: 312583f6618Sclaudio flags = VMOP_WAIT; 313583f6618Sclaudio /* FALLTHROUGH */ 314eb8d1da1Sreyk case CMD_STOP: 3153be9785fSreyk done = terminate_vm_complete(&imsg, &ret, 3163be9785fSreyk flags); 317eb8d1da1Sreyk break; 31895009bb4Sreyk case CMD_CONSOLE: 319eb8d1da1Sreyk case CMD_STATUS: 320e0b12962Sreyk case CMD_STOPALL: 321eb8d1da1Sreyk done = add_info(&imsg, &ret); 322eb8d1da1Sreyk break; 32352e954a3Spd case CMD_PAUSE: 32452e954a3Spd done = pause_vm_complete(&imsg, &ret); 32552e954a3Spd break; 326eed20f3bSpd case CMD_RECEIVE: 327eed20f3bSpd done = vm_start_complete(&imsg, &ret, 0); 328eed20f3bSpd break; 32952e954a3Spd case CMD_UNPAUSE: 33052e954a3Spd done = unpause_vm_complete(&imsg, &ret); 33152e954a3Spd break; 332eb8d1da1Sreyk default: 333eb8d1da1Sreyk done = 1; 334eb8d1da1Sreyk break; 335eb8d1da1Sreyk } 336eb8d1da1Sreyk 337eb8d1da1Sreyk imsg_free(&imsg); 338eb8d1da1Sreyk } 339eb8d1da1Sreyk } 340eb8d1da1Sreyk 341583f6618Sclaudio if (ret) 342583f6618Sclaudio return (1); 343583f6618Sclaudio else 344eb8d1da1Sreyk return (0); 345eb8d1da1Sreyk } 346eb8d1da1Sreyk 347eb8d1da1Sreyk void 348eb8d1da1Sreyk parse_free(struct parse_result *res) 349eb8d1da1Sreyk { 350eb8d1da1Sreyk size_t i; 351eb8d1da1Sreyk 352eb8d1da1Sreyk free(res->name); 353eb8d1da1Sreyk free(res->path); 35495ab188fSccardenas free(res->isopath); 3556429e633Sreyk free(res->instance); 356eb8d1da1Sreyk for (i = 0; i < res->ndisks; i++) 357eb8d1da1Sreyk free(res->disks[i]); 358eb8d1da1Sreyk free(res->disks); 359f224f92aSccardenas free(res->disktypes); 360eb8d1da1Sreyk memset(res, 0, sizeof(*res)); 361eb8d1da1Sreyk } 362eb8d1da1Sreyk 363eb8d1da1Sreyk int 364eb8d1da1Sreyk parse_ifs(struct parse_result *res, char *word, int val) 365eb8d1da1Sreyk { 366eb8d1da1Sreyk const char *error; 367eb8d1da1Sreyk 368eb8d1da1Sreyk if (word != NULL) { 3690f0fb1d3Skn val = strtonum(word, 1, INT_MAX, &error); 370eb8d1da1Sreyk if (error != NULL) { 3710f0fb1d3Skn warnx("count is %s: %s", error, word); 372eb8d1da1Sreyk return (-1); 373eb8d1da1Sreyk } 374eb8d1da1Sreyk } 375eb8d1da1Sreyk res->nifs = val; 376b657d36cSreyk 377b657d36cSreyk return (0); 378b657d36cSreyk } 379b657d36cSreyk 380b657d36cSreyk int 381b657d36cSreyk parse_network(struct parse_result *res, char *word) 382b657d36cSreyk { 383b657d36cSreyk char **nets; 384b657d36cSreyk char *s; 385b657d36cSreyk 386b657d36cSreyk if ((nets = reallocarray(res->nets, res->nnets + 1, 387b657d36cSreyk sizeof(char *))) == NULL) { 388b657d36cSreyk warn("reallocarray"); 389b657d36cSreyk return (-1); 390b657d36cSreyk } 391b657d36cSreyk if ((s = strdup(word)) == NULL) { 392b657d36cSreyk warn("strdup"); 393b657d36cSreyk return (-1); 394b657d36cSreyk } 395b657d36cSreyk nets[res->nnets] = s; 396b657d36cSreyk res->nets = nets; 397b657d36cSreyk res->nnets++; 398b657d36cSreyk 399eb8d1da1Sreyk return (0); 400eb8d1da1Sreyk } 401eb8d1da1Sreyk 402ead1b146Sdv void 403ead1b146Sdv parse_size(struct parse_result *res, char *word, const char *type) 404eb8d1da1Sreyk { 405e545c54cSdv char result[FMT_SCALED_STRSIZE]; 4064a2cfa82Skn long long val = 0; 4074a2cfa82Skn 408eb8d1da1Sreyk if (word != NULL) { 409ead1b146Sdv if (scan_scaled(word, &val) != 0) 410ead1b146Sdv err(1, "invalid %s size: %s", type, word); 411eb8d1da1Sreyk } 412eb8d1da1Sreyk 413ead1b146Sdv if (val < (1024 * 1024)) 414ead1b146Sdv errx(1, "%s size must be at least 1MB", type); 415eb8d1da1Sreyk 416ead1b146Sdv if (strcmp("memory", type) == 0 && val > VMM_MAX_VM_MEM_SIZE) { 417e545c54cSdv if (fmt_scaled(VMM_MAX_VM_MEM_SIZE, result) == 0) 418ead1b146Sdv errx(1, "memory size too large (limit is %s)", result); 419e545c54cSdv else 420ead1b146Sdv errx(1, "memory size too large"); 421e545c54cSdv } 422e545c54cSdv 423e545c54cSdv /* Round down to the megabyte. */ 424e545c54cSdv res->size = (val / (1024 * 1024)) * (1024 * 1024); 425e545c54cSdv 426e545c54cSdv if (res->size != (size_t)val) { 427e545c54cSdv if (fmt_scaled(res->size, result) == 0) 428ead1b146Sdv warnx("%s size rounded to %s", type, result); 429e545c54cSdv else 430ead1b146Sdv warnx("%s size rounded to %zuB", type, res->size); 431e545c54cSdv } 432eb8d1da1Sreyk } 433eb8d1da1Sreyk 434eb8d1da1Sreyk int 435e2ceadc1Sreyk parse_disktype(const char *s, const char **ret) 436f224f92aSccardenas { 437e2ceadc1Sreyk char buf[BUFSIZ]; 438e2ceadc1Sreyk const char *ext; 439e2ceadc1Sreyk int fd; 440e2ceadc1Sreyk ssize_t len; 441e2ceadc1Sreyk 442f224f92aSccardenas *ret = s; 443e2ceadc1Sreyk 444e2ceadc1Sreyk /* Try to parse the explicit format (qcow2:disk.qc2) */ 445e2ceadc1Sreyk if (strstr(s, RAW_FMT) == s && *(s + strlen(RAW_FMT)) == ':') { 446e2ceadc1Sreyk *ret = s + strlen(RAW_FMT) + 1; 447e2ceadc1Sreyk return (VMDF_RAW); 448f224f92aSccardenas } 449e2ceadc1Sreyk if (strstr(s, QCOW2_FMT) == s && *(s + strlen(QCOW2_FMT)) == ':') { 450e2ceadc1Sreyk *ret = s + strlen(QCOW2_FMT) + 1; 451e2ceadc1Sreyk return (VMDF_QCOW2); 452f224f92aSccardenas } 453e2ceadc1Sreyk 454e2ceadc1Sreyk /* Or try to derive the format from the file signature */ 455e2ceadc1Sreyk if ((fd = open(s, O_RDONLY)) != -1) { 456e2ceadc1Sreyk len = read(fd, buf, sizeof(buf)); 457e2ceadc1Sreyk close(fd); 458e2ceadc1Sreyk 459e2ceadc1Sreyk if (len >= (ssize_t)strlen(VM_MAGIC_QCOW) && 460e2ceadc1Sreyk strncmp(buf, VM_MAGIC_QCOW, 461e2ceadc1Sreyk strlen(VM_MAGIC_QCOW)) == 0) { 462e2ceadc1Sreyk /* Return qcow2, the version will be checked later */ 463e2ceadc1Sreyk return (VMDF_QCOW2); 464e2ceadc1Sreyk } 465e2ceadc1Sreyk } 466e2ceadc1Sreyk 467e2ceadc1Sreyk /* 468e2ceadc1Sreyk * Use the extension as a last option. This is needed for 469e2ceadc1Sreyk * 'vmctl create' as the file, and the signature, doesn't 470e2ceadc1Sreyk * exist yet. 471e2ceadc1Sreyk */ 472e2ceadc1Sreyk if ((ext = strrchr(s, '.')) != NULL && *(++ext) != '\0') { 473e2ceadc1Sreyk if (strcasecmp(ext, RAW_FMT) == 0) 474e2ceadc1Sreyk return (VMDF_RAW); 475e2ceadc1Sreyk else if (strcasecmp(ext, QCOW2_FMT) == 0) 476e2ceadc1Sreyk return (VMDF_QCOW2); 477e2ceadc1Sreyk } 478e2ceadc1Sreyk 479e2ceadc1Sreyk /* Fallback to raw */ 480e2ceadc1Sreyk return (VMDF_RAW); 481f224f92aSccardenas } 482f224f92aSccardenas 483f224f92aSccardenas int 484f224f92aSccardenas parse_disk(struct parse_result *res, char *word, int type) 485eb8d1da1Sreyk { 486eb8d1da1Sreyk char **disks; 487f224f92aSccardenas int *disktypes; 488eb8d1da1Sreyk char *s; 489eb8d1da1Sreyk 490eb8d1da1Sreyk if ((disks = reallocarray(res->disks, res->ndisks + 1, 491eb8d1da1Sreyk sizeof(char *))) == NULL) { 492eb8d1da1Sreyk warn("reallocarray"); 493eb8d1da1Sreyk return (-1); 494eb8d1da1Sreyk } 495f224f92aSccardenas if ((disktypes = reallocarray(res->disktypes, res->ndisks + 1, 496f224f92aSccardenas sizeof(int))) == NULL) { 497f224f92aSccardenas warn("reallocarray"); 498f224f92aSccardenas return -1; 499f224f92aSccardenas } 500eb8d1da1Sreyk if ((s = strdup(word)) == NULL) { 501eb8d1da1Sreyk warn("strdup"); 502eb8d1da1Sreyk return (-1); 503eb8d1da1Sreyk } 504eb8d1da1Sreyk disks[res->ndisks] = s; 505f224f92aSccardenas disktypes[res->ndisks] = type; 506eb8d1da1Sreyk res->disks = disks; 507f224f92aSccardenas res->disktypes = disktypes; 508eb8d1da1Sreyk res->ndisks++; 509eb8d1da1Sreyk 510eb8d1da1Sreyk return (0); 511eb8d1da1Sreyk } 512eb8d1da1Sreyk 513eb8d1da1Sreyk int 5144100d478Sjasper parse_vmid(struct parse_result *res, char *word, int needname) 515eb8d1da1Sreyk { 516eb8d1da1Sreyk const char *error; 51700b52b3dSreyk uint32_t id; 518eb8d1da1Sreyk 51900b52b3dSreyk if (word == NULL) { 52000b52b3dSreyk warnx("missing vmid argument"); 52100b52b3dSreyk return (-1); 52200b52b3dSreyk } 523e0b12962Sreyk if (*word == '-') { 524e0b12962Sreyk /* don't print a warning to allow command line options */ 525e0b12962Sreyk return (-1); 526e0b12962Sreyk } 527eb8d1da1Sreyk id = strtonum(word, 0, UINT32_MAX, &error); 52859fb7762Sreyk if (error == NULL) { 5294100d478Sjasper if (needname) { 530eed20f3bSpd warnx("invalid vm name"); 531eed20f3bSpd return (-1); 532eed20f3bSpd } else { 5334100d478Sjasper res->id = id; 5344100d478Sjasper res->name = NULL; 5354100d478Sjasper } 5364100d478Sjasper } else { 537eed20f3bSpd if (strlen(word) >= VMM_MAX_NAME_LEN) { 538eed20f3bSpd warnx("name too long"); 539eed20f3bSpd return (-1); 540eed20f3bSpd } 541eed20f3bSpd res->id = 0; 542eed20f3bSpd if ((res->name = strdup(word)) == NULL) 543eed20f3bSpd errx(1, "strdup"); 544eed20f3bSpd } 545eed20f3bSpd 546eed20f3bSpd return (0); 547eed20f3bSpd } 548eed20f3bSpd 549eed20f3bSpd int 5506429e633Sreyk parse_instance(struct parse_result *res, char *word) 5516429e633Sreyk { 5526429e633Sreyk if (strlen(word) >= VMM_MAX_NAME_LEN) { 5536429e633Sreyk warnx("instance vm name too long"); 5546429e633Sreyk return (-1); 5556429e633Sreyk } 5566429e633Sreyk res->id = 0; 5576429e633Sreyk if ((res->instance = strdup(word)) == NULL) 5586429e633Sreyk errx(1, "strdup"); 5596429e633Sreyk 5606429e633Sreyk return (0); 5616429e633Sreyk } 5626429e633Sreyk 5636429e633Sreyk int 564eb8d1da1Sreyk ctl_create(struct parse_result *res, int argc, char *argv[]) 565eb8d1da1Sreyk { 566e2ceadc1Sreyk int ch, ret, type; 567bd4b76f8Sreyk const char *disk, *format, *base = NULL, *input = NULL; 568eb8d1da1Sreyk 5694d2a1fb2Sreyk while ((ch = getopt(argc, argv, "b:i:s:")) != -1) { 570eb8d1da1Sreyk switch (ch) { 57173613953Sreyk case 'b': 57273613953Sreyk base = optarg; 57373613953Sreyk break; 5744d2a1fb2Sreyk case 'i': 5754d2a1fb2Sreyk input = optarg; 5764d2a1fb2Sreyk break; 5774d2a1fb2Sreyk case 's': 578ead1b146Sdv parse_size(res, optarg, "disk"); 5794d2a1fb2Sreyk break; 580eb8d1da1Sreyk default: 581eb8d1da1Sreyk ctl_usage(res->ctl); 582eb8d1da1Sreyk /* NOTREACHED */ 583eb8d1da1Sreyk } 584eb8d1da1Sreyk } 585c1e58e1aSkn argc -= optind; 586c1e58e1aSkn argv += optind; 5874d2a1fb2Sreyk 5882b3f5770Skn if (argc != 1) 589b0b31ffcSkn ctl_usage(res->ctl); 590b0b31ffcSkn 591bd4b76f8Sreyk type = parse_disktype(argv[0], &disk); 592bd4b76f8Sreyk 5933538c82eSdv if (pledge("stdio rpath wpath cpath", NULL) == -1) 594bd4b76f8Sreyk err(1, "pledge"); 595bd4b76f8Sreyk 5964d2a1fb2Sreyk if (input) { 5974d2a1fb2Sreyk if (base && input) 5984d2a1fb2Sreyk errx(1, "conflicting -b and -i arguments"); 5994d2a1fb2Sreyk return ctl_convert(input, disk, type, res->size); 6004d2a1fb2Sreyk } 6014d2a1fb2Sreyk 60273613953Sreyk if (base && type != VMDF_QCOW2) 60373613953Sreyk errx(1, "base images require qcow2 disk format"); 60473613953Sreyk if (res->size == 0 && !base) { 60573613953Sreyk fprintf(stderr, "could not create %s: missing size argument\n", 60673613953Sreyk disk); 607eb8d1da1Sreyk ctl_usage(res->ctl); 608eb8d1da1Sreyk } 609e2ceadc1Sreyk 6104d2a1fb2Sreyk if ((ret = create_imagefile(type, disk, base, res->size, &format)) != 0) { 611eb8d1da1Sreyk errno = ret; 612eb8d1da1Sreyk err(1, "create imagefile operation failed"); 613eb8d1da1Sreyk } else 614e2ceadc1Sreyk warnx("%s imagefile created", format); 615e2ceadc1Sreyk 616eb8d1da1Sreyk return (0); 617eb8d1da1Sreyk } 618eb8d1da1Sreyk 619eb8d1da1Sreyk int 6204d2a1fb2Sreyk ctl_convert(const char *srcfile, const char *dstfile, int dsttype, size_t dstsize) 6214d2a1fb2Sreyk { 6224d2a1fb2Sreyk struct { 6234d2a1fb2Sreyk int fd; 6244d2a1fb2Sreyk int type; 6254d2a1fb2Sreyk struct virtio_backing file; 6264d2a1fb2Sreyk const char *disk; 6274d2a1fb2Sreyk off_t size; 6284d2a1fb2Sreyk } src, dst; 6294d2a1fb2Sreyk int ret; 6304d2a1fb2Sreyk const char *format, *errstr = NULL; 6314d2a1fb2Sreyk uint8_t *buf = NULL, *zerobuf = NULL; 6324d2a1fb2Sreyk size_t buflen; 6334d2a1fb2Sreyk ssize_t len, rlen; 6344d2a1fb2Sreyk off_t off; 6354d2a1fb2Sreyk 6364d2a1fb2Sreyk memset(&src, 0, sizeof(src)); 6374d2a1fb2Sreyk memset(&dst, 0, sizeof(dst)); 6384d2a1fb2Sreyk 6394d2a1fb2Sreyk src.type = parse_disktype(srcfile, &src.disk); 6404d2a1fb2Sreyk dst.type = dsttype; 6414d2a1fb2Sreyk dst.disk = dstfile; 6424d2a1fb2Sreyk 6434d2a1fb2Sreyk if ((src.fd = open_imagefile(src.type, src.disk, O_RDONLY, 6444d2a1fb2Sreyk &src.file, &src.size)) == -1) { 6454d2a1fb2Sreyk errstr = "failed to open source image file"; 6464d2a1fb2Sreyk goto done; 6474d2a1fb2Sreyk } 6484d2a1fb2Sreyk 6494d2a1fb2Sreyk if (dstsize == 0) 6504d2a1fb2Sreyk dstsize = src.size; 6514d2a1fb2Sreyk if (dstsize < (size_t)src.size) { 6524d2a1fb2Sreyk errstr = "size cannot be smaller than input disk size"; 6534d2a1fb2Sreyk goto done; 6544d2a1fb2Sreyk } 6554d2a1fb2Sreyk 6564d2a1fb2Sreyk /* align to megabytes */ 65762df93eeSreyk dst.size = ALIGNSZ(dstsize, 1048576); 6584d2a1fb2Sreyk 6597ac3f975Syasuoka if ((ret = create_imagefile(dst.type, dst.disk, NULL, dst.size, 6607ac3f975Syasuoka &format)) != 0) { 6614d2a1fb2Sreyk errstr = "failed to create destination image file"; 6624d2a1fb2Sreyk goto done; 6634d2a1fb2Sreyk } 6644d2a1fb2Sreyk 6654d2a1fb2Sreyk if ((dst.fd = open_imagefile(dst.type, dst.disk, O_RDWR, 6664d2a1fb2Sreyk &dst.file, &dst.size)) == -1) { 6674d2a1fb2Sreyk errstr = "failed to open destination image file"; 6684d2a1fb2Sreyk goto done; 6694d2a1fb2Sreyk } 6704d2a1fb2Sreyk 6714d2a1fb2Sreyk if (pledge("stdio", NULL) == -1) 6724d2a1fb2Sreyk err(1, "pledge"); 6734d2a1fb2Sreyk 6744d2a1fb2Sreyk /* 6754d2a1fb2Sreyk * Use 64k buffers by default. This could also be adjusted to 6764d2a1fb2Sreyk * the backend cluster size. 6774d2a1fb2Sreyk */ 6784d2a1fb2Sreyk buflen = 1 << 16; 6794d2a1fb2Sreyk if ((buf = calloc(1, buflen)) == NULL || 6804d2a1fb2Sreyk (zerobuf = calloc(1, buflen)) == NULL) { 6814d2a1fb2Sreyk errstr = "failed to allocated buffers"; 6824d2a1fb2Sreyk goto done; 6834d2a1fb2Sreyk } 6844d2a1fb2Sreyk 6854d2a1fb2Sreyk for (off = 0; off < dst.size; off += len) { 6864d2a1fb2Sreyk /* Read input from the source image */ 6874d2a1fb2Sreyk if (off < src.size) { 6884d2a1fb2Sreyk len = MIN((off_t)buflen, src.size - off); 6894d2a1fb2Sreyk if ((rlen = src.file.pread(src.file.p, 6904d2a1fb2Sreyk buf, (size_t)len, off)) != len) { 6914d2a1fb2Sreyk errno = EIO; 6924d2a1fb2Sreyk errstr = "failed to read from source"; 6934d2a1fb2Sreyk goto done; 6944d2a1fb2Sreyk } 6954d2a1fb2Sreyk } else 6964d2a1fb2Sreyk len = 0; 6974d2a1fb2Sreyk 6984d2a1fb2Sreyk /* and pad the remaining bytes */ 6994d2a1fb2Sreyk if (len < (ssize_t)buflen) { 7004d2a1fb2Sreyk log_debug("%s: padding %zd zero bytes at offset %lld", 7014d2a1fb2Sreyk format, buflen - len, off + len); 7024d2a1fb2Sreyk memset(buf + len, 0, buflen - len); 7034d2a1fb2Sreyk len = buflen; 7044d2a1fb2Sreyk } 7054d2a1fb2Sreyk 7064d2a1fb2Sreyk /* 7074d2a1fb2Sreyk * No need to copy empty buffers. This allows the backend, 7084d2a1fb2Sreyk * sparse files or QCOW2 images, to save space in the 7094d2a1fb2Sreyk * destination file. 7104d2a1fb2Sreyk */ 7114d2a1fb2Sreyk if (memcmp(buf, zerobuf, buflen) == 0) 7124d2a1fb2Sreyk continue; 7134d2a1fb2Sreyk 7144d2a1fb2Sreyk log_debug("%s: writing %zd of %lld bytes at offset %lld", 7154d2a1fb2Sreyk format, len, dst.size, off); 7164d2a1fb2Sreyk 7174d2a1fb2Sreyk if ((rlen = dst.file.pwrite(dst.file.p, 7184d2a1fb2Sreyk buf, (size_t)len, off)) != len) { 7194d2a1fb2Sreyk errno = EIO; 7204d2a1fb2Sreyk errstr = "failed to write to destination"; 7214d2a1fb2Sreyk goto done; 7224d2a1fb2Sreyk } 7234d2a1fb2Sreyk } 7244d2a1fb2Sreyk 7254d2a1fb2Sreyk if (dstsize < (size_t)dst.size) 7264d2a1fb2Sreyk warnx("destination size rounded to %lld megabytes", 7274d2a1fb2Sreyk dst.size / 1048576); 7284d2a1fb2Sreyk 7294d2a1fb2Sreyk done: 7304d2a1fb2Sreyk free(buf); 7314d2a1fb2Sreyk free(zerobuf); 7324d2a1fb2Sreyk if (src.file.p != NULL) 7334d2a1fb2Sreyk src.file.close(src.file.p, 0); 7344d2a1fb2Sreyk if (dst.file.p != NULL) 7354d2a1fb2Sreyk dst.file.close(dst.file.p, 0); 7364d2a1fb2Sreyk if (errstr != NULL) 7374d2a1fb2Sreyk errx(1, "%s", errstr); 7384d2a1fb2Sreyk else 7394d2a1fb2Sreyk warnx("%s imagefile created", format); 7404d2a1fb2Sreyk 7414d2a1fb2Sreyk return (0); 7424d2a1fb2Sreyk } 7434d2a1fb2Sreyk 7444d2a1fb2Sreyk int 745eb8d1da1Sreyk ctl_status(struct parse_result *res, int argc, char *argv[]) 746eb8d1da1Sreyk { 74722b23f34Smlarkin int ch; 748986b002cSmlarkin 749986b002cSmlarkin while ((ch = getopt(argc, argv, "r")) != -1) { 750986b002cSmlarkin switch (ch) { 751986b002cSmlarkin case 'r': 752986b002cSmlarkin stat_rflag = 1; 753986b002cSmlarkin break; 754986b002cSmlarkin default: 755986b002cSmlarkin ctl_usage(res->ctl); 756986b002cSmlarkin /* NOTREACHED */ 757986b002cSmlarkin } 758986b002cSmlarkin } 759986b002cSmlarkin argc -= optind; 760986b002cSmlarkin argv += optind; 761986b002cSmlarkin 762986b002cSmlarkin if (argc == 1) { 763986b002cSmlarkin if (parse_vmid(res, argv[0], 0) == -1) 764986b002cSmlarkin errx(1, "invalid id: %s", argv[0]); 765986b002cSmlarkin } else if (argc > 1) 766eb8d1da1Sreyk ctl_usage(res->ctl); 767eb8d1da1Sreyk 768eb8d1da1Sreyk return (vmmaction(res)); 769eb8d1da1Sreyk } 770eb8d1da1Sreyk 771eb8d1da1Sreyk int 772eb8d1da1Sreyk ctl_load(struct parse_result *res, int argc, char *argv[]) 773eb8d1da1Sreyk { 7741f7fe034Sreyk if (argc != 2) 775eb8d1da1Sreyk ctl_usage(res->ctl); 776eb8d1da1Sreyk 7771f7fe034Sreyk if ((res->path = strdup(argv[1])) == NULL) 778008065a5Sreyk err(1, "strdup"); 779008065a5Sreyk 780008065a5Sreyk return (vmmaction(res)); 781eb8d1da1Sreyk } 782eb8d1da1Sreyk 783eb8d1da1Sreyk int 7843afb90b0Sreyk ctl_log(struct parse_result *res, int argc, char *argv[]) 7853afb90b0Sreyk { 7863afb90b0Sreyk if (argc != 2) 7873afb90b0Sreyk ctl_usage(res->ctl); 7883afb90b0Sreyk 7893afb90b0Sreyk if (strncasecmp("brief", argv[1], strlen(argv[1])) == 0) 7903afb90b0Sreyk res->verbose = 0; 7913afb90b0Sreyk else if (strncasecmp("verbose", argv[1], strlen(argv[1])) == 0) 7923afb90b0Sreyk res->verbose = 2; 7933afb90b0Sreyk else 7943afb90b0Sreyk ctl_usage(res->ctl); 7953afb90b0Sreyk 7963afb90b0Sreyk return (vmmaction(res)); 7973afb90b0Sreyk } 7983afb90b0Sreyk 7993afb90b0Sreyk int 8001f7fe034Sreyk ctl_reload(struct parse_result *res, int argc, char *argv[]) 8011f7fe034Sreyk { 8021f7fe034Sreyk if (argc != 1) 8031f7fe034Sreyk ctl_usage(res->ctl); 8041f7fe034Sreyk 8051f7fe034Sreyk return (vmmaction(res)); 8061f7fe034Sreyk } 8071f7fe034Sreyk 8081f7fe034Sreyk int 8091f7fe034Sreyk ctl_reset(struct parse_result *res, int argc, char *argv[]) 8101f7fe034Sreyk { 8111f7fe034Sreyk if (argc == 2) { 8121f7fe034Sreyk if (strcasecmp("all", argv[1]) == 0) 8131f7fe034Sreyk res->mode = CONFIG_ALL; 8141f7fe034Sreyk else if (strcasecmp("vms", argv[1]) == 0) 8151f7fe034Sreyk res->mode = CONFIG_VMS; 8161f7fe034Sreyk else if (strcasecmp("switches", argv[1]) == 0) 8171f7fe034Sreyk res->mode = CONFIG_SWITCHES; 8181f7fe034Sreyk else 8191f7fe034Sreyk ctl_usage(res->ctl); 8201f7fe034Sreyk } else if (argc > 2) 8211f7fe034Sreyk ctl_usage(res->ctl); 8221f7fe034Sreyk 8231f7fe034Sreyk if (res->mode == 0) 8241f7fe034Sreyk res->mode = CONFIG_ALL; 8251f7fe034Sreyk 8261f7fe034Sreyk return (vmmaction(res)); 8271f7fe034Sreyk } 8281f7fe034Sreyk 8291f7fe034Sreyk int 830eb8d1da1Sreyk ctl_start(struct parse_result *res, int argc, char *argv[]) 831eb8d1da1Sreyk { 832f224f92aSccardenas int ch, i, type; 833e2ceadc1Sreyk char path[PATH_MAX]; 834e2ceadc1Sreyk const char *s; 835eb8d1da1Sreyk 836b848b186Sdv /* We may require sendfd */ 837b848b186Sdv if (pledge("stdio rpath exec unix getpw unveil sendfd", NULL) == -1) 838b848b186Sdv err(1, "pledge"); 839b848b186Sdv 840917458a3Sclaudio while ((ch = getopt(argc, argv, "b:B:cd:i:Lm:n:r:t:")) != -1) { 841eb8d1da1Sreyk switch (ch) { 84238d0e0c3Sreyk case 'b': 843eb8d1da1Sreyk if (res->path) 84438d0e0c3Sreyk errx(1, "boot image specified multiple times"); 8455f82d0b6Sreyk if (realpath(optarg, path) == NULL) 84638d0e0c3Sreyk err(1, "invalid boot image path"); 8475f82d0b6Sreyk if ((res->path = strdup(path)) == NULL) 848eb8d1da1Sreyk errx(1, "strdup"); 849eb8d1da1Sreyk break; 850917458a3Sclaudio case 'B': 851917458a3Sclaudio if (res->bootdevice) 852917458a3Sclaudio errx(1, "boot device specified multiple times"); 853bdbc1844Sclaudio if (strcmp("disk", optarg) == 0) 854bdbc1844Sclaudio res->bootdevice = VMBOOTDEV_DISK; 855bdbc1844Sclaudio else if (strcmp("cdrom", optarg) == 0) 856bdbc1844Sclaudio res->bootdevice = VMBOOTDEV_CDROM; 857bdbc1844Sclaudio else if (strcmp("net", optarg) == 0) 858917458a3Sclaudio res->bootdevice = VMBOOTDEV_NET; 859bdbc1844Sclaudio else 860bdbc1844Sclaudio errx(1, "unknown boot device %s", optarg); 861917458a3Sclaudio break; 86295ab188fSccardenas case 'r': 86395ab188fSccardenas if (res->isopath) 86495ab188fSccardenas errx(1, "iso image specified multiple times"); 86595ab188fSccardenas if (realpath(optarg, path) == NULL) 86695ab188fSccardenas err(1, "invalid iso image path"); 86795ab188fSccardenas if ((res->isopath = strdup(path)) == NULL) 86895ab188fSccardenas errx(1, "strdup"); 86995ab188fSccardenas break; 87038d0e0c3Sreyk case 'c': 87138d0e0c3Sreyk tty_autoconnect = 1; 87238d0e0c3Sreyk break; 873470adcf5Sreyk case 'L': 874470adcf5Sreyk if (parse_network(res, ".") != 0) 875470adcf5Sreyk errx(1, "invalid network: %s", optarg); 876470adcf5Sreyk break; 877eb8d1da1Sreyk case 'm': 878eb8d1da1Sreyk if (res->size) 879eb8d1da1Sreyk errx(1, "memory specified multiple times"); 880ead1b146Sdv parse_size(res, optarg, "memory"); 881eb8d1da1Sreyk break; 882b657d36cSreyk case 'n': 883b657d36cSreyk if (parse_network(res, optarg) != 0) 884b657d36cSreyk errx(1, "invalid network: %s", optarg); 885b657d36cSreyk break; 886eb8d1da1Sreyk case 'd': 887f224f92aSccardenas type = parse_disktype(optarg, &s); 888f224f92aSccardenas if (realpath(s, path) == NULL) 8895f82d0b6Sreyk err(1, "invalid disk path"); 890f224f92aSccardenas if (parse_disk(res, path, type) != 0) 8915f82d0b6Sreyk errx(1, "invalid disk: %s", optarg); 892eb8d1da1Sreyk break; 893eb8d1da1Sreyk case 'i': 894eb8d1da1Sreyk if (res->nifs != -1) 895eb8d1da1Sreyk errx(1, "interfaces specified multiple times"); 896eb8d1da1Sreyk if (parse_ifs(res, optarg, 0) != 0) 897eb8d1da1Sreyk errx(1, "invalid interface count: %s", optarg); 898eb8d1da1Sreyk break; 899508a6e39Sreyk case 't': 900508a6e39Sreyk if (parse_instance(res, optarg) == -1) 901508a6e39Sreyk errx(1, "invalid name: %s", optarg); 902508a6e39Sreyk break; 903eb8d1da1Sreyk default: 904eb8d1da1Sreyk ctl_usage(res->ctl); 905eb8d1da1Sreyk /* NOTREACHED */ 906eb8d1da1Sreyk } 907eb8d1da1Sreyk } 908c1e58e1aSkn argc -= optind; 909c1e58e1aSkn argv += optind; 910eb8d1da1Sreyk 911bd4b76f8Sreyk if (argc != 1) 912b0b31ffcSkn ctl_usage(res->ctl); 913b0b31ffcSkn 914bd4b76f8Sreyk if (parse_vmid(res, argv[0], 0) == -1) 915bd4b76f8Sreyk errx(1, "invalid id: %s", argv[0]); 916bd4b76f8Sreyk 917b657d36cSreyk for (i = res->nnets; i < res->nifs; i++) { 918b657d36cSreyk /* Add interface that is not attached to a switch */ 919b657d36cSreyk if (parse_network(res, "") == -1) 920b657d36cSreyk return (-1); 921b657d36cSreyk } 922b657d36cSreyk if (res->nnets > res->nifs) 923b657d36cSreyk res->nifs = res->nnets; 924b657d36cSreyk 925eb8d1da1Sreyk return (vmmaction(res)); 926eb8d1da1Sreyk } 927eb8d1da1Sreyk 928eb8d1da1Sreyk int 929eb8d1da1Sreyk ctl_stop(struct parse_result *res, int argc, char *argv[]) 930eb8d1da1Sreyk { 931a261be2aStb int ch; 932f6e5c9ebSreyk 933e0b12962Sreyk while ((ch = getopt(argc, argv, "afw")) != -1) { 934f6e5c9ebSreyk switch (ch) { 935f6e5c9ebSreyk case 'f': 9363be9785fSreyk res->flags |= VMOP_FORCE; 9373be9785fSreyk break; 9383be9785fSreyk case 'w': 9393be9785fSreyk res->flags |= VMOP_WAIT; 940f6e5c9ebSreyk break; 941e0b12962Sreyk case 'a': 942e0b12962Sreyk res->action = CMD_STOPALL; 943e0b12962Sreyk break; 944f6e5c9ebSreyk default: 945eb8d1da1Sreyk ctl_usage(res->ctl); 946f6e5c9ebSreyk /* NOTREACHED */ 947f6e5c9ebSreyk } 948f6e5c9ebSreyk } 949c1e58e1aSkn argc -= optind; 950c1e58e1aSkn argv += optind; 951eb8d1da1Sreyk 952a261be2aStb if (res->action == CMD_STOPALL) { 953a261be2aStb if (argc != 0) 954f460dd9fSmlarkin ctl_usage(res->ctl); 955a261be2aStb } else { 956a261be2aStb if (argc != 1) 957b0b31ffcSkn ctl_usage(res->ctl); 958a261be2aStb if (parse_vmid(res, argv[0], 0) == -1) 959a261be2aStb errx(1, "invalid id: %s", argv[0]); 960a261be2aStb } 961e0b12962Sreyk 962eb8d1da1Sreyk return (vmmaction(res)); 963eb8d1da1Sreyk } 96495009bb4Sreyk 96595009bb4Sreyk int 96695009bb4Sreyk ctl_console(struct parse_result *res, int argc, char *argv[]) 96795009bb4Sreyk { 96895009bb4Sreyk if (argc == 2) { 9694100d478Sjasper if (parse_vmid(res, argv[1], 0) == -1) 97095009bb4Sreyk errx(1, "invalid id: %s", argv[1]); 97195009bb4Sreyk } else if (argc != 2) 97295009bb4Sreyk ctl_usage(res->ctl); 97395009bb4Sreyk 97495009bb4Sreyk return (vmmaction(res)); 97595009bb4Sreyk } 97695009bb4Sreyk 97752e954a3Spd int 978583f6618Sclaudio ctl_waitfor(struct parse_result *res, int argc, char *argv[]) 979583f6618Sclaudio { 980583f6618Sclaudio if (argc == 2) { 981583f6618Sclaudio if (parse_vmid(res, argv[1], 0) == -1) 982583f6618Sclaudio errx(1, "invalid id: %s", argv[1]); 983583f6618Sclaudio } else if (argc != 2) 984583f6618Sclaudio ctl_usage(res->ctl); 985583f6618Sclaudio 986583f6618Sclaudio return (vmmaction(res)); 987583f6618Sclaudio } 988583f6618Sclaudio 989583f6618Sclaudio int 99052e954a3Spd ctl_pause(struct parse_result *res, int argc, char *argv[]) 99152e954a3Spd { 99252e954a3Spd if (argc == 2) { 9934100d478Sjasper if (parse_vmid(res, argv[1], 0) == -1) 99452e954a3Spd errx(1, "invalid id: %s", argv[1]); 99552e954a3Spd } else if (argc != 2) 99652e954a3Spd ctl_usage(res->ctl); 99752e954a3Spd 99852e954a3Spd return (vmmaction(res)); 99952e954a3Spd } 100052e954a3Spd 100152e954a3Spd int 100252e954a3Spd ctl_unpause(struct parse_result *res, int argc, char *argv[]) 100352e954a3Spd { 100452e954a3Spd if (argc == 2) { 10054100d478Sjasper if (parse_vmid(res, argv[1], 0) == -1) 100652e954a3Spd errx(1, "invalid id: %s", argv[1]); 100752e954a3Spd } else if (argc != 2) 100852e954a3Spd ctl_usage(res->ctl); 100952e954a3Spd 101052e954a3Spd return (vmmaction(res)); 101152e954a3Spd } 101252e954a3Spd 1013eed20f3bSpd int 1014eed20f3bSpd ctl_send(struct parse_result *res, int argc, char *argv[]) 1015eed20f3bSpd { 10166955bc4dSccardenas if (pledge("stdio unix sendfd unveil", NULL) == -1) 1017eed20f3bSpd err(1, "pledge"); 1018eed20f3bSpd if (argc == 2) { 10194100d478Sjasper if (parse_vmid(res, argv[1], 0) == -1) 1020eed20f3bSpd errx(1, "invalid id: %s", argv[1]); 1021eed20f3bSpd } else if (argc != 2) 1022eed20f3bSpd ctl_usage(res->ctl); 1023eed20f3bSpd 1024eed20f3bSpd return (vmmaction(res)); 1025eed20f3bSpd } 1026eed20f3bSpd 1027eed20f3bSpd int 1028eed20f3bSpd ctl_receive(struct parse_result *res, int argc, char *argv[]) 1029eed20f3bSpd { 10306955bc4dSccardenas if (pledge("stdio unix sendfd unveil", NULL) == -1) 1031eed20f3bSpd err(1, "pledge"); 1032eed20f3bSpd if (argc == 2) { 10334100d478Sjasper if (parse_vmid(res, argv[1], 1) == -1) 1034eed20f3bSpd errx(1, "invalid id: %s", argv[1]); 1035eed20f3bSpd } else if (argc != 2) 1036eed20f3bSpd ctl_usage(res->ctl); 1037eed20f3bSpd 1038eed20f3bSpd return (vmmaction(res)); 1039eed20f3bSpd } 1040eed20f3bSpd 104195009bb4Sreyk __dead void 104295009bb4Sreyk ctl_openconsole(const char *name) 104395009bb4Sreyk { 104495009bb4Sreyk closefrom(STDERR_FILENO + 1); 10456955bc4dSccardenas if (unveil(VMCTL_CU, "x") == -1) 1046bc5a8259Sbeck err(1, "unveil %s", VMCTL_CU); 10475154e7dcSkn execl(VMCTL_CU, VMCTL_CU, "-r", "-l", name, "-s", "115200", 10485154e7dcSkn (char *)NULL); 104995009bb4Sreyk err(1, "failed to open the console"); 105095009bb4Sreyk } 1051