1 /* $OpenBSD: main.c,v 1.84 2024/11/21 13:39:34 claudio 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 45 static const char *socket_name = SOCKET_NAME; 46 static int ctl_sock = -1; 47 static int tty_autoconnect = 0; 48 int stat_rflag; 49 50 __dead void usage(void); 51 __dead void ctl_usage(struct ctl_command *); 52 53 int ctl_console(struct parse_result *, int, char *[]); 54 int ctl_convert(const char *, const char *, int, size_t); 55 int ctl_create(struct parse_result *, int, char *[]); 56 int ctl_load(struct parse_result *, int, char *[]); 57 int ctl_log(struct parse_result *, int, char *[]); 58 int ctl_reload(struct parse_result *, int, char *[]); 59 int ctl_reset(struct parse_result *, int, char *[]); 60 int ctl_start(struct parse_result *, int, char *[]); 61 int ctl_status(struct parse_result *, int, char *[]); 62 int ctl_stop(struct parse_result *, int, char *[]); 63 int ctl_waitfor(struct parse_result *, int, char *[]); 64 int ctl_pause(struct parse_result *, int, char *[]); 65 int ctl_unpause(struct parse_result *, int, char *[]); 66 int ctl_send(struct parse_result *, int, char *[]); 67 int ctl_receive(struct parse_result *, int, char *[]); 68 69 struct 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 92 usage(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 102 ctl_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 111 int 112 main(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 139 int 140 parse(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 185 int 186 vmmaction(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 if (imsgbuf_init(ibuf, ctl_sock) == -1) 213 err(1, "imsgbuf_init"); 214 imsgbuf_allow_fdpass(ibuf); 215 } 216 217 switch (res->action) { 218 case CMD_START: 219 ret = vm_start(res->id, res->name, res->size, res->nifs, 220 res->nets, res->ndisks, res->disks, res->disktypes, 221 res->path, res->isopath, res->instance, res->bootdevice); 222 if (ret) { 223 errno = ret; 224 err(1, "start VM operation failed"); 225 } 226 break; 227 case CMD_STOP: 228 terminate_vm(res->id, res->name, res->flags); 229 break; 230 case CMD_STATUS: 231 case CMD_CONSOLE: 232 case CMD_STOPALL: 233 get_info_vm(res->id, res->name, res->action, res->flags); 234 break; 235 case CMD_LOAD: 236 imsg_compose(ibuf, IMSG_VMDOP_LOAD, 0, 0, -1, 237 res->path, strlen(res->path) + 1); 238 break; 239 case CMD_LOG: 240 imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1, 241 &res->verbose, sizeof(res->verbose)); 242 break; 243 case CMD_RELOAD: 244 imsg_compose(ibuf, IMSG_VMDOP_RELOAD, 0, 0, -1, NULL, 0); 245 break; 246 case CMD_RESET: 247 imsg_compose(ibuf, IMSG_CTL_RESET, 0, 0, -1, 248 &res->mode, sizeof(res->mode)); 249 break; 250 case CMD_WAITFOR: 251 waitfor_vm(res->id, res->name); 252 break; 253 case CMD_PAUSE: 254 pause_vm(res->id, res->name); 255 break; 256 case CMD_UNPAUSE: 257 unpause_vm(res->id, res->name); 258 break; 259 case CMD_SEND: 260 send_vm(res->id, res->name); 261 done = 1; 262 ret = 0; 263 break; 264 case CMD_RECEIVE: 265 vm_receive(res->id, res->name); 266 break; 267 case CMD_CREATE: 268 case NONE: 269 /* The action is not expected here */ 270 errx(1, "invalid action %u", res->action); 271 break; 272 } 273 274 action = res->action; 275 flags = res->flags; 276 parse_free(res); 277 278 if (imsgbuf_flush(ibuf) == -1) 279 err(1, "write error"); 280 281 while (!done) { 282 if ((n = imsgbuf_read(ibuf)) == -1) 283 err(1, "read error"); 284 if (n == 0) 285 errx(1, "pipe closed"); 286 287 while (!done) { 288 if ((n = imsg_get(ibuf, &imsg)) == -1) 289 errx(1, "imsg_get error"); 290 if (n == 0) 291 break; 292 293 if (imsg.hdr.type == IMSG_CTL_FAIL) { 294 if (IMSG_DATA_SIZE(&imsg) == sizeof(ret)) 295 memcpy(&ret, imsg.data, sizeof(ret)); 296 else 297 ret = 0; 298 if (ret != 0) { 299 errno = ret; 300 err(1, "command failed"); 301 } else 302 errx(1, "command failed"); 303 } 304 305 ret = 0; 306 switch (action) { 307 case CMD_START: 308 done = vm_start_complete(&imsg, &ret, 309 tty_autoconnect); 310 break; 311 case CMD_WAITFOR: 312 flags = VMOP_WAIT; 313 /* FALLTHROUGH */ 314 case CMD_STOP: 315 done = terminate_vm_complete(&imsg, &ret, 316 flags); 317 break; 318 case CMD_CONSOLE: 319 case CMD_STATUS: 320 case CMD_STOPALL: 321 done = add_info(&imsg, &ret); 322 break; 323 case CMD_PAUSE: 324 done = pause_vm_complete(&imsg, &ret); 325 break; 326 case CMD_RECEIVE: 327 done = vm_start_complete(&imsg, &ret, 0); 328 break; 329 case CMD_UNPAUSE: 330 done = unpause_vm_complete(&imsg, &ret); 331 break; 332 default: 333 done = 1; 334 break; 335 } 336 337 imsg_free(&imsg); 338 } 339 } 340 341 if (ret) 342 return (1); 343 else 344 return (0); 345 } 346 347 void 348 parse_free(struct parse_result *res) 349 { 350 size_t i; 351 352 free(res->name); 353 free(res->path); 354 free(res->isopath); 355 free(res->instance); 356 for (i = 0; i < res->ndisks; i++) 357 free(res->disks[i]); 358 free(res->disks); 359 free(res->disktypes); 360 memset(res, 0, sizeof(*res)); 361 } 362 363 int 364 parse_ifs(struct parse_result *res, char *word, int val) 365 { 366 const char *error; 367 368 if (word != NULL) { 369 val = strtonum(word, 1, INT_MAX, &error); 370 if (error != NULL) { 371 warnx("count is %s: %s", error, word); 372 return (-1); 373 } 374 } 375 res->nifs = val; 376 377 return (0); 378 } 379 380 int 381 parse_network(struct parse_result *res, char *word) 382 { 383 char **nets; 384 char *s; 385 386 if ((nets = reallocarray(res->nets, res->nnets + 1, 387 sizeof(char *))) == NULL) { 388 warn("reallocarray"); 389 return (-1); 390 } 391 if ((s = strdup(word)) == NULL) { 392 warn("strdup"); 393 return (-1); 394 } 395 nets[res->nnets] = s; 396 res->nets = nets; 397 res->nnets++; 398 399 return (0); 400 } 401 402 void 403 parse_size(struct parse_result *res, char *word, const char *type) 404 { 405 char result[FMT_SCALED_STRSIZE]; 406 long long val = 0; 407 408 if (word != NULL) { 409 if (scan_scaled(word, &val) != 0) 410 err(1, "invalid %s size: %s", type, word); 411 } 412 413 if (val < (1024 * 1024)) 414 errx(1, "%s size must be at least 1MB", type); 415 416 if (strcmp("memory", type) == 0 && val > VMM_MAX_VM_MEM_SIZE) { 417 if (fmt_scaled(VMM_MAX_VM_MEM_SIZE, result) == 0) 418 errx(1, "memory size too large (limit is %s)", result); 419 else 420 errx(1, "memory size too large"); 421 } 422 423 /* Round down to the megabyte. */ 424 res->size = (val / (1024 * 1024)) * (1024 * 1024); 425 426 if (res->size != (size_t)val) { 427 if (fmt_scaled(res->size, result) == 0) 428 warnx("%s size rounded to %s", type, result); 429 else 430 warnx("%s size rounded to %zuB", type, res->size); 431 } 432 } 433 434 int 435 parse_disktype(const char *s, const char **ret) 436 { 437 char buf[BUFSIZ]; 438 const char *ext; 439 int fd; 440 ssize_t len; 441 442 *ret = s; 443 444 /* Try to parse the explicit format (qcow2:disk.qc2) */ 445 if (strstr(s, RAW_FMT) == s && *(s + strlen(RAW_FMT)) == ':') { 446 *ret = s + strlen(RAW_FMT) + 1; 447 return (VMDF_RAW); 448 } 449 if (strstr(s, QCOW2_FMT) == s && *(s + strlen(QCOW2_FMT)) == ':') { 450 *ret = s + strlen(QCOW2_FMT) + 1; 451 return (VMDF_QCOW2); 452 } 453 454 /* Or try to derive the format from the file signature */ 455 if ((fd = open(s, O_RDONLY)) != -1) { 456 len = read(fd, buf, sizeof(buf)); 457 close(fd); 458 459 if (len >= (ssize_t)strlen(VM_MAGIC_QCOW) && 460 strncmp(buf, VM_MAGIC_QCOW, 461 strlen(VM_MAGIC_QCOW)) == 0) { 462 /* Return qcow2, the version will be checked later */ 463 return (VMDF_QCOW2); 464 } 465 } 466 467 /* 468 * Use the extension as a last option. This is needed for 469 * 'vmctl create' as the file, and the signature, doesn't 470 * exist yet. 471 */ 472 if ((ext = strrchr(s, '.')) != NULL && *(++ext) != '\0') { 473 if (strcasecmp(ext, RAW_FMT) == 0) 474 return (VMDF_RAW); 475 else if (strcasecmp(ext, QCOW2_FMT) == 0) 476 return (VMDF_QCOW2); 477 } 478 479 /* Fallback to raw */ 480 return (VMDF_RAW); 481 } 482 483 int 484 parse_disk(struct parse_result *res, char *word, int type) 485 { 486 char **disks; 487 int *disktypes; 488 char *s; 489 490 if ((disks = reallocarray(res->disks, res->ndisks + 1, 491 sizeof(char *))) == NULL) { 492 warn("reallocarray"); 493 return (-1); 494 } 495 if ((disktypes = reallocarray(res->disktypes, res->ndisks + 1, 496 sizeof(int))) == NULL) { 497 warn("reallocarray"); 498 return -1; 499 } 500 if ((s = strdup(word)) == NULL) { 501 warn("strdup"); 502 return (-1); 503 } 504 disks[res->ndisks] = s; 505 disktypes[res->ndisks] = type; 506 res->disks = disks; 507 res->disktypes = disktypes; 508 res->ndisks++; 509 510 return (0); 511 } 512 513 int 514 parse_vmid(struct parse_result *res, char *word, int needname) 515 { 516 const char *error; 517 uint32_t id; 518 519 if (word == NULL) { 520 warnx("missing vmid argument"); 521 return (-1); 522 } 523 if (*word == '-') { 524 /* don't print a warning to allow command line options */ 525 return (-1); 526 } 527 id = strtonum(word, 0, UINT32_MAX, &error); 528 if (error == NULL) { 529 if (needname) { 530 warnx("invalid vm name"); 531 return (-1); 532 } else { 533 res->id = id; 534 res->name = NULL; 535 } 536 } else { 537 if (strlen(word) >= VMM_MAX_NAME_LEN) { 538 warnx("name too long"); 539 return (-1); 540 } 541 res->id = 0; 542 if ((res->name = strdup(word)) == NULL) 543 errx(1, "strdup"); 544 } 545 546 return (0); 547 } 548 549 int 550 parse_instance(struct parse_result *res, char *word) 551 { 552 if (strlen(word) >= VMM_MAX_NAME_LEN) { 553 warnx("instance vm name too long"); 554 return (-1); 555 } 556 res->id = 0; 557 if ((res->instance = strdup(word)) == NULL) 558 errx(1, "strdup"); 559 560 return (0); 561 } 562 563 int 564 ctl_create(struct parse_result *res, int argc, char *argv[]) 565 { 566 int ch, ret, type; 567 const char *disk, *format, *base = NULL, *input = NULL; 568 569 while ((ch = getopt(argc, argv, "b:i:s:")) != -1) { 570 switch (ch) { 571 case 'b': 572 base = optarg; 573 break; 574 case 'i': 575 input = optarg; 576 break; 577 case 's': 578 parse_size(res, optarg, "disk"); 579 break; 580 default: 581 ctl_usage(res->ctl); 582 /* NOTREACHED */ 583 } 584 } 585 argc -= optind; 586 argv += optind; 587 588 if (argc != 1) 589 ctl_usage(res->ctl); 590 591 type = parse_disktype(argv[0], &disk); 592 593 if (pledge("stdio rpath wpath cpath", NULL) == -1) 594 err(1, "pledge"); 595 596 if (input) { 597 if (base && input) 598 errx(1, "conflicting -b and -i arguments"); 599 return ctl_convert(input, disk, type, res->size); 600 } 601 602 if (base && type != VMDF_QCOW2) 603 errx(1, "base images require qcow2 disk format"); 604 if (res->size == 0 && !base) { 605 fprintf(stderr, "could not create %s: missing size argument\n", 606 disk); 607 ctl_usage(res->ctl); 608 } 609 610 if ((ret = create_imagefile(type, disk, base, res->size, &format)) != 0) { 611 errno = ret; 612 err(1, "create imagefile operation failed"); 613 } else 614 warnx("%s imagefile created", format); 615 616 return (0); 617 } 618 619 int 620 ctl_convert(const char *srcfile, const char *dstfile, int dsttype, size_t dstsize) 621 { 622 struct { 623 int fd; 624 int type; 625 struct virtio_backing file; 626 const char *disk; 627 off_t size; 628 } src, dst; 629 int ret; 630 const char *format, *errstr = NULL; 631 uint8_t *buf = NULL, *zerobuf = NULL; 632 size_t buflen; 633 ssize_t len, rlen; 634 off_t off; 635 636 memset(&src, 0, sizeof(src)); 637 memset(&dst, 0, sizeof(dst)); 638 639 src.type = parse_disktype(srcfile, &src.disk); 640 dst.type = dsttype; 641 dst.disk = dstfile; 642 643 if ((src.fd = open_imagefile(src.type, src.disk, O_RDONLY, 644 &src.file, &src.size)) == -1) { 645 errstr = "failed to open source image file"; 646 goto done; 647 } 648 649 if (dstsize == 0) 650 dstsize = src.size; 651 if (dstsize < (size_t)src.size) { 652 errstr = "size cannot be smaller than input disk size"; 653 goto done; 654 } 655 656 /* align to megabytes */ 657 dst.size = ALIGNSZ(dstsize, 1048576); 658 659 if ((ret = create_imagefile(dst.type, dst.disk, NULL, dst.size, 660 &format)) != 0) { 661 errstr = "failed to create destination image file"; 662 goto done; 663 } 664 665 if ((dst.fd = open_imagefile(dst.type, dst.disk, O_RDWR, 666 &dst.file, &dst.size)) == -1) { 667 errstr = "failed to open destination image file"; 668 goto done; 669 } 670 671 if (pledge("stdio", NULL) == -1) 672 err(1, "pledge"); 673 674 /* 675 * Use 64k buffers by default. This could also be adjusted to 676 * the backend cluster size. 677 */ 678 buflen = 1 << 16; 679 if ((buf = calloc(1, buflen)) == NULL || 680 (zerobuf = calloc(1, buflen)) == NULL) { 681 errstr = "failed to allocated buffers"; 682 goto done; 683 } 684 685 for (off = 0; off < dst.size; off += len) { 686 /* Read input from the source image */ 687 if (off < src.size) { 688 len = MIN((off_t)buflen, src.size - off); 689 if ((rlen = src.file.pread(src.file.p, 690 buf, (size_t)len, off)) != len) { 691 errno = EIO; 692 errstr = "failed to read from source"; 693 goto done; 694 } 695 } else 696 len = 0; 697 698 /* and pad the remaining bytes */ 699 if (len < (ssize_t)buflen) { 700 log_debug("%s: padding %zd zero bytes at offset %lld", 701 format, buflen - len, off + len); 702 memset(buf + len, 0, buflen - len); 703 len = buflen; 704 } 705 706 /* 707 * No need to copy empty buffers. This allows the backend, 708 * sparse files or QCOW2 images, to save space in the 709 * destination file. 710 */ 711 if (memcmp(buf, zerobuf, buflen) == 0) 712 continue; 713 714 log_debug("%s: writing %zd of %lld bytes at offset %lld", 715 format, len, dst.size, off); 716 717 if ((rlen = dst.file.pwrite(dst.file.p, 718 buf, (size_t)len, off)) != len) { 719 errno = EIO; 720 errstr = "failed to write to destination"; 721 goto done; 722 } 723 } 724 725 if (dstsize < (size_t)dst.size) 726 warnx("destination size rounded to %lld megabytes", 727 dst.size / 1048576); 728 729 done: 730 free(buf); 731 free(zerobuf); 732 if (src.file.p != NULL) 733 src.file.close(src.file.p, 0); 734 if (dst.file.p != NULL) 735 dst.file.close(dst.file.p, 0); 736 if (errstr != NULL) 737 errx(1, "%s", errstr); 738 else 739 warnx("%s imagefile created", format); 740 741 return (0); 742 } 743 744 int 745 ctl_status(struct parse_result *res, int argc, char *argv[]) 746 { 747 int ch; 748 749 while ((ch = getopt(argc, argv, "r")) != -1) { 750 switch (ch) { 751 case 'r': 752 stat_rflag = 1; 753 break; 754 default: 755 ctl_usage(res->ctl); 756 /* NOTREACHED */ 757 } 758 } 759 argc -= optind; 760 argv += optind; 761 762 if (argc == 1) { 763 if (parse_vmid(res, argv[0], 0) == -1) 764 errx(1, "invalid id: %s", argv[0]); 765 } else if (argc > 1) 766 ctl_usage(res->ctl); 767 768 return (vmmaction(res)); 769 } 770 771 int 772 ctl_load(struct parse_result *res, int argc, char *argv[]) 773 { 774 if (argc != 2) 775 ctl_usage(res->ctl); 776 777 if ((res->path = strdup(argv[1])) == NULL) 778 err(1, "strdup"); 779 780 return (vmmaction(res)); 781 } 782 783 int 784 ctl_log(struct parse_result *res, int argc, char *argv[]) 785 { 786 if (argc != 2) 787 ctl_usage(res->ctl); 788 789 if (strncasecmp("brief", argv[1], strlen(argv[1])) == 0) 790 res->verbose = 0; 791 else if (strncasecmp("verbose", argv[1], strlen(argv[1])) == 0) 792 res->verbose = 2; 793 else 794 ctl_usage(res->ctl); 795 796 return (vmmaction(res)); 797 } 798 799 int 800 ctl_reload(struct parse_result *res, int argc, char *argv[]) 801 { 802 if (argc != 1) 803 ctl_usage(res->ctl); 804 805 return (vmmaction(res)); 806 } 807 808 int 809 ctl_reset(struct parse_result *res, int argc, char *argv[]) 810 { 811 if (argc == 2) { 812 if (strcasecmp("all", argv[1]) == 0) 813 res->mode = CONFIG_ALL; 814 else if (strcasecmp("vms", argv[1]) == 0) 815 res->mode = CONFIG_VMS; 816 else if (strcasecmp("switches", argv[1]) == 0) 817 res->mode = CONFIG_SWITCHES; 818 else 819 ctl_usage(res->ctl); 820 } else if (argc > 2) 821 ctl_usage(res->ctl); 822 823 if (res->mode == 0) 824 res->mode = CONFIG_ALL; 825 826 return (vmmaction(res)); 827 } 828 829 int 830 ctl_start(struct parse_result *res, int argc, char *argv[]) 831 { 832 int ch, i, type; 833 char path[PATH_MAX]; 834 const char *s; 835 836 /* We may require sendfd */ 837 if (pledge("stdio rpath exec unix getpw unveil sendfd", NULL) == -1) 838 err(1, "pledge"); 839 840 while ((ch = getopt(argc, argv, "b:B:cd:i:Lm:n:r:t:")) != -1) { 841 switch (ch) { 842 case 'b': 843 if (res->path) 844 errx(1, "boot image specified multiple times"); 845 if (realpath(optarg, path) == NULL) 846 err(1, "invalid boot image path"); 847 if ((res->path = strdup(path)) == NULL) 848 errx(1, "strdup"); 849 break; 850 case 'B': 851 if (res->bootdevice) 852 errx(1, "boot device specified multiple times"); 853 if (strcmp("disk", optarg) == 0) 854 res->bootdevice = VMBOOTDEV_DISK; 855 else if (strcmp("cdrom", optarg) == 0) 856 res->bootdevice = VMBOOTDEV_CDROM; 857 else if (strcmp("net", optarg) == 0) 858 res->bootdevice = VMBOOTDEV_NET; 859 else 860 errx(1, "unknown boot device %s", optarg); 861 break; 862 case 'r': 863 if (res->isopath) 864 errx(1, "iso image specified multiple times"); 865 if (realpath(optarg, path) == NULL) 866 err(1, "invalid iso image path"); 867 if ((res->isopath = strdup(path)) == NULL) 868 errx(1, "strdup"); 869 break; 870 case 'c': 871 tty_autoconnect = 1; 872 break; 873 case 'L': 874 if (parse_network(res, ".") != 0) 875 errx(1, "invalid network: %s", optarg); 876 break; 877 case 'm': 878 if (res->size) 879 errx(1, "memory specified multiple times"); 880 parse_size(res, optarg, "memory"); 881 break; 882 case 'n': 883 if (parse_network(res, optarg) != 0) 884 errx(1, "invalid network: %s", optarg); 885 break; 886 case 'd': 887 type = parse_disktype(optarg, &s); 888 if (realpath(s, path) == NULL) 889 err(1, "invalid disk path"); 890 if (parse_disk(res, path, type) != 0) 891 errx(1, "invalid disk: %s", optarg); 892 break; 893 case 'i': 894 if (res->nifs != -1) 895 errx(1, "interfaces specified multiple times"); 896 if (parse_ifs(res, optarg, 0) != 0) 897 errx(1, "invalid interface count: %s", optarg); 898 break; 899 case 't': 900 if (parse_instance(res, optarg) == -1) 901 errx(1, "invalid name: %s", optarg); 902 break; 903 default: 904 ctl_usage(res->ctl); 905 /* NOTREACHED */ 906 } 907 } 908 argc -= optind; 909 argv += optind; 910 911 if (argc != 1) 912 ctl_usage(res->ctl); 913 914 if (parse_vmid(res, argv[0], 0) == -1) 915 errx(1, "invalid id: %s", argv[0]); 916 917 for (i = res->nnets; i < res->nifs; i++) { 918 /* Add interface that is not attached to a switch */ 919 if (parse_network(res, "") == -1) 920 return (-1); 921 } 922 if (res->nnets > res->nifs) 923 res->nifs = res->nnets; 924 925 return (vmmaction(res)); 926 } 927 928 int 929 ctl_stop(struct parse_result *res, int argc, char *argv[]) 930 { 931 int ch; 932 933 while ((ch = getopt(argc, argv, "afw")) != -1) { 934 switch (ch) { 935 case 'f': 936 res->flags |= VMOP_FORCE; 937 break; 938 case 'w': 939 res->flags |= VMOP_WAIT; 940 break; 941 case 'a': 942 res->action = CMD_STOPALL; 943 break; 944 default: 945 ctl_usage(res->ctl); 946 /* NOTREACHED */ 947 } 948 } 949 argc -= optind; 950 argv += optind; 951 952 if (res->action == CMD_STOPALL) { 953 if (argc != 0) 954 ctl_usage(res->ctl); 955 } else { 956 if (argc != 1) 957 ctl_usage(res->ctl); 958 if (parse_vmid(res, argv[0], 0) == -1) 959 errx(1, "invalid id: %s", argv[0]); 960 } 961 962 return (vmmaction(res)); 963 } 964 965 int 966 ctl_console(struct parse_result *res, int argc, char *argv[]) 967 { 968 if (argc == 2) { 969 if (parse_vmid(res, argv[1], 0) == -1) 970 errx(1, "invalid id: %s", argv[1]); 971 } else if (argc != 2) 972 ctl_usage(res->ctl); 973 974 return (vmmaction(res)); 975 } 976 977 int 978 ctl_waitfor(struct parse_result *res, int argc, char *argv[]) 979 { 980 if (argc == 2) { 981 if (parse_vmid(res, argv[1], 0) == -1) 982 errx(1, "invalid id: %s", argv[1]); 983 } else if (argc != 2) 984 ctl_usage(res->ctl); 985 986 return (vmmaction(res)); 987 } 988 989 int 990 ctl_pause(struct parse_result *res, int argc, char *argv[]) 991 { 992 if (argc == 2) { 993 if (parse_vmid(res, argv[1], 0) == -1) 994 errx(1, "invalid id: %s", argv[1]); 995 } else if (argc != 2) 996 ctl_usage(res->ctl); 997 998 return (vmmaction(res)); 999 } 1000 1001 int 1002 ctl_unpause(struct parse_result *res, int argc, char *argv[]) 1003 { 1004 if (argc == 2) { 1005 if (parse_vmid(res, argv[1], 0) == -1) 1006 errx(1, "invalid id: %s", argv[1]); 1007 } else if (argc != 2) 1008 ctl_usage(res->ctl); 1009 1010 return (vmmaction(res)); 1011 } 1012 1013 int 1014 ctl_send(struct parse_result *res, int argc, char *argv[]) 1015 { 1016 if (pledge("stdio unix sendfd unveil", NULL) == -1) 1017 err(1, "pledge"); 1018 if (argc == 2) { 1019 if (parse_vmid(res, argv[1], 0) == -1) 1020 errx(1, "invalid id: %s", argv[1]); 1021 } else if (argc != 2) 1022 ctl_usage(res->ctl); 1023 1024 return (vmmaction(res)); 1025 } 1026 1027 int 1028 ctl_receive(struct parse_result *res, int argc, char *argv[]) 1029 { 1030 if (pledge("stdio unix sendfd unveil", NULL) == -1) 1031 err(1, "pledge"); 1032 if (argc == 2) { 1033 if (parse_vmid(res, argv[1], 1) == -1) 1034 errx(1, "invalid id: %s", argv[1]); 1035 } else if (argc != 2) 1036 ctl_usage(res->ctl); 1037 1038 return (vmmaction(res)); 1039 } 1040 1041 __dead void 1042 ctl_openconsole(const char *name) 1043 { 1044 closefrom(STDERR_FILENO + 1); 1045 if (unveil(VMCTL_CU, "x") == -1) 1046 err(1, "unveil %s", VMCTL_CU); 1047 execl(VMCTL_CU, VMCTL_CU, "-r", "-l", name, "-s", "115200", 1048 (char *)NULL); 1049 err(1, "failed to open the console"); 1050 } 1051