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