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