xref: /openbsd-src/usr.sbin/vmd/config.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
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