1 /* $OpenBSD: main.c,v 1.35 2018/02/24 10:39:35 phessler 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 <util.h> 35 #include <imsg.h> 36 37 #include "vmd.h" 38 #include "proc.h" 39 #include "vmctl.h" 40 41 static const char *socket_name = SOCKET_NAME; 42 static int ctl_sock = -1; 43 static int tty_autoconnect = 0; 44 45 __dead void usage(void); 46 __dead void ctl_usage(struct ctl_command *); 47 48 int vmm_action(struct parse_result *); 49 50 int ctl_console(struct parse_result *, int, char *[]); 51 int ctl_create(struct parse_result *, int, char *[]); 52 int ctl_load(struct parse_result *, int, char *[]); 53 int ctl_log(struct parse_result *, int, char *[]); 54 int ctl_reload(struct parse_result *, int, char *[]); 55 int ctl_reset(struct parse_result *, int, char *[]); 56 int ctl_start(struct parse_result *, int, char *[]); 57 int ctl_status(struct parse_result *, int, char *[]); 58 int ctl_stop(struct parse_result *, int, char *[]); 59 int ctl_pause(struct parse_result *, int, char *[]); 60 int ctl_unpause(struct parse_result *, int, char *[]); 61 int ctl_send(struct parse_result *, int, char *[]); 62 int ctl_receive(struct parse_result *, int, char *[]); 63 64 struct ctl_command ctl_commands[] = { 65 { "console", CMD_CONSOLE, ctl_console, "id" }, 66 { "create", CMD_CREATE, ctl_create, "\"path\" -s size", 1 }, 67 { "load", CMD_LOAD, ctl_load, "\"path\"" }, 68 { "log", CMD_LOG, ctl_log, "(verbose|brief)" }, 69 { "reload", CMD_RELOAD, ctl_reload, "" }, 70 { "reset", CMD_RESET, ctl_reset, "[all|vms|switches]" }, 71 { "show", CMD_STATUS, ctl_status, "[id]" }, 72 { "start", CMD_START, ctl_start, "\"name\"" 73 " [-Lc] [-b image] [-r image] [-m size]\n" 74 "\t\t[-n switch] [-i count] [-d disk]*" }, 75 { "status", CMD_STATUS, ctl_status, "[id]" }, 76 { "stop", CMD_STOP, ctl_stop, "id" }, 77 { "pause", CMD_PAUSE, ctl_pause, "id" }, 78 { "unpause", CMD_UNPAUSE, ctl_unpause, "id" }, 79 { "send", CMD_SEND, ctl_send, "id", 1}, 80 { "receive", CMD_RECEIVE, ctl_receive, "id" , 1}, 81 { NULL } 82 }; 83 84 __dead void 85 usage(void) 86 { 87 extern char *__progname; 88 int i; 89 90 fprintf(stderr, "usage:\t%s command [arg ...]\n", 91 __progname); 92 for (i = 0; ctl_commands[i].name != NULL; i++) { 93 fprintf(stderr, "\t%s %s %s\n", __progname, 94 ctl_commands[i].name, ctl_commands[i].usage); 95 } 96 exit(1); 97 } 98 99 __dead void 100 ctl_usage(struct ctl_command *ctl) 101 { 102 extern char *__progname; 103 104 fprintf(stderr, "usage:\t%s %s %s\n", __progname, 105 ctl->name, ctl->usage); 106 exit(1); 107 } 108 109 int 110 main(int argc, char *argv[]) 111 { 112 int ch; 113 114 while ((ch = getopt(argc, argv, "")) != -1) { 115 switch (ch) { 116 default: 117 usage(); 118 /* NOTREACHED */ 119 } 120 } 121 argc -= optind; 122 argv += optind; 123 optreset = 1; 124 125 if (argc < 1) 126 usage(); 127 128 return (parse(argc, argv)); 129 } 130 131 int 132 parse(int argc, char *argv[]) 133 { 134 struct ctl_command *ctl = NULL; 135 struct parse_result res; 136 int i; 137 138 memset(&res, 0, sizeof(res)); 139 res.nifs = -1; 140 141 for (i = 0; ctl_commands[i].name != NULL; i++) { 142 if (strncmp(ctl_commands[i].name, 143 argv[0], strlen(argv[0])) == 0) { 144 if (ctl != NULL) { 145 fprintf(stderr, 146 "ambiguous argument: %s\n", argv[0]); 147 usage(); 148 } 149 ctl = &ctl_commands[i]; 150 } 151 } 152 153 if (ctl == NULL) { 154 fprintf(stderr, "unknown argument: %s\n", argv[0]); 155 usage(); 156 } 157 158 res.action = ctl->action; 159 res.ctl = ctl; 160 161 if (!ctl->has_pledge) { 162 /* pledge(2) default if command doesn't have its own pledge */ 163 if (pledge("stdio rpath exec unix getpw", NULL) == -1) 164 err(1, "pledge"); 165 } 166 if (ctl->main(&res, argc, argv) != 0) 167 err(1, "failed"); 168 169 if (ctl_sock != -1) { 170 close(ibuf->fd); 171 free(ibuf); 172 } 173 174 return (0); 175 } 176 177 int 178 vmmaction(struct parse_result *res) 179 { 180 struct sockaddr_un sun; 181 struct imsg imsg; 182 int done = 0; 183 int n; 184 int ret, action; 185 186 if (ctl_sock == -1) { 187 if ((ctl_sock = socket(AF_UNIX, 188 SOCK_STREAM|SOCK_CLOEXEC, 0)) == -1) 189 err(1, "socket"); 190 191 memset(&sun, 0, sizeof(sun)); 192 sun.sun_family = AF_UNIX; 193 strlcpy(sun.sun_path, socket_name, sizeof(sun.sun_path)); 194 195 if (connect(ctl_sock, 196 (struct sockaddr *)&sun, sizeof(sun)) == -1) 197 err(1, "connect: %s", socket_name); 198 199 if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) 200 err(1, "malloc"); 201 imsg_init(ibuf, ctl_sock); 202 } 203 204 switch (res->action) { 205 case CMD_START: 206 ret = vm_start(res->id, res->name, res->size, res->nifs, 207 res->nets, res->ndisks, res->disks, res->path, 208 res->isopath); 209 if (ret) { 210 errno = ret; 211 err(1, "start VM operation failed"); 212 } 213 break; 214 case CMD_STOP: 215 terminate_vm(res->id, res->name); 216 break; 217 case CMD_STATUS: 218 get_info_vm(res->id, res->name, 0); 219 break; 220 case CMD_CONSOLE: 221 get_info_vm(res->id, res->name, 1); 222 break; 223 case CMD_LOAD: 224 imsg_compose(ibuf, IMSG_VMDOP_LOAD, 0, 0, -1, 225 res->path, strlen(res->path) + 1); 226 break; 227 case CMD_LOG: 228 imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1, 229 &res->verbose, sizeof(res->verbose)); 230 break; 231 case CMD_RELOAD: 232 imsg_compose(ibuf, IMSG_VMDOP_RELOAD, 0, 0, -1, NULL, 0); 233 break; 234 case CMD_RESET: 235 imsg_compose(ibuf, IMSG_CTL_RESET, 0, 0, -1, 236 &res->mode, sizeof(res->mode)); 237 break; 238 case CMD_PAUSE: 239 pause_vm(res->id, res->name); 240 break; 241 case CMD_UNPAUSE: 242 unpause_vm(res->id, res->name); 243 break; 244 case CMD_SEND: 245 send_vm(res->id, res->name); 246 done = 1; 247 break; 248 case CMD_RECEIVE: 249 vm_receive(res->id, res->name); 250 break; 251 case CMD_CREATE: 252 case NONE: 253 break; 254 } 255 256 action = res->action; 257 parse_free(res); 258 259 while (ibuf->w.queued) 260 if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) 261 err(1, "write error"); 262 263 while (!done) { 264 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 265 errx(1, "imsg_read error"); 266 if (n == 0) 267 errx(1, "pipe closed"); 268 269 while (!done) { 270 if ((n = imsg_get(ibuf, &imsg)) == -1) 271 errx(1, "imsg_get error"); 272 if (n == 0) 273 break; 274 275 if (imsg.hdr.type == IMSG_CTL_FAIL) { 276 if (IMSG_DATA_SIZE(&imsg) == sizeof(ret)) 277 memcpy(&ret, imsg.data, sizeof(ret)); 278 else 279 ret = 0; 280 if (ret != 0) { 281 memcpy(&ret, imsg.data, sizeof(ret)); 282 errno = ret; 283 err(1, "command failed"); 284 } else 285 errx(1, "command failed"); 286 } 287 288 ret = 0; 289 switch (action) { 290 case CMD_START: 291 done = vm_start_complete(&imsg, &ret, 292 tty_autoconnect); 293 break; 294 case CMD_STOP: 295 done = terminate_vm_complete(&imsg, &ret); 296 break; 297 case CMD_CONSOLE: 298 case CMD_STATUS: 299 done = add_info(&imsg, &ret); 300 break; 301 case CMD_PAUSE: 302 done = pause_vm_complete(&imsg, &ret); 303 break; 304 case CMD_RECEIVE: 305 done = vm_start_complete(&imsg, &ret, 0); 306 break; 307 case CMD_UNPAUSE: 308 done = unpause_vm_complete(&imsg, &ret); 309 break; 310 default: 311 done = 1; 312 break; 313 } 314 315 imsg_free(&imsg); 316 } 317 } 318 319 return (0); 320 } 321 322 void 323 parse_free(struct parse_result *res) 324 { 325 size_t i; 326 327 free(res->name); 328 free(res->path); 329 free(res->isopath); 330 for (i = 0; i < res->ndisks; i++) 331 free(res->disks[i]); 332 free(res->disks); 333 memset(res, 0, sizeof(*res)); 334 } 335 336 int 337 parse_ifs(struct parse_result *res, char *word, int val) 338 { 339 const char *error; 340 341 if (word != NULL) { 342 val = strtonum(word, 0, INT_MAX, &error); 343 if (error != NULL) { 344 warnx("invalid count \"%s\": %s", word, error); 345 return (-1); 346 } 347 } 348 res->nifs = val; 349 350 return (0); 351 } 352 353 int 354 parse_network(struct parse_result *res, char *word) 355 { 356 char **nets; 357 char *s; 358 359 if ((nets = reallocarray(res->nets, res->nnets + 1, 360 sizeof(char *))) == NULL) { 361 warn("reallocarray"); 362 return (-1); 363 } 364 if ((s = strdup(word)) == NULL) { 365 warn("strdup"); 366 return (-1); 367 } 368 nets[res->nnets] = s; 369 res->nets = nets; 370 res->nnets++; 371 372 return (0); 373 } 374 375 int 376 parse_size(struct parse_result *res, char *word, long long val) 377 { 378 if (word != NULL) { 379 if (scan_scaled(word, &val) != 0) { 380 warn("invalid size: %s", word); 381 return (-1); 382 } 383 } 384 385 if (val < (1024 * 1024)) { 386 warnx("size must be at least one megabyte"); 387 return (-1); 388 } else 389 res->size = val / 1024 / 1024; 390 391 if ((res->size * 1024 * 1024) != val) 392 warnx("size rounded to %lld megabytes", res->size); 393 394 return (0); 395 } 396 397 int 398 parse_disk(struct parse_result *res, char *word) 399 { 400 char **disks; 401 char *s; 402 403 if ((disks = reallocarray(res->disks, res->ndisks + 1, 404 sizeof(char *))) == NULL) { 405 warn("reallocarray"); 406 return (-1); 407 } 408 if ((s = strdup(word)) == NULL) { 409 warn("strdup"); 410 return (-1); 411 } 412 disks[res->ndisks] = s; 413 res->disks = disks; 414 res->ndisks++; 415 416 return (0); 417 } 418 419 int 420 parse_vmid(struct parse_result *res, char *word, int needname) 421 { 422 const char *error; 423 uint32_t id; 424 425 if (word == NULL) { 426 warnx("missing vmid argument"); 427 return (-1); 428 } 429 id = strtonum(word, 0, UINT32_MAX, &error); 430 if (error == NULL) { 431 if (needname) { 432 warnx("invalid vm name"); 433 return (-1); 434 } else { 435 res->id = id; 436 res->name = NULL; 437 } 438 } else { 439 if (strlen(word) >= VMM_MAX_NAME_LEN) { 440 warnx("name too long"); 441 return (-1); 442 } 443 res->id = 0; 444 if ((res->name = strdup(word)) == NULL) 445 errx(1, "strdup"); 446 } 447 448 return (0); 449 } 450 451 int 452 ctl_create(struct parse_result *res, int argc, char *argv[]) 453 { 454 int ch, ret; 455 const char *paths[2]; 456 457 if (argc < 2) 458 ctl_usage(res->ctl); 459 460 paths[0] = argv[1]; 461 paths[1] = NULL; 462 if (pledge("stdio rpath wpath cpath", NULL) == -1) 463 err(1, "pledge"); 464 argc--; 465 argv++; 466 467 while ((ch = getopt(argc, argv, "s:")) != -1) { 468 switch (ch) { 469 case 's': 470 if (parse_size(res, optarg, 0) != 0) 471 errx(1, "invalid size: %s", optarg); 472 break; 473 default: 474 ctl_usage(res->ctl); 475 /* NOTREACHED */ 476 } 477 } 478 479 if (res->size == 0) { 480 fprintf(stderr, "missing size argument\n"); 481 ctl_usage(res->ctl); 482 } 483 ret = create_imagefile(paths[0], res->size); 484 if (ret != 0) { 485 errno = ret; 486 err(1, "create imagefile operation failed"); 487 } else 488 warnx("imagefile created"); 489 return (0); 490 } 491 492 int 493 ctl_status(struct parse_result *res, int argc, char *argv[]) 494 { 495 if (argc == 2) { 496 if (parse_vmid(res, argv[1], 0) == -1) 497 errx(1, "invalid id: %s", argv[1]); 498 } else if (argc > 2) 499 ctl_usage(res->ctl); 500 501 return (vmmaction(res)); 502 } 503 504 int 505 ctl_load(struct parse_result *res, int argc, char *argv[]) 506 { 507 if (argc != 2) 508 ctl_usage(res->ctl); 509 510 if ((res->path = strdup(argv[1])) == NULL) 511 err(1, "strdup"); 512 513 return (vmmaction(res)); 514 } 515 516 int 517 ctl_log(struct parse_result *res, int argc, char *argv[]) 518 { 519 if (argc != 2) 520 ctl_usage(res->ctl); 521 522 if (strncasecmp("brief", argv[1], strlen(argv[1])) == 0) 523 res->verbose = 0; 524 else if (strncasecmp("verbose", argv[1], strlen(argv[1])) == 0) 525 res->verbose = 2; 526 else 527 ctl_usage(res->ctl); 528 529 return (vmmaction(res)); 530 } 531 532 int 533 ctl_reload(struct parse_result *res, int argc, char *argv[]) 534 { 535 if (argc != 1) 536 ctl_usage(res->ctl); 537 538 return (vmmaction(res)); 539 } 540 541 int 542 ctl_reset(struct parse_result *res, int argc, char *argv[]) 543 { 544 if (argc == 2) { 545 if (strcasecmp("all", argv[1]) == 0) 546 res->mode = CONFIG_ALL; 547 else if (strcasecmp("vms", argv[1]) == 0) 548 res->mode = CONFIG_VMS; 549 else if (strcasecmp("switches", argv[1]) == 0) 550 res->mode = CONFIG_SWITCHES; 551 else 552 ctl_usage(res->ctl); 553 } else if (argc > 2) 554 ctl_usage(res->ctl); 555 556 if (res->mode == 0) 557 res->mode = CONFIG_ALL; 558 559 return (vmmaction(res)); 560 } 561 562 int 563 ctl_start(struct parse_result *res, int argc, char *argv[]) 564 { 565 int ch, i; 566 char path[PATH_MAX]; 567 568 if (argc < 2) 569 ctl_usage(res->ctl); 570 571 if (parse_vmid(res, argv[1], 0) == -1) 572 errx(1, "invalid id: %s", argv[1]); 573 574 argc--; 575 argv++; 576 577 while ((ch = getopt(argc, argv, "b:r:cLm:n:d:i:")) != -1) { 578 switch (ch) { 579 case 'b': 580 if (res->path) 581 errx(1, "boot image specified multiple times"); 582 if (realpath(optarg, path) == NULL) 583 err(1, "invalid boot image path"); 584 if ((res->path = strdup(path)) == NULL) 585 errx(1, "strdup"); 586 break; 587 case 'r': 588 if (res->isopath) 589 errx(1, "iso image specified multiple times"); 590 if (realpath(optarg, path) == NULL) 591 err(1, "invalid iso image path"); 592 if ((res->isopath = strdup(path)) == NULL) 593 errx(1, "strdup"); 594 break; 595 case 'c': 596 tty_autoconnect = 1; 597 break; 598 case 'L': 599 if (parse_network(res, ".") != 0) 600 errx(1, "invalid network: %s", optarg); 601 break; 602 case 'm': 603 if (res->size) 604 errx(1, "memory specified multiple times"); 605 if (parse_size(res, optarg, 0) != 0) 606 errx(1, "invalid memory size: %s", optarg); 607 break; 608 case 'n': 609 if (parse_network(res, optarg) != 0) 610 errx(1, "invalid network: %s", optarg); 611 break; 612 case 'd': 613 if (realpath(optarg, path) == NULL) 614 err(1, "invalid disk path"); 615 if (parse_disk(res, path) != 0) 616 errx(1, "invalid disk: %s", optarg); 617 break; 618 case 'i': 619 if (res->nifs != -1) 620 errx(1, "interfaces specified multiple times"); 621 if (parse_ifs(res, optarg, 0) != 0) 622 errx(1, "invalid interface count: %s", optarg); 623 break; 624 default: 625 ctl_usage(res->ctl); 626 /* NOTREACHED */ 627 } 628 } 629 630 for (i = res->nnets; i < res->nifs; i++) { 631 /* Add interface that is not attached to a switch */ 632 if (parse_network(res, "") == -1) 633 return (-1); 634 } 635 if (res->nnets > res->nifs) 636 res->nifs = res->nnets; 637 638 return (vmmaction(res)); 639 } 640 641 int 642 ctl_stop(struct parse_result *res, int argc, char *argv[]) 643 { 644 if (argc == 2) { 645 if (parse_vmid(res, argv[1], 0) == -1) 646 errx(1, "invalid id: %s", argv[1]); 647 } else if (argc != 2) 648 ctl_usage(res->ctl); 649 650 return (vmmaction(res)); 651 } 652 653 int 654 ctl_console(struct parse_result *res, int argc, char *argv[]) 655 { 656 if (argc == 2) { 657 if (parse_vmid(res, argv[1], 0) == -1) 658 errx(1, "invalid id: %s", argv[1]); 659 } else if (argc != 2) 660 ctl_usage(res->ctl); 661 662 return (vmmaction(res)); 663 } 664 665 int 666 ctl_pause(struct parse_result *res, int argc, char *argv[]) 667 { 668 if (argc == 2) { 669 if (parse_vmid(res, argv[1], 0) == -1) 670 errx(1, "invalid id: %s", argv[1]); 671 } else if (argc != 2) 672 ctl_usage(res->ctl); 673 674 return (vmmaction(res)); 675 } 676 677 int 678 ctl_unpause(struct parse_result *res, int argc, char *argv[]) 679 { 680 if (argc == 2) { 681 if (parse_vmid(res, argv[1], 0) == -1) 682 errx(1, "invalid id: %s", argv[1]); 683 } else if (argc != 2) 684 ctl_usage(res->ctl); 685 686 return (vmmaction(res)); 687 } 688 689 int 690 ctl_send(struct parse_result *res, int argc, char *argv[]) 691 { 692 if (pledge("stdio unix sendfd", NULL) == -1) 693 err(1, "pledge"); 694 if (argc == 2) { 695 if (parse_vmid(res, argv[1], 0) == -1) 696 errx(1, "invalid id: %s", argv[1]); 697 } else if (argc != 2) 698 ctl_usage(res->ctl); 699 700 return (vmmaction(res)); 701 } 702 703 int 704 ctl_receive(struct parse_result *res, int argc, char *argv[]) 705 { 706 if (pledge("stdio unix sendfd", NULL) == -1) 707 err(1, "pledge"); 708 if (argc == 2) { 709 if (parse_vmid(res, argv[1], 1) == -1) 710 errx(1, "invalid id: %s", argv[1]); 711 } else if (argc != 2) 712 ctl_usage(res->ctl); 713 714 return (vmmaction(res)); 715 } 716 717 __dead void 718 ctl_openconsole(const char *name) 719 { 720 closefrom(STDERR_FILENO + 1); 721 execl(VMCTL_CU, VMCTL_CU, "-l", name, "-s", "115200", (char *)NULL); 722 err(1, "failed to open the console"); 723 } 724