1 /* $OpenBSD: vmctl.c,v 1.88 2023/05/02 13:02:51 jsg Exp $ */ 2 3 /* 4 * Copyright (c) 2014 Mike Larkin <mlarkin@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/queue.h> 20 #include <sys/uio.h> 21 #include <sys/stat.h> 22 #include <sys/socket.h> 23 #include <sys/un.h> 24 25 #include <ctype.h> 26 #include <err.h> 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <imsg.h> 30 #include <limits.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <unistd.h> 35 #include <util.h> 36 #include <pwd.h> 37 #include <grp.h> 38 39 #include "vmd.h" 40 #include "virtio.h" 41 #include "vmctl.h" 42 #include "atomicio.h" 43 44 extern char *__progname; 45 uint32_t info_id; 46 char info_name[VMM_MAX_NAME_LEN]; 47 enum actions info_action; 48 unsigned int info_flags; 49 50 struct imsgbuf *ibuf; 51 52 /* 53 * vm_start 54 * 55 * Request vmd to start the VM defined by the supplied parameters 56 * 57 * Parameters: 58 * start_id: optional ID of the VM 59 * name: optional name of the VM 60 * memsize: memory size (in bytes) of the VM to create 61 * nnics: number of vionet network interfaces to create 62 * nics: switch names of the network interfaces to create 63 * ndisks: number of disk images 64 * disks: disk image file names 65 * kernel: kernel image to load 66 * iso: iso image file 67 * instance: create instance from vm 68 * 69 * Return: 70 * 0 if the request to start the VM was sent successfully. 71 * ENOMEM if a memory allocation failure occurred. 72 */ 73 int 74 vm_start(uint32_t start_id, const char *name, size_t memsize, int nnics, 75 char **nics, int ndisks, char **disks, int *disktypes, char *kernel, 76 char *iso, char *instance, unsigned int bootdevice) 77 { 78 struct vmop_create_params *vmc; 79 struct vm_create_params *vcp; 80 unsigned int flags = 0; 81 int i; 82 const char *s; 83 84 if (kernel) { 85 if (unveil(kernel, "r") == -1) 86 err(1, "unveil boot kernel"); 87 } else { 88 /* We can drop sendfd promise. */ 89 if (pledge("stdio rpath exec unix getpw unveil", NULL) == -1) 90 err(1, "pledge"); 91 } 92 93 if (memsize) 94 flags |= VMOP_CREATE_MEMORY; 95 if (nnics) 96 flags |= VMOP_CREATE_NETWORK; 97 if (ndisks) 98 flags |= VMOP_CREATE_DISK; 99 if (kernel) 100 flags |= VMOP_CREATE_KERNEL; 101 if (iso) 102 flags |= VMOP_CREATE_CDROM; 103 if (instance) 104 flags |= VMOP_CREATE_INSTANCE; 105 else if (flags != 0) { 106 if (memsize < 1) 107 memsize = VM_DEFAULT_MEMORY; 108 if (ndisks > VM_MAX_DISKS_PER_VM) 109 errx(1, "too many disks"); 110 else if (kernel == NULL && ndisks == 0) 111 warnx("starting without disks"); 112 if (kernel == NULL && ndisks == 0 && !iso) 113 errx(1, "no kernel or disk/cdrom specified"); 114 if (nnics == -1) 115 nnics = 0; 116 if (nnics > VM_MAX_NICS_PER_VM) 117 errx(1, "too many network interfaces"); 118 if (kernel == NULL && nnics == 0) 119 warnx("starting without network interfaces"); 120 } 121 122 if ((vmc = calloc(1, sizeof(struct vmop_create_params))) == NULL) 123 return (ENOMEM); 124 vmc->vmc_kernel = -1; 125 vmc->vmc_flags = flags; 126 127 /* vcp includes configuration that is shared with the kernel */ 128 vcp = &vmc->vmc_params; 129 130 /* 131 * XXX: vmd(8) fills in the actual memory ranges. vmctl(8) 132 * just passes in the actual memory size here. 133 */ 134 vcp->vcp_nmemranges = 1; 135 vcp->vcp_memranges[0].vmr_size = memsize; 136 137 vcp->vcp_ncpus = 1; 138 vcp->vcp_id = start_id; 139 140 vmc->vmc_ndisks = ndisks; 141 vmc->vmc_nnics = nnics; 142 143 for (i = 0 ; i < ndisks; i++) { 144 if (strlcpy(vmc->vmc_disks[i], disks[i], 145 sizeof(vmc->vmc_disks[i])) >= 146 sizeof(vmc->vmc_disks[i])) 147 errx(1, "disk path too long"); 148 vmc->vmc_disktypes[i] = disktypes[i]; 149 } 150 for (i = 0 ; i < nnics; i++) { 151 vmc->vmc_ifflags[i] = VMIFF_UP; 152 153 if (strcmp(".", nics[i]) == 0) { 154 /* Add a "local" interface */ 155 (void)strlcpy(vmc->vmc_ifswitch[i], "", 156 sizeof(vmc->vmc_ifswitch[i])); 157 vmc->vmc_ifflags[i] |= VMIFF_LOCAL; 158 } else { 159 /* Add an interface to a switch */ 160 if (strlcpy(vmc->vmc_ifswitch[i], nics[i], 161 sizeof(vmc->vmc_ifswitch[i])) >= 162 sizeof(vmc->vmc_ifswitch[i])) 163 errx(1, "interface name too long"); 164 } 165 } 166 if (name != NULL) { 167 /* 168 * Allow VMs names with alphanumeric characters, dot, hyphen 169 * and underscore. But disallow dot, hyphen and underscore at 170 * the start. 171 */ 172 if (*name == '-' || *name == '.' || *name == '_') 173 errx(1, "invalid VM name"); 174 175 for (s = name; *s != '\0'; ++s) { 176 if (!(isalnum(*s) || *s == '.' || *s == '-' || 177 *s == '_')) 178 errx(1, "invalid VM name"); 179 } 180 181 if (strlcpy(vcp->vcp_name, name, 182 sizeof(vcp->vcp_name)) >= sizeof(vcp->vcp_name)) 183 errx(1, "vm name too long"); 184 } 185 if (kernel != NULL) { 186 if (strnlen(kernel, PATH_MAX) == PATH_MAX) 187 errx(1, "kernel name too long"); 188 vmc->vmc_kernel = open(kernel, O_RDONLY); 189 if (vmc->vmc_kernel == -1) 190 err(1, "cannot open kernel '%s'", kernel); 191 } 192 if (iso != NULL) 193 if (strlcpy(vmc->vmc_cdrom, iso, 194 sizeof(vmc->vmc_cdrom)) >= sizeof(vmc->vmc_cdrom)) 195 errx(1, "cdrom name too long"); 196 if (instance != NULL) 197 if (strlcpy(vmc->vmc_instance, instance, 198 sizeof(vmc->vmc_instance)) >= sizeof(vmc->vmc_instance)) 199 errx(1, "instance vm name too long"); 200 vmc->vmc_bootdevice = bootdevice; 201 202 imsg_compose(ibuf, IMSG_VMDOP_START_VM_REQUEST, 0, 0, vmc->vmc_kernel, 203 vmc, sizeof(struct vmop_create_params)); 204 205 free(vmc); 206 return (0); 207 } 208 209 /* 210 * vm_start_complete 211 * 212 * Callback function invoked when we are expecting an 213 * IMSG_VMDOP_START_VMM_RESPONSE message indicating the completion of 214 * a start vm operation. 215 * 216 * Parameters: 217 * imsg : response imsg received from vmd 218 * ret : return value 219 * autoconnect : open the console after startup 220 * 221 * Return: 222 * Always 1 to indicate we have processed the return message (even if it 223 * was an incorrect/failure message) 224 * 225 * The function also sets 'ret' to the error code as follows: 226 * 0 : Message successfully processed 227 * EINVAL: Invalid or unexpected response from vmd 228 * EIO : vm_start command failed 229 * ENOENT: a specified component of the VM could not be found (disk image, 230 * BIOS firmware image, etc) 231 */ 232 int 233 vm_start_complete(struct imsg *imsg, int *ret, int autoconnect) 234 { 235 struct vmop_result *vmr; 236 int res; 237 238 if (imsg->hdr.type == IMSG_VMDOP_START_VM_RESPONSE) { 239 vmr = (struct vmop_result *)imsg->data; 240 res = vmr->vmr_result; 241 if (res) { 242 switch (res) { 243 case VMD_BIOS_MISSING: 244 warnx("vmm bios firmware file not found."); 245 *ret = ENOENT; 246 break; 247 case VMD_DISK_MISSING: 248 warnx("could not open disk image(s)"); 249 *ret = ENOENT; 250 break; 251 case VMD_CDROM_MISSING: 252 warnx("could not find specified iso image"); 253 *ret = ENOENT; 254 break; 255 case VMD_CDROM_INVALID: 256 warnx("specified iso image is not a regular " 257 "file"); 258 *ret = ENOENT; 259 break; 260 case VMD_PARENT_INVALID: 261 warnx("invalid template"); 262 *ret = EINVAL; 263 break; 264 default: 265 errno = res; 266 warn("start vm command failed"); 267 *ret = EIO; 268 } 269 } else if (autoconnect) { 270 /* does not return */ 271 ctl_openconsole(vmr->vmr_ttyname); 272 } else { 273 warnx("started vm %d successfully, tty %s", 274 vmr->vmr_id, vmr->vmr_ttyname); 275 *ret = 0; 276 } 277 } else { 278 warnx("unexpected response received from vmd"); 279 *ret = EINVAL; 280 } 281 282 return (1); 283 } 284 285 void 286 send_vm(uint32_t id, const char *name) 287 { 288 struct vmop_id vid; 289 int fds[2], readn, writen; 290 long pagesz; 291 char *buf; 292 293 pagesz = getpagesize(); 294 buf = malloc(pagesz); 295 if (buf == NULL) 296 errx(1, "%s: memory allocation failure", __func__); 297 298 memset(&vid, 0, sizeof(vid)); 299 vid.vid_id = id; 300 if (name != NULL) 301 strlcpy(vid.vid_name, name, sizeof(vid.vid_name)); 302 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) == -1) { 303 warnx("%s: socketpair creation failed", __func__); 304 } else { 305 imsg_compose(ibuf, IMSG_VMDOP_SEND_VM_REQUEST, 0, 0, fds[0], 306 &vid, sizeof(vid)); 307 imsg_flush(ibuf); 308 while (1) { 309 readn = atomicio(read, fds[1], buf, pagesz); 310 if (!readn) 311 break; 312 writen = atomicio(vwrite, STDOUT_FILENO, buf, 313 readn); 314 if (writen != readn) 315 break; 316 } 317 if (vid.vid_id) 318 warnx("sent vm %d successfully", vid.vid_id); 319 else 320 warnx("sent vm %s successfully", vid.vid_name); 321 } 322 323 free(buf); 324 } 325 326 void 327 vm_receive(uint32_t id, const char *name) 328 { 329 struct vmop_id vid; 330 int fds[2], readn, writen; 331 long pagesz; 332 char *buf; 333 334 pagesz = getpagesize(); 335 buf = malloc(pagesz); 336 if (buf == NULL) 337 errx(1, "%s: memory allocation failure", __func__); 338 339 memset(&vid, 0, sizeof(vid)); 340 if (name != NULL) 341 strlcpy(vid.vid_name, name, sizeof(vid.vid_name)); 342 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) == -1) { 343 warnx("%s: socketpair creation failed", __func__); 344 } else { 345 imsg_compose(ibuf, IMSG_VMDOP_RECEIVE_VM_REQUEST, 0, 0, fds[0], 346 &vid, sizeof(vid)); 347 imsg_flush(ibuf); 348 while (1) { 349 readn = atomicio(read, STDIN_FILENO, buf, pagesz); 350 if (!readn) { 351 close(fds[1]); 352 break; 353 } 354 writen = atomicio(vwrite, fds[1], buf, readn); 355 if (writen != readn) 356 break; 357 } 358 } 359 360 free(buf); 361 } 362 363 void 364 pause_vm(uint32_t pause_id, const char *name) 365 { 366 struct vmop_id vid; 367 368 memset(&vid, 0, sizeof(vid)); 369 vid.vid_id = pause_id; 370 if (name != NULL) 371 (void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name)); 372 373 imsg_compose(ibuf, IMSG_VMDOP_PAUSE_VM, 0, 0, -1, 374 &vid, sizeof(vid)); 375 } 376 377 int 378 pause_vm_complete(struct imsg *imsg, int *ret) 379 { 380 struct vmop_result *vmr; 381 int res; 382 383 if (imsg->hdr.type == IMSG_VMDOP_PAUSE_VM_RESPONSE) { 384 vmr = (struct vmop_result *)imsg->data; 385 res = vmr->vmr_result; 386 if (res) { 387 errno = res; 388 warn("pause vm command failed"); 389 *ret = EIO; 390 } else { 391 warnx("paused vm %d successfully", vmr->vmr_id); 392 *ret = 0; 393 } 394 } else { 395 warnx("unexpected response received from vmd"); 396 *ret = EINVAL; 397 } 398 399 return (1); 400 } 401 402 void 403 unpause_vm(uint32_t pause_id, const char *name) 404 { 405 struct vmop_id vid; 406 407 memset(&vid, 0, sizeof(vid)); 408 vid.vid_id = pause_id; 409 if (name != NULL) 410 (void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name)); 411 412 imsg_compose(ibuf, IMSG_VMDOP_UNPAUSE_VM, 0, 0, -1, 413 &vid, sizeof(vid)); 414 } 415 416 int 417 unpause_vm_complete(struct imsg *imsg, int *ret) 418 { 419 struct vmop_result *vmr; 420 int res; 421 422 if (imsg->hdr.type == IMSG_VMDOP_UNPAUSE_VM_RESPONSE) { 423 vmr = (struct vmop_result *)imsg->data; 424 res = vmr->vmr_result; 425 if (res) { 426 errno = res; 427 warn("unpause vm command failed"); 428 *ret = EIO; 429 } else { 430 warnx("unpaused vm %d successfully", vmr->vmr_id); 431 *ret = 0; 432 } 433 } else { 434 warnx("unexpected response received from vmd"); 435 *ret = EINVAL; 436 } 437 438 return (1); 439 } 440 441 /* 442 * terminate_vm 443 * 444 * Request vmd to stop the VM indicated 445 * 446 * Parameters: 447 * terminate_id: ID of the vm to be terminated 448 * name: optional name of the VM to be terminated 449 * flags: VMOP_FORCE or VMOP_WAIT flags 450 */ 451 void 452 terminate_vm(uint32_t terminate_id, const char *name, unsigned int flags) 453 { 454 struct vmop_id vid; 455 456 memset(&vid, 0, sizeof(vid)); 457 vid.vid_id = terminate_id; 458 if (name != NULL) { 459 (void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name)); 460 fprintf(stderr, "stopping vm %s: ", name); 461 } else { 462 fprintf(stderr, "stopping vm: "); 463 } 464 465 vid.vid_flags = flags & (VMOP_FORCE|VMOP_WAIT); 466 467 imsg_compose(ibuf, IMSG_VMDOP_TERMINATE_VM_REQUEST, 468 0, 0, -1, &vid, sizeof(vid)); 469 } 470 471 /* 472 * terminate_vm_complete 473 * 474 * Callback function invoked when we are waiting for the response from an 475 * IMSG_VMDOP_TERMINATE_VM_REQUEST. We expect a reply of either an 476 * IMSG_VMDOP_TERMINATE_VM_EVENT indicating the termination of a vm or an 477 * IMSG_VMDOP_TERMINATE_VM_RESPONSE with a success/failure result. 478 * 479 * Parameters: 480 * imsg : response imsg received from vmd 481 * ret : return value 482 * flags: VMOP_FORCE or VMOP_WAIT flags 483 * 484 * Return: 485 * Always 1 to indicate we have processed the return message (even if it 486 * was an incorrect/failure message) 487 * 488 * The function also sets 'ret' to the error code as follows: 489 * 0 : Message successfully processed 490 * EINVAL: Invalid or unexpected response from vmd 491 * EIO : terminate_vm command failed 492 */ 493 int 494 terminate_vm_complete(struct imsg *imsg, int *ret, unsigned int flags) 495 { 496 struct vmop_result *vmr; 497 int res; 498 499 switch (imsg->hdr.type) { 500 case IMSG_VMDOP_TERMINATE_VM_RESPONSE: 501 IMSG_SIZE_CHECK(imsg, &vmr); 502 vmr = (struct vmop_result *)imsg->data; 503 res = vmr->vmr_result; 504 505 switch (res) { 506 case 0: 507 fprintf(stderr, "requested to shutdown vm %d\n", 508 vmr->vmr_id); 509 *ret = 0; 510 break; 511 case VMD_VM_STOP_INVALID: 512 fprintf(stderr, 513 "cannot stop vm that is not running\n"); 514 *ret = EINVAL; 515 break; 516 case ENOENT: 517 fprintf(stderr, "vm not found\n"); 518 *ret = EIO; 519 break; 520 case EINTR: 521 fprintf(stderr, "interrupted call\n"); 522 *ret = EIO; 523 break; 524 default: 525 errno = res; 526 fprintf(stderr, "failed: %s\n", 527 strerror(res)); 528 *ret = EIO; 529 } 530 break; 531 case IMSG_VMDOP_TERMINATE_VM_EVENT: 532 IMSG_SIZE_CHECK(imsg, &vmr); 533 vmr = (struct vmop_result *)imsg->data; 534 if (flags & VMOP_WAIT) { 535 fprintf(stderr, "terminated vm %d\n", vmr->vmr_id); 536 } else if (flags & VMOP_FORCE) { 537 fprintf(stderr, "forced to terminate vm %d\n", 538 vmr->vmr_id); 539 } 540 *ret = 0; 541 break; 542 default: 543 fprintf(stderr, "unexpected response received from vmd\n"); 544 *ret = EINVAL; 545 } 546 errno = *ret; 547 548 return (1); 549 } 550 551 /* 552 * terminate_all 553 * 554 * Request to stop all VMs gracefully 555 * 556 * Parameters 557 * list: the vm information (consolidated) returned from vmd via imsg 558 * ct : the size (number of elements in 'list') of the result 559 * flags: VMOP_FORCE or VMOP_WAIT flags 560 */ 561 void 562 terminate_all(struct vmop_info_result *list, size_t ct, unsigned int flags) 563 { 564 struct vm_info_result *vir; 565 struct vmop_info_result *vmi; 566 struct parse_result res; 567 size_t i; 568 569 for (i = 0; i < ct; i++) { 570 vmi = &list[i]; 571 vir = &vmi->vir_info; 572 573 /* The VM is already stopped */ 574 if (vir->vir_creator_pid == 0 || vir->vir_id == 0) 575 continue; 576 577 memset(&res, 0, sizeof(res)); 578 res.action = CMD_STOP; 579 res.id = 0; 580 res.flags = info_flags; 581 582 if ((res.name = strdup(vir->vir_name)) == NULL) 583 errx(1, "strdup"); 584 585 vmmaction(&res); 586 } 587 } 588 589 /* 590 * waitfor_vm 591 * 592 * Wait until vmd stopped the indicated VM 593 * 594 * Parameters: 595 * terminate_id: ID of the vm to be terminated 596 * name: optional name of the VM to be terminated 597 */ 598 void 599 waitfor_vm(uint32_t terminate_id, const char *name) 600 { 601 struct vmop_id vid; 602 603 memset(&vid, 0, sizeof(vid)); 604 vid.vid_id = terminate_id; 605 if (name != NULL) { 606 (void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name)); 607 fprintf(stderr, "waiting for vm %s: ", name); 608 } else { 609 fprintf(stderr, "waiting for vm: "); 610 } 611 612 imsg_compose(ibuf, IMSG_VMDOP_WAIT_VM_REQUEST, 613 0, 0, -1, &vid, sizeof(vid)); 614 } 615 616 /* 617 * get_info_vm 618 * 619 * Return the list of all running VMs or find a specific VM by ID or name. 620 * 621 * Parameters: 622 * id: optional ID of a VM to list 623 * name: optional name of a VM to list 624 * action: if CMD_CONSOLE or CMD_STOP open a console or terminate the VM. 625 * flags: optional flags used by the CMD_STOP action. 626 * 627 * Request a list of running VMs from vmd 628 */ 629 void 630 get_info_vm(uint32_t id, const char *name, enum actions action, 631 unsigned int flags) 632 { 633 info_id = id; 634 if (name != NULL) 635 (void)strlcpy(info_name, name, sizeof(info_name)); 636 info_action = action; 637 info_flags = flags; 638 imsg_compose(ibuf, IMSG_VMDOP_GET_INFO_VM_REQUEST, 0, 0, -1, NULL, 0); 639 } 640 641 /* 642 * check_info_id 643 * 644 * Check if requested name or ID of a VM matches specified arguments 645 * 646 * Parameters: 647 * name: name of the VM 648 * id: ID of the VM 649 */ 650 int 651 check_info_id(const char *name, uint32_t id) 652 { 653 if (info_id == 0 && *info_name == '\0') 654 return (-1); 655 if (info_id != 0 && info_id == id) 656 return (1); 657 if (*info_name != '\0' && name && strcmp(info_name, name) == 0) 658 return (1); 659 return (0); 660 } 661 662 /* 663 * add_info 664 * 665 * Callback function invoked when we are expecting an 666 * IMSG_VMDOP_GET_INFO_VM_DATA message indicating the receipt of additional 667 * "list vm" data, or an IMSG_VMDOP_GET_INFO_VM_END_DATA message indicating 668 * that no additional "list vm" data will be forthcoming. 669 * 670 * Parameters: 671 * imsg : response imsg received from vmd 672 * ret : return value 673 * 674 * Return: 675 * 0 : the returned data was successfully added to the "list vm" data. 676 * The caller can expect more data. 677 * 1 : IMSG_VMDOP_GET_INFO_VM_END_DATA received (caller should not call 678 * add_info again), or an error occurred adding the returned data 679 * to the "list vm" data. The caller should check the value of 680 * 'ret' to determine which case occurred. 681 * 682 * This function does not return if a VM is found and info_action is CMD_CONSOLE 683 * 684 * The function also sets 'ret' to the error code as follows: 685 * 0 : Message successfully processed 686 * EINVAL: Invalid or unexpected response from vmd 687 * ENOMEM: memory allocation failure 688 */ 689 int 690 add_info(struct imsg *imsg, int *ret) 691 { 692 static size_t ct = 0; 693 static struct vmop_info_result *vir = NULL; 694 695 if (imsg->hdr.type == IMSG_VMDOP_GET_INFO_VM_DATA) { 696 vir = reallocarray(vir, ct + 1, 697 sizeof(struct vmop_info_result)); 698 if (vir == NULL) { 699 *ret = ENOMEM; 700 return (1); 701 } 702 memcpy(&vir[ct], imsg->data, sizeof(struct vmop_info_result)); 703 ct++; 704 *ret = 0; 705 return (0); 706 } else if (imsg->hdr.type == IMSG_VMDOP_GET_INFO_VM_END_DATA) { 707 switch (info_action) { 708 case CMD_CONSOLE: 709 vm_console(vir, ct); 710 break; 711 case CMD_STOPALL: 712 terminate_all(vir, ct, info_flags); 713 break; 714 default: 715 print_vm_info(vir, ct); 716 break; 717 } 718 free(vir); 719 *ret = 0; 720 return (1); 721 } else { 722 *ret = EINVAL; 723 return (1); 724 } 725 } 726 727 /* 728 * vm_state 729 * 730 * Returns a string representing the current VM state, note that the order 731 * matters. A paused VM does have the VM_STATE_RUNNING bit set, but 732 * VM_STATE_PAUSED is more significant to report. Same goes for stopping VMs. 733 * 734 * Parameters 735 * vm_state: mask indicating the vm state 736 */ 737 const char * 738 vm_state(unsigned int mask) 739 { 740 if (mask & VM_STATE_PAUSED) 741 return "paused"; 742 else if (mask & VM_STATE_WAITING) 743 return "waiting"; 744 else if (mask & VM_STATE_SHUTDOWN) 745 return "stopping"; 746 else if (mask & VM_STATE_RUNNING) 747 return "running"; 748 /* Presence of absence of other flags */ 749 else if (!mask || (mask & VM_STATE_DISABLED)) 750 return "stopped"; 751 752 return "unknown"; 753 } 754 755 /* 756 * print_vm_info 757 * 758 * Prints the vm information returned from vmd in 'list' to stdout. 759 * 760 * Parameters 761 * list: the vm information (consolidated) returned from vmd via imsg 762 * ct : the size (number of elements in 'list') of the result 763 */ 764 void 765 print_vm_info(struct vmop_info_result *list, size_t ct) 766 { 767 struct vm_info_result *vir; 768 struct vmop_info_result *vmi; 769 size_t i; 770 char *tty; 771 char curmem[FMT_SCALED_STRSIZE]; 772 char maxmem[FMT_SCALED_STRSIZE]; 773 char user[16], group[16]; 774 const char *name; 775 int running; 776 777 printf("%5s %5s %5s %7s %7s %7s %12s %8s %s\n", "ID", "PID", "VCPUS", 778 "MAXMEM", "CURMEM", "TTY", "OWNER", "STATE", "NAME"); 779 780 for (i = 0; i < ct; i++) { 781 vmi = &list[i]; 782 vir = &vmi->vir_info; 783 running = (vir->vir_creator_pid != 0 && vir->vir_id != 0); 784 if (check_info_id(vir->vir_name, vir->vir_id)) { 785 /* get user name */ 786 name = user_from_uid(vmi->vir_uid, 1); 787 if (name == NULL) 788 (void)snprintf(user, sizeof(user), 789 "%d", vmi->vir_uid); 790 else 791 (void)strlcpy(user, name, sizeof(user)); 792 /* get group name */ 793 if (vmi->vir_gid != -1) { 794 name = group_from_gid(vmi->vir_gid, 1); 795 if (name == NULL) 796 (void)snprintf(group, sizeof(group), 797 ":%lld", vmi->vir_gid); 798 else 799 (void)snprintf(group, sizeof(group), 800 ":%s", name); 801 (void)strlcat(user, group, sizeof(user)); 802 } 803 804 (void)strlcpy(curmem, "-", sizeof(curmem)); 805 (void)strlcpy(maxmem, "-", sizeof(maxmem)); 806 807 (void)fmt_scaled(vir->vir_memory_size, maxmem); 808 809 if (running) { 810 if (*vmi->vir_ttyname == '\0') 811 tty = "-"; 812 /* get tty - skip /dev/ path */ 813 else if ((tty = strrchr(vmi->vir_ttyname, 814 '/')) == NULL || *++tty == '\0') 815 tty = list[i].vir_ttyname; 816 817 (void)fmt_scaled(vir->vir_used_size, curmem); 818 819 /* running vm */ 820 printf("%5u %5u %5zd %7s %7s %7s %12s %8s %s\n", 821 vir->vir_id, vir->vir_creator_pid, 822 vir->vir_ncpus, maxmem, curmem, 823 tty, user, vm_state(vmi->vir_state), 824 vir->vir_name); 825 } else { 826 /* disabled vm */ 827 printf("%5u %5s %5zd %7s %7s %7s %12s %8s %s\n", 828 vir->vir_id, "-", 829 vir->vir_ncpus, maxmem, curmem, 830 "-", user, vm_state(vmi->vir_state), 831 vir->vir_name); 832 } 833 } 834 } 835 } 836 837 /* 838 * vm_console 839 * 840 * Connects to the vm console returned from vmd in 'list'. 841 * 842 * Parameters 843 * list: the vm information (consolidated) returned from vmd via imsg 844 * ct : the size (number of elements in 'list') of the result 845 */ 846 __dead void 847 vm_console(struct vmop_info_result *list, size_t ct) 848 { 849 struct vmop_info_result *vir; 850 size_t i; 851 852 for (i = 0; i < ct; i++) { 853 vir = &list[i]; 854 if ((check_info_id(vir->vir_info.vir_name, 855 vir->vir_info.vir_id) > 0) && 856 (vir->vir_ttyname[0] != '\0')) { 857 /* does not return */ 858 ctl_openconsole(vir->vir_ttyname); 859 } 860 } 861 862 errx(1, "console not found"); 863 } 864 865 /* 866 * open_imagefile 867 * 868 * Open an imagefile with the specified type, path and size. 869 * 870 * Parameters: 871 * type : format of the image file 872 * imgfile_path: path to the image file to create 873 * flags : flags for open(2), e.g. O_RDONLY 874 * file : file structure 875 * sz : size of the image file 876 * 877 * Return: 878 * fd : Returns file descriptor of the new image file 879 * -1 : Operation failed. errno is set. 880 */ 881 int 882 open_imagefile(int type, const char *imgfile_path, int flags, 883 struct virtio_backing *file, off_t *sz) 884 { 885 int fd, ret, basefd[VM_MAX_BASE_PER_DISK], nfd, i; 886 char path[PATH_MAX]; 887 888 *sz = 0; 889 if ((fd = open(imgfile_path, flags)) == -1) 890 return (-1); 891 892 basefd[0] = fd; 893 nfd = 1; 894 895 errno = 0; 896 switch (type) { 897 case VMDF_QCOW2: 898 if (strlcpy(path, imgfile_path, sizeof(path)) >= sizeof(path)) 899 return (-1); 900 for (i = 0; i < VM_MAX_BASE_PER_DISK - 1; i++, nfd++) { 901 if ((ret = virtio_qcow2_get_base(basefd[i], 902 path, sizeof(path), imgfile_path)) == -1) { 903 log_debug("%s: failed to get base %d", __func__, i); 904 return -1; 905 } else if (ret == 0) 906 break; 907 908 if ((basefd[i + 1] = open(path, O_RDONLY)) == -1) { 909 log_warn("%s: failed to open base %s", 910 __func__, path); 911 return (-1); 912 } 913 } 914 ret = virtio_qcow2_init(file, sz, basefd, nfd); 915 break; 916 default: 917 ret = virtio_raw_init(file, sz, &fd, 1); 918 break; 919 } 920 921 if (ret == -1) { 922 for (i = 0; i < nfd; i++) 923 close(basefd[i]); 924 return (-1); 925 } 926 927 return (fd); 928 } 929 930 /* 931 * create_imagefile 932 * 933 * Create an empty imagefile with the specified type, path and size. 934 * 935 * Parameters: 936 * type : format of the image file 937 * imgfile_path: path to the image file to create 938 * base_path : path to the qcow2 base image 939 * imgsize : size of the image file to create (in bytes) 940 * format : string identifying the format 941 * 942 * Return: 943 * EEXIST: The requested image file already exists 944 * 0 : Image file successfully created 945 * Exxxx : Various other Exxxx errno codes due to other I/O errors 946 */ 947 int 948 create_imagefile(int type, const char *imgfile_path, const char *base_path, 949 uint64_t imgsize, const char **format) 950 { 951 int ret; 952 953 switch (type) { 954 case VMDF_QCOW2: 955 *format = "qcow2"; 956 ret = virtio_qcow2_create(imgfile_path, base_path, imgsize); 957 break; 958 default: 959 *format = "raw"; 960 ret = virtio_raw_create(imgfile_path, imgsize); 961 break; 962 } 963 964 return (ret); 965 } 966