1 /* $OpenBSD: main.c,v 1.32 2017/08/15 15:51:54 jasper 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] [-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 bzero(&sun, 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 if (ret) { 208 errno = ret; 209 err(1, "start VM operation failed"); 210 } 211 break; 212 case CMD_STOP: 213 terminate_vm(res->id, res->name); 214 break; 215 case CMD_STATUS: 216 get_info_vm(res->id, res->name, 0); 217 break; 218 case CMD_CONSOLE: 219 get_info_vm(res->id, res->name, 1); 220 break; 221 case CMD_LOAD: 222 imsg_compose(ibuf, IMSG_VMDOP_LOAD, 0, 0, -1, 223 res->path, strlen(res->path) + 1); 224 break; 225 case CMD_LOG: 226 imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1, 227 &res->verbose, sizeof(res->verbose)); 228 break; 229 case CMD_RELOAD: 230 imsg_compose(ibuf, IMSG_VMDOP_RELOAD, 0, 0, -1, NULL, 0); 231 break; 232 case CMD_RESET: 233 imsg_compose(ibuf, IMSG_CTL_RESET, 0, 0, -1, 234 &res->mode, sizeof(res->mode)); 235 break; 236 case CMD_PAUSE: 237 pause_vm(res->id, res->name); 238 break; 239 case CMD_UNPAUSE: 240 unpause_vm(res->id, res->name); 241 break; 242 case CMD_SEND: 243 send_vm(res->id, res->name); 244 done = 1; 245 break; 246 case CMD_RECEIVE: 247 vm_receive(res->id, res->name); 248 break; 249 case CMD_CREATE: 250 case NONE: 251 break; 252 } 253 254 action = res->action; 255 parse_free(res); 256 257 while (ibuf->w.queued) 258 if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) 259 err(1, "write error"); 260 261 while (!done) { 262 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 263 errx(1, "imsg_read error"); 264 if (n == 0) 265 errx(1, "pipe closed"); 266 267 while (!done) { 268 if ((n = imsg_get(ibuf, &imsg)) == -1) 269 errx(1, "imsg_get error"); 270 if (n == 0) 271 break; 272 273 if (imsg.hdr.type == IMSG_CTL_FAIL) { 274 if (IMSG_DATA_SIZE(&imsg) == sizeof(ret)) 275 memcpy(&ret, imsg.data, sizeof(ret)); 276 else 277 ret = 0; 278 if (ret != 0) { 279 memcpy(&ret, imsg.data, sizeof(ret)); 280 errno = ret; 281 err(1, "command failed"); 282 } else 283 errx(1, "command failed"); 284 } 285 286 ret = 0; 287 switch (action) { 288 case CMD_START: 289 done = vm_start_complete(&imsg, &ret, 290 tty_autoconnect); 291 break; 292 case CMD_STOP: 293 done = terminate_vm_complete(&imsg, &ret); 294 break; 295 case CMD_CONSOLE: 296 case CMD_STATUS: 297 done = add_info(&imsg, &ret); 298 break; 299 case CMD_PAUSE: 300 done = pause_vm_complete(&imsg, &ret); 301 break; 302 case CMD_RECEIVE: 303 done = vm_start_complete(&imsg, &ret, 0); 304 break; 305 case CMD_UNPAUSE: 306 done = unpause_vm_complete(&imsg, &ret); 307 break; 308 default: 309 done = 1; 310 break; 311 } 312 313 imsg_free(&imsg); 314 } 315 } 316 317 return (0); 318 } 319 320 void 321 parse_free(struct parse_result *res) 322 { 323 size_t i; 324 325 free(res->name); 326 free(res->path); 327 for (i = 0; i < res->ndisks; i++) 328 free(res->disks[i]); 329 free(res->disks); 330 memset(res, 0, sizeof(*res)); 331 } 332 333 int 334 parse_ifs(struct parse_result *res, char *word, int val) 335 { 336 const char *error; 337 338 if (word != NULL) { 339 val = strtonum(word, 0, INT_MAX, &error); 340 if (error != NULL) { 341 warnx("invalid count \"%s\": %s", word, error); 342 return (-1); 343 } 344 } 345 res->nifs = val; 346 347 return (0); 348 } 349 350 int 351 parse_network(struct parse_result *res, char *word) 352 { 353 char **nets; 354 char *s; 355 356 if ((nets = reallocarray(res->nets, res->nnets + 1, 357 sizeof(char *))) == NULL) { 358 warn("reallocarray"); 359 return (-1); 360 } 361 if ((s = strdup(word)) == NULL) { 362 warn("strdup"); 363 return (-1); 364 } 365 nets[res->nnets] = s; 366 res->nets = nets; 367 res->nnets++; 368 369 return (0); 370 } 371 372 int 373 parse_size(struct parse_result *res, char *word, long long val) 374 { 375 if (word != NULL) { 376 if (scan_scaled(word, &val) != 0) { 377 warn("invalid size: %s", word); 378 return (-1); 379 } 380 } 381 382 if (val < (1024 * 1024)) { 383 warnx("size must be at least one megabyte"); 384 return (-1); 385 } else 386 res->size = val / 1024 / 1024; 387 388 if ((res->size * 1024 * 1024) != val) 389 warnx("size rounded to %lld megabytes", res->size); 390 391 return (0); 392 } 393 394 int 395 parse_disk(struct parse_result *res, char *word) 396 { 397 char **disks; 398 char *s; 399 400 if ((disks = reallocarray(res->disks, res->ndisks + 1, 401 sizeof(char *))) == NULL) { 402 warn("reallocarray"); 403 return (-1); 404 } 405 if ((s = strdup(word)) == NULL) { 406 warn("strdup"); 407 return (-1); 408 } 409 disks[res->ndisks] = s; 410 res->disks = disks; 411 res->ndisks++; 412 413 return (0); 414 } 415 416 int 417 parse_vmid(struct parse_result *res, char *word, int needname) 418 { 419 const char *error; 420 uint32_t id; 421 422 if (word == NULL) { 423 warnx("missing vmid argument"); 424 return (-1); 425 } 426 id = strtonum(word, 0, UINT32_MAX, &error); 427 if (error == NULL) { 428 if (needname) { 429 warnx("invalid vm name"); 430 return (-1); 431 } else { 432 res->id = id; 433 res->name = NULL; 434 } 435 } else { 436 if (strlen(word) >= VMM_MAX_NAME_LEN) { 437 warnx("name too long"); 438 return (-1); 439 } 440 res->id = 0; 441 if ((res->name = strdup(word)) == NULL) 442 errx(1, "strdup"); 443 } 444 445 return (0); 446 } 447 448 int 449 ctl_create(struct parse_result *res, int argc, char *argv[]) 450 { 451 int ch, ret; 452 const char *paths[2]; 453 454 if (argc < 2) 455 ctl_usage(res->ctl); 456 457 paths[0] = argv[1]; 458 paths[1] = NULL; 459 if (pledge("stdio rpath wpath cpath", NULL) == -1) 460 err(1, "pledge"); 461 argc--; 462 argv++; 463 464 while ((ch = getopt(argc, argv, "s:")) != -1) { 465 switch (ch) { 466 case 's': 467 if (parse_size(res, optarg, 0) != 0) 468 errx(1, "invalid size: %s", optarg); 469 break; 470 default: 471 ctl_usage(res->ctl); 472 /* NOTREACHED */ 473 } 474 } 475 476 if (res->size == 0) { 477 fprintf(stderr, "missing size argument\n"); 478 ctl_usage(res->ctl); 479 } 480 ret = create_imagefile(paths[0], res->size); 481 if (ret != 0) { 482 errno = ret; 483 err(1, "create imagefile operation failed"); 484 } else 485 warnx("imagefile created"); 486 return (0); 487 } 488 489 int 490 ctl_status(struct parse_result *res, int argc, char *argv[]) 491 { 492 if (argc == 2) { 493 if (parse_vmid(res, argv[1], 0) == -1) 494 errx(1, "invalid id: %s", argv[1]); 495 } else if (argc > 2) 496 ctl_usage(res->ctl); 497 498 return (vmmaction(res)); 499 } 500 501 int 502 ctl_load(struct parse_result *res, int argc, char *argv[]) 503 { 504 if (argc != 2) 505 ctl_usage(res->ctl); 506 507 if ((res->path = strdup(argv[1])) == NULL) 508 err(1, "strdup"); 509 510 return (vmmaction(res)); 511 } 512 513 int 514 ctl_log(struct parse_result *res, int argc, char *argv[]) 515 { 516 if (argc != 2) 517 ctl_usage(res->ctl); 518 519 if (strncasecmp("brief", argv[1], strlen(argv[1])) == 0) 520 res->verbose = 0; 521 else if (strncasecmp("verbose", argv[1], strlen(argv[1])) == 0) 522 res->verbose = 2; 523 else 524 ctl_usage(res->ctl); 525 526 return (vmmaction(res)); 527 } 528 529 int 530 ctl_reload(struct parse_result *res, int argc, char *argv[]) 531 { 532 if (argc != 1) 533 ctl_usage(res->ctl); 534 535 return (vmmaction(res)); 536 } 537 538 int 539 ctl_reset(struct parse_result *res, int argc, char *argv[]) 540 { 541 if (argc == 2) { 542 if (strcasecmp("all", argv[1]) == 0) 543 res->mode = CONFIG_ALL; 544 else if (strcasecmp("vms", argv[1]) == 0) 545 res->mode = CONFIG_VMS; 546 else if (strcasecmp("switches", argv[1]) == 0) 547 res->mode = CONFIG_SWITCHES; 548 else 549 ctl_usage(res->ctl); 550 } else if (argc > 2) 551 ctl_usage(res->ctl); 552 553 if (res->mode == 0) 554 res->mode = CONFIG_ALL; 555 556 return (vmmaction(res)); 557 } 558 559 int 560 ctl_start(struct parse_result *res, int argc, char *argv[]) 561 { 562 int ch, i; 563 char path[PATH_MAX]; 564 565 if (argc < 2) 566 ctl_usage(res->ctl); 567 568 if (parse_vmid(res, argv[1], 0) == -1) 569 errx(1, "invalid id: %s", argv[1]); 570 571 argc--; 572 argv++; 573 574 while ((ch = getopt(argc, argv, "b:cLm:n:d:i:")) != -1) { 575 switch (ch) { 576 case 'b': 577 if (res->path) 578 errx(1, "boot image specified multiple times"); 579 if (realpath(optarg, path) == NULL) 580 err(1, "invalid boot image path"); 581 if ((res->path = strdup(path)) == NULL) 582 errx(1, "strdup"); 583 break; 584 case 'c': 585 tty_autoconnect = 1; 586 break; 587 case 'L': 588 if (parse_network(res, ".") != 0) 589 errx(1, "invalid network: %s", optarg); 590 break; 591 case 'm': 592 if (res->size) 593 errx(1, "memory specified multiple times"); 594 if (parse_size(res, optarg, 0) != 0) 595 errx(1, "invalid memory size: %s", optarg); 596 break; 597 case 'n': 598 if (parse_network(res, optarg) != 0) 599 errx(1, "invalid network: %s", optarg); 600 break; 601 case 'd': 602 if (realpath(optarg, path) == NULL) 603 err(1, "invalid disk path"); 604 if (parse_disk(res, path) != 0) 605 errx(1, "invalid disk: %s", optarg); 606 break; 607 case 'i': 608 if (res->nifs != -1) 609 errx(1, "interfaces specified multiple times"); 610 if (parse_ifs(res, optarg, 0) != 0) 611 errx(1, "invalid interface count: %s", optarg); 612 break; 613 default: 614 ctl_usage(res->ctl); 615 /* NOTREACHED */ 616 } 617 } 618 619 for (i = res->nnets; i < res->nifs; i++) { 620 /* Add interface that is not attached to a switch */ 621 if (parse_network(res, "") == -1) 622 return (-1); 623 } 624 if (res->nnets > res->nifs) 625 res->nifs = res->nnets; 626 627 return (vmmaction(res)); 628 } 629 630 int 631 ctl_stop(struct parse_result *res, int argc, char *argv[]) 632 { 633 if (argc == 2) { 634 if (parse_vmid(res, argv[1], 0) == -1) 635 errx(1, "invalid id: %s", argv[1]); 636 } else if (argc != 2) 637 ctl_usage(res->ctl); 638 639 return (vmmaction(res)); 640 } 641 642 int 643 ctl_console(struct parse_result *res, int argc, char *argv[]) 644 { 645 if (argc == 2) { 646 if (parse_vmid(res, argv[1], 0) == -1) 647 errx(1, "invalid id: %s", argv[1]); 648 } else if (argc != 2) 649 ctl_usage(res->ctl); 650 651 return (vmmaction(res)); 652 } 653 654 int 655 ctl_pause(struct parse_result *res, int argc, char *argv[]) 656 { 657 if (argc == 2) { 658 if (parse_vmid(res, argv[1], 0) == -1) 659 errx(1, "invalid id: %s", argv[1]); 660 } else if (argc != 2) 661 ctl_usage(res->ctl); 662 663 return (vmmaction(res)); 664 } 665 666 int 667 ctl_unpause(struct parse_result *res, int argc, char *argv[]) 668 { 669 if (argc == 2) { 670 if (parse_vmid(res, argv[1], 0) == -1) 671 errx(1, "invalid id: %s", argv[1]); 672 } else if (argc != 2) 673 ctl_usage(res->ctl); 674 675 return (vmmaction(res)); 676 } 677 678 int 679 ctl_send(struct parse_result *res, int argc, char *argv[]) 680 { 681 if (pledge("stdio unix sendfd", NULL) == -1) 682 err(1, "pledge"); 683 if (argc == 2) { 684 if (parse_vmid(res, argv[1], 0) == -1) 685 errx(1, "invalid id: %s", argv[1]); 686 } else if (argc != 2) 687 ctl_usage(res->ctl); 688 689 return (vmmaction(res)); 690 } 691 692 int 693 ctl_receive(struct parse_result *res, int argc, char *argv[]) 694 { 695 if (pledge("stdio unix sendfd", NULL) == -1) 696 err(1, "pledge"); 697 if (argc == 2) { 698 if (parse_vmid(res, argv[1], 1) == -1) 699 errx(1, "invalid id: %s", argv[1]); 700 } else if (argc != 2) 701 ctl_usage(res->ctl); 702 703 return (vmmaction(res)); 704 } 705 706 __dead void 707 ctl_openconsole(const char *name) 708 { 709 closefrom(STDERR_FILENO + 1); 710 execl(VMCTL_CU, VMCTL_CU, "-l", name, "-s", "115200", (char *)NULL); 711 err(1, "failed to open the console"); 712 } 713