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