vmd.c revision 1.32
1/* $OpenBSD: vmd.c,v 1.32 2016/10/05 17:30:13 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 24#include <stdio.h> 25#include <stdlib.h> 26#include <string.h> 27#include <errno.h> 28#include <event.h> 29#include <fcntl.h> 30#include <pwd.h> 31#include <signal.h> 32#include <syslog.h> 33#include <unistd.h> 34#include <ctype.h> 35 36#include "proc.h" 37#include "vmd.h" 38 39__dead void usage(void); 40 41int main(int, char **); 42int vmd_configure(void); 43void vmd_sighdlr(int sig, short event, void *arg); 44void vmd_shutdown(void); 45int vmd_control_run(void); 46int vmd_dispatch_control(int, struct privsep_proc *, struct imsg *); 47int vmd_dispatch_vmm(int, struct privsep_proc *, struct imsg *); 48 49struct vmd *env; 50 51static struct privsep_proc procs[] = { 52 /* Keep "priv" on top as procs[0] */ 53 { "priv", PROC_PRIV, NULL, priv }, 54 { "control", PROC_CONTROL, vmd_dispatch_control, control }, 55 { "vmm", PROC_VMM, vmd_dispatch_vmm, vmm }, 56 57}; 58 59/* For the privileged process */ 60static struct privsep_proc *proc_priv = &procs[0]; 61static struct passwd proc_privpw; 62 63int 64vmd_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg) 65{ 66 struct privsep *ps = p->p_ps; 67 int res = 0, cmd = 0, v = 0; 68 struct vmop_create_params vmc; 69 struct vmop_id vid; 70 struct vm_terminate_params vtp; 71 struct vmop_result vmr; 72 struct vmd_vm *vm = NULL; 73 char *str = NULL; 74 uint32_t id = 0; 75 76 switch (imsg->hdr.type) { 77 case IMSG_VMDOP_START_VM_REQUEST: 78 IMSG_SIZE_CHECK(imsg, &vmc); 79 memcpy(&vmc, imsg->data, sizeof(vmc)); 80 res = config_getvm(ps, &vmc, -1, imsg->hdr.peerid); 81 if (res == -1) { 82 res = errno; 83 cmd = IMSG_VMDOP_START_VM_RESPONSE; 84 } 85 break; 86 case IMSG_VMDOP_TERMINATE_VM_REQUEST: 87 IMSG_SIZE_CHECK(imsg, &vid); 88 memcpy(&vid, imsg->data, sizeof(vid)); 89 if ((id = vid.vid_id) == 0) { 90 /* Lookup vm (id) by name */ 91 if ((vm = vm_getbyname(vid.vid_name)) == NULL) { 92 res = ENOENT; 93 cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE; 94 break; 95 } 96 id = vm->vm_params.vcp_id; 97 } 98 memset(&vtp, 0, sizeof(vtp)); 99 vtp.vtp_vm_id = id; 100 if (proc_compose_imsg(ps, PROC_VMM, -1, imsg->hdr.type, 101 imsg->hdr.peerid, -1, &vtp, sizeof(vtp)) == -1) 102 return (-1); 103 break; 104 case IMSG_VMDOP_GET_INFO_VM_REQUEST: 105 proc_forward_imsg(ps, imsg, PROC_VMM, -1); 106 break; 107 case IMSG_VMDOP_RELOAD: 108 v = 1; 109 case IMSG_VMDOP_LOAD: 110 if (IMSG_DATA_SIZE(imsg) > 0) 111 str = get_string((uint8_t *)imsg->data, 112 IMSG_DATA_SIZE(imsg)); 113 vmd_reload(v, str); 114 free(str); 115 break; 116 default: 117 return (-1); 118 } 119 120 switch (cmd) { 121 case 0: 122 break; 123 case IMSG_VMDOP_START_VM_RESPONSE: 124 case IMSG_VMDOP_TERMINATE_VM_RESPONSE: 125 memset(&vmr, 0, sizeof(vmr)); 126 vmr.vmr_result = res; 127 vmr.vmr_id = id; 128 if (proc_compose_imsg(ps, PROC_CONTROL, -1, cmd, 129 imsg->hdr.peerid, -1, &vmr, sizeof(vmr)) == -1) 130 return (-1); 131 break; 132 default: 133 if (proc_compose_imsg(ps, PROC_CONTROL, -1, cmd, 134 imsg->hdr.peerid, -1, &res, sizeof(res)) == -1) 135 return (-1); 136 break; 137 } 138 139 return (0); 140} 141 142int 143vmd_dispatch_vmm(int fd, struct privsep_proc *p, struct imsg *imsg) 144{ 145 struct vmop_result vmr; 146 struct privsep *ps = p->p_ps; 147 int res = 0; 148 struct vmd_vm *vm; 149 struct vm_create_params *vcp; 150 struct vmop_info_result vir; 151 152 switch (imsg->hdr.type) { 153 case IMSG_VMDOP_START_VM_RESPONSE: 154 IMSG_SIZE_CHECK(imsg, &vmr); 155 memcpy(&vmr, imsg->data, sizeof(vmr)); 156 if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) 157 fatalx("%s: invalid vm response", __func__); 158 vm->vm_pid = vmr.vmr_pid; 159 vcp = &vm->vm_params; 160 vcp->vcp_id = vmr.vmr_id; 161 162 /* 163 * If the peerid is not -1, forward the response back to the 164 * the control socket. If it is -1, the request originated 165 * from the parent, not the control socket. 166 */ 167 if (vm->vm_peerid != (uint32_t)-1) { 168 vmr.vmr_result = res; 169 (void)strlcpy(vmr.vmr_ttyname, vm->vm_ttyname, 170 sizeof(vmr.vmr_ttyname)); 171 if (proc_compose_imsg(ps, PROC_CONTROL, -1, 172 imsg->hdr.type, vm->vm_peerid, -1, 173 &vmr, sizeof(vmr)) == -1) { 174 errno = vmr.vmr_result; 175 log_warn("%s: failed to foward vm result", 176 vcp->vcp_name); 177 vm_remove(vm); 178 return (-1); 179 } 180 } 181 182 if (vmr.vmr_result) { 183 errno = vmr.vmr_result; 184 log_warn("%s: failed to start vm", vcp->vcp_name); 185 vm_remove(vm); 186 break; 187 } 188 189 /* Now configure all the interfaces */ 190 if (vm_priv_ifconfig(ps, vm) == -1) { 191 log_warn("%s: failed to configure vm", vcp->vcp_name); 192 vm_remove(vm); 193 break; 194 } 195 196 log_info("%s: started vm %d successfully, tty %s", 197 vcp->vcp_name, vcp->vcp_id, vm->vm_ttyname); 198 break; 199 case IMSG_VMDOP_TERMINATE_VM_RESPONSE: 200 case IMSG_VMDOP_TERMINATE_VM_EVENT: 201 IMSG_SIZE_CHECK(imsg, &vmr); 202 memcpy(&vmr, imsg->data, sizeof(vmr)); 203 if (imsg->hdr.type == IMSG_VMDOP_TERMINATE_VM_RESPONSE) 204 proc_forward_imsg(ps, imsg, PROC_CONTROL, -1); 205 if (vmr.vmr_result == 0) { 206 /* Remove local reference */ 207 vm = vm_getbyid(vmr.vmr_id); 208 vm_remove(vm); 209 } 210 break; 211 case IMSG_VMDOP_GET_INFO_VM_DATA: 212 IMSG_SIZE_CHECK(imsg, &vir); 213 memcpy(&vir, imsg->data, sizeof(vir)); 214 if ((vm = vm_getbyid(vir.vir_info.vir_id)) != NULL) { 215 (void)strlcpy(vir.vir_ttyname, vm->vm_ttyname, 216 sizeof(vir.vir_ttyname)); 217 } 218 if (proc_compose_imsg(ps, PROC_CONTROL, -1, imsg->hdr.type, 219 imsg->hdr.peerid, -1, &vir, sizeof(vir)) == -1) { 220 vm_remove(vm); 221 return (-1); 222 } 223 break; 224 case IMSG_VMDOP_GET_INFO_VM_END_DATA: 225 IMSG_SIZE_CHECK(imsg, &res); 226 proc_forward_imsg(ps, imsg, PROC_CONTROL, -1); 227 break; 228 default: 229 return (-1); 230 } 231 232 return (0); 233} 234 235void 236vmd_sighdlr(int sig, short event, void *arg) 237{ 238 if (privsep_process != PROC_PARENT) 239 return; 240 241 switch (sig) { 242 case SIGHUP: 243 log_info("%s: reload requested with SIGHUP", __func__); 244 245 /* 246 * This is safe because libevent uses async signal handlers 247 * that run in the event loop and not in signal context. 248 */ 249 vmd_reload(1, NULL); 250 break; 251 case SIGPIPE: 252 log_info("%s: ignoring SIGPIPE", __func__); 253 break; 254 case SIGUSR1: 255 log_info("%s: ignoring SIGUSR1", __func__); 256 break; 257 case SIGTERM: 258 case SIGINT: 259 vmd_shutdown(); 260 break; 261 default: 262 fatalx("unexpected signal"); 263 } 264} 265 266__dead void 267usage(void) 268{ 269 extern char *__progname; 270 fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n", 271 __progname); 272 exit(1); 273} 274 275int 276main(int argc, char **argv) 277{ 278 struct privsep *ps; 279 int ch; 280 const char *conffile = VMD_CONF; 281 enum privsep_procid proc_id = PROC_PARENT; 282 int proc_instance = 0; 283 const char *errp, *title = NULL; 284 int argc0 = argc; 285 286 /* log to stderr until daemonized */ 287 log_init(1, LOG_DAEMON); 288 289 if ((env = calloc(1, sizeof(*env))) == NULL) 290 fatal("calloc: env"); 291 292 while ((ch = getopt(argc, argv, "D:P:I:df:vn")) != -1) { 293 switch (ch) { 294 case 'D': 295 if (cmdline_symset(optarg) < 0) 296 log_warnx("could not parse macro definition %s", 297 optarg); 298 break; 299 case 'd': 300 env->vmd_debug = 2; 301 break; 302 case 'f': 303 conffile = optarg; 304 break; 305 case 'v': 306 env->vmd_verbose++; 307 break; 308 case 'n': 309 env->vmd_noaction = 1; 310 break; 311 case 'P': 312 title = optarg; 313 proc_id = proc_getid(procs, nitems(procs), title); 314 if (proc_id == PROC_MAX) 315 fatalx("invalid process name"); 316 break; 317 case 'I': 318 proc_instance = strtonum(optarg, 0, 319 PROC_MAX_INSTANCES, &errp); 320 if (errp) 321 fatalx("invalid process instance"); 322 break; 323 default: 324 usage(); 325 } 326 } 327 328 argc -= optind; 329 if (argc > 0) 330 usage(); 331 332 if (env->vmd_noaction && !env->vmd_debug) 333 env->vmd_debug = 1; 334 335 /* check for root privileges */ 336 if (env->vmd_noaction == 0) { 337 if (geteuid()) 338 fatalx("need root privileges"); 339 } 340 341 ps = &env->vmd_ps; 342 ps->ps_env = env; 343 env->vmd_fd = -1; 344 345 if (config_init(env) == -1) 346 fatal("failed to initialize configuration"); 347 348 if ((ps->ps_pw = getpwnam(VMD_USER)) == NULL) 349 fatal("unknown user %s", VMD_USER); 350 351 /* First proc runs as root without pledge but in default chroot */ 352 proc_priv->p_pw = &proc_privpw; /* initialized to all 0 */ 353 proc_priv->p_chroot = ps->ps_pw->pw_dir; /* from VMD_USER */ 354 355 /* Open /dev/vmm */ 356 if (env->vmd_noaction == 0) { 357 env->vmd_fd = open(VMM_NODE, O_RDWR); 358 if (env->vmd_fd == -1) 359 fatal("%s", VMM_NODE); 360 } 361 362 /* Configure the control socket */ 363 ps->ps_csock.cs_name = SOCKET_NAME; 364 TAILQ_INIT(&ps->ps_rcsocks); 365 366 /* Configuration will be parsed after forking the children */ 367 env->vmd_conffile = conffile; 368 369 log_init(env->vmd_debug, LOG_DAEMON); 370 log_verbose(env->vmd_verbose); 371 372 if (env->vmd_noaction) 373 ps->ps_noaction = 1; 374 ps->ps_instance = proc_instance; 375 if (title != NULL) 376 ps->ps_title[proc_id] = title; 377 378 /* only the parent returns */ 379 proc_init(ps, procs, nitems(procs), argc0, argv, proc_id); 380 381 log_procinit("parent"); 382 if (!env->vmd_debug && daemon(0, 0) == -1) 383 fatal("can't daemonize"); 384 385 if (ps->ps_noaction == 0) 386 log_info("startup"); 387 388 event_init(); 389 390 signal_set(&ps->ps_evsigint, SIGINT, vmd_sighdlr, ps); 391 signal_set(&ps->ps_evsigterm, SIGTERM, vmd_sighdlr, ps); 392 signal_set(&ps->ps_evsighup, SIGHUP, vmd_sighdlr, ps); 393 signal_set(&ps->ps_evsigpipe, SIGPIPE, vmd_sighdlr, ps); 394 signal_set(&ps->ps_evsigusr1, SIGUSR1, vmd_sighdlr, ps); 395 396 signal_add(&ps->ps_evsigint, NULL); 397 signal_add(&ps->ps_evsigterm, NULL); 398 signal_add(&ps->ps_evsighup, NULL); 399 signal_add(&ps->ps_evsigpipe, NULL); 400 signal_add(&ps->ps_evsigusr1, NULL); 401 402 if (!env->vmd_noaction) 403 proc_connect(ps); 404 405 if (vmd_configure() == -1) 406 fatalx("configuration failed"); 407 408 event_dispatch(); 409 410 log_debug("parent exiting"); 411 412 return (0); 413} 414 415int 416vmd_configure(void) 417{ 418 /* 419 * pledge in the parent process: 420 * stdio - for malloc and basic I/O including events. 421 * rpath - for reload to open and read the configuration files. 422 * wpath - for opening disk images and tap devices. 423 * tty - for openpty. 424 * proc - run kill to terminate its children safely. 425 * sendfd - for disks, interfaces and other fds. 426 */ 427 if (pledge("stdio rpath wpath proc tty sendfd", NULL) == -1) 428 fatal("pledge"); 429 430 if (parse_config(env->vmd_conffile) == -1) { 431 proc_kill(&env->vmd_ps); 432 exit(1); 433 } 434 435 if (env->vmd_noaction) { 436 fprintf(stderr, "configuration OK\n"); 437 proc_kill(&env->vmd_ps); 438 exit(0); 439 } 440 441 return (0); 442} 443 444void 445vmd_reload(int reset, const char *filename) 446{ 447 /* Switch back to the default config file */ 448 if (filename == NULL || *filename == '\0') 449 filename = env->vmd_conffile; 450 451 log_debug("%s: level %d config file %s", __func__, reset, filename); 452 453 if (reset) 454 config_setreset(env, CONFIG_ALL); 455 456 if (parse_config(filename) == -1) { 457 log_debug("%s: failed to load config file %s", 458 __func__, filename); 459 } 460} 461 462void 463vmd_shutdown(void) 464{ 465 proc_kill(&env->vmd_ps); 466 free(env); 467 468 log_warnx("parent terminating"); 469 exit(0); 470} 471 472struct vmd_vm * 473vm_getbyvmid(uint32_t vmid) 474{ 475 struct vmd_vm *vm; 476 477 TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { 478 if (vm->vm_vmid == vmid) 479 return (vm); 480 } 481 482 return (NULL); 483} 484 485struct vmd_vm * 486vm_getbyid(uint32_t id) 487{ 488 struct vmd_vm *vm; 489 490 TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { 491 if (vm->vm_params.vcp_id == id) 492 return (vm); 493 } 494 495 return (NULL); 496} 497 498struct vmd_vm * 499vm_getbyname(const char *name) 500{ 501 struct vmd_vm *vm; 502 503 if (name == NULL) 504 return (NULL); 505 TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { 506 if (strcmp(vm->vm_params.vcp_name, name) == 0) 507 return (vm); 508 } 509 510 return (NULL); 511} 512 513struct vmd_vm * 514vm_getbypid(pid_t pid) 515{ 516 struct vmd_vm *vm; 517 518 TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { 519 if (vm->vm_pid == pid) 520 return (vm); 521 } 522 523 return (NULL); 524} 525 526void 527vm_remove(struct vmd_vm *vm) 528{ 529 unsigned int i; 530 531 if (vm == NULL) 532 return; 533 534 TAILQ_REMOVE(env->vmd_vms, vm, vm_entry); 535 536 for (i = 0; i < VMM_MAX_DISKS_PER_VM; i++) { 537 if (vm->vm_disks[i] != -1) 538 close(vm->vm_disks[i]); 539 } 540 for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) { 541 if (vm->vm_ifs[i].vif_fd != -1) 542 close(vm->vm_ifs[i].vif_fd); 543 free(vm->vm_ifs[i].vif_name); 544 free(vm->vm_ifs[i].vif_switch); 545 } 546 if (vm->vm_kernel != -1) 547 close(vm->vm_kernel); 548 if (vm->vm_tty != -1) 549 close(vm->vm_tty); 550 551 free(vm->vm_ttyname); 552 free(vm); 553} 554 555void 556switch_remove(struct vmd_switch *vsw) 557{ 558 struct vmd_if *vif; 559 560 if (vsw == NULL) 561 return; 562 563 TAILQ_REMOVE(env->vmd_switches, vsw, sw_entry); 564 565 while ((vif = TAILQ_FIRST(&vsw->sw_ifs)) != NULL) { 566 free(vif->vif_name); 567 free(vif->vif_switch); 568 TAILQ_REMOVE(&vsw->sw_ifs, vif, vif_entry); 569 free(vif); 570 } 571 572 free(vsw->sw_name); 573 free(vsw); 574} 575 576struct vmd_switch * 577switch_getbyname(const char *name) 578{ 579 struct vmd_switch *vsw; 580 581 if (name == NULL) 582 return (NULL); 583 TAILQ_FOREACH(vsw, env->vmd_switches, sw_entry) { 584 if (strcmp(vsw->sw_name, name) == 0) 585 return (vsw); 586 } 587 588 return (NULL); 589} 590 591char * 592get_string(uint8_t *ptr, size_t len) 593{ 594 size_t i; 595 596 for (i = 0; i < len; i++) 597 if (!isprint(ptr[i])) 598 break; 599 600 return strndup(ptr, i); 601} 602