1/* $OpenBSD: main.c,v 1.78 2024/05/18 06:45:00 jsg Exp $ */ 2 3/* 4 * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/types.h> 20#include <sys/socket.h> 21#include <sys/queue.h> 22#include <sys/un.h> 23 24#include <err.h> 25#include <errno.h> 26#include <stdio.h> 27#include <stdlib.h> 28#include <stdint.h> 29#include <limits.h> 30#include <string.h> 31#include <syslog.h> 32#include <unistd.h> 33#include <fcntl.h> 34#include <util.h> 35#include <imsg.h> 36 37#include "vmd.h" 38#include "virtio.h" 39#include "proc.h" 40#include "vmctl.h" 41 42#define RAW_FMT "raw" 43#define QCOW2_FMT "qcow2" 44 45static const char *socket_name = SOCKET_NAME; 46static int ctl_sock = -1; 47static int tty_autoconnect = 0; 48int stat_rflag; 49 50__dead void usage(void); 51__dead void ctl_usage(struct ctl_command *); 52 53int ctl_console(struct parse_result *, int, char *[]); 54int ctl_convert(const char *, const char *, int, size_t); 55int ctl_create(struct parse_result *, int, char *[]); 56int ctl_load(struct parse_result *, int, char *[]); 57int ctl_log(struct parse_result *, int, char *[]); 58int ctl_reload(struct parse_result *, int, char *[]); 59int ctl_reset(struct parse_result *, int, char *[]); 60int ctl_start(struct parse_result *, int, char *[]); 61int ctl_status(struct parse_result *, int, char *[]); 62int ctl_stop(struct parse_result *, int, char *[]); 63int ctl_waitfor(struct parse_result *, int, char *[]); 64int ctl_pause(struct parse_result *, int, char *[]); 65int ctl_unpause(struct parse_result *, int, char *[]); 66int ctl_send(struct parse_result *, int, char *[]); 67int ctl_receive(struct parse_result *, int, char *[]); 68 69struct ctl_command ctl_commands[] = { 70 { "console", CMD_CONSOLE, ctl_console, "id" }, 71 { "create", CMD_CREATE, ctl_create, 72 "[-b base | -i disk] [-s size] disk", 1 }, 73 { "load", CMD_LOAD, ctl_load, "filename" }, 74 { "log", CMD_LOG, ctl_log, "[brief | verbose]" }, 75 { "pause", CMD_PAUSE, ctl_pause, "id" }, 76 { "receive", CMD_RECEIVE, ctl_receive, "name" , 1}, 77 { "reload", CMD_RELOAD, ctl_reload, "" }, 78 { "reset", CMD_RESET, ctl_reset, "[all | switches | vms]" }, 79 { "send", CMD_SEND, ctl_send, "id", 1}, 80 { "show", CMD_STATUS, ctl_status, "[id]" }, 81 { "start", CMD_START, ctl_start, 82 "[-cL] [-B device] [-b path] [-d disk] [-i count]\n" 83 "\t\t[-m size] [-n switch] [-r path] [-t name] id | name", 1}, 84 { "status", CMD_STATUS, ctl_status, "[-r] [id]" }, 85 { "stop", CMD_STOP, ctl_stop, "[-fw] [id | -a]" }, 86 { "unpause", CMD_UNPAUSE, ctl_unpause, "id" }, 87 { "wait", CMD_WAITFOR, ctl_waitfor, "id" }, 88 { NULL } 89}; 90 91__dead void 92usage(void) 93{ 94 extern char *__progname; 95 96 fprintf(stderr, "usage:\t%s [-v] command [arg ...]\n", __progname); 97 98 exit(1); 99} 100 101__dead void 102ctl_usage(struct ctl_command *ctl) 103{ 104 extern char *__progname; 105 106 fprintf(stderr, "usage:\t%s [-v] %s %s\n", __progname, 107 ctl->name, ctl->usage); 108 exit(1); 109} 110 111int 112main(int argc, char *argv[]) 113{ 114 int ch, verbose = 1; 115 116 while ((ch = getopt(argc, argv, "v")) != -1) { 117 switch (ch) { 118 case 'v': 119 verbose = 2; 120 break; 121 default: 122 usage(); 123 /* NOTREACHED */ 124 } 125 } 126 argc -= optind; 127 argv += optind; 128 optreset = 1; 129 optind = 1; 130 131 if (argc < 1) 132 usage(); 133 134 log_init(verbose, LOG_DAEMON); 135 136 return (parse(argc, argv)); 137} 138 139int 140parse(int argc, char *argv[]) 141{ 142 struct ctl_command *ctl = NULL; 143 struct parse_result res; 144 int i; 145 146 memset(&res, 0, sizeof(res)); 147 res.nifs = -1; 148 149 for (i = 0; ctl_commands[i].name != NULL; i++) { 150 if (strncmp(ctl_commands[i].name, 151 argv[0], strlen(argv[0])) == 0) { 152 if (ctl != NULL) { 153 fprintf(stderr, 154 "ambiguous argument: %s\n", argv[0]); 155 usage(); 156 } 157 ctl = &ctl_commands[i]; 158 } 159 } 160 161 if (ctl == NULL) { 162 fprintf(stderr, "unknown argument: %s\n", argv[0]); 163 usage(); 164 } 165 166 res.action = ctl->action; 167 res.ctl = ctl; 168 169 if (!ctl->has_pledge) { 170 /* pledge(2) default if command doesn't have its own pledge */ 171 if (pledge("stdio rpath exec unix getpw unveil", NULL) == -1) 172 err(1, "pledge"); 173 } 174 if (ctl->main(&res, argc, argv) != 0) 175 exit(1); 176 177 if (ctl_sock != -1) { 178 close(ibuf->fd); 179 free(ibuf); 180 } 181 182 return (0); 183} 184 185int 186vmmaction(struct parse_result *res) 187{ 188 struct sockaddr_un sun; 189 struct imsg imsg; 190 int done = 0; 191 int n; 192 int ret, action; 193 unsigned int flags; 194 195 if (ctl_sock == -1) { 196 if (unveil(SOCKET_NAME, "w") == -1) 197 err(1, "unveil %s", SOCKET_NAME); 198 if ((ctl_sock = socket(AF_UNIX, 199 SOCK_STREAM|SOCK_CLOEXEC, 0)) == -1) 200 err(1, "socket"); 201 202 memset(&sun, 0, sizeof(sun)); 203 sun.sun_family = AF_UNIX; 204 strlcpy(sun.sun_path, socket_name, sizeof(sun.sun_path)); 205 206 if (connect(ctl_sock, 207 (struct sockaddr *)&sun, sizeof(sun)) == -1) 208 err(1, "connect: %s", socket_name); 209 210 if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) 211 err(1, "malloc"); 212 imsg_init(ibuf, ctl_sock); 213 } 214 215 switch (res->action) { 216 case CMD_START: 217 ret = vm_start(res->id, res->name, res->size, res->nifs, 218 res->nets, res->ndisks, res->disks, res->disktypes, 219 res->path, res->isopath, res->instance, res->bootdevice); 220 if (ret) { 221 errno = ret; 222 err(1, "start VM operation failed"); 223 } 224 break; 225 case CMD_STOP: 226 terminate_vm(res->id, res->name, res->flags); 227 break; 228 case CMD_STATUS: 229 case CMD_CONSOLE: 230 case CMD_STOPALL: 231 get_info_vm(res->id, res->name, res->action, res->flags); 232 break; 233 case CMD_LOAD: 234 imsg_compose(ibuf, IMSG_VMDOP_LOAD, 0, 0, -1, 235 res->path, strlen(res->path) + 1); 236 break; 237 case CMD_LOG: 238 imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1, 239 &res->verbose, sizeof(res->verbose)); 240 break; 241 case CMD_RELOAD: 242 imsg_compose(ibuf, IMSG_VMDOP_RELOAD, 0, 0, -1, NULL, 0); 243 break; 244 case CMD_RESET: 245 imsg_compose(ibuf, IMSG_CTL_RESET, 0, 0, -1, 246 &res->mode, sizeof(res->mode)); 247 break; 248 case CMD_WAITFOR: 249 waitfor_vm(res->id, res->name); 250 break; 251 case CMD_PAUSE: 252 pause_vm(res->id, res->name); 253 break; 254 case CMD_UNPAUSE: 255 unpause_vm(res->id, res->name); 256 break; 257 case CMD_SEND: 258 send_vm(res->id, res->name); 259 done = 1; 260 ret = 0; 261 break; 262 case CMD_RECEIVE: 263 vm_receive(res->id, res->name); 264 break; 265 case CMD_CREATE: 266 case NONE: 267 /* The action is not expected here */ 268 errx(1, "invalid action %u", res->action); 269 break; 270 } 271 272 action = res->action; 273 flags = res->flags; 274 parse_free(res); 275 276 while (ibuf->w.queued) 277 if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) 278 err(1, "write error"); 279 280 while (!done) { 281 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 282 errx(1, "imsg_read error"); 283 if (n == 0) 284 errx(1, "pipe closed"); 285 286 while (!done) { 287 if ((n = imsg_get(ibuf, &imsg)) == -1) 288 errx(1, "imsg_get error"); 289 if (n == 0) 290 break; 291 292 if (imsg.hdr.type == IMSG_CTL_FAIL) { 293 if (IMSG_DATA_SIZE(&imsg) == sizeof(ret)) 294 memcpy(&ret, imsg.data, sizeof(ret)); 295 else 296 ret = 0; 297 if (ret != 0) { 298 errno = ret; 299 err(1, "command failed"); 300 } else 301 errx(1, "command failed"); 302 } 303 304 ret = 0; 305 switch (action) { 306 case CMD_START: 307 done = vm_start_complete(&imsg, &ret, 308 tty_autoconnect); 309 break; 310 case CMD_WAITFOR: 311 flags = VMOP_WAIT; 312 /* FALLTHROUGH */ 313 case CMD_STOP: 314 done = terminate_vm_complete(&imsg, &ret, 315 flags); 316 break; 317 case CMD_CONSOLE: 318 case CMD_STATUS: 319 case CMD_STOPALL: 320 done = add_info(&imsg, &ret); 321 break; 322 case CMD_PAUSE: 323 done = pause_vm_complete(&imsg, &ret); 324 break; 325 case CMD_RECEIVE: 326 done = vm_start_complete(&imsg, &ret, 0); 327 break; 328 case CMD_UNPAUSE: 329 done = unpause_vm_complete(&imsg, &ret); 330 break; 331 default: 332 done = 1; 333 break; 334 } 335 336 imsg_free(&imsg); 337 } 338 } 339 340 if (ret) 341 return (1); 342 else 343 return (0); 344} 345 346void 347parse_free(struct parse_result *res) 348{ 349 size_t i; 350 351 free(res->name); 352 free(res->path); 353 free(res->isopath); 354 free(res->instance); 355 for (i = 0; i < res->ndisks; i++) 356 free(res->disks[i]); 357 free(res->disks); 358 free(res->disktypes); 359 memset(res, 0, sizeof(*res)); 360} 361 362int 363parse_ifs(struct parse_result *res, char *word, int val) 364{ 365 const char *error; 366 367 if (word != NULL) { 368 val = strtonum(word, 1, INT_MAX, &error); 369 if (error != NULL) { 370 warnx("count is %s: %s", error, word); 371 return (-1); 372 } 373 } 374 res->nifs = val; 375 376 return (0); 377} 378 379int 380parse_network(struct parse_result *res, char *word) 381{ 382 char **nets; 383 char *s; 384 385 if ((nets = reallocarray(res->nets, res->nnets + 1, 386 sizeof(char *))) == NULL) { 387 warn("reallocarray"); 388 return (-1); 389 } 390 if ((s = strdup(word)) == NULL) { 391 warn("strdup"); 392 return (-1); 393 } 394 nets[res->nnets] = s; 395 res->nets = nets; 396 res->nnets++; 397 398 return (0); 399} 400 401void 402parse_size(struct parse_result *res, char *word, const char *type) 403{ 404 char result[FMT_SCALED_STRSIZE]; 405 long long val = 0; 406 407 if (word != NULL) { 408 if (scan_scaled(word, &val) != 0) 409 err(1, "invalid %s size: %s", type, word); 410 } 411 412 if (val < (1024 * 1024)) 413 errx(1, "%s size must be at least 1MB", type); 414 415 if (strcmp("memory", type) == 0 && val > VMM_MAX_VM_MEM_SIZE) { 416 if (fmt_scaled(VMM_MAX_VM_MEM_SIZE, result) == 0) 417 errx(1, "memory size too large (limit is %s)", result); 418 else 419 errx(1, "memory size too large"); 420 } 421 422 /* Round down to the megabyte. */ 423 res->size = (val / (1024 * 1024)) * (1024 * 1024); 424 425 if (res->size != (size_t)val) { 426 if (fmt_scaled(res->size, result) == 0) 427 warnx("%s size rounded to %s", type, result); 428 else 429 warnx("%s size rounded to %zuB", type, res->size); 430 } 431} 432 433int 434parse_disktype(const char *s, const char **ret) 435{ 436 char buf[BUFSIZ]; 437 const char *ext; 438 int fd; 439 ssize_t len; 440 441 *ret = s; 442 443 /* Try to parse the explicit format (qcow2:disk.qc2) */ 444 if (strstr(s, RAW_FMT) == s && *(s + strlen(RAW_FMT)) == ':') { 445 *ret = s + strlen(RAW_FMT) + 1; 446 return (VMDF_RAW); 447 } 448 if (strstr(s, QCOW2_FMT) == s && *(s + strlen(QCOW2_FMT)) == ':') { 449 *ret = s + strlen(QCOW2_FMT) + 1; 450 return (VMDF_QCOW2); 451 } 452 453 /* Or try to derive the format from the file signature */ 454 if ((fd = open(s, O_RDONLY)) != -1) { 455 len = read(fd, buf, sizeof(buf)); 456 close(fd); 457 458 if (len >= (ssize_t)strlen(VM_MAGIC_QCOW) && 459 strncmp(buf, VM_MAGIC_QCOW, 460 strlen(VM_MAGIC_QCOW)) == 0) { 461 /* Return qcow2, the version will be checked later */ 462 return (VMDF_QCOW2); 463 } 464 } 465 466 /* 467 * Use the extension as a last option. This is needed for 468 * 'vmctl create' as the file, and the signature, doesn't 469 * exist yet. 470 */ 471 if ((ext = strrchr(s, '.')) != NULL && *(++ext) != '\0') { 472 if (strcasecmp(ext, RAW_FMT) == 0) 473 return (VMDF_RAW); 474 else if (strcasecmp(ext, QCOW2_FMT) == 0) 475 return (VMDF_QCOW2); 476 } 477 478 /* Fallback to raw */ 479 return (VMDF_RAW); 480} 481 482int 483parse_disk(struct parse_result *res, char *word, int type) 484{ 485 char **disks; 486 int *disktypes; 487 char *s; 488 489 if ((disks = reallocarray(res->disks, res->ndisks + 1, 490 sizeof(char *))) == NULL) { 491 warn("reallocarray"); 492 return (-1); 493 } 494 if ((disktypes = reallocarray(res->disktypes, res->ndisks + 1, 495 sizeof(int))) == NULL) { 496 warn("reallocarray"); 497 return -1; 498 } 499 if ((s = strdup(word)) == NULL) { 500 warn("strdup"); 501 return (-1); 502 } 503 disks[res->ndisks] = s; 504 disktypes[res->ndisks] = type; 505 res->disks = disks; 506 res->disktypes = disktypes; 507 res->ndisks++; 508 509 return (0); 510} 511 512int 513parse_vmid(struct parse_result *res, char *word, int needname) 514{ 515 const char *error; 516 uint32_t id; 517 518 if (word == NULL) { 519 warnx("missing vmid argument"); 520 return (-1); 521 } 522 if (*word == '-') { 523 /* don't print a warning to allow command line options */ 524 return (-1); 525 } 526 id = strtonum(word, 0, UINT32_MAX, &error); 527 if (error == NULL) { 528 if (needname) { 529 warnx("invalid vm name"); 530 return (-1); 531 } else { 532 res->id = id; 533 res->name = NULL; 534 } 535 } else { 536 if (strlen(word) >= VMM_MAX_NAME_LEN) { 537 warnx("name too long"); 538 return (-1); 539 } 540 res->id = 0; 541 if ((res->name = strdup(word)) == NULL) 542 errx(1, "strdup"); 543 } 544 545 return (0); 546} 547 548int 549parse_instance(struct parse_result *res, char *word) 550{ 551 if (strlen(word) >= VMM_MAX_NAME_LEN) { 552 warnx("instance vm name too long"); 553 return (-1); 554 } 555 res->id = 0; 556 if ((res->instance = strdup(word)) == NULL) 557 errx(1, "strdup"); 558 559 return (0); 560} 561 562int 563ctl_create(struct parse_result *res, int argc, char *argv[]) 564{ 565 int ch, ret, type; 566 const char *disk, *format, *base = NULL, *input = NULL; 567 568 while ((ch = getopt(argc, argv, "b:i:s:")) != -1) { 569 switch (ch) { 570 case 'b': 571 base = optarg; 572 break; 573 case 'i': 574 input = optarg; 575 break; 576 case 's': 577 parse_size(res, optarg, "disk"); 578 break; 579 default: 580 ctl_usage(res->ctl); 581 /* NOTREACHED */ 582 } 583 } 584 argc -= optind; 585 argv += optind; 586 587 if (argc != 1) 588 ctl_usage(res->ctl); 589 590 type = parse_disktype(argv[0], &disk); 591 592 if (pledge("stdio rpath wpath cpath", NULL) == -1) 593 err(1, "pledge"); 594 595 if (input) { 596 if (base && input) 597 errx(1, "conflicting -b and -i arguments"); 598 return ctl_convert(input, disk, type, res->size); 599 } 600 601 if (base && type != VMDF_QCOW2) 602 errx(1, "base images require qcow2 disk format"); 603 if (res->size == 0 && !base) { 604 fprintf(stderr, "could not create %s: missing size argument\n", 605 disk); 606 ctl_usage(res->ctl); 607 } 608 609 if ((ret = create_imagefile(type, disk, base, res->size, &format)) != 0) { 610 errno = ret; 611 err(1, "create imagefile operation failed"); 612 } else 613 warnx("%s imagefile created", format); 614 615 return (0); 616} 617 618int 619ctl_convert(const char *srcfile, const char *dstfile, int dsttype, size_t dstsize) 620{ 621 struct { 622 int fd; 623 int type; 624 struct virtio_backing file; 625 const char *disk; 626 off_t size; 627 } src, dst; 628 int ret; 629 const char *format, *errstr = NULL; 630 uint8_t *buf = NULL, *zerobuf = NULL; 631 size_t buflen; 632 ssize_t len, rlen; 633 off_t off; 634 635 memset(&src, 0, sizeof(src)); 636 memset(&dst, 0, sizeof(dst)); 637 638 src.type = parse_disktype(srcfile, &src.disk); 639 dst.type = dsttype; 640 dst.disk = dstfile; 641 642 if ((src.fd = open_imagefile(src.type, src.disk, O_RDONLY, 643 &src.file, &src.size)) == -1) { 644 errstr = "failed to open source image file"; 645 goto done; 646 } 647 648 if (dstsize == 0) 649 dstsize = src.size; 650 if (dstsize < (size_t)src.size) { 651 errstr = "size cannot be smaller than input disk size"; 652 goto done; 653 } 654 655 /* align to megabytes */ 656 dst.size = ALIGNSZ(dstsize, 1048576); 657 658 if ((ret = create_imagefile(dst.type, dst.disk, NULL, dst.size, 659 &format)) != 0) { 660 errstr = "failed to create destination image file"; 661 goto done; 662 } 663 664 if ((dst.fd = open_imagefile(dst.type, dst.disk, O_RDWR, 665 &dst.file, &dst.size)) == -1) { 666 errstr = "failed to open destination image file"; 667 goto done; 668 } 669 670 if (pledge("stdio", NULL) == -1) 671 err(1, "pledge"); 672 673 /* 674 * Use 64k buffers by default. This could also be adjusted to 675 * the backend cluster size. 676 */ 677 buflen = 1 << 16; 678 if ((buf = calloc(1, buflen)) == NULL || 679 (zerobuf = calloc(1, buflen)) == NULL) { 680 errstr = "failed to allocated buffers"; 681 goto done; 682 } 683 684 for (off = 0; off < dst.size; off += len) { 685 /* Read input from the source image */ 686 if (off < src.size) { 687 len = MIN((off_t)buflen, src.size - off); 688 if ((rlen = src.file.pread(src.file.p, 689 buf, (size_t)len, off)) != len) { 690 errno = EIO; 691 errstr = "failed to read from source"; 692 goto done; 693 } 694 } else 695 len = 0; 696 697 /* and pad the remaining bytes */ 698 if (len < (ssize_t)buflen) { 699 log_debug("%s: padding %zd zero bytes at offset %lld", 700 format, buflen - len, off + len); 701 memset(buf + len, 0, buflen - len); 702 len = buflen; 703 } 704 705 /* 706 * No need to copy empty buffers. This allows the backend, 707 * sparse files or QCOW2 images, to save space in the 708 * destination file. 709 */ 710 if (memcmp(buf, zerobuf, buflen) == 0) 711 continue; 712 713 log_debug("%s: writing %zd of %lld bytes at offset %lld", 714 format, len, dst.size, off); 715 716 if ((rlen = dst.file.pwrite(dst.file.p, 717 buf, (size_t)len, off)) != len) { 718 errno = EIO; 719 errstr = "failed to write to destination"; 720 goto done; 721 } 722 } 723 724 if (dstsize < (size_t)dst.size) 725 warnx("destination size rounded to %lld megabytes", 726 dst.size / 1048576); 727 728 done: 729 free(buf); 730 free(zerobuf); 731 if (src.file.p != NULL) 732 src.file.close(src.file.p, 0); 733 if (dst.file.p != NULL) 734 dst.file.close(dst.file.p, 0); 735 if (errstr != NULL) 736 errx(1, "%s", errstr); 737 else 738 warnx("%s imagefile created", format); 739 740 return (0); 741} 742 743int 744ctl_status(struct parse_result *res, int argc, char *argv[]) 745{ 746 char ch; 747 748 while ((ch = getopt(argc, argv, "r")) != -1) { 749 switch (ch) { 750 case 'r': 751 stat_rflag = 1; 752 break; 753 default: 754 ctl_usage(res->ctl); 755 /* NOTREACHED */ 756 } 757 } 758 argc -= optind; 759 argv += optind; 760 761 if (argc == 1) { 762 if (parse_vmid(res, argv[0], 0) == -1) 763 errx(1, "invalid id: %s", argv[0]); 764 } else if (argc > 1) 765 ctl_usage(res->ctl); 766 767 return (vmmaction(res)); 768} 769 770int 771ctl_load(struct parse_result *res, int argc, char *argv[]) 772{ 773 if (argc != 2) 774 ctl_usage(res->ctl); 775 776 if ((res->path = strdup(argv[1])) == NULL) 777 err(1, "strdup"); 778 779 return (vmmaction(res)); 780} 781 782int 783ctl_log(struct parse_result *res, int argc, char *argv[]) 784{ 785 if (argc != 2) 786 ctl_usage(res->ctl); 787 788 if (strncasecmp("brief", argv[1], strlen(argv[1])) == 0) 789 res->verbose = 0; 790 else if (strncasecmp("verbose", argv[1], strlen(argv[1])) == 0) 791 res->verbose = 2; 792 else 793 ctl_usage(res->ctl); 794 795 return (vmmaction(res)); 796} 797 798int 799ctl_reload(struct parse_result *res, int argc, char *argv[]) 800{ 801 if (argc != 1) 802 ctl_usage(res->ctl); 803 804 return (vmmaction(res)); 805} 806 807int 808ctl_reset(struct parse_result *res, int argc, char *argv[]) 809{ 810 if (argc == 2) { 811 if (strcasecmp("all", argv[1]) == 0) 812 res->mode = CONFIG_ALL; 813 else if (strcasecmp("vms", argv[1]) == 0) 814 res->mode = CONFIG_VMS; 815 else if (strcasecmp("switches", argv[1]) == 0) 816 res->mode = CONFIG_SWITCHES; 817 else 818 ctl_usage(res->ctl); 819 } else if (argc > 2) 820 ctl_usage(res->ctl); 821 822 if (res->mode == 0) 823 res->mode = CONFIG_ALL; 824 825 return (vmmaction(res)); 826} 827 828int 829ctl_start(struct parse_result *res, int argc, char *argv[]) 830{ 831 int ch, i, type; 832 char path[PATH_MAX]; 833 const char *s; 834 835 /* We may require sendfd */ 836 if (pledge("stdio rpath exec unix getpw unveil sendfd", NULL) == -1) 837 err(1, "pledge"); 838 839 while ((ch = getopt(argc, argv, "b:B:cd:i:Lm:n:r:t:")) != -1) { 840 switch (ch) { 841 case 'b': 842 if (res->path) 843 errx(1, "boot image specified multiple times"); 844 if (realpath(optarg, path) == NULL) 845 err(1, "invalid boot image path"); 846 if ((res->path = strdup(path)) == NULL) 847 errx(1, "strdup"); 848 break; 849 case 'B': 850 if (res->bootdevice) 851 errx(1, "boot device specified multiple times"); 852 if (strcmp("disk", optarg) == 0) 853 res->bootdevice = VMBOOTDEV_DISK; 854 else if (strcmp("cdrom", optarg) == 0) 855 res->bootdevice = VMBOOTDEV_CDROM; 856 else if (strcmp("net", optarg) == 0) 857 res->bootdevice = VMBOOTDEV_NET; 858 else 859 errx(1, "unknown boot device %s", optarg); 860 break; 861 case 'r': 862 if (res->isopath) 863 errx(1, "iso image specified multiple times"); 864 if (realpath(optarg, path) == NULL) 865 err(1, "invalid iso image path"); 866 if ((res->isopath = strdup(path)) == NULL) 867 errx(1, "strdup"); 868 break; 869 case 'c': 870 tty_autoconnect = 1; 871 break; 872 case 'L': 873 if (parse_network(res, ".") != 0) 874 errx(1, "invalid network: %s", optarg); 875 break; 876 case 'm': 877 if (res->size) 878 errx(1, "memory specified multiple times"); 879 parse_size(res, optarg, "memory"); 880 break; 881 case 'n': 882 if (parse_network(res, optarg) != 0) 883 errx(1, "invalid network: %s", optarg); 884 break; 885 case 'd': 886 type = parse_disktype(optarg, &s); 887 if (realpath(s, path) == NULL) 888 err(1, "invalid disk path"); 889 if (parse_disk(res, path, type) != 0) 890 errx(1, "invalid disk: %s", optarg); 891 break; 892 case 'i': 893 if (res->nifs != -1) 894 errx(1, "interfaces specified multiple times"); 895 if (parse_ifs(res, optarg, 0) != 0) 896 errx(1, "invalid interface count: %s", optarg); 897 break; 898 case 't': 899 if (parse_instance(res, optarg) == -1) 900 errx(1, "invalid name: %s", optarg); 901 break; 902 default: 903 ctl_usage(res->ctl); 904 /* NOTREACHED */ 905 } 906 } 907 argc -= optind; 908 argv += optind; 909 910 if (argc != 1) 911 ctl_usage(res->ctl); 912 913 if (parse_vmid(res, argv[0], 0) == -1) 914 errx(1, "invalid id: %s", argv[0]); 915 916 for (i = res->nnets; i < res->nifs; i++) { 917 /* Add interface that is not attached to a switch */ 918 if (parse_network(res, "") == -1) 919 return (-1); 920 } 921 if (res->nnets > res->nifs) 922 res->nifs = res->nnets; 923 924 return (vmmaction(res)); 925} 926 927int 928ctl_stop(struct parse_result *res, int argc, char *argv[]) 929{ 930 int ch; 931 932 while ((ch = getopt(argc, argv, "afw")) != -1) { 933 switch (ch) { 934 case 'f': 935 res->flags |= VMOP_FORCE; 936 break; 937 case 'w': 938 res->flags |= VMOP_WAIT; 939 break; 940 case 'a': 941 res->action = CMD_STOPALL; 942 break; 943 default: 944 ctl_usage(res->ctl); 945 /* NOTREACHED */ 946 } 947 } 948 argc -= optind; 949 argv += optind; 950 951 if (res->action == CMD_STOPALL) { 952 if (argc != 0) 953 ctl_usage(res->ctl); 954 } else { 955 if (argc != 1) 956 ctl_usage(res->ctl); 957 if (parse_vmid(res, argv[0], 0) == -1) 958 errx(1, "invalid id: %s", argv[0]); 959 } 960 961 return (vmmaction(res)); 962} 963 964int 965ctl_console(struct parse_result *res, int argc, char *argv[]) 966{ 967 if (argc == 2) { 968 if (parse_vmid(res, argv[1], 0) == -1) 969 errx(1, "invalid id: %s", argv[1]); 970 } else if (argc != 2) 971 ctl_usage(res->ctl); 972 973 return (vmmaction(res)); 974} 975 976int 977ctl_waitfor(struct parse_result *res, int argc, char *argv[]) 978{ 979 if (argc == 2) { 980 if (parse_vmid(res, argv[1], 0) == -1) 981 errx(1, "invalid id: %s", argv[1]); 982 } else if (argc != 2) 983 ctl_usage(res->ctl); 984 985 return (vmmaction(res)); 986} 987 988int 989ctl_pause(struct parse_result *res, int argc, char *argv[]) 990{ 991 if (argc == 2) { 992 if (parse_vmid(res, argv[1], 0) == -1) 993 errx(1, "invalid id: %s", argv[1]); 994 } else if (argc != 2) 995 ctl_usage(res->ctl); 996 997 return (vmmaction(res)); 998} 999 1000int 1001ctl_unpause(struct parse_result *res, int argc, char *argv[]) 1002{ 1003 if (argc == 2) { 1004 if (parse_vmid(res, argv[1], 0) == -1) 1005 errx(1, "invalid id: %s", argv[1]); 1006 } else if (argc != 2) 1007 ctl_usage(res->ctl); 1008 1009 return (vmmaction(res)); 1010} 1011 1012int 1013ctl_send(struct parse_result *res, int argc, char *argv[]) 1014{ 1015 if (pledge("stdio unix sendfd unveil", NULL) == -1) 1016 err(1, "pledge"); 1017 if (argc == 2) { 1018 if (parse_vmid(res, argv[1], 0) == -1) 1019 errx(1, "invalid id: %s", argv[1]); 1020 } else if (argc != 2) 1021 ctl_usage(res->ctl); 1022 1023 return (vmmaction(res)); 1024} 1025 1026int 1027ctl_receive(struct parse_result *res, int argc, char *argv[]) 1028{ 1029 if (pledge("stdio unix sendfd unveil", NULL) == -1) 1030 err(1, "pledge"); 1031 if (argc == 2) { 1032 if (parse_vmid(res, argv[1], 1) == -1) 1033 errx(1, "invalid id: %s", argv[1]); 1034 } else if (argc != 2) 1035 ctl_usage(res->ctl); 1036 1037 return (vmmaction(res)); 1038} 1039 1040__dead void 1041ctl_openconsole(const char *name) 1042{ 1043 closefrom(STDERR_FILENO + 1); 1044 if (unveil(VMCTL_CU, "x") == -1) 1045 err(1, "unveil %s", VMCTL_CU); 1046 execl(VMCTL_CU, VMCTL_CU, "-r", "-l", name, "-s", "115200", 1047 (char *)NULL); 1048 err(1, "failed to open the console"); 1049} 1050