1 /* $OpenBSD: main.c,v 1.44 2018/10/01 09:31:15 reyk 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 <machine/vmmvar.h> 25 26 #include <err.h> 27 #include <errno.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <stdint.h> 31 #include <limits.h> 32 #include <string.h> 33 #include <unistd.h> 34 #include <fcntl.h> 35 #include <util.h> 36 #include <imsg.h> 37 38 #include "vmd.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 49 __dead void usage(void); 50 __dead void ctl_usage(struct ctl_command *); 51 52 int vmm_action(struct parse_result *); 53 54 int ctl_console(struct parse_result *, int, char *[]); 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_pause(struct parse_result *, int, char *[]); 64 int ctl_unpause(struct parse_result *, int, char *[]); 65 int ctl_send(struct parse_result *, int, char *[]); 66 int ctl_receive(struct parse_result *, int, char *[]); 67 68 struct ctl_command ctl_commands[] = { 69 { "console", CMD_CONSOLE, ctl_console, "id" }, 70 { "create", CMD_CREATE, ctl_create, "\"path\" -s size", 1 }, 71 { "load", CMD_LOAD, ctl_load, "\"path\"" }, 72 { "log", CMD_LOG, ctl_log, "[verbose|brief]" }, 73 { "reload", CMD_RELOAD, ctl_reload, "" }, 74 { "reset", CMD_RESET, ctl_reset, "[all|vms|switches]" }, 75 { "show", CMD_STATUS, ctl_status, "[id]" }, 76 { "start", CMD_START, ctl_start, "\"name\"" 77 " [-Lc] [-b image] [-r image] [-m size]\n" 78 "\t\t[-n switch] [-i count] [-d disk]* [-t name]" }, 79 { "status", CMD_STATUS, ctl_status, "[id]" }, 80 { "stop", CMD_STOP, ctl_stop, "[id|-a] [-fw]" }, 81 { "pause", CMD_PAUSE, ctl_pause, "id" }, 82 { "unpause", CMD_UNPAUSE, ctl_unpause, "id" }, 83 { "send", CMD_SEND, ctl_send, "id", 1}, 84 { "receive", CMD_RECEIVE, ctl_receive, "id" , 1}, 85 { NULL } 86 }; 87 88 __dead void 89 usage(void) 90 { 91 extern char *__progname; 92 int i; 93 94 fprintf(stderr, "usage:\t%s command [arg ...]\n", 95 __progname); 96 for (i = 0; ctl_commands[i].name != NULL; i++) { 97 fprintf(stderr, "\t%s %s %s\n", __progname, 98 ctl_commands[i].name, ctl_commands[i].usage); 99 } 100 exit(1); 101 } 102 103 __dead void 104 ctl_usage(struct ctl_command *ctl) 105 { 106 extern char *__progname; 107 108 fprintf(stderr, "usage:\t%s %s %s\n", __progname, 109 ctl->name, ctl->usage); 110 exit(1); 111 } 112 113 int 114 main(int argc, char *argv[]) 115 { 116 int ch; 117 118 while ((ch = getopt(argc, argv, "")) != -1) { 119 switch (ch) { 120 default: 121 usage(); 122 /* NOTREACHED */ 123 } 124 } 125 argc -= optind; 126 argv += optind; 127 optreset = 1; 128 129 if (argc < 1) 130 usage(); 131 132 return (parse(argc, argv)); 133 } 134 135 int 136 parse(int argc, char *argv[]) 137 { 138 struct ctl_command *ctl = NULL; 139 struct parse_result res; 140 int i; 141 142 memset(&res, 0, sizeof(res)); 143 res.nifs = -1; 144 145 for (i = 0; ctl_commands[i].name != NULL; i++) { 146 if (strncmp(ctl_commands[i].name, 147 argv[0], strlen(argv[0])) == 0) { 148 if (ctl != NULL) { 149 fprintf(stderr, 150 "ambiguous argument: %s\n", argv[0]); 151 usage(); 152 } 153 ctl = &ctl_commands[i]; 154 } 155 } 156 157 if (ctl == NULL) { 158 fprintf(stderr, "unknown argument: %s\n", argv[0]); 159 usage(); 160 } 161 162 res.action = ctl->action; 163 res.ctl = ctl; 164 165 if (!ctl->has_pledge) { 166 /* pledge(2) default if command doesn't have its own pledge */ 167 if (pledge("stdio rpath exec unix getpw unveil", NULL) == -1) 168 err(1, "pledge"); 169 } 170 if (ctl->main(&res, argc, argv) != 0) 171 err(1, "failed"); 172 173 if (ctl_sock != -1) { 174 close(ibuf->fd); 175 free(ibuf); 176 } 177 178 return (0); 179 } 180 181 int 182 vmmaction(struct parse_result *res) 183 { 184 struct sockaddr_un sun; 185 struct imsg imsg; 186 int done = 0; 187 int n; 188 int ret, action; 189 unsigned int flags; 190 191 if (ctl_sock == -1) { 192 if (unveil(SOCKET_NAME, "r") == -1) 193 err(1, "unveil"); 194 if ((ctl_sock = socket(AF_UNIX, 195 SOCK_STREAM|SOCK_CLOEXEC, 0)) == -1) 196 err(1, "socket"); 197 198 memset(&sun, 0, sizeof(sun)); 199 sun.sun_family = AF_UNIX; 200 strlcpy(sun.sun_path, socket_name, sizeof(sun.sun_path)); 201 202 if (connect(ctl_sock, 203 (struct sockaddr *)&sun, sizeof(sun)) == -1) 204 err(1, "connect: %s", socket_name); 205 206 if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) 207 err(1, "malloc"); 208 imsg_init(ibuf, ctl_sock); 209 } 210 211 switch (res->action) { 212 case CMD_START: 213 ret = vm_start(res->id, res->name, res->size, res->nifs, 214 res->nets, res->ndisks, res->disks, res->disktypes, 215 res->path, res->isopath, res->instance); 216 if (ret) { 217 errno = ret; 218 err(1, "start VM operation failed"); 219 } 220 break; 221 case CMD_STOP: 222 terminate_vm(res->id, res->name, res->flags); 223 break; 224 case CMD_STATUS: 225 case CMD_CONSOLE: 226 case CMD_STOPALL: 227 get_info_vm(res->id, res->name, res->action, res->flags); 228 break; 229 case CMD_LOAD: 230 imsg_compose(ibuf, IMSG_VMDOP_LOAD, 0, 0, -1, 231 res->path, strlen(res->path) + 1); 232 break; 233 case CMD_LOG: 234 imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1, 235 &res->verbose, sizeof(res->verbose)); 236 break; 237 case CMD_RELOAD: 238 imsg_compose(ibuf, IMSG_VMDOP_RELOAD, 0, 0, -1, NULL, 0); 239 break; 240 case CMD_RESET: 241 imsg_compose(ibuf, IMSG_CTL_RESET, 0, 0, -1, 242 &res->mode, sizeof(res->mode)); 243 break; 244 case CMD_PAUSE: 245 pause_vm(res->id, res->name); 246 break; 247 case CMD_UNPAUSE: 248 unpause_vm(res->id, res->name); 249 break; 250 case CMD_SEND: 251 send_vm(res->id, res->name); 252 done = 1; 253 break; 254 case CMD_RECEIVE: 255 vm_receive(res->id, res->name); 256 break; 257 case CMD_CREATE: 258 case NONE: 259 break; 260 } 261 262 action = res->action; 263 flags = res->flags; 264 parse_free(res); 265 266 while (ibuf->w.queued) 267 if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) 268 err(1, "write error"); 269 270 while (!done) { 271 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 272 errx(1, "imsg_read error"); 273 if (n == 0) 274 errx(1, "pipe closed"); 275 276 while (!done) { 277 if ((n = imsg_get(ibuf, &imsg)) == -1) 278 errx(1, "imsg_get error"); 279 if (n == 0) 280 break; 281 282 if (imsg.hdr.type == IMSG_CTL_FAIL) { 283 if (IMSG_DATA_SIZE(&imsg) == sizeof(ret)) 284 memcpy(&ret, imsg.data, sizeof(ret)); 285 else 286 ret = 0; 287 if (ret != 0) { 288 memcpy(&ret, imsg.data, sizeof(ret)); 289 errno = ret; 290 err(1, "command failed"); 291 } else 292 errx(1, "command failed"); 293 } 294 295 ret = 0; 296 switch (action) { 297 case CMD_START: 298 done = vm_start_complete(&imsg, &ret, 299 tty_autoconnect); 300 break; 301 case CMD_STOP: 302 done = terminate_vm_complete(&imsg, &ret, 303 flags); 304 break; 305 case CMD_CONSOLE: 306 case CMD_STATUS: 307 case CMD_STOPALL: 308 done = add_info(&imsg, &ret); 309 break; 310 case CMD_PAUSE: 311 done = pause_vm_complete(&imsg, &ret); 312 break; 313 case CMD_RECEIVE: 314 done = vm_start_complete(&imsg, &ret, 0); 315 break; 316 case CMD_UNPAUSE: 317 done = unpause_vm_complete(&imsg, &ret); 318 break; 319 default: 320 done = 1; 321 break; 322 } 323 324 imsg_free(&imsg); 325 } 326 } 327 328 return (0); 329 } 330 331 void 332 parse_free(struct parse_result *res) 333 { 334 size_t i; 335 336 free(res->name); 337 free(res->path); 338 free(res->isopath); 339 free(res->instance); 340 for (i = 0; i < res->ndisks; i++) 341 free(res->disks[i]); 342 free(res->disks); 343 free(res->disktypes); 344 memset(res, 0, sizeof(*res)); 345 } 346 347 int 348 parse_ifs(struct parse_result *res, char *word, int val) 349 { 350 const char *error; 351 352 if (word != NULL) { 353 val = strtonum(word, 0, INT_MAX, &error); 354 if (error != NULL) { 355 warnx("invalid count \"%s\": %s", word, error); 356 return (-1); 357 } 358 } 359 res->nifs = val; 360 361 return (0); 362 } 363 364 int 365 parse_network(struct parse_result *res, char *word) 366 { 367 char **nets; 368 char *s; 369 370 if ((nets = reallocarray(res->nets, res->nnets + 1, 371 sizeof(char *))) == NULL) { 372 warn("reallocarray"); 373 return (-1); 374 } 375 if ((s = strdup(word)) == NULL) { 376 warn("strdup"); 377 return (-1); 378 } 379 nets[res->nnets] = s; 380 res->nets = nets; 381 res->nnets++; 382 383 return (0); 384 } 385 386 int 387 parse_size(struct parse_result *res, char *word, long long val) 388 { 389 if (word != NULL) { 390 if (scan_scaled(word, &val) != 0) { 391 warn("invalid size: %s", word); 392 return (-1); 393 } 394 } 395 396 if (val < (1024 * 1024)) { 397 warnx("size must be at least one megabyte"); 398 return (-1); 399 } else 400 res->size = val / 1024 / 1024; 401 402 if ((res->size * 1024 * 1024) != val) 403 warnx("size rounded to %lld megabytes", res->size); 404 405 return (0); 406 } 407 408 int 409 parse_disktype(const char *s, const char **ret) 410 { 411 char buf[BUFSIZ]; 412 const char *ext; 413 int fd; 414 ssize_t len; 415 416 *ret = s; 417 418 /* Try to parse the explicit format (qcow2:disk.qc2) */ 419 if (strstr(s, RAW_FMT) == s && *(s + strlen(RAW_FMT)) == ':') { 420 *ret = s + strlen(RAW_FMT) + 1; 421 return (VMDF_RAW); 422 } 423 if (strstr(s, QCOW2_FMT) == s && *(s + strlen(QCOW2_FMT)) == ':') { 424 *ret = s + strlen(QCOW2_FMT) + 1; 425 return (VMDF_QCOW2); 426 } 427 428 /* Or try to derive the format from the file signature */ 429 if ((fd = open(s, O_RDONLY)) != -1) { 430 len = read(fd, buf, sizeof(buf)); 431 close(fd); 432 433 if (len >= (ssize_t)strlen(VM_MAGIC_QCOW) && 434 strncmp(buf, VM_MAGIC_QCOW, 435 strlen(VM_MAGIC_QCOW)) == 0) { 436 /* Return qcow2, the version will be checked later */ 437 return (VMDF_QCOW2); 438 } 439 } 440 441 /* 442 * Use the extension as a last option. This is needed for 443 * 'vmctl create' as the file, and the signature, doesn't 444 * exist yet. 445 */ 446 if ((ext = strrchr(s, '.')) != NULL && *(++ext) != '\0') { 447 if (strcasecmp(ext, RAW_FMT) == 0) 448 return (VMDF_RAW); 449 else if (strcasecmp(ext, QCOW2_FMT) == 0) 450 return (VMDF_QCOW2); 451 } 452 453 /* Fallback to raw */ 454 return (VMDF_RAW); 455 } 456 457 int 458 parse_disk(struct parse_result *res, char *word, int type) 459 { 460 char **disks; 461 int *disktypes; 462 char *s; 463 464 if ((disks = reallocarray(res->disks, res->ndisks + 1, 465 sizeof(char *))) == NULL) { 466 warn("reallocarray"); 467 return (-1); 468 } 469 if ((disktypes = reallocarray(res->disktypes, res->ndisks + 1, 470 sizeof(int))) == NULL) { 471 warn("reallocarray"); 472 return -1; 473 } 474 if ((s = strdup(word)) == NULL) { 475 warn("strdup"); 476 return (-1); 477 } 478 disks[res->ndisks] = s; 479 disktypes[res->ndisks] = type; 480 res->disks = disks; 481 res->disktypes = disktypes; 482 res->ndisks++; 483 484 return (0); 485 } 486 487 int 488 parse_vmid(struct parse_result *res, char *word, int needname) 489 { 490 const char *error; 491 uint32_t id; 492 493 if (word == NULL) { 494 warnx("missing vmid argument"); 495 return (-1); 496 } 497 if (*word == '-') { 498 /* don't print a warning to allow command line options */ 499 return (-1); 500 } 501 id = strtonum(word, 0, UINT32_MAX, &error); 502 if (error == NULL) { 503 if (needname) { 504 warnx("invalid vm name"); 505 return (-1); 506 } else { 507 res->id = id; 508 res->name = NULL; 509 } 510 } else { 511 if (strlen(word) >= VMM_MAX_NAME_LEN) { 512 warnx("name too long"); 513 return (-1); 514 } 515 res->id = 0; 516 if ((res->name = strdup(word)) == NULL) 517 errx(1, "strdup"); 518 } 519 520 return (0); 521 } 522 523 int 524 parse_instance(struct parse_result *res, char *word) 525 { 526 if (strlen(word) >= VMM_MAX_NAME_LEN) { 527 warnx("instance vm name too long"); 528 return (-1); 529 } 530 res->id = 0; 531 if ((res->instance = strdup(word)) == NULL) 532 errx(1, "strdup"); 533 534 return (0); 535 } 536 537 int 538 ctl_create(struct parse_result *res, int argc, char *argv[]) 539 { 540 int ch, ret, type; 541 const char *paths[2], *disk, *format; 542 543 if (argc < 2) 544 ctl_usage(res->ctl); 545 546 type = parse_disktype(argv[1], &disk); 547 548 paths[0] = disk; 549 paths[1] = NULL; 550 551 if (unveil(paths[0], "rwc") == -1) 552 err(1, "unveil"); 553 554 if (pledge("stdio rpath wpath cpath", NULL) == -1) 555 err(1, "pledge"); 556 argc--; 557 argv++; 558 559 while ((ch = getopt(argc, argv, "s:")) != -1) { 560 switch (ch) { 561 case 's': 562 if (parse_size(res, optarg, 0) != 0) 563 errx(1, "invalid size: %s", optarg); 564 break; 565 default: 566 ctl_usage(res->ctl); 567 /* NOTREACHED */ 568 } 569 } 570 571 if (res->size == 0) { 572 fprintf(stderr, "missing size argument\n"); 573 ctl_usage(res->ctl); 574 } 575 576 if (type == VMDF_QCOW2) { 577 format = "qcow2"; 578 ret = create_qc2_imagefile(paths[0], res->size); 579 } else { 580 format = "raw"; 581 ret = create_raw_imagefile(paths[0], res->size); 582 } 583 584 if (ret != 0) { 585 errno = ret; 586 err(1, "create imagefile operation failed"); 587 } else 588 warnx("%s imagefile created", format); 589 590 return (0); 591 } 592 593 int 594 ctl_status(struct parse_result *res, int argc, char *argv[]) 595 { 596 if (argc == 2) { 597 if (parse_vmid(res, argv[1], 0) == -1) 598 errx(1, "invalid id: %s", argv[1]); 599 } else if (argc > 2) 600 ctl_usage(res->ctl); 601 602 return (vmmaction(res)); 603 } 604 605 int 606 ctl_load(struct parse_result *res, int argc, char *argv[]) 607 { 608 if (argc != 2) 609 ctl_usage(res->ctl); 610 611 if ((res->path = strdup(argv[1])) == NULL) 612 err(1, "strdup"); 613 614 return (vmmaction(res)); 615 } 616 617 int 618 ctl_log(struct parse_result *res, int argc, char *argv[]) 619 { 620 if (argc != 2) 621 ctl_usage(res->ctl); 622 623 if (strncasecmp("brief", argv[1], strlen(argv[1])) == 0) 624 res->verbose = 0; 625 else if (strncasecmp("verbose", argv[1], strlen(argv[1])) == 0) 626 res->verbose = 2; 627 else 628 ctl_usage(res->ctl); 629 630 return (vmmaction(res)); 631 } 632 633 int 634 ctl_reload(struct parse_result *res, int argc, char *argv[]) 635 { 636 if (argc != 1) 637 ctl_usage(res->ctl); 638 639 return (vmmaction(res)); 640 } 641 642 int 643 ctl_reset(struct parse_result *res, int argc, char *argv[]) 644 { 645 if (argc == 2) { 646 if (strcasecmp("all", argv[1]) == 0) 647 res->mode = CONFIG_ALL; 648 else if (strcasecmp("vms", argv[1]) == 0) 649 res->mode = CONFIG_VMS; 650 else if (strcasecmp("switches", argv[1]) == 0) 651 res->mode = CONFIG_SWITCHES; 652 else 653 ctl_usage(res->ctl); 654 } else if (argc > 2) 655 ctl_usage(res->ctl); 656 657 if (res->mode == 0) 658 res->mode = CONFIG_ALL; 659 660 return (vmmaction(res)); 661 } 662 663 int 664 ctl_start(struct parse_result *res, int argc, char *argv[]) 665 { 666 int ch, i, type; 667 char path[PATH_MAX]; 668 const char *s; 669 670 if (argc < 2) 671 ctl_usage(res->ctl); 672 673 if (parse_vmid(res, argv[1], 0) == -1) 674 errx(1, "invalid id: %s", argv[1]); 675 676 argc--; 677 argv++; 678 679 while ((ch = getopt(argc, argv, "b:r:cLm:n:d:i:t:")) != -1) { 680 switch (ch) { 681 case 'b': 682 if (res->path) 683 errx(1, "boot image specified multiple times"); 684 if (realpath(optarg, path) == NULL) 685 err(1, "invalid boot image path"); 686 if ((res->path = strdup(path)) == NULL) 687 errx(1, "strdup"); 688 break; 689 case 'r': 690 if (res->isopath) 691 errx(1, "iso image specified multiple times"); 692 if (realpath(optarg, path) == NULL) 693 err(1, "invalid iso image path"); 694 if ((res->isopath = strdup(path)) == NULL) 695 errx(1, "strdup"); 696 break; 697 case 'c': 698 tty_autoconnect = 1; 699 break; 700 case 'L': 701 if (parse_network(res, ".") != 0) 702 errx(1, "invalid network: %s", optarg); 703 break; 704 case 'm': 705 if (res->size) 706 errx(1, "memory specified multiple times"); 707 if (parse_size(res, optarg, 0) != 0) 708 errx(1, "invalid memory size: %s", optarg); 709 break; 710 case 'n': 711 if (parse_network(res, optarg) != 0) 712 errx(1, "invalid network: %s", optarg); 713 break; 714 case 'd': 715 type = parse_disktype(optarg, &s); 716 if (realpath(s, path) == NULL) 717 err(1, "invalid disk path"); 718 if (parse_disk(res, path, type) != 0) 719 errx(1, "invalid disk: %s", optarg); 720 break; 721 case 'i': 722 if (res->nifs != -1) 723 errx(1, "interfaces specified multiple times"); 724 if (parse_ifs(res, optarg, 0) != 0) 725 errx(1, "invalid interface count: %s", optarg); 726 break; 727 case 't': 728 if (parse_instance(res, optarg) == -1) 729 errx(1, "invalid name: %s", optarg); 730 break; 731 default: 732 ctl_usage(res->ctl); 733 /* NOTREACHED */ 734 } 735 } 736 737 for (i = res->nnets; i < res->nifs; i++) { 738 /* Add interface that is not attached to a switch */ 739 if (parse_network(res, "") == -1) 740 return (-1); 741 } 742 if (res->nnets > res->nifs) 743 res->nifs = res->nnets; 744 745 return (vmmaction(res)); 746 } 747 748 int 749 ctl_stop(struct parse_result *res, int argc, char *argv[]) 750 { 751 int ch, ret; 752 753 if (argc < 2) 754 ctl_usage(res->ctl); 755 756 if ((ret = parse_vmid(res, argv[1], 0)) == 0) { 757 argc--; 758 argv++; 759 } 760 761 while ((ch = getopt(argc, argv, "afw")) != -1) { 762 switch (ch) { 763 case 'f': 764 res->flags |= VMOP_FORCE; 765 break; 766 case 'w': 767 res->flags |= VMOP_WAIT; 768 break; 769 case 'a': 770 res->action = CMD_STOPALL; 771 break; 772 default: 773 ctl_usage(res->ctl); 774 /* NOTREACHED */ 775 } 776 } 777 778 /* VM id is only expected without the -a flag */ 779 if ((res->action != CMD_STOPALL && ret == -1) || 780 (res->action == CMD_STOPALL && ret != -1)) 781 errx(1, "invalid id: %s", argv[1]); 782 783 return (vmmaction(res)); 784 } 785 786 int 787 ctl_console(struct parse_result *res, int argc, char *argv[]) 788 { 789 if (argc == 2) { 790 if (parse_vmid(res, argv[1], 0) == -1) 791 errx(1, "invalid id: %s", argv[1]); 792 } else if (argc != 2) 793 ctl_usage(res->ctl); 794 795 return (vmmaction(res)); 796 } 797 798 int 799 ctl_pause(struct parse_result *res, int argc, char *argv[]) 800 { 801 if (argc == 2) { 802 if (parse_vmid(res, argv[1], 0) == -1) 803 errx(1, "invalid id: %s", argv[1]); 804 } else if (argc != 2) 805 ctl_usage(res->ctl); 806 807 return (vmmaction(res)); 808 } 809 810 int 811 ctl_unpause(struct parse_result *res, int argc, char *argv[]) 812 { 813 if (argc == 2) { 814 if (parse_vmid(res, argv[1], 0) == -1) 815 errx(1, "invalid id: %s", argv[1]); 816 } else if (argc != 2) 817 ctl_usage(res->ctl); 818 819 return (vmmaction(res)); 820 } 821 822 int 823 ctl_send(struct parse_result *res, int argc, char *argv[]) 824 { 825 if (pledge("stdio unix sendfd unveil", NULL) == -1) 826 err(1, "pledge"); 827 if (argc == 2) { 828 if (parse_vmid(res, argv[1], 0) == -1) 829 errx(1, "invalid id: %s", argv[1]); 830 } else if (argc != 2) 831 ctl_usage(res->ctl); 832 833 return (vmmaction(res)); 834 } 835 836 int 837 ctl_receive(struct parse_result *res, int argc, char *argv[]) 838 { 839 if (pledge("stdio unix sendfd unveil", NULL) == -1) 840 err(1, "pledge"); 841 if (argc == 2) { 842 if (parse_vmid(res, argv[1], 1) == -1) 843 errx(1, "invalid id: %s", argv[1]); 844 } else if (argc != 2) 845 ctl_usage(res->ctl); 846 847 return (vmmaction(res)); 848 } 849 850 __dead void 851 ctl_openconsole(const char *name) 852 { 853 closefrom(STDERR_FILENO + 1); 854 if (unveil(VMCTL_CU, "x") == -1) 855 err(1, "unveil"); 856 execl(VMCTL_CU, VMCTL_CU, "-l", name, "-s", "115200", (char *)NULL); 857 err(1, "failed to open the console"); 858 } 859