vmd.c revision 1.51
1/* $OpenBSD: vmd.c,v 1.51 2017/02/27 14:37:58 reyk 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/param.h> /* nitems */ 20#include <sys/queue.h> 21#include <sys/wait.h> 22#include <sys/cdefs.h> 23#include <sys/tty.h> 24#include <sys/ioctl.h> 25 26#include <stdio.h> 27#include <stdlib.h> 28#include <string.h> 29#include <termios.h> 30#include <errno.h> 31#include <event.h> 32#include <fcntl.h> 33#include <pwd.h> 34#include <signal.h> 35#include <syslog.h> 36#include <unistd.h> 37#include <ctype.h> 38 39#include "proc.h" 40#include "vmd.h" 41 42__dead void usage(void); 43 44int main(int, char **); 45int vmd_configure(void); 46void vmd_sighdlr(int sig, short event, void *arg); 47void vmd_shutdown(void); 48int vmd_control_run(void); 49int vmd_dispatch_control(int, struct privsep_proc *, struct imsg *); 50int vmd_dispatch_vmm(int, struct privsep_proc *, struct imsg *); 51 52struct vmd *env; 53 54static struct privsep_proc procs[] = { 55 /* Keep "priv" on top as procs[0] */ 56 { "priv", PROC_PRIV, NULL, priv }, 57 { "control", PROC_CONTROL, vmd_dispatch_control, control }, 58 { "vmm", PROC_VMM, vmd_dispatch_vmm, vmm, vmm_shutdown }, 59}; 60 61/* For the privileged process */ 62static struct privsep_proc *proc_priv = &procs[0]; 63static struct passwd proc_privpw; 64 65int 66vmd_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg) 67{ 68 struct privsep *ps = p->p_ps; 69 int res = 0, ret = 0, cmd = 0, verbose; 70 unsigned int v = 0; 71 struct vmop_create_params vmc; 72 struct vmop_id vid; 73 struct vm_terminate_params vtp; 74 struct vmop_result vmr; 75 struct vmd_vm *vm = NULL; 76 char *str = NULL; 77 uint32_t id = 0; 78 79 switch (imsg->hdr.type) { 80 case IMSG_VMDOP_START_VM_REQUEST: 81 IMSG_SIZE_CHECK(imsg, &vmc); 82 memcpy(&vmc, imsg->data, sizeof(vmc)); 83 ret = vm_register(ps, &vmc, &vm, 0); 84 if (vmc.vmc_flags == 0) { 85 /* start an existing VM with pre-configured options */ 86 if (!(ret == -1 && errno == EALREADY)) { 87 res = errno; 88 cmd = IMSG_VMDOP_START_VM_RESPONSE; 89 } 90 } else if (ret != 0) { 91 res = errno; 92 cmd = IMSG_VMDOP_START_VM_RESPONSE; 93 } 94 if (res == 0 && 95 config_setvm(ps, vm, imsg->hdr.peerid) == -1) { 96 res = errno; 97 cmd = IMSG_VMDOP_START_VM_RESPONSE; 98 } 99 break; 100 case IMSG_VMDOP_TERMINATE_VM_REQUEST: 101 IMSG_SIZE_CHECK(imsg, &vid); 102 memcpy(&vid, imsg->data, sizeof(vid)); 103 if ((id = vid.vid_id) == 0) { 104 /* Lookup vm (id) by name */ 105 if ((vm = vm_getbyname(vid.vid_name)) == NULL) { 106 res = ENOENT; 107 cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE; 108 break; 109 } 110 id = vm->vm_params.vmc_params.vcp_id; 111 } 112 memset(&vtp, 0, sizeof(vtp)); 113 vtp.vtp_vm_id = id; 114 if (proc_compose_imsg(ps, PROC_VMM, -1, imsg->hdr.type, 115 imsg->hdr.peerid, -1, &vtp, sizeof(vtp)) == -1) 116 return (-1); 117 break; 118 case IMSG_VMDOP_GET_INFO_VM_REQUEST: 119 proc_forward_imsg(ps, imsg, PROC_VMM, -1); 120 break; 121 case IMSG_VMDOP_LOAD: 122 IMSG_SIZE_CHECK(imsg, str); /* at least one byte for path */ 123 str = get_string((uint8_t *)imsg->data, 124 IMSG_DATA_SIZE(imsg)); 125 case IMSG_VMDOP_RELOAD: 126 vmd_reload(0, str); 127 free(str); 128 break; 129 case IMSG_CTL_RESET: 130 IMSG_SIZE_CHECK(imsg, &v); 131 memcpy(&v, imsg->data, sizeof(v)); 132 vmd_reload(v, str); 133 break; 134 case IMSG_CTL_VERBOSE: 135 IMSG_SIZE_CHECK(imsg, &verbose); 136 memcpy(&verbose, imsg->data, sizeof(verbose)); 137 log_setverbose(verbose); 138 139 proc_forward_imsg(ps, imsg, PROC_VMM, -1); 140 proc_forward_imsg(ps, imsg, PROC_PRIV, -1); 141 break; 142 default: 143 return (-1); 144 } 145 146 switch (cmd) { 147 case 0: 148 break; 149 case IMSG_VMDOP_START_VM_RESPONSE: 150 case IMSG_VMDOP_TERMINATE_VM_RESPONSE: 151 memset(&vmr, 0, sizeof(vmr)); 152 vmr.vmr_result = res; 153 vmr.vmr_id = id; 154 if (proc_compose_imsg(ps, PROC_CONTROL, -1, cmd, 155 imsg->hdr.peerid, -1, &vmr, sizeof(vmr)) == -1) 156 return (-1); 157 break; 158 default: 159 if (proc_compose_imsg(ps, PROC_CONTROL, -1, cmd, 160 imsg->hdr.peerid, -1, &res, sizeof(res)) == -1) 161 return (-1); 162 break; 163 } 164 165 return (0); 166} 167 168int 169vmd_dispatch_vmm(int fd, struct privsep_proc *p, struct imsg *imsg) 170{ 171 struct vmop_result vmr; 172 struct privsep *ps = p->p_ps; 173 int res = 0; 174 struct vmd_vm *vm; 175 struct vm_create_params *vcp; 176 struct vmop_info_result vir; 177 178 switch (imsg->hdr.type) { 179 case IMSG_VMDOP_START_VM_RESPONSE: 180 IMSG_SIZE_CHECK(imsg, &vmr); 181 memcpy(&vmr, imsg->data, sizeof(vmr)); 182 if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) 183 fatalx("%s: invalid vm response", __func__); 184 vm->vm_pid = vmr.vmr_pid; 185 vcp = &vm->vm_params.vmc_params; 186 vcp->vcp_id = vmr.vmr_id; 187 188 /* 189 * If the peerid is not -1, forward the response back to the 190 * the control socket. If it is -1, the request originated 191 * from the parent, not the control socket. 192 */ 193 if (vm->vm_peerid != (uint32_t)-1) { 194 vmr.vmr_result = res; 195 (void)strlcpy(vmr.vmr_ttyname, vm->vm_ttyname, 196 sizeof(vmr.vmr_ttyname)); 197 if (proc_compose_imsg(ps, PROC_CONTROL, -1, 198 imsg->hdr.type, vm->vm_peerid, -1, 199 &vmr, sizeof(vmr)) == -1) { 200 errno = vmr.vmr_result; 201 log_warn("%s: failed to foward vm result", 202 vcp->vcp_name); 203 vm_remove(vm); 204 return (-1); 205 } 206 } 207 208 if (vmr.vmr_result) { 209 errno = vmr.vmr_result; 210 log_warn("%s: failed to start vm", vcp->vcp_name); 211 vm_remove(vm); 212 break; 213 } 214 215 /* Now configure all the interfaces */ 216 if (vm_priv_ifconfig(ps, vm) == -1) { 217 log_warn("%s: failed to configure vm", vcp->vcp_name); 218 vm_remove(vm); 219 break; 220 } 221 222 log_info("%s: started vm %d successfully, tty %s", 223 vcp->vcp_name, vcp->vcp_id, vm->vm_ttyname); 224 break; 225 case IMSG_VMDOP_TERMINATE_VM_RESPONSE: 226 IMSG_SIZE_CHECK(imsg, &vmr); 227 memcpy(&vmr, imsg->data, sizeof(vmr)); 228 proc_forward_imsg(ps, imsg, PROC_CONTROL, -1); 229 if (vmr.vmr_result == 0) { 230 vm = vm_getbyid(vmr.vmr_id); 231 if (vm->vm_from_config) 232 vm->vm_running = 0; 233 else 234 vm_remove(vm); 235 } 236 break; 237 case IMSG_VMDOP_TERMINATE_VM_EVENT: 238 IMSG_SIZE_CHECK(imsg, &vmr); 239 memcpy(&vmr, imsg->data, sizeof(vmr)); 240 if ((vm = vm_getbyid(vmr.vmr_id)) == NULL) 241 break; 242 if (vmr.vmr_result == 0) { 243 if (vm->vm_from_config) 244 vm->vm_running = 0; 245 else 246 vm_remove(vm); 247 } else if (vmr.vmr_result == EAGAIN) { 248 /* Stop VM instance but keep the tty open */ 249 vm_stop(vm, 1); 250 config_setvm(ps, vm, (uint32_t)-1); 251 } 252 break; 253 case IMSG_VMDOP_GET_INFO_VM_DATA: 254 IMSG_SIZE_CHECK(imsg, &vir); 255 memcpy(&vir, imsg->data, sizeof(vir)); 256 if ((vm = vm_getbyid(vir.vir_info.vir_id)) != NULL) { 257 (void)strlcpy(vir.vir_ttyname, vm->vm_ttyname, 258 sizeof(vir.vir_ttyname)); 259 } 260 if (proc_compose_imsg(ps, PROC_CONTROL, -1, imsg->hdr.type, 261 imsg->hdr.peerid, -1, &vir, sizeof(vir)) == -1) { 262 vm_remove(vm); 263 return (-1); 264 } 265 break; 266 case IMSG_VMDOP_GET_INFO_VM_END_DATA: 267 /* 268 * PROC_VMM has responded with the *running* VMs, now we 269 * append the others. These use the special value 0 for their 270 * kernel id to indicate that they are not running. 271 */ 272 TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { 273 if (!vm->vm_running) { 274 memset(&vir, 0, sizeof(vir)); 275 vir.vir_info.vir_id = 0; 276 strlcpy(vir.vir_info.vir_name, 277 vm->vm_params.vmc_params.vcp_name, 278 VMM_MAX_NAME_LEN); 279 vir.vir_info.vir_memory_size = 280 vm->vm_params.vmc_params.vcp_memranges[0].vmr_size; 281 vir.vir_info.vir_ncpus = 282 vm->vm_params.vmc_params.vcp_ncpus; 283 if (proc_compose_imsg(ps, PROC_CONTROL, -1, 284 IMSG_VMDOP_GET_INFO_VM_DATA, 285 imsg->hdr.peerid, -1, &vir, 286 sizeof(vir)) == -1) { 287 vm_remove(vm); 288 return (-1); 289 } 290 } 291 } 292 IMSG_SIZE_CHECK(imsg, &res); 293 proc_forward_imsg(ps, imsg, PROC_CONTROL, -1); 294 break; 295 default: 296 return (-1); 297 } 298 299 return (0); 300} 301 302void 303vmd_sighdlr(int sig, short event, void *arg) 304{ 305 if (privsep_process != PROC_PARENT) 306 return; 307 308 switch (sig) { 309 case SIGHUP: 310 log_info("%s: reload requested with SIGHUP", __func__); 311 312 /* 313 * This is safe because libevent uses async signal handlers 314 * that run in the event loop and not in signal context. 315 */ 316 vmd_reload(0, NULL); 317 break; 318 case SIGPIPE: 319 log_info("%s: ignoring SIGPIPE", __func__); 320 break; 321 case SIGUSR1: 322 log_info("%s: ignoring SIGUSR1", __func__); 323 break; 324 case SIGTERM: 325 case SIGINT: 326 vmd_shutdown(); 327 break; 328 default: 329 fatalx("unexpected signal"); 330 } 331} 332 333__dead void 334usage(void) 335{ 336 extern char *__progname; 337 fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n", 338 __progname); 339 exit(1); 340} 341 342int 343main(int argc, char **argv) 344{ 345 struct privsep *ps; 346 int ch; 347 const char *conffile = VMD_CONF; 348 enum privsep_procid proc_id = PROC_PARENT; 349 int proc_instance = 0; 350 const char *errp, *title = NULL; 351 int argc0 = argc; 352 353 /* log to stderr until daemonized */ 354 log_init(1, LOG_DAEMON); 355 356 if ((env = calloc(1, sizeof(*env))) == NULL) 357 fatal("calloc: env"); 358 359 while ((ch = getopt(argc, argv, "D:P:I:df:vn")) != -1) { 360 switch (ch) { 361 case 'D': 362 if (cmdline_symset(optarg) < 0) 363 log_warnx("could not parse macro definition %s", 364 optarg); 365 break; 366 case 'd': 367 env->vmd_debug = 2; 368 break; 369 case 'f': 370 conffile = optarg; 371 break; 372 case 'v': 373 env->vmd_verbose++; 374 break; 375 case 'n': 376 env->vmd_noaction = 1; 377 break; 378 case 'P': 379 title = optarg; 380 proc_id = proc_getid(procs, nitems(procs), title); 381 if (proc_id == PROC_MAX) 382 fatalx("invalid process name"); 383 break; 384 case 'I': 385 proc_instance = strtonum(optarg, 0, 386 PROC_MAX_INSTANCES, &errp); 387 if (errp) 388 fatalx("invalid process instance"); 389 break; 390 default: 391 usage(); 392 } 393 } 394 395 argc -= optind; 396 if (argc > 0) 397 usage(); 398 399 if (env->vmd_noaction && !env->vmd_debug) 400 env->vmd_debug = 1; 401 402 /* check for root privileges */ 403 if (env->vmd_noaction == 0) { 404 if (geteuid()) 405 fatalx("need root privileges"); 406 } 407 408 ps = &env->vmd_ps; 409 ps->ps_env = env; 410 env->vmd_fd = -1; 411 412 if (config_init(env) == -1) 413 fatal("failed to initialize configuration"); 414 415 if ((ps->ps_pw = getpwnam(VMD_USER)) == NULL) 416 fatal("unknown user %s", VMD_USER); 417 418 /* First proc runs as root without pledge but in default chroot */ 419 proc_priv->p_pw = &proc_privpw; /* initialized to all 0 */ 420 proc_priv->p_chroot = ps->ps_pw->pw_dir; /* from VMD_USER */ 421 422 /* Open /dev/vmm */ 423 if (env->vmd_noaction == 0) { 424 env->vmd_fd = open(VMM_NODE, O_RDWR); 425 if (env->vmd_fd == -1) 426 fatal("%s", VMM_NODE); 427 } 428 429 /* Configure the control socket */ 430 ps->ps_csock.cs_name = SOCKET_NAME; 431 TAILQ_INIT(&ps->ps_rcsocks); 432 433 /* Configuration will be parsed after forking the children */ 434 env->vmd_conffile = conffile; 435 436 log_init(env->vmd_debug, LOG_DAEMON); 437 log_setverbose(env->vmd_verbose); 438 439 if (env->vmd_noaction) 440 ps->ps_noaction = 1; 441 ps->ps_instance = proc_instance; 442 if (title != NULL) 443 ps->ps_title[proc_id] = title; 444 445 /* only the parent returns */ 446 proc_init(ps, procs, nitems(procs), argc0, argv, proc_id); 447 448 log_procinit("parent"); 449 if (!env->vmd_debug && daemon(0, 0) == -1) 450 fatal("can't daemonize"); 451 452 if (ps->ps_noaction == 0) 453 log_info("startup"); 454 455 event_init(); 456 457 signal_set(&ps->ps_evsigint, SIGINT, vmd_sighdlr, ps); 458 signal_set(&ps->ps_evsigterm, SIGTERM, vmd_sighdlr, ps); 459 signal_set(&ps->ps_evsighup, SIGHUP, vmd_sighdlr, ps); 460 signal_set(&ps->ps_evsigpipe, SIGPIPE, vmd_sighdlr, ps); 461 signal_set(&ps->ps_evsigusr1, SIGUSR1, vmd_sighdlr, ps); 462 463 signal_add(&ps->ps_evsigint, NULL); 464 signal_add(&ps->ps_evsigterm, NULL); 465 signal_add(&ps->ps_evsighup, NULL); 466 signal_add(&ps->ps_evsigpipe, NULL); 467 signal_add(&ps->ps_evsigusr1, NULL); 468 469 if (!env->vmd_noaction) 470 proc_connect(ps); 471 472 if (vmd_configure() == -1) 473 fatalx("configuration failed"); 474 475 event_dispatch(); 476 477 log_debug("parent exiting"); 478 479 return (0); 480} 481 482int 483vmd_configure(void) 484{ 485 struct vmd_vm *vm; 486 struct vmd_switch *vsw; 487 488 if ((env->vmd_ptmfd = open(PATH_PTMDEV, O_RDWR|O_CLOEXEC)) == -1) 489 fatal("open %s", PATH_PTMDEV); 490 491 /* 492 * pledge in the parent process: 493 * stdio - for malloc and basic I/O including events. 494 * rpath - for reload to open and read the configuration files. 495 * wpath - for opening disk images and tap devices. 496 * tty - for openpty. 497 * proc - run kill to terminate its children safely. 498 * sendfd - for disks, interfaces and other fds. 499 */ 500 if (pledge("stdio rpath wpath proc tty sendfd", NULL) == -1) 501 fatal("pledge"); 502 503 if (parse_config(env->vmd_conffile) == -1) { 504 proc_kill(&env->vmd_ps); 505 exit(1); 506 } 507 508 if (env->vmd_noaction) { 509 fprintf(stderr, "configuration OK\n"); 510 proc_kill(&env->vmd_ps); 511 exit(0); 512 } 513 514 TAILQ_FOREACH(vsw, env->vmd_switches, sw_entry) { 515 if (vsw->sw_running) 516 continue; 517 if (vm_priv_brconfig(&env->vmd_ps, vsw) == -1) { 518 log_warn("%s: failed to create switch %s", 519 __func__, vsw->sw_name); 520 switch_remove(vsw); 521 return (-1); 522 } 523 } 524 525 TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { 526 if (vm->vm_disabled) { 527 log_debug("%s: not creating vm %s (disabled)", 528 __func__, 529 vm->vm_params.vmc_params.vcp_name); 530 continue; 531 } 532 if (config_setvm(&env->vmd_ps, vm, -1) == -1) 533 return (-1); 534 } 535 536 return (0); 537} 538 539void 540vmd_reload(unsigned int reset, const char *filename) 541{ 542 struct vmd_vm *vm, *next_vm; 543 struct vmd_switch *vsw; 544 int reload = 0; 545 546 /* Switch back to the default config file */ 547 if (filename == NULL || *filename == '\0') { 548 filename = env->vmd_conffile; 549 reload = 1; 550 } 551 552 log_debug("%s: level %d config file %s", __func__, reset, filename); 553 554 if (reset) { 555 /* Purge the configuration */ 556 config_purge(env, reset); 557 config_setreset(env, reset); 558 } else { 559 /* 560 * Load or reload the configuration. 561 * 562 * Reloading removes all non-running VMs before processing the 563 * config file, whereas loading only adds to the existing list 564 * of VMs. 565 */ 566 567 if (reload) { 568 TAILQ_FOREACH_SAFE(vm, env->vmd_vms, vm_entry, next_vm) { 569 if (vm->vm_running == 0) 570 vm_remove(vm); 571 } 572 } 573 574 if (parse_config(filename) == -1) { 575 log_debug("%s: failed to load config file %s", 576 __func__, filename); 577 } 578 579 TAILQ_FOREACH(vsw, env->vmd_switches, sw_entry) { 580 if (vsw->sw_running) 581 continue; 582 if (vm_priv_brconfig(&env->vmd_ps, vsw) == -1) { 583 log_warn("%s: failed to create switch %s", 584 __func__, vsw->sw_name); 585 switch_remove(vsw); 586 return; 587 } 588 } 589 590 TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { 591 if (vm->vm_running == 0) { 592 if (vm->vm_disabled) { 593 log_debug("%s: not creating vm %s" 594 " (disabled)", __func__, 595 vm->vm_params.vmc_params.vcp_name); 596 continue; 597 } 598 if (config_setvm(&env->vmd_ps, vm, -1) == -1) 599 return; 600 } else { 601 log_debug("%s: not creating vm \"%s\": " 602 "(running)", __func__, 603 vm->vm_params.vmc_params.vcp_name); 604 } 605 } 606 } 607} 608 609void 610vmd_shutdown(void) 611{ 612 struct vmd_vm *vm, *vm_next; 613 614 TAILQ_FOREACH_SAFE(vm, env->vmd_vms, vm_entry, vm_next) { 615 vm_remove(vm); 616 } 617 618 proc_kill(&env->vmd_ps); 619 free(env); 620 621 log_warnx("parent terminating"); 622 exit(0); 623} 624 625struct vmd_vm * 626vm_getbyvmid(uint32_t vmid) 627{ 628 struct vmd_vm *vm; 629 630 TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { 631 if (vm->vm_vmid == vmid) 632 return (vm); 633 } 634 635 return (NULL); 636} 637 638struct vmd_vm * 639vm_getbyid(uint32_t id) 640{ 641 struct vmd_vm *vm; 642 643 TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { 644 if (vm->vm_params.vmc_params.vcp_id == id) 645 return (vm); 646 } 647 648 return (NULL); 649} 650 651struct vmd_vm * 652vm_getbyname(const char *name) 653{ 654 struct vmd_vm *vm; 655 656 if (name == NULL) 657 return (NULL); 658 TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { 659 if (strcmp(vm->vm_params.vmc_params.vcp_name, name) == 0) 660 return (vm); 661 } 662 663 return (NULL); 664} 665 666struct vmd_vm * 667vm_getbypid(pid_t pid) 668{ 669 struct vmd_vm *vm; 670 671 TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { 672 if (vm->vm_pid == pid) 673 return (vm); 674 } 675 676 return (NULL); 677} 678 679void 680vm_stop(struct vmd_vm *vm, int keeptty) 681{ 682 unsigned int i; 683 684 if (vm == NULL) 685 return; 686 687 vm->vm_running = 0; 688 689 if (vm->vm_iev.ibuf.fd != -1) { 690 event_del(&vm->vm_iev.ev); 691 close(vm->vm_iev.ibuf.fd); 692 } 693 for (i = 0; i < VMM_MAX_DISKS_PER_VM; i++) { 694 if (vm->vm_disks[i] != -1) { 695 close(vm->vm_disks[i]); 696 vm->vm_disks[i] = -1; 697 } 698 } 699 for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) { 700 if (vm->vm_ifs[i].vif_fd != -1) { 701 close(vm->vm_ifs[i].vif_fd); 702 vm->vm_ifs[i].vif_fd = -1; 703 } 704 free(vm->vm_ifs[i].vif_name); 705 free(vm->vm_ifs[i].vif_switch); 706 free(vm->vm_ifs[i].vif_group); 707 vm->vm_ifs[i].vif_name = NULL; 708 vm->vm_ifs[i].vif_switch = NULL; 709 vm->vm_ifs[i].vif_group = NULL; 710 } 711 if (vm->vm_kernel != -1) { 712 close(vm->vm_kernel); 713 vm->vm_kernel = -1; 714 } 715 if (!keeptty) 716 vm_closetty(vm); 717} 718 719void 720vm_remove(struct vmd_vm *vm) 721{ 722 if (vm == NULL) 723 return; 724 725 TAILQ_REMOVE(env->vmd_vms, vm, vm_entry); 726 vm_stop(vm, 0); 727 free(vm); 728} 729 730int 731vm_register(struct privsep *ps, struct vmop_create_params *vmc, 732 struct vmd_vm **ret_vm, uint32_t id) 733{ 734 struct vmd_vm *vm = NULL; 735 struct vm_create_params *vcp = &vmc->vmc_params; 736 unsigned int i; 737 738 errno = 0; 739 *ret_vm = NULL; 740 741 if ((vm = vm_getbyname(vcp->vcp_name)) != NULL) { 742 *ret_vm = vm; 743 errno = EALREADY; 744 goto fail; 745 } 746 747 if (vmc->vmc_flags == 0) { 748 errno = ENOENT; 749 goto fail; 750 } 751 if (vcp->vcp_ncpus == 0) 752 vcp->vcp_ncpus = 1; 753 if (vcp->vcp_memranges[0].vmr_size == 0) 754 vcp->vcp_memranges[0].vmr_size = VM_DEFAULT_MEMORY; 755 if (vcp->vcp_ncpus > VMM_MAX_VCPUS_PER_VM) { 756 log_warnx("invalid number of CPUs"); 757 goto fail; 758 } else if (vcp->vcp_ndisks > VMM_MAX_DISKS_PER_VM) { 759 log_warnx("invalid number of disks"); 760 goto fail; 761 } else if (vcp->vcp_nnics > VMM_MAX_NICS_PER_VM) { 762 log_warnx("invalid number of interfaces"); 763 goto fail; 764 } else if (strlen(vcp->vcp_kernel) == 0 && vcp->vcp_ndisks == 0) { 765 log_warnx("no kernel or disk specified"); 766 goto fail; 767 } 768 769 if ((vm = calloc(1, sizeof(*vm))) == NULL) 770 goto fail; 771 772 memcpy(&vm->vm_params, vmc, sizeof(vm->vm_params)); 773 vm->vm_pid = -1; 774 vm->vm_tty = -1; 775 776 for (i = 0; i < vcp->vcp_ndisks; i++) 777 vm->vm_disks[i] = -1; 778 for (i = 0; i < vcp->vcp_nnics; i++) 779 vm->vm_ifs[i].vif_fd = -1; 780 vm->vm_kernel = -1; 781 vm->vm_iev.ibuf.fd = -1; 782 783 if (++env->vmd_nvm == 0) 784 fatalx("too many vms"); 785 786 /* Assign a new internal Id if not specified */ 787 vm->vm_vmid = id == 0 ? env->vmd_nvm : id; 788 789 TAILQ_INSERT_TAIL(env->vmd_vms, vm, vm_entry); 790 791 *ret_vm = vm; 792 return (0); 793 fail: 794 if (errno == 0) 795 errno = EINVAL; 796 return (-1); 797} 798 799int 800vm_opentty(struct vmd_vm *vm) 801{ 802 struct ptmget ptm; 803 804 /* 805 * Open tty with pre-opened PTM fd 806 */ 807 if ((ioctl(env->vmd_ptmfd, PTMGET, &ptm) == -1)) 808 return (-1); 809 810 vm->vm_tty = ptm.cfd; 811 close(ptm.sfd); 812 if ((vm->vm_ttyname = strdup(ptm.sn)) == NULL) 813 goto fail; 814 815 return (0); 816 fail: 817 vm_closetty(vm); 818 return (-1); 819} 820 821void 822vm_closetty(struct vmd_vm *vm) 823{ 824 if (vm->vm_tty != -1) { 825 close(vm->vm_tty); 826 vm->vm_tty = -1; 827 } 828 free(vm->vm_ttyname); 829 vm->vm_ttyname = NULL; 830} 831 832void 833switch_remove(struct vmd_switch *vsw) 834{ 835 struct vmd_if *vif; 836 837 if (vsw == NULL) 838 return; 839 840 TAILQ_REMOVE(env->vmd_switches, vsw, sw_entry); 841 842 while ((vif = TAILQ_FIRST(&vsw->sw_ifs)) != NULL) { 843 free(vif->vif_name); 844 free(vif->vif_switch); 845 TAILQ_REMOVE(&vsw->sw_ifs, vif, vif_entry); 846 free(vif); 847 } 848 849 free(vsw->sw_group); 850 free(vsw->sw_name); 851 free(vsw); 852} 853 854struct vmd_switch * 855switch_getbyname(const char *name) 856{ 857 struct vmd_switch *vsw; 858 859 if (name == NULL) 860 return (NULL); 861 TAILQ_FOREACH(vsw, env->vmd_switches, sw_entry) { 862 if (strcmp(vsw->sw_name, name) == 0) 863 return (vsw); 864 } 865 866 return (NULL); 867} 868 869char * 870get_string(uint8_t *ptr, size_t len) 871{ 872 size_t i; 873 874 for (i = 0; i < len; i++) 875 if (!isprint(ptr[i])) 876 break; 877 878 return strndup(ptr, i); 879} 880