1 /* $OpenBSD: config.c,v 1.11 2016/09/03 10:20:06 stefan 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/types.h> 20 #include <sys/queue.h> 21 #include <sys/time.h> 22 #include <sys/uio.h> 23 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <termios.h> 27 #include <unistd.h> 28 #include <limits.h> 29 #include <string.h> 30 #include <fcntl.h> 31 #include <util.h> 32 #include <errno.h> 33 #include <imsg.h> 34 35 #include "proc.h" 36 #include "vmd.h" 37 38 int 39 config_init(struct vmd *env) 40 { 41 struct privsep *ps = &env->vmd_ps; 42 unsigned int what; 43 44 /* Global configuration */ 45 if (privsep_process == PROC_PARENT) { 46 ps->ps_what[PROC_PARENT] = CONFIG_ALL; 47 ps->ps_what[PROC_VMM] = CONFIG_VMS; 48 } 49 50 /* Other configuration */ 51 what = ps->ps_what[privsep_process]; 52 if (what & CONFIG_VMS) { 53 if ((env->vmd_vms = calloc(1, sizeof(*env->vmd_vms))) == NULL) 54 return (-1); 55 TAILQ_INIT(env->vmd_vms); 56 } 57 58 return (0); 59 } 60 61 void 62 config_purge(struct vmd *env, unsigned int reset) 63 { 64 struct privsep *ps = &env->vmd_ps; 65 struct vmd_vm *vm; 66 unsigned int what; 67 68 what = ps->ps_what[privsep_process] & reset; 69 if (what & CONFIG_VMS && env->vmd_vms != NULL) { 70 while ((vm = TAILQ_FIRST(env->vmd_vms)) != NULL) 71 vm_remove(vm); 72 env->vmd_vmcount = 0; 73 } 74 } 75 76 int 77 config_setreset(struct vmd *env, unsigned int reset) 78 { 79 struct privsep *ps = &env->vmd_ps; 80 unsigned int id; 81 82 for (id = 0; id < PROC_MAX; id++) { 83 if ((reset & ps->ps_what[id]) == 0 || 84 id == privsep_process) 85 continue; 86 proc_compose(ps, id, IMSG_CTL_RESET, &reset, sizeof(reset)); 87 } 88 89 return (0); 90 } 91 92 int 93 config_getreset(struct vmd *env, struct imsg *imsg) 94 { 95 unsigned int mode; 96 97 IMSG_SIZE_CHECK(imsg, &mode); 98 memcpy(&mode, imsg->data, sizeof(mode)); 99 100 config_purge(env, mode); 101 102 return (0); 103 } 104 105 int 106 config_getvm(struct privsep *ps, struct vm_create_params *vcp, 107 int kernel_fd, uint32_t peerid) 108 { 109 struct vmd *env = ps->ps_env; 110 struct vmd_vm *vm = NULL; 111 unsigned int i; 112 int fd, ttys_fd; 113 int kernfd = -1, *diskfds = NULL, *tapfds = NULL; 114 int saved_errno = 0; 115 116 errno = 0; 117 118 if (vm_getbyname(vcp->vcp_name) != NULL) { 119 errno = EALREADY; 120 goto fail; 121 } 122 123 if (vcp->vcp_ncpus == 0) 124 vcp->vcp_ncpus = 1; 125 if (vcp->vcp_ncpus > VMM_MAX_VCPUS_PER_VM) { 126 log_debug("invalid number of CPUs"); 127 goto fail; 128 } else if (vcp->vcp_ndisks > VMM_MAX_DISKS_PER_VM) { 129 log_debug("invalid number of disks"); 130 goto fail; 131 } else if (vcp->vcp_nnics > VMM_MAX_NICS_PER_VM) { 132 log_debug("invalid number of interfaces"); 133 goto fail; 134 } 135 136 if ((vm = calloc(1, sizeof(*vm))) == NULL) 137 goto fail; 138 139 memcpy(&vm->vm_params, vcp, sizeof(vm->vm_params)); 140 vm->vm_pid = -1; 141 142 for (i = 0; i < vcp->vcp_ndisks; i++) 143 vm->vm_disks[i] = -1; 144 for (i = 0; i < vcp->vcp_nnics; i++) 145 vm->vm_ifs[i] = -1; 146 vm->vm_kernel = -1; 147 vm->vm_vmid = env->vmd_nvm + 1; 148 149 if (vm_getbyvmid(vm->vm_vmid) != NULL) 150 fatalx("too many vms"); 151 152 TAILQ_INSERT_TAIL(env->vmd_vms, vm, vm_entry); 153 154 if (privsep_process != PROC_PARENT) { 155 if (kernel_fd == -1) { 156 log_debug("invalid kernel fd"); 157 goto fail; 158 } 159 vm->vm_kernel = kernel_fd; 160 } else { 161 diskfds = reallocarray(NULL, vcp->vcp_ndisks, sizeof(*diskfds)); 162 if (diskfds == NULL) { 163 saved_errno = errno; 164 log_warn("%s: cannot allocate disk fds", __func__); 165 goto fail; 166 } 167 for (i = 0; i < vcp->vcp_ndisks; i++) 168 diskfds[i] = -1; 169 170 tapfds = reallocarray(NULL, vcp->vcp_nnics, sizeof(*tapfds)); 171 if (tapfds == NULL) { 172 saved_errno = errno; 173 log_warn("%s: cannot allocate tap fds", __func__); 174 goto fail; 175 } 176 for (i = 0; i < vcp->vcp_nnics; i++) 177 tapfds[i] = -1; 178 179 vm->vm_peerid = peerid; 180 181 /* Open kernel for child */ 182 if ((kernfd = open(vcp->vcp_kernel, O_RDONLY)) == -1) { 183 saved_errno = errno; 184 log_warn("%s: can't open kernel %s", __func__, 185 vcp->vcp_kernel); 186 goto fail; 187 } 188 189 /* Open disk images for child */ 190 for (i = 0 ; i < vcp->vcp_ndisks; i++) { 191 if ((diskfds[i] = 192 open(vcp->vcp_disks[i], O_RDWR)) == -1) { 193 saved_errno = errno; 194 log_warn("%s: can't open %s", __func__, 195 vcp->vcp_disks[i]); 196 goto fail; 197 } 198 } 199 200 /* Open disk network interfaces */ 201 for (i = 0 ; i < vcp->vcp_nnics; i++) { 202 if ((tapfds[i] = opentap()) == -1) { 203 saved_errno = errno; 204 log_warn("%s: can't open tap", __func__); 205 goto fail; 206 } 207 } 208 209 /* Open TTY */ 210 if (openpty(&fd, &ttys_fd, 211 vm->vm_ttyname, NULL, NULL) == -1) { 212 saved_errno = errno; 213 log_warn("%s: can't open tty", __func__); 214 goto fail; 215 } 216 close(ttys_fd); 217 218 /* Send VM information */ 219 proc_compose_imsg(ps, PROC_VMM, -1, 220 IMSG_VMDOP_START_VM_REQUEST, vm->vm_vmid, kernfd, 221 vcp, sizeof(*vcp)); 222 for (i = 0; i < vcp->vcp_ndisks; i++) { 223 proc_compose_imsg(ps, PROC_VMM, -1, 224 IMSG_VMDOP_START_VM_DISK, vm->vm_vmid, diskfds[i], 225 &i, sizeof(i)); 226 } 227 for (i = 0; i < vcp->vcp_nnics; i++) { 228 proc_compose_imsg(ps, PROC_VMM, -1, 229 IMSG_VMDOP_START_VM_IF, vm->vm_vmid, tapfds[i], 230 &i, sizeof(i)); 231 } 232 233 proc_compose_imsg(ps, PROC_VMM, -1, 234 IMSG_VMDOP_START_VM_END, vm->vm_vmid, fd, 235 vcp, sizeof(*vcp)); 236 } 237 238 free(diskfds); 239 free(tapfds); 240 241 env->vmd_nvm++; 242 return (0); 243 244 fail: 245 if (kernfd != -1) 246 close(kernfd); 247 if (diskfds != NULL) { 248 for (i = 0; i < vcp->vcp_ndisks; i++) 249 close(diskfds[i]); 250 free(diskfds); 251 } 252 if (tapfds != NULL) { 253 for (i = 0; i < vcp->vcp_nnics; i++) 254 close(tapfds[i]); 255 free(tapfds); 256 } 257 258 vm_remove(vm); 259 errno = saved_errno; 260 if (errno == 0) 261 errno = EINVAL; 262 return (-1); 263 } 264 265 int 266 config_getdisk(struct privsep *ps, struct imsg *imsg) 267 { 268 struct vmd_vm *vm; 269 unsigned int n; 270 271 errno = 0; 272 if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) { 273 errno = ENOENT; 274 return (-1); 275 } 276 277 IMSG_SIZE_CHECK(imsg, &n); 278 memcpy(&n, imsg->data, sizeof(n)); 279 280 if (n >= vm->vm_params.vcp_ndisks || 281 vm->vm_disks[n] != -1 || imsg->fd == -1) { 282 log_debug("invalid disk id"); 283 errno = EINVAL; 284 return (-1); 285 } 286 vm->vm_disks[n] = imsg->fd; 287 288 return (0); 289 } 290 291 int 292 config_getif(struct privsep *ps, struct imsg *imsg) 293 { 294 struct vmd_vm *vm; 295 unsigned int n; 296 297 errno = 0; 298 if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) { 299 errno = ENOENT; 300 return (-1); 301 } 302 303 IMSG_SIZE_CHECK(imsg, &n); 304 memcpy(&n, imsg->data, sizeof(n)); 305 if (n >= vm->vm_params.vcp_nnics || 306 vm->vm_ifs[n] != -1 || imsg->fd == -1) { 307 log_debug("invalid interface id"); 308 errno = EINVAL; 309 return (-1); 310 } 311 vm->vm_ifs[n] = imsg->fd; 312 313 return (0); 314 } 315