1 |
|
|
/* $OpenBSD: vmd.c,v 1.69 2017/09/08 06:24:31 mlarkin 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/stat.h> |
24 |
|
|
#include <sys/tty.h> |
25 |
|
|
#include <sys/ioctl.h> |
26 |
|
|
|
27 |
|
|
#include <stdio.h> |
28 |
|
|
#include <stdlib.h> |
29 |
|
|
#include <string.h> |
30 |
|
|
#include <termios.h> |
31 |
|
|
#include <errno.h> |
32 |
|
|
#include <event.h> |
33 |
|
|
#include <fcntl.h> |
34 |
|
|
#include <pwd.h> |
35 |
|
|
#include <signal.h> |
36 |
|
|
#include <syslog.h> |
37 |
|
|
#include <unistd.h> |
38 |
|
|
#include <ctype.h> |
39 |
|
|
#include <pwd.h> |
40 |
|
|
#include <grp.h> |
41 |
|
|
|
42 |
|
|
#include <machine/specialreg.h> |
43 |
|
|
#include <machine/vmmvar.h> |
44 |
|
|
|
45 |
|
|
#include "proc.h" |
46 |
|
|
#include "atomicio.h" |
47 |
|
|
#include "vmd.h" |
48 |
|
|
|
49 |
|
|
__dead void usage(void); |
50 |
|
|
|
51 |
|
|
int main(int, char **); |
52 |
|
|
int vmd_configure(void); |
53 |
|
|
void vmd_sighdlr(int sig, short event, void *arg); |
54 |
|
|
void vmd_shutdown(void); |
55 |
|
|
int vmd_control_run(void); |
56 |
|
|
int vmd_dispatch_control(int, struct privsep_proc *, struct imsg *); |
57 |
|
|
int vmd_dispatch_vmm(int, struct privsep_proc *, struct imsg *); |
58 |
|
|
int check_vmh(struct vm_dump_header *); |
59 |
|
|
|
60 |
|
|
struct vmd *env; |
61 |
|
|
|
62 |
|
|
static struct privsep_proc procs[] = { |
63 |
|
|
/* Keep "priv" on top as procs[0] */ |
64 |
|
|
{ "priv", PROC_PRIV, NULL, priv }, |
65 |
|
|
{ "control", PROC_CONTROL, vmd_dispatch_control, control }, |
66 |
|
|
{ "vmm", PROC_VMM, vmd_dispatch_vmm, vmm, vmm_shutdown }, |
67 |
|
|
}; |
68 |
|
|
|
69 |
|
|
/* For the privileged process */ |
70 |
|
|
static struct privsep_proc *proc_priv = &procs[0]; |
71 |
|
|
static struct passwd proc_privpw; |
72 |
|
|
|
73 |
|
|
int |
74 |
|
|
vmd_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg) |
75 |
|
|
{ |
76 |
|
|
struct privsep *ps = p->p_ps; |
77 |
|
|
int res = 0, ret = 0, cmd = 0, verbose; |
78 |
|
|
unsigned int v = 0; |
79 |
|
|
struct vmop_create_params vmc; |
80 |
|
|
struct vmop_id vid; |
81 |
|
|
struct vm_terminate_params vtp; |
82 |
|
|
struct vmop_result vmr; |
83 |
|
|
struct vm_dump_header vmh; |
84 |
|
|
struct vmd_vm *vm = NULL; |
85 |
|
|
char *str = NULL; |
86 |
|
|
uint32_t id = 0; |
87 |
|
|
|
88 |
|
|
switch (imsg->hdr.type) { |
89 |
|
|
case IMSG_VMDOP_START_VM_REQUEST: |
90 |
|
|
IMSG_SIZE_CHECK(imsg, &vmc); |
91 |
|
|
memcpy(&vmc, imsg->data, sizeof(vmc)); |
92 |
|
|
ret = vm_register(ps, &vmc, &vm, 0, vmc.vmc_uid); |
93 |
|
|
if (vmc.vmc_flags == 0) { |
94 |
|
|
/* start an existing VM with pre-configured options */ |
95 |
|
|
if (!(ret == -1 && errno == EALREADY && |
96 |
|
|
vm->vm_running == 0)) { |
97 |
|
|
res = errno; |
98 |
|
|
cmd = IMSG_VMDOP_START_VM_RESPONSE; |
99 |
|
|
} |
100 |
|
|
} else if (ret != 0) { |
101 |
|
|
res = errno; |
102 |
|
|
cmd = IMSG_VMDOP_START_VM_RESPONSE; |
103 |
|
|
} |
104 |
|
|
if (res == 0 && |
105 |
|
|
config_setvm(ps, vm, imsg->hdr.peerid, vmc.vmc_uid) == -1) { |
106 |
|
|
res = errno; |
107 |
|
|
cmd = IMSG_VMDOP_START_VM_RESPONSE; |
108 |
|
|
} |
109 |
|
|
break; |
110 |
|
|
case IMSG_VMDOP_TERMINATE_VM_REQUEST: |
111 |
|
|
IMSG_SIZE_CHECK(imsg, &vid); |
112 |
|
|
memcpy(&vid, imsg->data, sizeof(vid)); |
113 |
|
|
if ((id = vid.vid_id) == 0) { |
114 |
|
|
/* Lookup vm (id) by name */ |
115 |
|
|
if ((vm = vm_getbyname(vid.vid_name)) == NULL) { |
116 |
|
|
res = ENOENT; |
117 |
|
|
cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE; |
118 |
|
|
break; |
119 |
|
|
} else if (vm->vm_shutdown) { |
120 |
|
|
res = EALREADY; |
121 |
|
|
cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE; |
122 |
|
|
break; |
123 |
|
|
} else if (vm->vm_running == 0) { |
124 |
|
|
res = EINVAL; |
125 |
|
|
cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE; |
126 |
|
|
break; |
127 |
|
|
} |
128 |
|
|
id = vm->vm_vmid; |
129 |
|
|
} else if ((vm = vm_getbyvmid(id)) == NULL) { |
130 |
|
|
res = ENOENT; |
131 |
|
|
cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE; |
132 |
|
|
break; |
133 |
|
|
} |
134 |
|
|
if (vm_checkperm(vm, vid.vid_uid) != 0) { |
135 |
|
|
res = EPERM; |
136 |
|
|
cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE; |
137 |
|
|
break; |
138 |
|
|
} |
139 |
|
|
memset(&vtp, 0, sizeof(vtp)); |
140 |
|
|
vtp.vtp_vm_id = id; |
141 |
|
|
if (proc_compose_imsg(ps, PROC_VMM, -1, imsg->hdr.type, |
142 |
|
|
imsg->hdr.peerid, -1, &vtp, sizeof(vtp)) == -1) |
143 |
|
|
return (-1); |
144 |
|
|
break; |
145 |
|
|
case IMSG_VMDOP_GET_INFO_VM_REQUEST: |
146 |
|
|
proc_forward_imsg(ps, imsg, PROC_VMM, -1); |
147 |
|
|
break; |
148 |
|
|
case IMSG_VMDOP_LOAD: |
149 |
|
|
IMSG_SIZE_CHECK(imsg, str); /* at least one byte for path */ |
150 |
|
|
str = get_string((uint8_t *)imsg->data, |
151 |
|
|
IMSG_DATA_SIZE(imsg)); |
152 |
|
|
case IMSG_VMDOP_RELOAD: |
153 |
|
|
if (vmd_reload(0, str) == -1) |
154 |
|
|
cmd = IMSG_CTL_FAIL; |
155 |
|
|
else |
156 |
|
|
cmd = IMSG_CTL_OK; |
157 |
|
|
free(str); |
158 |
|
|
break; |
159 |
|
|
case IMSG_CTL_RESET: |
160 |
|
|
IMSG_SIZE_CHECK(imsg, &v); |
161 |
|
|
memcpy(&v, imsg->data, sizeof(v)); |
162 |
|
|
if (vmd_reload(v, NULL) == -1) |
163 |
|
|
cmd = IMSG_CTL_FAIL; |
164 |
|
|
else |
165 |
|
|
cmd = IMSG_CTL_OK; |
166 |
|
|
break; |
167 |
|
|
case IMSG_CTL_VERBOSE: |
168 |
|
|
IMSG_SIZE_CHECK(imsg, &verbose); |
169 |
|
|
memcpy(&verbose, imsg->data, sizeof(verbose)); |
170 |
|
|
log_setverbose(verbose); |
171 |
|
|
|
172 |
|
|
proc_forward_imsg(ps, imsg, PROC_VMM, -1); |
173 |
|
|
proc_forward_imsg(ps, imsg, PROC_PRIV, -1); |
174 |
|
|
cmd = IMSG_CTL_OK; |
175 |
|
|
break; |
176 |
|
|
case IMSG_VMDOP_PAUSE_VM: |
177 |
|
|
case IMSG_VMDOP_UNPAUSE_VM: |
178 |
|
|
IMSG_SIZE_CHECK(imsg, &vid); |
179 |
|
|
memcpy(&vid, imsg->data, sizeof(vid)); |
180 |
|
|
if (vid.vid_id == 0) { |
181 |
|
|
if ((vm = vm_getbyname(vid.vid_name)) == NULL) { |
182 |
|
|
res = ENOENT; |
183 |
|
|
cmd = IMSG_VMDOP_PAUSE_VM_RESPONSE; |
184 |
|
|
break; |
185 |
|
|
} else { |
186 |
|
|
vid.vid_id = vm->vm_vmid; |
187 |
|
|
} |
188 |
|
|
} |
189 |
|
|
proc_compose_imsg(ps, PROC_VMM, -1, imsg->hdr.type, |
190 |
|
|
imsg->hdr.peerid, -1, &vid, sizeof(vid)); |
191 |
|
|
break; |
192 |
|
|
case IMSG_VMDOP_SEND_VM_REQUEST: |
193 |
|
|
IMSG_SIZE_CHECK(imsg, &vid); |
194 |
|
|
memcpy(&vid, imsg->data, sizeof(vid)); |
195 |
|
|
id = vid.vid_id; |
196 |
|
|
if (vid.vid_id == 0) { |
197 |
|
|
if ((vm = vm_getbyname(vid.vid_name)) == NULL) { |
198 |
|
|
res = ENOENT; |
199 |
|
|
cmd = IMSG_VMDOP_SEND_VM_RESPONSE; |
200 |
|
|
close(imsg->fd); |
201 |
|
|
break; |
202 |
|
|
} else { |
203 |
|
|
vid.vid_id = vm->vm_vmid; |
204 |
|
|
} |
205 |
|
|
} else if ((vm = vm_getbyvmid(vid.vid_id)) == NULL) { |
206 |
|
|
res = ENOENT; |
207 |
|
|
cmd = IMSG_VMDOP_SEND_VM_RESPONSE; |
208 |
|
|
close(imsg->fd); |
209 |
|
|
break; |
210 |
|
|
} else { |
211 |
|
|
} |
212 |
|
|
vmr.vmr_id = vid.vid_id; |
213 |
|
|
log_debug("%s: sending fd to vmm", __func__); |
214 |
|
|
proc_compose_imsg(ps, PROC_VMM, -1, imsg->hdr.type, |
215 |
|
|
imsg->hdr.peerid, imsg->fd, &vid, sizeof(vid)); |
216 |
|
|
break; |
217 |
|
|
case IMSG_VMDOP_RECEIVE_VM_REQUEST: |
218 |
|
|
IMSG_SIZE_CHECK(imsg, &vid); |
219 |
|
|
memcpy(&vid, imsg->data, sizeof(vid)); |
220 |
|
|
if (imsg->fd == -1) { |
221 |
|
|
log_warnx("%s: invalid fd", __func__); |
222 |
|
|
return (-1); |
223 |
|
|
} |
224 |
|
|
if (atomicio(read, imsg->fd, &vmh, sizeof(vmh)) != |
225 |
|
|
sizeof(vmh)) { |
226 |
|
|
log_warnx("%s: error reading vmh from recevied vm", |
227 |
|
|
__func__); |
228 |
|
|
res = EIO; |
229 |
|
|
close(imsg->fd); |
230 |
|
|
cmd = IMSG_VMDOP_START_VM_RESPONSE; |
231 |
|
|
break; |
232 |
|
|
} |
233 |
|
|
if(check_vmh(&vmh)) { |
234 |
|
|
res = ENOENT; |
235 |
|
|
close(imsg->fd); |
236 |
|
|
cmd = IMSG_VMDOP_START_VM_RESPONSE; |
237 |
|
|
break; |
238 |
|
|
} |
239 |
|
|
if (atomicio(read, imsg->fd, &vmc, sizeof(vmc)) != |
240 |
|
|
sizeof(vmc)) { |
241 |
|
|
log_warnx("%s: error reading vmc from recevied vm", |
242 |
|
|
__func__); |
243 |
|
|
res = EIO; |
244 |
|
|
close(imsg->fd); |
245 |
|
|
cmd = IMSG_VMDOP_START_VM_RESPONSE; |
246 |
|
|
break; |
247 |
|
|
} |
248 |
|
|
strlcpy(vmc.vmc_params.vcp_name, vid.vid_name, |
249 |
|
|
sizeof(vmc.vmc_params.vcp_name)); |
250 |
|
|
vmc.vmc_params.vcp_id = 0; |
251 |
|
|
|
252 |
|
|
ret = vm_register(ps, &vmc, &vm, 0, vmc.vmc_uid); |
253 |
|
|
if (ret != 0) { |
254 |
|
|
res = errno; |
255 |
|
|
cmd = IMSG_VMDOP_START_VM_RESPONSE; |
256 |
|
|
close(imsg->fd); |
257 |
|
|
} else { |
258 |
|
|
vm->vm_received = 1; |
259 |
|
|
config_setvm(ps, vm, imsg->hdr.peerid, vmc.vmc_uid); |
260 |
|
|
log_debug("%s: sending fd to vmm", __func__); |
261 |
|
|
proc_compose_imsg(ps, PROC_VMM, -1, |
262 |
|
|
IMSG_VMDOP_RECEIVE_VM_END, vm->vm_vmid, imsg->fd, |
263 |
|
|
NULL, 0); |
264 |
|
|
} |
265 |
|
|
break; |
266 |
|
|
default: |
267 |
|
|
return (-1); |
268 |
|
|
} |
269 |
|
|
|
270 |
|
|
switch (cmd) { |
271 |
|
|
case 0: |
272 |
|
|
break; |
273 |
|
|
case IMSG_VMDOP_START_VM_RESPONSE: |
274 |
|
|
case IMSG_VMDOP_TERMINATE_VM_RESPONSE: |
275 |
|
|
memset(&vmr, 0, sizeof(vmr)); |
276 |
|
|
vmr.vmr_result = res; |
277 |
|
|
vmr.vmr_id = id; |
278 |
|
|
if (proc_compose_imsg(ps, PROC_CONTROL, -1, cmd, |
279 |
|
|
imsg->hdr.peerid, -1, &vmr, sizeof(vmr)) == -1) |
280 |
|
|
return (-1); |
281 |
|
|
break; |
282 |
|
|
default: |
283 |
|
|
if (proc_compose_imsg(ps, PROC_CONTROL, -1, cmd, |
284 |
|
|
imsg->hdr.peerid, -1, &res, sizeof(res)) == -1) |
285 |
|
|
return (-1); |
286 |
|
|
break; |
287 |
|
|
} |
288 |
|
|
|
289 |
|
|
return (0); |
290 |
|
|
} |
291 |
|
|
|
292 |
|
|
int |
293 |
|
|
vmd_dispatch_vmm(int fd, struct privsep_proc *p, struct imsg *imsg) |
294 |
|
|
{ |
295 |
|
|
struct vmop_result vmr; |
296 |
|
|
struct privsep *ps = p->p_ps; |
297 |
|
|
int res = 0; |
298 |
|
|
struct vmd_vm *vm; |
299 |
|
|
struct vm_create_params *vcp; |
300 |
|
|
struct vmop_info_result vir; |
301 |
|
|
|
302 |
|
|
switch (imsg->hdr.type) { |
303 |
|
|
case IMSG_VMDOP_PAUSE_VM_RESPONSE: |
304 |
|
|
IMSG_SIZE_CHECK(imsg, &vmr); |
305 |
|
|
memcpy(&vmr, imsg->data, sizeof(vmr)); |
306 |
|
|
if ((vm = vm_getbyvmid(vmr.vmr_id)) == NULL) |
307 |
|
|
break; |
308 |
|
|
proc_compose_imsg(ps, PROC_CONTROL, -1, |
309 |
|
|
imsg->hdr.type, imsg->hdr.peerid, -1, |
310 |
|
|
imsg->data, sizeof(imsg->data)); |
311 |
|
|
log_info("%s: paused vm %d successfully", |
312 |
|
|
vm->vm_params.vmc_params.vcp_name, |
313 |
|
|
vm->vm_vmid); |
314 |
|
|
break; |
315 |
|
|
case IMSG_VMDOP_UNPAUSE_VM_RESPONSE: |
316 |
|
|
IMSG_SIZE_CHECK(imsg, &vmr); |
317 |
|
|
memcpy(&vmr, imsg->data, sizeof(vmr)); |
318 |
|
|
if ((vm = vm_getbyvmid(vmr.vmr_id)) == NULL) |
319 |
|
|
break; |
320 |
|
|
proc_compose_imsg(ps, PROC_CONTROL, -1, |
321 |
|
|
imsg->hdr.type, imsg->hdr.peerid, -1, |
322 |
|
|
imsg->data, sizeof(imsg->data)); |
323 |
|
|
log_info("%s: unpaused vm %d successfully.", |
324 |
|
|
vm->vm_params.vmc_params.vcp_name, |
325 |
|
|
vm->vm_vmid); |
326 |
|
|
break; |
327 |
|
|
case IMSG_VMDOP_START_VM_RESPONSE: |
328 |
|
|
IMSG_SIZE_CHECK(imsg, &vmr); |
329 |
|
|
memcpy(&vmr, imsg->data, sizeof(vmr)); |
330 |
|
|
if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL) |
331 |
|
|
break; |
332 |
|
|
vm->vm_pid = vmr.vmr_pid; |
333 |
|
|
vcp = &vm->vm_params.vmc_params; |
334 |
|
|
vcp->vcp_id = vmr.vmr_id; |
335 |
|
|
|
336 |
|
|
/* |
337 |
|
|
* If the peerid is not -1, forward the response back to the |
338 |
|
|
* the control socket. If it is -1, the request originated |
339 |
|
|
* from the parent, not the control socket. |
340 |
|
|
*/ |
341 |
|
|
if (vm->vm_peerid != (uint32_t)-1) { |
342 |
|
|
(void)strlcpy(vmr.vmr_ttyname, vm->vm_ttyname, |
343 |
|
|
sizeof(vmr.vmr_ttyname)); |
344 |
|
|
if (proc_compose_imsg(ps, PROC_CONTROL, -1, |
345 |
|
|
imsg->hdr.type, vm->vm_peerid, -1, |
346 |
|
|
&vmr, sizeof(vmr)) == -1) { |
347 |
|
|
errno = vmr.vmr_result; |
348 |
|
|
log_warn("%s: failed to foward vm result", |
349 |
|
|
vcp->vcp_name); |
350 |
|
|
vm_remove(vm); |
351 |
|
|
return (-1); |
352 |
|
|
} |
353 |
|
|
} |
354 |
|
|
|
355 |
|
|
if (vmr.vmr_result) { |
356 |
|
|
errno = vmr.vmr_result; |
357 |
|
|
log_warn("%s: failed to start vm", vcp->vcp_name); |
358 |
|
|
vm_remove(vm); |
359 |
|
|
break; |
360 |
|
|
} |
361 |
|
|
|
362 |
|
|
/* Now configure all the interfaces */ |
363 |
|
|
if (vm_priv_ifconfig(ps, vm) == -1) { |
364 |
|
|
log_warn("%s: failed to configure vm", vcp->vcp_name); |
365 |
|
|
vm_remove(vm); |
366 |
|
|
break; |
367 |
|
|
} |
368 |
|
|
|
369 |
|
|
log_info("%s: started vm %d successfully, tty %s", |
370 |
|
|
vcp->vcp_name, vm->vm_vmid, vm->vm_ttyname); |
371 |
|
|
break; |
372 |
|
|
case IMSG_VMDOP_TERMINATE_VM_RESPONSE: |
373 |
|
|
IMSG_SIZE_CHECK(imsg, &vmr); |
374 |
|
|
memcpy(&vmr, imsg->data, sizeof(vmr)); |
375 |
|
|
log_debug("%s: forwarding TERMINATE VM for vm id %d", |
376 |
|
|
__func__, vmr.vmr_id); |
377 |
|
|
proc_forward_imsg(ps, imsg, PROC_CONTROL, -1); |
378 |
|
|
if ((vm = vm_getbyvmid(vmr.vmr_id)) == NULL) |
379 |
|
|
break; |
380 |
|
|
if (vmr.vmr_result == 0) { |
381 |
|
|
/* Mark VM as shutting down */ |
382 |
|
|
vm->vm_shutdown = 1; |
383 |
|
|
} |
384 |
|
|
break; |
385 |
|
|
case IMSG_VMDOP_SEND_VM_RESPONSE: |
386 |
|
|
IMSG_SIZE_CHECK(imsg, &vmr); |
387 |
|
|
memcpy(&vmr, imsg->data, sizeof(vmr)); |
388 |
|
|
if ((vm = vm_getbyvmid(vmr.vmr_id)) == NULL) |
389 |
|
|
break; |
390 |
|
|
if (!vmr.vmr_result) |
391 |
|
|
log_info("%s: sent vm %d successfully.", |
392 |
|
|
vm->vm_params.vmc_params.vcp_name, |
393 |
|
|
vm->vm_vmid); |
394 |
|
|
case IMSG_VMDOP_TERMINATE_VM_EVENT: |
395 |
|
|
IMSG_SIZE_CHECK(imsg, &vmr); |
396 |
|
|
memcpy(&vmr, imsg->data, sizeof(vmr)); |
397 |
|
|
log_debug("%s: handling TERMINATE_EVENT for vm id %d", |
398 |
|
|
__func__, vmr.vmr_id); |
399 |
|
|
if ((vm = vm_getbyvmid(vmr.vmr_id)) == NULL) |
400 |
|
|
break; |
401 |
|
|
if (vmr.vmr_result == 0) { |
402 |
|
|
if (vm->vm_from_config) { |
403 |
|
|
log_debug("%s: about to stop vm id %d", |
404 |
|
|
__func__, vm->vm_vmid); |
405 |
|
|
vm_stop(vm, 0); |
406 |
|
|
} else { |
407 |
|
|
log_debug("%s: about to remove vm %d", |
408 |
|
|
__func__, vm->vm_vmid); |
409 |
|
|
vm_remove(vm); |
410 |
|
|
} |
411 |
|
|
} else if (vmr.vmr_result == EAGAIN) { |
412 |
|
|
/* Stop VM instance but keep the tty open */ |
413 |
|
|
log_debug("%s: about to stop vm id %d with tty open", |
414 |
|
|
__func__, vm->vm_vmid); |
415 |
|
|
vm_stop(vm, 1); |
416 |
|
|
config_setvm(ps, vm, (uint32_t)-1, 0); |
417 |
|
|
} |
418 |
|
|
break; |
419 |
|
|
case IMSG_VMDOP_GET_INFO_VM_DATA: |
420 |
|
|
IMSG_SIZE_CHECK(imsg, &vir); |
421 |
|
|
memcpy(&vir, imsg->data, sizeof(vir)); |
422 |
|
|
if ((vm = vm_getbyvmid(vir.vir_info.vir_id)) != NULL) { |
423 |
|
|
memset(vir.vir_ttyname, 0, sizeof(vir.vir_ttyname)); |
424 |
|
|
if (vm->vm_ttyname != NULL) |
425 |
|
|
strlcpy(vir.vir_ttyname, vm->vm_ttyname, |
426 |
|
|
sizeof(vir.vir_ttyname)); |
427 |
|
|
if (vm->vm_shutdown) { |
428 |
|
|
/* XXX there might be a nicer way */ |
429 |
|
|
(void)strlcat(vir.vir_info.vir_name, |
430 |
|
|
" - stopping", |
431 |
|
|
sizeof(vir.vir_info.vir_name)); |
432 |
|
|
} |
433 |
|
|
/* get the user id who started the vm */ |
434 |
|
|
vir.vir_uid = vm->vm_uid; |
435 |
|
|
vir.vir_gid = vm->vm_params.vmc_gid; |
436 |
|
|
} |
437 |
|
|
if (proc_compose_imsg(ps, PROC_CONTROL, -1, imsg->hdr.type, |
438 |
|
|
imsg->hdr.peerid, -1, &vir, sizeof(vir)) == -1) { |
439 |
|
|
log_debug("%s: GET_INFO_VM failed for vm %d, removing", |
440 |
|
|
__func__, vm->vm_vmid); |
441 |
|
|
vm_remove(vm); |
442 |
|
|
return (-1); |
443 |
|
|
} |
444 |
|
|
break; |
445 |
|
|
case IMSG_VMDOP_GET_INFO_VM_END_DATA: |
446 |
|
|
/* |
447 |
|
|
* PROC_VMM has responded with the *running* VMs, now we |
448 |
|
|
* append the others. These use the special value 0 for their |
449 |
|
|
* kernel id to indicate that they are not running. |
450 |
|
|
*/ |
451 |
|
|
TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { |
452 |
|
|
if (!vm->vm_running) { |
453 |
|
|
memset(&vir, 0, sizeof(vir)); |
454 |
|
|
vir.vir_info.vir_id = vm->vm_vmid; |
455 |
|
|
strlcpy(vir.vir_info.vir_name, |
456 |
|
|
vm->vm_params.vmc_params.vcp_name, |
457 |
|
|
VMM_MAX_NAME_LEN); |
458 |
|
|
vir.vir_info.vir_memory_size = |
459 |
|
|
vm->vm_params.vmc_params.vcp_memranges[0].vmr_size; |
460 |
|
|
vir.vir_info.vir_ncpus = |
461 |
|
|
vm->vm_params.vmc_params.vcp_ncpus; |
462 |
|
|
/* get the configured user id for this vm */ |
463 |
|
|
vir.vir_uid = vm->vm_params.vmc_uid; |
464 |
|
|
vir.vir_gid = vm->vm_params.vmc_gid; |
465 |
|
|
if (proc_compose_imsg(ps, PROC_CONTROL, -1, |
466 |
|
|
IMSG_VMDOP_GET_INFO_VM_DATA, |
467 |
|
|
imsg->hdr.peerid, -1, &vir, |
468 |
|
|
sizeof(vir)) == -1) { |
469 |
|
|
log_debug("%s: GET_INFO_VM_END failed", |
470 |
|
|
__func__); |
471 |
|
|
vm_remove(vm); |
472 |
|
|
return (-1); |
473 |
|
|
} |
474 |
|
|
} |
475 |
|
|
} |
476 |
|
|
IMSG_SIZE_CHECK(imsg, &res); |
477 |
|
|
proc_forward_imsg(ps, imsg, PROC_CONTROL, -1); |
478 |
|
|
break; |
479 |
|
|
default: |
480 |
|
|
return (-1); |
481 |
|
|
} |
482 |
|
|
|
483 |
|
|
return (0); |
484 |
|
|
} |
485 |
|
|
|
486 |
|
|
int |
487 |
|
|
check_vmh(struct vm_dump_header *vmh) { |
488 |
|
|
int i; |
489 |
|
|
unsigned int code, leaf; |
490 |
|
|
unsigned int a, b, c, d; |
491 |
|
|
|
492 |
|
|
|
493 |
|
|
if (vmh->vmh_version != VM_DUMP_VERSION) { |
494 |
|
|
log_warnx("%s: incompatible dump version", __func__); |
495 |
|
|
return (-1); |
496 |
|
|
} |
497 |
|
|
|
498 |
|
|
for (i = 0; i < VM_DUMP_HEADER_CPUID_COUNT; i++) { |
499 |
|
|
code = vmh->vmh_cpuids[i].code; |
500 |
|
|
leaf = vmh->vmh_cpuids[i].leaf; |
501 |
|
|
if (leaf != 0x00) { |
502 |
|
|
log_debug("%s: invalid leaf 0x%x for code 0x%x", |
503 |
|
|
__func__, leaf, code); |
504 |
|
|
return (-1); |
505 |
|
|
} |
506 |
|
|
|
507 |
|
|
switch(code) { |
508 |
|
|
case 0x00: |
509 |
|
|
CPUID_LEAF(code, leaf, a, b, c, d); |
510 |
|
|
if (vmh->vmh_cpuids[i].a > a) { |
511 |
|
|
log_debug("%s: incompatible cpuid level", __func__); |
512 |
|
|
return (-1); |
513 |
|
|
} |
514 |
|
|
if (!(vmh->vmh_cpuids[i].b == b && |
515 |
|
|
vmh->vmh_cpuids[i].c == c && |
516 |
|
|
vmh->vmh_cpuids[i].d == d)) { |
517 |
|
|
log_debug("%s: incompatible cpu brand", __func__); |
518 |
|
|
return (-1); |
519 |
|
|
} |
520 |
|
|
break; |
521 |
|
|
|
522 |
|
|
case 0x01: |
523 |
|
|
CPUID_LEAF(code, leaf, a, b, c, d); |
524 |
|
|
if ((vmh->vmh_cpuids[i].c & c & VMM_CPUIDECX_MASK) != |
525 |
|
|
(vmh->vmh_cpuids[i].c & VMM_CPUIDECX_MASK)) { |
526 |
|
|
log_debug("%s: incompatible cpu features " |
527 |
|
|
"code: 0x%x leaf: 0x%x reg: c", __func__, |
528 |
|
|
code, leaf); |
529 |
|
|
return (-1); |
530 |
|
|
} |
531 |
|
|
if ((vmh->vmh_cpuids[i].d & d & VMM_CPUIDEDX_MASK) != |
532 |
|
|
(vmh->vmh_cpuids[i].d & VMM_CPUIDEDX_MASK)) { |
533 |
|
|
log_debug("%s: incompatible cpu features " |
534 |
|
|
"code: 0x%x leaf: 0x%x reg: d", __func__, |
535 |
|
|
code, leaf); |
536 |
|
|
return (-1); |
537 |
|
|
} |
538 |
|
|
break; |
539 |
|
|
|
540 |
|
|
case 0x07: |
541 |
|
|
CPUID_LEAF(code, leaf, a, b, c, d); |
542 |
|
|
if ((vmh->vmh_cpuids[i].b & b & VMM_SEFF0EBX_MASK) != |
543 |
|
|
(vmh->vmh_cpuids[i].b & VMM_SEFF0EBX_MASK)) { |
544 |
|
|
log_debug("%s: incompatible cpu features " |
545 |
|
|
"code: 0x%x leaf: 0x%x reg: c", __func__, |
546 |
|
|
code, leaf); |
547 |
|
|
return (-1); |
548 |
|
|
} |
549 |
|
|
if ((vmh->vmh_cpuids[i].c & c & VMM_SEFF0ECX_MASK) != |
550 |
|
|
(vmh->vmh_cpuids[i].c & VMM_SEFF0ECX_MASK)) { |
551 |
|
|
log_debug("%s: incompatible cpu features " |
552 |
|
|
"code: 0x%x leaf: 0x%x reg: d", __func__, |
553 |
|
|
code, leaf); |
554 |
|
|
return (-1); |
555 |
|
|
} |
556 |
|
|
break; |
557 |
|
|
|
558 |
|
|
case 0x0d: |
559 |
|
|
CPUID_LEAF(code, leaf, a, b, c, d); |
560 |
|
|
if (vmh->vmh_cpuids[i].b > b) { |
561 |
|
|
log_debug("%s: incompatible cpu: insufficient " |
562 |
|
|
"max save area for enabled XCR0 features", |
563 |
|
|
__func__); |
564 |
|
|
return (-1); |
565 |
|
|
} |
566 |
|
|
if (vmh->vmh_cpuids[i].c > c) { |
567 |
|
|
log_debug("%s: incompatible cpu: insufficient " |
568 |
|
|
"max save area for supported XCR0 features", |
569 |
|
|
__func__); |
570 |
|
|
return (-1); |
571 |
|
|
} |
572 |
|
|
break; |
573 |
|
|
|
574 |
|
|
case 0x80000001: |
575 |
|
|
CPUID_LEAF(code, leaf, a, b, c, d); |
576 |
|
|
if ((vmh->vmh_cpuids[i].a & a) != vmh->vmh_cpuids[i].a) { |
577 |
|
|
log_debug("%s: incompatible cpu features " |
578 |
|
|
"code: 0x%x leaf: 0x%x reg: a", __func__, |
579 |
|
|
code, leaf); |
580 |
|
|
return (-1); |
581 |
|
|
} |
582 |
|
|
if ((vmh->vmh_cpuids[i].c & c) != vmh->vmh_cpuids[i].c) { |
583 |
|
|
log_debug("%s: incompatible cpu features " |
584 |
|
|
"code: 0x%x leaf: 0x%x reg: c", __func__, |
585 |
|
|
code, leaf); |
586 |
|
|
return (-1); |
587 |
|
|
} |
588 |
|
|
if ((vmh->vmh_cpuids[i].d & d) != vmh->vmh_cpuids[i].d) { |
589 |
|
|
log_debug("%s: incompatible cpu features " |
590 |
|
|
"code: 0x%x leaf: 0x%x reg: d", __func__, |
591 |
|
|
code, leaf); |
592 |
|
|
return (-1); |
593 |
|
|
} |
594 |
|
|
break; |
595 |
|
|
|
596 |
|
|
default: |
597 |
|
|
log_debug("%s: unknown code 0x%x", __func__, code); |
598 |
|
|
return (-1); |
599 |
|
|
} |
600 |
|
|
} |
601 |
|
|
|
602 |
|
|
return (0); |
603 |
|
|
} |
604 |
|
|
|
605 |
|
|
void |
606 |
|
|
vmd_sighdlr(int sig, short event, void *arg) |
607 |
|
|
{ |
608 |
|
|
if (privsep_process != PROC_PARENT) |
609 |
|
|
return; |
610 |
|
|
log_debug("%s: handling signal", __func__); |
611 |
|
|
|
612 |
|
|
switch (sig) { |
613 |
|
|
case SIGHUP: |
614 |
|
|
log_info("%s: reload requested with SIGHUP", __func__); |
615 |
|
|
|
616 |
|
|
/* |
617 |
|
|
* This is safe because libevent uses async signal handlers |
618 |
|
|
* that run in the event loop and not in signal context. |
619 |
|
|
*/ |
620 |
|
|
(void)vmd_reload(0, NULL); |
621 |
|
|
break; |
622 |
|
|
case SIGPIPE: |
623 |
|
|
log_info("%s: ignoring SIGPIPE", __func__); |
624 |
|
|
break; |
625 |
|
|
case SIGUSR1: |
626 |
|
|
log_info("%s: ignoring SIGUSR1", __func__); |
627 |
|
|
break; |
628 |
|
|
case SIGTERM: |
629 |
|
|
case SIGINT: |
630 |
|
|
vmd_shutdown(); |
631 |
|
|
break; |
632 |
|
|
default: |
633 |
|
|
fatalx("unexpected signal"); |
634 |
|
|
} |
635 |
|
|
} |
636 |
|
|
|
637 |
|
|
__dead void |
638 |
|
|
usage(void) |
639 |
|
|
{ |
640 |
|
|
extern char *__progname; |
641 |
|
|
fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n", |
642 |
|
|
__progname); |
643 |
|
|
exit(1); |
644 |
|
|
} |
645 |
|
|
|
646 |
|
|
int |
647 |
|
|
main(int argc, char **argv) |
648 |
|
|
{ |
649 |
|
|
struct privsep *ps; |
650 |
|
|
int ch; |
651 |
|
|
const char *conffile = VMD_CONF; |
652 |
|
|
enum privsep_procid proc_id = PROC_PARENT; |
653 |
|
|
int proc_instance = 0; |
654 |
|
|
const char *errp, *title = NULL; |
655 |
|
|
int argc0 = argc; |
656 |
|
|
|
657 |
|
|
/* log to stderr until daemonized */ |
658 |
|
|
log_init(1, LOG_DAEMON); |
659 |
|
|
|
660 |
|
|
if ((env = calloc(1, sizeof(*env))) == NULL) |
661 |
|
|
fatal("calloc: env"); |
662 |
|
|
|
663 |
|
|
while ((ch = getopt(argc, argv, "D:P:I:df:vn")) != -1) { |
664 |
|
|
switch (ch) { |
665 |
|
|
case 'D': |
666 |
|
|
if (cmdline_symset(optarg) < 0) |
667 |
|
|
log_warnx("could not parse macro definition %s", |
668 |
|
|
optarg); |
669 |
|
|
break; |
670 |
|
|
case 'd': |
671 |
|
|
env->vmd_debug = 2; |
672 |
|
|
break; |
673 |
|
|
case 'f': |
674 |
|
|
conffile = optarg; |
675 |
|
|
break; |
676 |
|
|
case 'v': |
677 |
|
|
env->vmd_verbose++; |
678 |
|
|
break; |
679 |
|
|
case 'n': |
680 |
|
|
env->vmd_noaction = 1; |
681 |
|
|
break; |
682 |
|
|
case 'P': |
683 |
|
|
title = optarg; |
684 |
|
|
proc_id = proc_getid(procs, nitems(procs), title); |
685 |
|
|
if (proc_id == PROC_MAX) |
686 |
|
|
fatalx("invalid process name"); |
687 |
|
|
break; |
688 |
|
|
case 'I': |
689 |
|
|
proc_instance = strtonum(optarg, 0, |
690 |
|
|
PROC_MAX_INSTANCES, &errp); |
691 |
|
|
if (errp) |
692 |
|
|
fatalx("invalid process instance"); |
693 |
|
|
break; |
694 |
|
|
default: |
695 |
|
|
usage(); |
696 |
|
|
} |
697 |
|
|
} |
698 |
|
|
|
699 |
|
|
argc -= optind; |
700 |
|
|
if (argc > 0) |
701 |
|
|
usage(); |
702 |
|
|
|
703 |
|
|
if (env->vmd_noaction && !env->vmd_debug) |
704 |
|
|
env->vmd_debug = 1; |
705 |
|
|
|
706 |
|
|
/* check for root privileges */ |
707 |
|
|
if (env->vmd_noaction == 0) { |
708 |
|
|
if (geteuid()) |
709 |
|
|
fatalx("need root privileges"); |
710 |
|
|
} |
711 |
|
|
|
712 |
|
|
ps = &env->vmd_ps; |
713 |
|
|
ps->ps_env = env; |
714 |
|
|
env->vmd_fd = -1; |
715 |
|
|
|
716 |
|
|
if (config_init(env) == -1) |
717 |
|
|
fatal("failed to initialize configuration"); |
718 |
|
|
|
719 |
|
|
if ((ps->ps_pw = getpwnam(VMD_USER)) == NULL) |
720 |
|
|
fatal("unknown user %s", VMD_USER); |
721 |
|
|
|
722 |
|
|
/* First proc runs as root without pledge but in default chroot */ |
723 |
|
|
proc_priv->p_pw = &proc_privpw; /* initialized to all 0 */ |
724 |
|
|
proc_priv->p_chroot = ps->ps_pw->pw_dir; /* from VMD_USER */ |
725 |
|
|
|
726 |
|
|
/* Open /dev/vmm */ |
727 |
|
|
if (env->vmd_noaction == 0) { |
728 |
|
|
env->vmd_fd = open(VMM_NODE, O_RDWR); |
729 |
|
|
if (env->vmd_fd == -1) |
730 |
|
|
fatal("%s", VMM_NODE); |
731 |
|
|
} |
732 |
|
|
|
733 |
|
|
/* Configure the control socket */ |
734 |
|
|
ps->ps_csock.cs_name = SOCKET_NAME; |
735 |
|
|
TAILQ_INIT(&ps->ps_rcsocks); |
736 |
|
|
|
737 |
|
|
/* Configuration will be parsed after forking the children */ |
738 |
|
|
env->vmd_conffile = conffile; |
739 |
|
|
|
740 |
|
|
log_init(env->vmd_debug, LOG_DAEMON); |
741 |
|
|
log_setverbose(env->vmd_verbose); |
742 |
|
|
|
743 |
|
|
if (env->vmd_noaction) |
744 |
|
|
ps->ps_noaction = 1; |
745 |
|
|
ps->ps_instance = proc_instance; |
746 |
|
|
if (title != NULL) |
747 |
|
|
ps->ps_title[proc_id] = title; |
748 |
|
|
|
749 |
|
|
/* only the parent returns */ |
750 |
|
|
proc_init(ps, procs, nitems(procs), argc0, argv, proc_id); |
751 |
|
|
|
752 |
|
|
log_procinit("parent"); |
753 |
|
|
if (!env->vmd_debug && daemon(0, 0) == -1) |
754 |
|
|
fatal("can't daemonize"); |
755 |
|
|
|
756 |
|
|
if (ps->ps_noaction == 0) |
757 |
|
|
log_info("startup"); |
758 |
|
|
|
759 |
|
|
event_init(); |
760 |
|
|
|
761 |
|
|
signal_set(&ps->ps_evsigint, SIGINT, vmd_sighdlr, ps); |
762 |
|
|
signal_set(&ps->ps_evsigterm, SIGTERM, vmd_sighdlr, ps); |
763 |
|
|
signal_set(&ps->ps_evsighup, SIGHUP, vmd_sighdlr, ps); |
764 |
|
|
signal_set(&ps->ps_evsigpipe, SIGPIPE, vmd_sighdlr, ps); |
765 |
|
|
signal_set(&ps->ps_evsigusr1, SIGUSR1, vmd_sighdlr, ps); |
766 |
|
|
|
767 |
|
|
signal_add(&ps->ps_evsigint, NULL); |
768 |
|
|
signal_add(&ps->ps_evsigterm, NULL); |
769 |
|
|
signal_add(&ps->ps_evsighup, NULL); |
770 |
|
|
signal_add(&ps->ps_evsigpipe, NULL); |
771 |
|
|
signal_add(&ps->ps_evsigusr1, NULL); |
772 |
|
|
|
773 |
|
|
if (!env->vmd_noaction) |
774 |
|
|
proc_connect(ps); |
775 |
|
|
|
776 |
|
|
if (vmd_configure() == -1) |
777 |
|
|
fatalx("configuration failed"); |
778 |
|
|
|
779 |
|
|
event_dispatch(); |
780 |
|
|
|
781 |
|
|
log_debug("parent exiting"); |
782 |
|
|
|
783 |
|
|
return (0); |
784 |
|
|
} |
785 |
|
|
|
786 |
|
|
int |
787 |
|
|
vmd_configure(void) |
788 |
|
|
{ |
789 |
|
|
struct vmd_vm *vm; |
790 |
|
|
struct vmd_switch *vsw; |
791 |
|
|
|
792 |
|
|
if ((env->vmd_ptmfd = open(PATH_PTMDEV, O_RDWR|O_CLOEXEC)) == -1) |
793 |
|
|
fatal("open %s", PATH_PTMDEV); |
794 |
|
|
|
795 |
|
|
/* |
796 |
|
|
* pledge in the parent process: |
797 |
|
|
* stdio - for malloc and basic I/O including events. |
798 |
|
|
* rpath - for reload to open and read the configuration files. |
799 |
|
|
* wpath - for opening disk images and tap devices. |
800 |
|
|
* tty - for openpty. |
801 |
|
|
* proc - run kill to terminate its children safely. |
802 |
|
|
* sendfd - for disks, interfaces and other fds. |
803 |
|
|
* recvfd - for send and receive. |
804 |
|
|
* getpw - lookup user or group id by name. |
805 |
|
|
* chown, fattr - change tty ownership |
806 |
|
|
*/ |
807 |
|
|
if (pledge("stdio rpath wpath proc tty recvfd sendfd getpw" |
808 |
|
|
" chown fattr", NULL) == -1) |
809 |
|
|
fatal("pledge"); |
810 |
|
|
|
811 |
|
|
if (parse_config(env->vmd_conffile) == -1) { |
812 |
|
|
proc_kill(&env->vmd_ps); |
813 |
|
|
exit(1); |
814 |
|
|
} |
815 |
|
|
|
816 |
|
|
if (env->vmd_noaction) { |
817 |
|
|
fprintf(stderr, "configuration OK\n"); |
818 |
|
|
proc_kill(&env->vmd_ps); |
819 |
|
|
exit(0); |
820 |
|
|
} |
821 |
|
|
|
822 |
|
|
TAILQ_FOREACH(vsw, env->vmd_switches, sw_entry) { |
823 |
|
|
if (vsw->sw_running) |
824 |
|
|
continue; |
825 |
|
|
if (vm_priv_brconfig(&env->vmd_ps, vsw) == -1) { |
826 |
|
|
log_warn("%s: failed to create switch %s", |
827 |
|
|
__func__, vsw->sw_name); |
828 |
|
|
switch_remove(vsw); |
829 |
|
|
return (-1); |
830 |
|
|
} |
831 |
|
|
} |
832 |
|
|
|
833 |
|
|
TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { |
834 |
|
|
if (vm->vm_disabled) { |
835 |
|
|
log_debug("%s: not creating vm %s (disabled)", |
836 |
|
|
__func__, |
837 |
|
|
vm->vm_params.vmc_params.vcp_name); |
838 |
|
|
continue; |
839 |
|
|
} |
840 |
|
|
if (config_setvm(&env->vmd_ps, vm, -1, 0) == -1) |
841 |
|
|
return (-1); |
842 |
|
|
} |
843 |
|
|
|
844 |
|
|
/* Send shared global configuration to all children */ |
845 |
|
|
if (config_setconfig(env) == -1) |
846 |
|
|
return (-1); |
847 |
|
|
|
848 |
|
|
return (0); |
849 |
|
|
} |
850 |
|
|
|
851 |
|
|
int |
852 |
|
|
vmd_reload(unsigned int reset, const char *filename) |
853 |
|
|
{ |
854 |
|
|
struct vmd_vm *vm, *next_vm; |
855 |
|
|
struct vmd_switch *vsw; |
856 |
|
|
int reload = 0; |
857 |
|
|
|
858 |
|
|
/* Switch back to the default config file */ |
859 |
|
|
if (filename == NULL || *filename == '\0') { |
860 |
|
|
filename = env->vmd_conffile; |
861 |
|
|
reload = 1; |
862 |
|
|
} |
863 |
|
|
|
864 |
|
|
log_debug("%s: level %d config file %s", __func__, reset, filename); |
865 |
|
|
|
866 |
|
|
if (reset) { |
867 |
|
|
/* Purge the configuration */ |
868 |
|
|
config_purge(env, reset); |
869 |
|
|
config_setreset(env, reset); |
870 |
|
|
} else { |
871 |
|
|
/* |
872 |
|
|
* Load or reload the configuration. |
873 |
|
|
* |
874 |
|
|
* Reloading removes all non-running VMs before processing the |
875 |
|
|
* config file, whereas loading only adds to the existing list |
876 |
|
|
* of VMs. |
877 |
|
|
*/ |
878 |
|
|
|
879 |
|
|
if (reload) { |
880 |
|
|
TAILQ_FOREACH_SAFE(vm, env->vmd_vms, vm_entry, next_vm) { |
881 |
|
|
if (vm->vm_running == 0) { |
882 |
|
|
log_debug("%s: calling vm_remove", |
883 |
|
|
__func__); |
884 |
|
|
vm_remove(vm); |
885 |
|
|
} |
886 |
|
|
} |
887 |
|
|
|
888 |
|
|
/* Update shared global configuration in all children */ |
889 |
|
|
if (config_setconfig(env) == -1) |
890 |
|
|
return (-1); |
891 |
|
|
} |
892 |
|
|
|
893 |
|
|
if (parse_config(filename) == -1) { |
894 |
|
|
log_debug("%s: failed to load config file %s", |
895 |
|
|
__func__, filename); |
896 |
|
|
return (-1); |
897 |
|
|
} |
898 |
|
|
|
899 |
|
|
TAILQ_FOREACH(vsw, env->vmd_switches, sw_entry) { |
900 |
|
|
if (vsw->sw_running) |
901 |
|
|
continue; |
902 |
|
|
if (vm_priv_brconfig(&env->vmd_ps, vsw) == -1) { |
903 |
|
|
log_warn("%s: failed to create switch %s", |
904 |
|
|
__func__, vsw->sw_name); |
905 |
|
|
switch_remove(vsw); |
906 |
|
|
return (-1); |
907 |
|
|
} |
908 |
|
|
} |
909 |
|
|
|
910 |
|
|
TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { |
911 |
|
|
if (vm->vm_running == 0) { |
912 |
|
|
if (vm->vm_disabled) { |
913 |
|
|
log_debug("%s: not creating vm %s" |
914 |
|
|
" (disabled)", __func__, |
915 |
|
|
vm->vm_params.vmc_params.vcp_name); |
916 |
|
|
continue; |
917 |
|
|
} |
918 |
|
|
if (config_setvm(&env->vmd_ps, vm, -1, 0) == -1) |
919 |
|
|
return (-1); |
920 |
|
|
} else { |
921 |
|
|
log_debug("%s: not creating vm \"%s\": " |
922 |
|
|
"(running)", __func__, |
923 |
|
|
vm->vm_params.vmc_params.vcp_name); |
924 |
|
|
} |
925 |
|
|
} |
926 |
|
|
} |
927 |
|
|
|
928 |
|
|
return (0); |
929 |
|
|
} |
930 |
|
|
|
931 |
|
|
void |
932 |
|
|
vmd_shutdown(void) |
933 |
|
|
{ |
934 |
|
|
struct vmd_vm *vm, *vm_next; |
935 |
|
|
|
936 |
|
|
log_debug("%s: performing shutdown", __func__); |
937 |
|
|
TAILQ_FOREACH_SAFE(vm, env->vmd_vms, vm_entry, vm_next) { |
938 |
|
|
vm_remove(vm); |
939 |
|
|
} |
940 |
|
|
|
941 |
|
|
proc_kill(&env->vmd_ps); |
942 |
|
|
free(env); |
943 |
|
|
|
944 |
|
|
log_warnx("parent terminating"); |
945 |
|
|
exit(0); |
946 |
|
|
} |
947 |
|
|
|
948 |
|
|
struct vmd_vm * |
949 |
|
|
vm_getbyvmid(uint32_t vmid) |
950 |
|
|
{ |
951 |
|
|
struct vmd_vm *vm; |
952 |
|
|
|
953 |
|
|
if (vmid == 0) |
954 |
|
|
return (NULL); |
955 |
|
|
TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { |
956 |
|
|
if (vm->vm_vmid == vmid) |
957 |
|
|
return (vm); |
958 |
|
|
} |
959 |
|
|
|
960 |
|
|
return (NULL); |
961 |
|
|
} |
962 |
|
|
|
963 |
|
|
struct vmd_vm * |
964 |
|
|
vm_getbyid(uint32_t id) |
965 |
|
|
{ |
966 |
|
|
struct vmd_vm *vm; |
967 |
|
|
|
968 |
|
|
if (id == 0) |
969 |
|
|
return (NULL); |
970 |
|
|
TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { |
971 |
|
|
if (vm->vm_params.vmc_params.vcp_id == id) |
972 |
|
|
return (vm); |
973 |
|
|
} |
974 |
|
|
|
975 |
|
|
return (NULL); |
976 |
|
|
} |
977 |
|
|
|
978 |
|
|
uint32_t |
979 |
|
|
vm_id2vmid(uint32_t id, struct vmd_vm *vm) |
980 |
|
|
{ |
981 |
|
|
if (vm == NULL && (vm = vm_getbyid(id)) == NULL) |
982 |
|
|
return (0); |
983 |
|
|
dprintf("%s: vmm id %u is vmid %u", __func__, |
984 |
|
|
id, vm->vm_vmid); |
985 |
|
|
return (vm->vm_vmid); |
986 |
|
|
} |
987 |
|
|
|
988 |
|
|
uint32_t |
989 |
|
|
vm_vmid2id(uint32_t vmid, struct vmd_vm *vm) |
990 |
|
|
{ |
991 |
|
|
if (vm == NULL && (vm = vm_getbyvmid(vmid)) == NULL) |
992 |
|
|
return (0); |
993 |
|
|
dprintf("%s: vmid %u is vmm id %u", __func__, |
994 |
|
|
vmid, vm->vm_params.vmc_params.vcp_id); |
995 |
|
|
return (vm->vm_params.vmc_params.vcp_id); |
996 |
|
|
} |
997 |
|
|
|
998 |
|
|
struct vmd_vm * |
999 |
|
|
vm_getbyname(const char *name) |
1000 |
|
|
{ |
1001 |
|
|
struct vmd_vm *vm; |
1002 |
|
|
|
1003 |
|
|
if (name == NULL) |
1004 |
|
|
return (NULL); |
1005 |
|
|
TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { |
1006 |
|
|
if (strcmp(vm->vm_params.vmc_params.vcp_name, name) == 0) |
1007 |
|
|
return (vm); |
1008 |
|
|
} |
1009 |
|
|
|
1010 |
|
|
return (NULL); |
1011 |
|
|
} |
1012 |
|
|
|
1013 |
|
|
struct vmd_vm * |
1014 |
|
|
vm_getbypid(pid_t pid) |
1015 |
|
|
{ |
1016 |
|
|
struct vmd_vm *vm; |
1017 |
|
|
|
1018 |
|
|
TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { |
1019 |
|
|
if (vm->vm_pid == pid) |
1020 |
|
|
return (vm); |
1021 |
|
|
} |
1022 |
|
|
|
1023 |
|
|
return (NULL); |
1024 |
|
|
} |
1025 |
|
|
|
1026 |
|
|
void |
1027 |
|
|
vm_stop(struct vmd_vm *vm, int keeptty) |
1028 |
|
|
{ |
1029 |
|
|
unsigned int i; |
1030 |
|
|
|
1031 |
|
|
if (vm == NULL) |
1032 |
|
|
return; |
1033 |
|
|
|
1034 |
|
|
log_debug("%s: stopping vm %d", __func__, vm->vm_vmid); |
1035 |
|
|
vm->vm_running = 0; |
1036 |
|
|
vm->vm_shutdown = 0; |
1037 |
|
|
|
1038 |
|
|
if (vm->vm_iev.ibuf.fd != -1) { |
1039 |
|
|
event_del(&vm->vm_iev.ev); |
1040 |
|
|
close(vm->vm_iev.ibuf.fd); |
1041 |
|
|
} |
1042 |
|
|
for (i = 0; i < VMM_MAX_DISKS_PER_VM; i++) { |
1043 |
|
|
if (vm->vm_disks[i] != -1) { |
1044 |
|
|
close(vm->vm_disks[i]); |
1045 |
|
|
vm->vm_disks[i] = -1; |
1046 |
|
|
} |
1047 |
|
|
} |
1048 |
|
|
for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) { |
1049 |
|
|
if (vm->vm_ifs[i].vif_fd != -1) { |
1050 |
|
|
close(vm->vm_ifs[i].vif_fd); |
1051 |
|
|
vm->vm_ifs[i].vif_fd = -1; |
1052 |
|
|
} |
1053 |
|
|
free(vm->vm_ifs[i].vif_name); |
1054 |
|
|
free(vm->vm_ifs[i].vif_switch); |
1055 |
|
|
free(vm->vm_ifs[i].vif_group); |
1056 |
|
|
vm->vm_ifs[i].vif_name = NULL; |
1057 |
|
|
vm->vm_ifs[i].vif_switch = NULL; |
1058 |
|
|
vm->vm_ifs[i].vif_group = NULL; |
1059 |
|
|
} |
1060 |
|
|
if (vm->vm_kernel != -1) { |
1061 |
|
|
close(vm->vm_kernel); |
1062 |
|
|
vm->vm_kernel = -1; |
1063 |
|
|
} |
1064 |
|
|
vm->vm_uid = 0; |
1065 |
|
|
if (!keeptty) |
1066 |
|
|
vm_closetty(vm); |
1067 |
|
|
} |
1068 |
|
|
|
1069 |
|
|
void |
1070 |
|
|
vm_remove(struct vmd_vm *vm) |
1071 |
|
|
{ |
1072 |
|
|
if (vm == NULL) |
1073 |
|
|
return; |
1074 |
|
|
|
1075 |
|
|
log_debug("%s: removing vm id %d from running config", |
1076 |
|
|
__func__, vm->vm_vmid); |
1077 |
|
|
TAILQ_REMOVE(env->vmd_vms, vm, vm_entry); |
1078 |
|
|
log_debug("%s: calling vm_stop", __func__); |
1079 |
|
|
vm_stop(vm, 0); |
1080 |
|
|
free(vm); |
1081 |
|
|
} |
1082 |
|
|
|
1083 |
|
|
int |
1084 |
|
|
vm_register(struct privsep *ps, struct vmop_create_params *vmc, |
1085 |
|
|
struct vmd_vm **ret_vm, uint32_t id, uid_t uid) |
1086 |
|
|
{ |
1087 |
|
|
struct vmd_vm *vm = NULL; |
1088 |
|
|
struct vm_create_params *vcp = &vmc->vmc_params; |
1089 |
|
|
static const uint8_t zero_mac[ETHER_ADDR_LEN]; |
1090 |
|
|
uint32_t rng; |
1091 |
|
|
unsigned int i; |
1092 |
|
|
struct vmd_switch *sw; |
1093 |
|
|
char *s; |
1094 |
|
|
|
1095 |
|
|
errno = 0; |
1096 |
|
|
*ret_vm = NULL; |
1097 |
|
|
|
1098 |
|
|
if ((vm = vm_getbyname(vcp->vcp_name)) != NULL || |
1099 |
|
|
(vm = vm_getbyvmid(vcp->vcp_id)) != NULL) { |
1100 |
|
|
if (vm_checkperm(vm, uid) != 0 || vmc->vmc_flags != 0) { |
1101 |
|
|
errno = EPERM; |
1102 |
|
|
goto fail; |
1103 |
|
|
} |
1104 |
|
|
*ret_vm = vm; |
1105 |
|
|
errno = EALREADY; |
1106 |
|
|
goto fail; |
1107 |
|
|
} |
1108 |
|
|
|
1109 |
|
|
/* |
1110 |
|
|
* non-root users can only start existing VMs |
1111 |
|
|
* XXX there could be a mechanism to allow overriding some options |
1112 |
|
|
*/ |
1113 |
|
|
if (vm_checkperm(NULL, uid) != 0) { |
1114 |
|
|
errno = EPERM; |
1115 |
|
|
goto fail; |
1116 |
|
|
} |
1117 |
|
|
if (vmc->vmc_flags == 0) { |
1118 |
|
|
errno = ENOENT; |
1119 |
|
|
goto fail; |
1120 |
|
|
} |
1121 |
|
|
if (vcp->vcp_ncpus == 0) |
1122 |
|
|
vcp->vcp_ncpus = 1; |
1123 |
|
|
if (vcp->vcp_memranges[0].vmr_size == 0) |
1124 |
|
|
vcp->vcp_memranges[0].vmr_size = VM_DEFAULT_MEMORY; |
1125 |
|
|
if (vcp->vcp_ncpus > VMM_MAX_VCPUS_PER_VM) { |
1126 |
|
|
log_warnx("invalid number of CPUs"); |
1127 |
|
|
goto fail; |
1128 |
|
|
} else if (vcp->vcp_ndisks > VMM_MAX_DISKS_PER_VM) { |
1129 |
|
|
log_warnx("invalid number of disks"); |
1130 |
|
|
goto fail; |
1131 |
|
|
} else if (vcp->vcp_nnics > VMM_MAX_NICS_PER_VM) { |
1132 |
|
|
log_warnx("invalid number of interfaces"); |
1133 |
|
|
goto fail; |
1134 |
|
|
} else if (strlen(vcp->vcp_kernel) == 0 && vcp->vcp_ndisks == 0) { |
1135 |
|
|
log_warnx("no kernel or disk specified"); |
1136 |
|
|
goto fail; |
1137 |
|
|
} else if (strlen(vcp->vcp_name) == 0) { |
1138 |
|
|
log_warnx("invalid VM name"); |
1139 |
|
|
goto fail; |
1140 |
|
|
} else if (*vcp->vcp_name == '-' || *vcp->vcp_name == '.' || |
1141 |
|
|
*vcp->vcp_name == '_') { |
1142 |
|
|
log_warnx("Invalid VM name"); |
1143 |
|
|
goto fail; |
1144 |
|
|
} else { |
1145 |
|
|
for (s = vcp->vcp_name; *s != '\0'; ++s) { |
1146 |
|
|
if (!(isalnum(*s) || *s == '.' || *s == '-' || |
1147 |
|
|
*s == '_')) { |
1148 |
|
|
log_warnx("Invalid VM name"); |
1149 |
|
|
goto fail; |
1150 |
|
|
} |
1151 |
|
|
} |
1152 |
|
|
} |
1153 |
|
|
|
1154 |
|
|
if ((vm = calloc(1, sizeof(*vm))) == NULL) |
1155 |
|
|
goto fail; |
1156 |
|
|
|
1157 |
|
|
memcpy(&vm->vm_params, vmc, sizeof(vm->vm_params)); |
1158 |
|
|
vmc = &vm->vm_params; |
1159 |
|
|
vcp = &vmc->vmc_params; |
1160 |
|
|
vm->vm_pid = -1; |
1161 |
|
|
vm->vm_tty = -1; |
1162 |
|
|
vm->vm_receive_fd = -1; |
1163 |
|
|
vm->vm_paused = 0; |
1164 |
|
|
|
1165 |
|
|
for (i = 0; i < vcp->vcp_ndisks; i++) |
1166 |
|
|
vm->vm_disks[i] = -1; |
1167 |
|
|
for (i = 0; i < vcp->vcp_nnics; i++) { |
1168 |
|
|
vm->vm_ifs[i].vif_fd = -1; |
1169 |
|
|
|
1170 |
|
|
if ((sw = switch_getbyname(vmc->vmc_ifswitch[i])) != NULL) { |
1171 |
|
|
/* overwrite the rdomain, if configured on the switch */ |
1172 |
|
|
if (sw->sw_flags & VMIFF_RDOMAIN) |
1173 |
|
|
vmc->vmc_ifrdomain[i] = sw->sw_rdomain; |
1174 |
|
|
|
1175 |
|
|
/* inherit per-interface flags from the switch */ |
1176 |
|
|
vmc->vmc_ifflags[i] |= (sw->sw_flags & VMIFF_OPTMASK); |
1177 |
|
|
} |
1178 |
|
|
|
1179 |
|
|
/* |
1180 |
|
|
* If the MAC address is zero, always randomize it in vmd(8) |
1181 |
|
|
* because we cannot rely on the guest OS to do the right |
1182 |
|
|
* thing like OpenBSD does. Based on ether_fakeaddr() |
1183 |
|
|
* from the kernel, incremented by one to differentiate |
1184 |
|
|
* the source. |
1185 |
|
|
*/ |
1186 |
|
|
if (memcmp(zero_mac, &vcp->vcp_macs[i], ETHER_ADDR_LEN) == 0) { |
1187 |
|
|
rng = arc4random(); |
1188 |
|
|
vcp->vcp_macs[i][0] = 0xfe; |
1189 |
|
|
vcp->vcp_macs[i][1] = 0xe1; |
1190 |
|
|
vcp->vcp_macs[i][2] = 0xba + 1; |
1191 |
|
|
vcp->vcp_macs[i][3] = 0xd0 | ((i + 1) & 0xf); |
1192 |
|
|
vcp->vcp_macs[i][4] = rng; |
1193 |
|
|
vcp->vcp_macs[i][5] = rng >> 8; |
1194 |
|
|
} |
1195 |
|
|
} |
1196 |
|
|
vm->vm_kernel = -1; |
1197 |
|
|
vm->vm_iev.ibuf.fd = -1; |
1198 |
|
|
|
1199 |
|
|
if (++env->vmd_nvm == 0) |
1200 |
|
|
fatalx("too many vms"); |
1201 |
|
|
|
1202 |
|
|
/* Assign a new internal Id if not specified */ |
1203 |
|
|
vm->vm_vmid = id == 0 ? env->vmd_nvm : id; |
1204 |
|
|
|
1205 |
|
|
log_debug("%s: registering vm %d", __func__, vm->vm_vmid); |
1206 |
|
|
TAILQ_INSERT_TAIL(env->vmd_vms, vm, vm_entry); |
1207 |
|
|
|
1208 |
|
|
*ret_vm = vm; |
1209 |
|
|
return (0); |
1210 |
|
|
fail: |
1211 |
|
|
if (errno == 0) |
1212 |
|
|
errno = EINVAL; |
1213 |
|
|
return (-1); |
1214 |
|
|
} |
1215 |
|
|
|
1216 |
|
|
int |
1217 |
|
|
vm_checkperm(struct vmd_vm *vm, uid_t uid) |
1218 |
|
|
{ |
1219 |
|
|
struct group *gr; |
1220 |
|
|
struct passwd *pw; |
1221 |
|
|
char **grmem; |
1222 |
|
|
|
1223 |
|
|
/* root has no restrictions */ |
1224 |
|
|
if (uid == 0) |
1225 |
|
|
return (0); |
1226 |
|
|
|
1227 |
|
|
if (vm == NULL) |
1228 |
|
|
return (-1); |
1229 |
|
|
|
1230 |
|
|
/* check supplementary groups */ |
1231 |
|
|
if (vm->vm_params.vmc_gid != -1 && |
1232 |
|
|
(pw = getpwuid(uid)) != NULL && |
1233 |
|
|
(gr = getgrgid(vm->vm_params.vmc_gid)) != NULL) { |
1234 |
|
|
for (grmem = gr->gr_mem; *grmem; grmem++) |
1235 |
|
|
if (strcmp(*grmem, pw->pw_name) == 0) |
1236 |
|
|
return (0); |
1237 |
|
|
} |
1238 |
|
|
|
1239 |
|
|
/* check user */ |
1240 |
|
|
if ((vm->vm_running && vm->vm_uid == uid) || |
1241 |
|
|
(!vm->vm_running && vm->vm_params.vmc_uid == uid)) |
1242 |
|
|
return (0); |
1243 |
|
|
|
1244 |
|
|
return (-1); |
1245 |
|
|
} |
1246 |
|
|
|
1247 |
|
|
int |
1248 |
|
|
vm_opentty(struct vmd_vm *vm) |
1249 |
|
|
{ |
1250 |
|
|
struct ptmget ptm; |
1251 |
|
|
struct stat st; |
1252 |
|
|
struct group *gr; |
1253 |
|
|
uid_t uid; |
1254 |
|
|
gid_t gid; |
1255 |
|
|
mode_t mode; |
1256 |
|
|
|
1257 |
|
|
/* |
1258 |
|
|
* Open tty with pre-opened PTM fd |
1259 |
|
|
*/ |
1260 |
|
|
if ((ioctl(env->vmd_ptmfd, PTMGET, &ptm) == -1)) |
1261 |
|
|
return (-1); |
1262 |
|
|
|
1263 |
|
|
vm->vm_tty = ptm.cfd; |
1264 |
|
|
close(ptm.sfd); |
1265 |
|
|
if ((vm->vm_ttyname = strdup(ptm.sn)) == NULL) |
1266 |
|
|
goto fail; |
1267 |
|
|
|
1268 |
|
|
uid = vm->vm_uid; |
1269 |
|
|
gid = vm->vm_params.vmc_gid; |
1270 |
|
|
|
1271 |
|
|
if (vm->vm_params.vmc_gid != -1) { |
1272 |
|
|
mode = 0660; |
1273 |
|
|
} else if ((gr = getgrnam("tty")) != NULL) { |
1274 |
|
|
gid = gr->gr_gid; |
1275 |
|
|
mode = 0620; |
1276 |
|
|
} else { |
1277 |
|
|
mode = 0600; |
1278 |
|
|
gid = 0; |
1279 |
|
|
} |
1280 |
|
|
|
1281 |
|
|
log_debug("%s: vm %s tty %s uid %d gid %d mode %o", |
1282 |
|
|
__func__, vm->vm_params.vmc_params.vcp_name, |
1283 |
|
|
vm->vm_ttyname, uid, gid, mode); |
1284 |
|
|
|
1285 |
|
|
/* |
1286 |
|
|
* Change ownership and mode of the tty as required. |
1287 |
|
|
* Loosely based on the implementation of sshpty.c |
1288 |
|
|
*/ |
1289 |
|
|
if (stat(vm->vm_ttyname, &st) == -1) |
1290 |
|
|
goto fail; |
1291 |
|
|
|
1292 |
|
|
if (st.st_uid != uid || st.st_gid != gid) { |
1293 |
|
|
if (chown(vm->vm_ttyname, uid, gid) == -1) { |
1294 |
|
|
log_warn("chown %s %d %d failed, uid %d", |
1295 |
|
|
vm->vm_ttyname, uid, gid, getuid()); |
1296 |
|
|
|
1297 |
|
|
/* Ignore failure on read-only filesystems */ |
1298 |
|
|
if (!((errno == EROFS) && |
1299 |
|
|
(st.st_uid == uid || st.st_uid == 0))) |
1300 |
|
|
goto fail; |
1301 |
|
|
} |
1302 |
|
|
} |
1303 |
|
|
|
1304 |
|
|
if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) { |
1305 |
|
|
if (chmod(vm->vm_ttyname, mode) == -1) { |
1306 |
|
|
log_warn("chmod %s %o failed, uid %d", |
1307 |
|
|
vm->vm_ttyname, mode, getuid()); |
1308 |
|
|
|
1309 |
|
|
/* Ignore failure on read-only filesystems */ |
1310 |
|
|
if (!((errno == EROFS) && |
1311 |
|
|
(st.st_uid == uid || st.st_uid == 0))) |
1312 |
|
|
goto fail; |
1313 |
|
|
} |
1314 |
|
|
} |
1315 |
|
|
|
1316 |
|
|
return (0); |
1317 |
|
|
fail: |
1318 |
|
|
vm_closetty(vm); |
1319 |
|
|
return (-1); |
1320 |
|
|
} |
1321 |
|
|
|
1322 |
|
|
void |
1323 |
|
|
vm_closetty(struct vmd_vm *vm) |
1324 |
|
|
{ |
1325 |
|
|
if (vm->vm_tty != -1) { |
1326 |
|
|
/* Release and close the tty */ |
1327 |
|
|
if (fchown(vm->vm_tty, 0, 0) == -1) |
1328 |
|
|
log_warn("chown %s 0 0 failed", vm->vm_ttyname); |
1329 |
|
|
if (fchmod(vm->vm_tty, 0666) == -1) |
1330 |
|
|
log_warn("chmod %s 0666 failed", vm->vm_ttyname); |
1331 |
|
|
close(vm->vm_tty); |
1332 |
|
|
vm->vm_tty = -1; |
1333 |
|
|
} |
1334 |
|
|
free(vm->vm_ttyname); |
1335 |
|
|
vm->vm_ttyname = NULL; |
1336 |
|
|
} |
1337 |
|
|
|
1338 |
|
|
void |
1339 |
|
|
switch_remove(struct vmd_switch *vsw) |
1340 |
|
|
{ |
1341 |
|
|
struct vmd_if *vif; |
1342 |
|
|
|
1343 |
|
|
if (vsw == NULL) |
1344 |
|
|
return; |
1345 |
|
|
|
1346 |
|
|
TAILQ_REMOVE(env->vmd_switches, vsw, sw_entry); |
1347 |
|
|
|
1348 |
|
|
while ((vif = TAILQ_FIRST(&vsw->sw_ifs)) != NULL) { |
1349 |
|
|
free(vif->vif_name); |
1350 |
|
|
free(vif->vif_switch); |
1351 |
|
|
TAILQ_REMOVE(&vsw->sw_ifs, vif, vif_entry); |
1352 |
|
|
free(vif); |
1353 |
|
|
} |
1354 |
|
|
|
1355 |
|
|
free(vsw->sw_group); |
1356 |
|
|
free(vsw->sw_name); |
1357 |
|
|
free(vsw); |
1358 |
|
|
} |
1359 |
|
|
|
1360 |
|
|
struct vmd_switch * |
1361 |
|
|
switch_getbyname(const char *name) |
1362 |
|
|
{ |
1363 |
|
|
struct vmd_switch *vsw; |
1364 |
|
|
|
1365 |
|
|
if (name == NULL) |
1366 |
|
|
return (NULL); |
1367 |
|
|
TAILQ_FOREACH(vsw, env->vmd_switches, sw_entry) { |
1368 |
|
|
if (strcmp(vsw->sw_name, name) == 0) |
1369 |
|
|
return (vsw); |
1370 |
|
|
} |
1371 |
|
|
|
1372 |
|
|
return (NULL); |
1373 |
|
|
} |
1374 |
|
|
|
1375 |
|
|
char * |
1376 |
|
|
get_string(uint8_t *ptr, size_t len) |
1377 |
|
|
{ |
1378 |
|
|
size_t i; |
1379 |
|
|
|
1380 |
|
|
for (i = 0; i < len; i++) |
1381 |
|
|
if (!isprint(ptr[i])) |
1382 |
|
|
break; |
1383 |
|
|
|
1384 |
|
|
return strndup(ptr, i); |
1385 |
|
|
} |
1386 |
|
|
|
1387 |
|
|
uint32_t |
1388 |
|
|
prefixlen2mask(uint8_t prefixlen) |
1389 |
|
|
{ |
1390 |
|
|
if (prefixlen == 0) |
1391 |
|
|
return (0); |
1392 |
|
|
|
1393 |
|
|
if (prefixlen > 32) |
1394 |
|
|
prefixlen = 32; |
1395 |
|
|
|
1396 |
|
|
return (htonl(0xffffffff << (32 - prefixlen))); |
1397 |
|
|
} |