1 |
|
|
/* $OpenBSD: vmd.c,v 1.74 2017/11/11 02:50:07 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 ret %d", |
398 |
|
|
__func__, vmr.vmr_id, vmr.vmr_result); |
399 |
|
|
if ((vm = vm_getbyvmid(vmr.vmr_id)) == NULL) { |
400 |
|
|
log_debug("%s: vm %d is no longer available", |
401 |
|
|
__func__, vmr.vmr_id); |
402 |
|
|
break; |
403 |
|
|
} |
404 |
|
|
if (vmr.vmr_result != EAGAIN) { |
405 |
|
|
if (vm->vm_from_config) { |
406 |
|
|
log_debug("%s: about to stop vm id %d", |
407 |
|
|
__func__, vm->vm_vmid); |
408 |
|
|
vm_stop(vm, 0); |
409 |
|
|
} else { |
410 |
|
|
log_debug("%s: about to remove vm %d", |
411 |
|
|
__func__, vm->vm_vmid); |
412 |
|
|
vm_remove(vm); |
413 |
|
|
} |
414 |
|
|
} else { |
415 |
|
|
/* Stop VM instance but keep the tty open */ |
416 |
|
|
log_debug("%s: about to stop vm id %d with tty open", |
417 |
|
|
__func__, vm->vm_vmid); |
418 |
|
|
vm_stop(vm, 1); |
419 |
|
|
config_setvm(ps, vm, (uint32_t)-1, vm->vm_uid); |
420 |
|
|
} |
421 |
|
|
break; |
422 |
|
|
case IMSG_VMDOP_GET_INFO_VM_DATA: |
423 |
|
|
IMSG_SIZE_CHECK(imsg, &vir); |
424 |
|
|
memcpy(&vir, imsg->data, sizeof(vir)); |
425 |
|
|
if ((vm = vm_getbyvmid(vir.vir_info.vir_id)) != NULL) { |
426 |
|
|
memset(vir.vir_ttyname, 0, sizeof(vir.vir_ttyname)); |
427 |
|
|
if (vm->vm_ttyname != NULL) |
428 |
|
|
strlcpy(vir.vir_ttyname, vm->vm_ttyname, |
429 |
|
|
sizeof(vir.vir_ttyname)); |
430 |
|
|
if (vm->vm_shutdown) { |
431 |
|
|
/* XXX there might be a nicer way */ |
432 |
|
|
(void)strlcat(vir.vir_info.vir_name, |
433 |
|
|
" - stopping", |
434 |
|
|
sizeof(vir.vir_info.vir_name)); |
435 |
|
|
} |
436 |
|
|
/* get the user id who started the vm */ |
437 |
|
|
vir.vir_uid = vm->vm_uid; |
438 |
|
|
vir.vir_gid = vm->vm_params.vmc_gid; |
439 |
|
|
} |
440 |
|
|
if (proc_compose_imsg(ps, PROC_CONTROL, -1, imsg->hdr.type, |
441 |
|
|
imsg->hdr.peerid, -1, &vir, sizeof(vir)) == -1) { |
442 |
|
|
log_debug("%s: GET_INFO_VM failed for vm %d, removing", |
443 |
|
|
__func__, vm->vm_vmid); |
444 |
|
|
vm_remove(vm); |
445 |
|
|
return (-1); |
446 |
|
|
} |
447 |
|
|
break; |
448 |
|
|
case IMSG_VMDOP_GET_INFO_VM_END_DATA: |
449 |
|
|
/* |
450 |
|
|
* PROC_VMM has responded with the *running* VMs, now we |
451 |
|
|
* append the others. These use the special value 0 for their |
452 |
|
|
* kernel id to indicate that they are not running. |
453 |
|
|
*/ |
454 |
|
|
TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { |
455 |
|
|
if (!vm->vm_running) { |
456 |
|
|
memset(&vir, 0, sizeof(vir)); |
457 |
|
|
vir.vir_info.vir_id = vm->vm_vmid; |
458 |
|
|
strlcpy(vir.vir_info.vir_name, |
459 |
|
|
vm->vm_params.vmc_params.vcp_name, |
460 |
|
|
VMM_MAX_NAME_LEN); |
461 |
|
|
vir.vir_info.vir_memory_size = |
462 |
|
|
vm->vm_params.vmc_params.vcp_memranges[0].vmr_size; |
463 |
|
|
vir.vir_info.vir_ncpus = |
464 |
|
|
vm->vm_params.vmc_params.vcp_ncpus; |
465 |
|
|
/* get the configured user id for this vm */ |
466 |
|
|
vir.vir_uid = vm->vm_params.vmc_uid; |
467 |
|
|
vir.vir_gid = vm->vm_params.vmc_gid; |
468 |
|
|
if (proc_compose_imsg(ps, PROC_CONTROL, -1, |
469 |
|
|
IMSG_VMDOP_GET_INFO_VM_DATA, |
470 |
|
|
imsg->hdr.peerid, -1, &vir, |
471 |
|
|
sizeof(vir)) == -1) { |
472 |
|
|
log_debug("%s: GET_INFO_VM_END failed", |
473 |
|
|
__func__); |
474 |
|
|
vm_remove(vm); |
475 |
|
|
return (-1); |
476 |
|
|
} |
477 |
|
|
} |
478 |
|
|
} |
479 |
|
|
IMSG_SIZE_CHECK(imsg, &res); |
480 |
|
|
proc_forward_imsg(ps, imsg, PROC_CONTROL, -1); |
481 |
|
|
break; |
482 |
|
|
default: |
483 |
|
|
return (-1); |
484 |
|
|
} |
485 |
|
|
|
486 |
|
|
return (0); |
487 |
|
|
} |
488 |
|
|
|
489 |
|
|
int |
490 |
|
|
check_vmh(struct vm_dump_header *vmh) { |
491 |
|
|
int i; |
492 |
|
|
unsigned int code, leaf; |
493 |
|
|
unsigned int a, b, c, d; |
494 |
|
|
|
495 |
|
|
|
496 |
|
|
if (vmh->vmh_version != VM_DUMP_VERSION) { |
497 |
|
|
log_warnx("%s: incompatible dump version", __func__); |
498 |
|
|
return (-1); |
499 |
|
|
} |
500 |
|
|
|
501 |
|
|
for (i = 0; i < VM_DUMP_HEADER_CPUID_COUNT; i++) { |
502 |
|
|
code = vmh->vmh_cpuids[i].code; |
503 |
|
|
leaf = vmh->vmh_cpuids[i].leaf; |
504 |
|
|
if (leaf != 0x00) { |
505 |
|
|
log_debug("%s: invalid leaf 0x%x for code 0x%x", |
506 |
|
|
__func__, leaf, code); |
507 |
|
|
return (-1); |
508 |
|
|
} |
509 |
|
|
|
510 |
|
|
switch(code) { |
511 |
|
|
case 0x00: |
512 |
|
|
CPUID_LEAF(code, leaf, a, b, c, d); |
513 |
|
|
if (vmh->vmh_cpuids[i].a > a) { |
514 |
|
|
log_debug("%s: incompatible cpuid level", __func__); |
515 |
|
|
return (-1); |
516 |
|
|
} |
517 |
|
|
if (!(vmh->vmh_cpuids[i].b == b && |
518 |
|
|
vmh->vmh_cpuids[i].c == c && |
519 |
|
|
vmh->vmh_cpuids[i].d == d)) { |
520 |
|
|
log_debug("%s: incompatible cpu brand", __func__); |
521 |
|
|
return (-1); |
522 |
|
|
} |
523 |
|
|
break; |
524 |
|
|
|
525 |
|
|
case 0x01: |
526 |
|
|
CPUID_LEAF(code, leaf, a, b, c, d); |
527 |
|
|
if ((vmh->vmh_cpuids[i].c & c & VMM_CPUIDECX_MASK) != |
528 |
|
|
(vmh->vmh_cpuids[i].c & VMM_CPUIDECX_MASK)) { |
529 |
|
|
log_debug("%s: incompatible cpu features " |
530 |
|
|
"code: 0x%x leaf: 0x%x reg: c", __func__, |
531 |
|
|
code, leaf); |
532 |
|
|
return (-1); |
533 |
|
|
} |
534 |
|
|
if ((vmh->vmh_cpuids[i].d & d & VMM_CPUIDEDX_MASK) != |
535 |
|
|
(vmh->vmh_cpuids[i].d & VMM_CPUIDEDX_MASK)) { |
536 |
|
|
log_debug("%s: incompatible cpu features " |
537 |
|
|
"code: 0x%x leaf: 0x%x reg: d", __func__, |
538 |
|
|
code, leaf); |
539 |
|
|
return (-1); |
540 |
|
|
} |
541 |
|
|
break; |
542 |
|
|
|
543 |
|
|
case 0x07: |
544 |
|
|
CPUID_LEAF(code, leaf, a, b, c, d); |
545 |
|
|
if ((vmh->vmh_cpuids[i].b & b & VMM_SEFF0EBX_MASK) != |
546 |
|
|
(vmh->vmh_cpuids[i].b & VMM_SEFF0EBX_MASK)) { |
547 |
|
|
log_debug("%s: incompatible cpu features " |
548 |
|
|
"code: 0x%x leaf: 0x%x reg: c", __func__, |
549 |
|
|
code, leaf); |
550 |
|
|
return (-1); |
551 |
|
|
} |
552 |
|
|
if ((vmh->vmh_cpuids[i].c & c & VMM_SEFF0ECX_MASK) != |
553 |
|
|
(vmh->vmh_cpuids[i].c & VMM_SEFF0ECX_MASK)) { |
554 |
|
|
log_debug("%s: incompatible cpu features " |
555 |
|
|
"code: 0x%x leaf: 0x%x reg: d", __func__, |
556 |
|
|
code, leaf); |
557 |
|
|
return (-1); |
558 |
|
|
} |
559 |
|
|
break; |
560 |
|
|
|
561 |
|
|
case 0x0d: |
562 |
|
|
CPUID_LEAF(code, leaf, a, b, c, d); |
563 |
|
|
if (vmh->vmh_cpuids[i].b > b) { |
564 |
|
|
log_debug("%s: incompatible cpu: insufficient " |
565 |
|
|
"max save area for enabled XCR0 features", |
566 |
|
|
__func__); |
567 |
|
|
return (-1); |
568 |
|
|
} |
569 |
|
|
if (vmh->vmh_cpuids[i].c > c) { |
570 |
|
|
log_debug("%s: incompatible cpu: insufficient " |
571 |
|
|
"max save area for supported XCR0 features", |
572 |
|
|
__func__); |
573 |
|
|
return (-1); |
574 |
|
|
} |
575 |
|
|
break; |
576 |
|
|
|
577 |
|
|
case 0x80000001: |
578 |
|
|
CPUID_LEAF(code, leaf, a, b, c, d); |
579 |
|
|
if ((vmh->vmh_cpuids[i].a & a) != vmh->vmh_cpuids[i].a) { |
580 |
|
|
log_debug("%s: incompatible cpu features " |
581 |
|
|
"code: 0x%x leaf: 0x%x reg: a", __func__, |
582 |
|
|
code, leaf); |
583 |
|
|
return (-1); |
584 |
|
|
} |
585 |
|
|
if ((vmh->vmh_cpuids[i].c & c) != vmh->vmh_cpuids[i].c) { |
586 |
|
|
log_debug("%s: incompatible cpu features " |
587 |
|
|
"code: 0x%x leaf: 0x%x reg: c", __func__, |
588 |
|
|
code, leaf); |
589 |
|
|
return (-1); |
590 |
|
|
} |
591 |
|
|
if ((vmh->vmh_cpuids[i].d & d) != vmh->vmh_cpuids[i].d) { |
592 |
|
|
log_debug("%s: incompatible cpu features " |
593 |
|
|
"code: 0x%x leaf: 0x%x reg: d", __func__, |
594 |
|
|
code, leaf); |
595 |
|
|
return (-1); |
596 |
|
|
} |
597 |
|
|
break; |
598 |
|
|
|
599 |
|
|
default: |
600 |
|
|
log_debug("%s: unknown code 0x%x", __func__, code); |
601 |
|
|
return (-1); |
602 |
|
|
} |
603 |
|
|
} |
604 |
|
|
|
605 |
|
|
return (0); |
606 |
|
|
} |
607 |
|
|
|
608 |
|
|
void |
609 |
|
|
vmd_sighdlr(int sig, short event, void *arg) |
610 |
|
|
{ |
611 |
|
|
if (privsep_process != PROC_PARENT) |
612 |
|
|
return; |
613 |
|
|
log_debug("%s: handling signal", __func__); |
614 |
|
|
|
615 |
|
|
switch (sig) { |
616 |
|
|
case SIGHUP: |
617 |
|
|
log_info("%s: reload requested with SIGHUP", __func__); |
618 |
|
|
|
619 |
|
|
/* |
620 |
|
|
* This is safe because libevent uses async signal handlers |
621 |
|
|
* that run in the event loop and not in signal context. |
622 |
|
|
*/ |
623 |
|
|
(void)vmd_reload(0, NULL); |
624 |
|
|
break; |
625 |
|
|
case SIGPIPE: |
626 |
|
|
log_info("%s: ignoring SIGPIPE", __func__); |
627 |
|
|
break; |
628 |
|
|
case SIGUSR1: |
629 |
|
|
log_info("%s: ignoring SIGUSR1", __func__); |
630 |
|
|
break; |
631 |
|
|
case SIGTERM: |
632 |
|
|
case SIGINT: |
633 |
|
|
vmd_shutdown(); |
634 |
|
|
break; |
635 |
|
|
default: |
636 |
|
|
fatalx("unexpected signal"); |
637 |
|
|
} |
638 |
|
|
} |
639 |
|
|
|
640 |
|
|
__dead void |
641 |
|
|
usage(void) |
642 |
|
|
{ |
643 |
|
|
extern char *__progname; |
644 |
|
|
fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n", |
645 |
|
|
__progname); |
646 |
|
|
exit(1); |
647 |
|
|
} |
648 |
|
|
|
649 |
|
|
int |
650 |
|
|
main(int argc, char **argv) |
651 |
|
|
{ |
652 |
|
|
struct privsep *ps; |
653 |
|
|
int ch; |
654 |
|
|
const char *conffile = VMD_CONF; |
655 |
|
|
enum privsep_procid proc_id = PROC_PARENT; |
656 |
|
|
int proc_instance = 0; |
657 |
|
|
const char *errp, *title = NULL; |
658 |
|
|
int argc0 = argc; |
659 |
|
|
|
660 |
|
|
/* log to stderr until daemonized */ |
661 |
|
|
log_init(1, LOG_DAEMON); |
662 |
|
|
|
663 |
|
|
if ((env = calloc(1, sizeof(*env))) == NULL) |
664 |
|
|
fatal("calloc: env"); |
665 |
|
|
|
666 |
|
|
while ((ch = getopt(argc, argv, "D:P:I:df:vn")) != -1) { |
667 |
|
|
switch (ch) { |
668 |
|
|
case 'D': |
669 |
|
|
if (cmdline_symset(optarg) < 0) |
670 |
|
|
log_warnx("could not parse macro definition %s", |
671 |
|
|
optarg); |
672 |
|
|
break; |
673 |
|
|
case 'd': |
674 |
|
|
env->vmd_debug = 2; |
675 |
|
|
break; |
676 |
|
|
case 'f': |
677 |
|
|
conffile = optarg; |
678 |
|
|
break; |
679 |
|
|
case 'v': |
680 |
|
|
env->vmd_verbose++; |
681 |
|
|
break; |
682 |
|
|
case 'n': |
683 |
|
|
env->vmd_noaction = 1; |
684 |
|
|
break; |
685 |
|
|
case 'P': |
686 |
|
|
title = optarg; |
687 |
|
|
proc_id = proc_getid(procs, nitems(procs), title); |
688 |
|
|
if (proc_id == PROC_MAX) |
689 |
|
|
fatalx("invalid process name"); |
690 |
|
|
break; |
691 |
|
|
case 'I': |
692 |
|
|
proc_instance = strtonum(optarg, 0, |
693 |
|
|
PROC_MAX_INSTANCES, &errp); |
694 |
|
|
if (errp) |
695 |
|
|
fatalx("invalid process instance"); |
696 |
|
|
break; |
697 |
|
|
default: |
698 |
|
|
usage(); |
699 |
|
|
} |
700 |
|
|
} |
701 |
|
|
|
702 |
|
|
argc -= optind; |
703 |
|
|
if (argc > 0) |
704 |
|
|
usage(); |
705 |
|
|
|
706 |
|
|
if (env->vmd_noaction && !env->vmd_debug) |
707 |
|
|
env->vmd_debug = 1; |
708 |
|
|
|
709 |
|
|
/* check for root privileges */ |
710 |
|
|
if (env->vmd_noaction == 0) { |
711 |
|
|
if (geteuid()) |
712 |
|
|
fatalx("need root privileges"); |
713 |
|
|
} |
714 |
|
|
|
715 |
|
|
ps = &env->vmd_ps; |
716 |
|
|
ps->ps_env = env; |
717 |
|
|
env->vmd_fd = -1; |
718 |
|
|
|
719 |
|
|
if (config_init(env) == -1) |
720 |
|
|
fatal("failed to initialize configuration"); |
721 |
|
|
|
722 |
|
|
if ((ps->ps_pw = getpwnam(VMD_USER)) == NULL) |
723 |
|
|
fatal("unknown user %s", VMD_USER); |
724 |
|
|
|
725 |
|
|
/* First proc runs as root without pledge but in default chroot */ |
726 |
|
|
proc_priv->p_pw = &proc_privpw; /* initialized to all 0 */ |
727 |
|
|
proc_priv->p_chroot = ps->ps_pw->pw_dir; /* from VMD_USER */ |
728 |
|
|
|
729 |
|
|
/* Open /dev/vmm */ |
730 |
|
|
if (env->vmd_noaction == 0) { |
731 |
|
|
env->vmd_fd = open(VMM_NODE, O_RDWR); |
732 |
|
|
if (env->vmd_fd == -1) |
733 |
|
|
fatal("%s", VMM_NODE); |
734 |
|
|
} |
735 |
|
|
|
736 |
|
|
/* Configure the control socket */ |
737 |
|
|
ps->ps_csock.cs_name = SOCKET_NAME; |
738 |
|
|
TAILQ_INIT(&ps->ps_rcsocks); |
739 |
|
|
|
740 |
|
|
/* Configuration will be parsed after forking the children */ |
741 |
|
|
env->vmd_conffile = conffile; |
742 |
|
|
|
743 |
|
|
log_init(env->vmd_debug, LOG_DAEMON); |
744 |
|
|
log_setverbose(env->vmd_verbose); |
745 |
|
|
|
746 |
|
|
if (env->vmd_noaction) |
747 |
|
|
ps->ps_noaction = 1; |
748 |
|
|
ps->ps_instance = proc_instance; |
749 |
|
|
if (title != NULL) |
750 |
|
|
ps->ps_title[proc_id] = title; |
751 |
|
|
|
752 |
|
|
/* only the parent returns */ |
753 |
|
|
proc_init(ps, procs, nitems(procs), argc0, argv, proc_id); |
754 |
|
|
|
755 |
|
|
log_procinit("parent"); |
756 |
|
|
if (!env->vmd_debug && daemon(0, 0) == -1) |
757 |
|
|
fatal("can't daemonize"); |
758 |
|
|
|
759 |
|
|
if (ps->ps_noaction == 0) |
760 |
|
|
log_info("startup"); |
761 |
|
|
|
762 |
|
|
event_init(); |
763 |
|
|
|
764 |
|
|
signal_set(&ps->ps_evsigint, SIGINT, vmd_sighdlr, ps); |
765 |
|
|
signal_set(&ps->ps_evsigterm, SIGTERM, vmd_sighdlr, ps); |
766 |
|
|
signal_set(&ps->ps_evsighup, SIGHUP, vmd_sighdlr, ps); |
767 |
|
|
signal_set(&ps->ps_evsigpipe, SIGPIPE, vmd_sighdlr, ps); |
768 |
|
|
signal_set(&ps->ps_evsigusr1, SIGUSR1, vmd_sighdlr, ps); |
769 |
|
|
|
770 |
|
|
signal_add(&ps->ps_evsigint, NULL); |
771 |
|
|
signal_add(&ps->ps_evsigterm, NULL); |
772 |
|
|
signal_add(&ps->ps_evsighup, NULL); |
773 |
|
|
signal_add(&ps->ps_evsigpipe, NULL); |
774 |
|
|
signal_add(&ps->ps_evsigusr1, NULL); |
775 |
|
|
|
776 |
|
|
if (!env->vmd_noaction) |
777 |
|
|
proc_connect(ps); |
778 |
|
|
|
779 |
|
|
if (vmd_configure() == -1) |
780 |
|
|
fatalx("configuration failed"); |
781 |
|
|
|
782 |
|
|
event_dispatch(); |
783 |
|
|
|
784 |
|
|
log_debug("parent exiting"); |
785 |
|
|
|
786 |
|
|
return (0); |
787 |
|
|
} |
788 |
|
|
|
789 |
|
|
int |
790 |
|
|
vmd_configure(void) |
791 |
|
|
{ |
792 |
|
|
struct vmd_vm *vm; |
793 |
|
|
struct vmd_switch *vsw; |
794 |
|
|
|
795 |
|
|
if ((env->vmd_ptmfd = open(PATH_PTMDEV, O_RDWR|O_CLOEXEC)) == -1) |
796 |
|
|
fatal("open %s", PATH_PTMDEV); |
797 |
|
|
|
798 |
|
|
/* |
799 |
|
|
* pledge in the parent process: |
800 |
|
|
* stdio - for malloc and basic I/O including events. |
801 |
|
|
* rpath - for reload to open and read the configuration files. |
802 |
|
|
* wpath - for opening disk images and tap devices. |
803 |
|
|
* tty - for openpty. |
804 |
|
|
* proc - run kill to terminate its children safely. |
805 |
|
|
* sendfd - for disks, interfaces and other fds. |
806 |
|
|
* recvfd - for send and receive. |
807 |
|
|
* getpw - lookup user or group id by name. |
808 |
|
|
* chown, fattr - change tty ownership |
809 |
|
|
*/ |
810 |
|
|
if (pledge("stdio rpath wpath proc tty recvfd sendfd getpw" |
811 |
|
|
" chown fattr", NULL) == -1) |
812 |
|
|
fatal("pledge"); |
813 |
|
|
|
814 |
|
|
if (parse_config(env->vmd_conffile) == -1) { |
815 |
|
|
proc_kill(&env->vmd_ps); |
816 |
|
|
exit(1); |
817 |
|
|
} |
818 |
|
|
|
819 |
|
|
if (env->vmd_noaction) { |
820 |
|
|
fprintf(stderr, "configuration OK\n"); |
821 |
|
|
proc_kill(&env->vmd_ps); |
822 |
|
|
exit(0); |
823 |
|
|
} |
824 |
|
|
|
825 |
|
|
TAILQ_FOREACH(vsw, env->vmd_switches, sw_entry) { |
826 |
|
|
if (vsw->sw_running) |
827 |
|
|
continue; |
828 |
|
|
if (vm_priv_brconfig(&env->vmd_ps, vsw) == -1) { |
829 |
|
|
log_warn("%s: failed to create switch %s", |
830 |
|
|
__func__, vsw->sw_name); |
831 |
|
|
switch_remove(vsw); |
832 |
|
|
return (-1); |
833 |
|
|
} |
834 |
|
|
} |
835 |
|
|
|
836 |
|
|
TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { |
837 |
|
|
if (vm->vm_disabled) { |
838 |
|
|
log_debug("%s: not creating vm %s (disabled)", |
839 |
|
|
__func__, |
840 |
|
|
vm->vm_params.vmc_params.vcp_name); |
841 |
|
|
continue; |
842 |
|
|
} |
843 |
|
|
if (config_setvm(&env->vmd_ps, vm, -1, 0) == -1) |
844 |
|
|
return (-1); |
845 |
|
|
} |
846 |
|
|
|
847 |
|
|
/* Send shared global configuration to all children */ |
848 |
|
|
if (config_setconfig(env) == -1) |
849 |
|
|
return (-1); |
850 |
|
|
|
851 |
|
|
return (0); |
852 |
|
|
} |
853 |
|
|
|
854 |
|
|
int |
855 |
|
|
vmd_reload(unsigned int reset, const char *filename) |
856 |
|
|
{ |
857 |
|
|
struct vmd_vm *vm, *next_vm; |
858 |
|
|
struct vmd_switch *vsw; |
859 |
|
|
int reload = 0; |
860 |
|
|
|
861 |
|
|
/* Switch back to the default config file */ |
862 |
|
|
if (filename == NULL || *filename == '\0') { |
863 |
|
|
filename = env->vmd_conffile; |
864 |
|
|
reload = 1; |
865 |
|
|
} |
866 |
|
|
|
867 |
|
|
log_debug("%s: level %d config file %s", __func__, reset, filename); |
868 |
|
|
|
869 |
|
|
if (reset) { |
870 |
|
|
/* Purge the configuration */ |
871 |
|
|
config_purge(env, reset); |
872 |
|
|
config_setreset(env, reset); |
873 |
|
|
} else { |
874 |
|
|
/* |
875 |
|
|
* Load or reload the configuration. |
876 |
|
|
* |
877 |
|
|
* Reloading removes all non-running VMs before processing the |
878 |
|
|
* config file, whereas loading only adds to the existing list |
879 |
|
|
* of VMs. |
880 |
|
|
*/ |
881 |
|
|
|
882 |
|
|
if (reload) { |
883 |
|
|
TAILQ_FOREACH_SAFE(vm, env->vmd_vms, vm_entry, next_vm) { |
884 |
|
|
if (vm->vm_running == 0) { |
885 |
|
|
log_debug("%s: calling vm_remove", |
886 |
|
|
__func__); |
887 |
|
|
vm_remove(vm); |
888 |
|
|
} |
889 |
|
|
} |
890 |
|
|
|
891 |
|
|
/* Update shared global configuration in all children */ |
892 |
|
|
if (config_setconfig(env) == -1) |
893 |
|
|
return (-1); |
894 |
|
|
} |
895 |
|
|
|
896 |
|
|
if (parse_config(filename) == -1) { |
897 |
|
|
log_debug("%s: failed to load config file %s", |
898 |
|
|
__func__, filename); |
899 |
|
|
return (-1); |
900 |
|
|
} |
901 |
|
|
|
902 |
|
|
TAILQ_FOREACH(vsw, env->vmd_switches, sw_entry) { |
903 |
|
|
if (vsw->sw_running) |
904 |
|
|
continue; |
905 |
|
|
if (vm_priv_brconfig(&env->vmd_ps, vsw) == -1) { |
906 |
|
|
log_warn("%s: failed to create switch %s", |
907 |
|
|
__func__, vsw->sw_name); |
908 |
|
|
switch_remove(vsw); |
909 |
|
|
return (-1); |
910 |
|
|
} |
911 |
|
|
} |
912 |
|
|
|
913 |
|
|
TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { |
914 |
|
|
if (vm->vm_running == 0) { |
915 |
|
|
if (vm->vm_disabled) { |
916 |
|
|
log_debug("%s: not creating vm %s" |
917 |
|
|
" (disabled)", __func__, |
918 |
|
|
vm->vm_params.vmc_params.vcp_name); |
919 |
|
|
continue; |
920 |
|
|
} |
921 |
|
|
if (config_setvm(&env->vmd_ps, vm, -1, 0) == -1) |
922 |
|
|
return (-1); |
923 |
|
|
} else { |
924 |
|
|
log_debug("%s: not creating vm \"%s\": " |
925 |
|
|
"(running)", __func__, |
926 |
|
|
vm->vm_params.vmc_params.vcp_name); |
927 |
|
|
} |
928 |
|
|
} |
929 |
|
|
} |
930 |
|
|
|
931 |
|
|
return (0); |
932 |
|
|
} |
933 |
|
|
|
934 |
|
|
void |
935 |
|
|
vmd_shutdown(void) |
936 |
|
|
{ |
937 |
|
|
struct vmd_vm *vm, *vm_next; |
938 |
|
|
|
939 |
|
|
log_debug("%s: performing shutdown", __func__); |
940 |
|
|
TAILQ_FOREACH_SAFE(vm, env->vmd_vms, vm_entry, vm_next) { |
941 |
|
|
vm_remove(vm); |
942 |
|
|
} |
943 |
|
|
|
944 |
|
|
proc_kill(&env->vmd_ps); |
945 |
|
|
free(env); |
946 |
|
|
|
947 |
|
|
log_warnx("parent terminating"); |
948 |
|
|
exit(0); |
949 |
|
|
} |
950 |
|
|
|
951 |
|
|
struct vmd_vm * |
952 |
|
|
vm_getbyvmid(uint32_t vmid) |
953 |
|
|
{ |
954 |
|
|
struct vmd_vm *vm; |
955 |
|
|
|
956 |
|
|
if (vmid == 0) |
957 |
|
|
return (NULL); |
958 |
|
|
TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { |
959 |
|
|
if (vm->vm_vmid == vmid) |
960 |
|
|
return (vm); |
961 |
|
|
} |
962 |
|
|
|
963 |
|
|
return (NULL); |
964 |
|
|
} |
965 |
|
|
|
966 |
|
|
struct vmd_vm * |
967 |
|
|
vm_getbyid(uint32_t id) |
968 |
|
|
{ |
969 |
|
|
struct vmd_vm *vm; |
970 |
|
|
|
971 |
|
|
if (id == 0) |
972 |
|
|
return (NULL); |
973 |
|
|
TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { |
974 |
|
|
if (vm->vm_params.vmc_params.vcp_id == id) |
975 |
|
|
return (vm); |
976 |
|
|
} |
977 |
|
|
|
978 |
|
|
return (NULL); |
979 |
|
|
} |
980 |
|
|
|
981 |
|
|
uint32_t |
982 |
|
|
vm_id2vmid(uint32_t id, struct vmd_vm *vm) |
983 |
|
|
{ |
984 |
|
|
if (vm == NULL && (vm = vm_getbyid(id)) == NULL) |
985 |
|
|
return (0); |
986 |
|
|
dprintf("%s: vmm id %u is vmid %u", __func__, |
987 |
|
|
id, vm->vm_vmid); |
988 |
|
|
return (vm->vm_vmid); |
989 |
|
|
} |
990 |
|
|
|
991 |
|
|
uint32_t |
992 |
|
|
vm_vmid2id(uint32_t vmid, struct vmd_vm *vm) |
993 |
|
|
{ |
994 |
|
|
if (vm == NULL && (vm = vm_getbyvmid(vmid)) == NULL) |
995 |
|
|
return (0); |
996 |
|
|
dprintf("%s: vmid %u is vmm id %u", __func__, |
997 |
|
|
vmid, vm->vm_params.vmc_params.vcp_id); |
998 |
|
|
return (vm->vm_params.vmc_params.vcp_id); |
999 |
|
|
} |
1000 |
|
|
|
1001 |
|
|
struct vmd_vm * |
1002 |
|
|
vm_getbyname(const char *name) |
1003 |
|
|
{ |
1004 |
|
|
struct vmd_vm *vm; |
1005 |
|
|
|
1006 |
|
|
if (name == NULL) |
1007 |
|
|
return (NULL); |
1008 |
|
|
TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { |
1009 |
|
|
if (strcmp(vm->vm_params.vmc_params.vcp_name, name) == 0) |
1010 |
|
|
return (vm); |
1011 |
|
|
} |
1012 |
|
|
|
1013 |
|
|
return (NULL); |
1014 |
|
|
} |
1015 |
|
|
|
1016 |
|
|
struct vmd_vm * |
1017 |
|
|
vm_getbypid(pid_t pid) |
1018 |
|
|
{ |
1019 |
|
|
struct vmd_vm *vm; |
1020 |
|
|
|
1021 |
|
|
TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) { |
1022 |
|
|
if (vm->vm_pid == pid) |
1023 |
|
|
return (vm); |
1024 |
|
|
} |
1025 |
|
|
|
1026 |
|
|
return (NULL); |
1027 |
|
|
} |
1028 |
|
|
|
1029 |
|
|
void |
1030 |
|
|
vm_stop(struct vmd_vm *vm, int keeptty) |
1031 |
|
|
{ |
1032 |
|
|
unsigned int i; |
1033 |
|
|
|
1034 |
|
|
if (vm == NULL) |
1035 |
|
|
return; |
1036 |
|
|
|
1037 |
|
|
log_debug("%s: stopping vm %d", __func__, vm->vm_vmid); |
1038 |
|
|
vm->vm_running = 0; |
1039 |
|
|
vm->vm_shutdown = 0; |
1040 |
|
|
|
1041 |
|
|
if (vm->vm_iev.ibuf.fd != -1) { |
1042 |
|
|
event_del(&vm->vm_iev.ev); |
1043 |
|
|
close(vm->vm_iev.ibuf.fd); |
1044 |
|
|
} |
1045 |
|
|
for (i = 0; i < VMM_MAX_DISKS_PER_VM; i++) { |
1046 |
|
|
if (vm->vm_disks[i] != -1) { |
1047 |
|
|
close(vm->vm_disks[i]); |
1048 |
|
|
vm->vm_disks[i] = -1; |
1049 |
|
|
} |
1050 |
|
|
} |
1051 |
|
|
for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) { |
1052 |
|
|
if (vm->vm_ifs[i].vif_fd != -1) { |
1053 |
|
|
close(vm->vm_ifs[i].vif_fd); |
1054 |
|
|
vm->vm_ifs[i].vif_fd = -1; |
1055 |
|
|
} |
1056 |
|
|
free(vm->vm_ifs[i].vif_name); |
1057 |
|
|
free(vm->vm_ifs[i].vif_switch); |
1058 |
|
|
free(vm->vm_ifs[i].vif_group); |
1059 |
|
|
vm->vm_ifs[i].vif_name = NULL; |
1060 |
|
|
vm->vm_ifs[i].vif_switch = NULL; |
1061 |
|
|
vm->vm_ifs[i].vif_group = NULL; |
1062 |
|
|
} |
1063 |
|
|
if (vm->vm_kernel != -1) { |
1064 |
|
|
close(vm->vm_kernel); |
1065 |
|
|
vm->vm_kernel = -1; |
1066 |
|
|
} |
1067 |
|
|
if (!keeptty) { |
1068 |
|
|
vm_closetty(vm); |
1069 |
|
|
vm->vm_uid = 0; |
1070 |
|
|
} |
1071 |
|
|
} |
1072 |
|
|
|
1073 |
|
|
void |
1074 |
|
|
vm_remove(struct vmd_vm *vm) |
1075 |
|
|
{ |
1076 |
|
|
if (vm == NULL) |
1077 |
|
|
return; |
1078 |
|
|
|
1079 |
|
|
log_debug("%s: removing vm id %d from running config", |
1080 |
|
|
__func__, vm->vm_vmid); |
1081 |
|
|
TAILQ_REMOVE(env->vmd_vms, vm, vm_entry); |
1082 |
|
|
log_debug("%s: calling vm_stop", __func__); |
1083 |
|
|
vm_stop(vm, 0); |
1084 |
|
|
free(vm); |
1085 |
|
|
} |
1086 |
|
|
|
1087 |
|
|
int |
1088 |
|
|
vm_register(struct privsep *ps, struct vmop_create_params *vmc, |
1089 |
|
|
struct vmd_vm **ret_vm, uint32_t id, uid_t uid) |
1090 |
|
|
{ |
1091 |
|
|
struct vmd_vm *vm = NULL; |
1092 |
|
|
struct vm_create_params *vcp = &vmc->vmc_params; |
1093 |
|
|
static const uint8_t zero_mac[ETHER_ADDR_LEN]; |
1094 |
|
|
uint32_t rng; |
1095 |
|
|
unsigned int i; |
1096 |
|
|
struct vmd_switch *sw; |
1097 |
|
|
char *s; |
1098 |
|
|
|
1099 |
|
|
errno = 0; |
1100 |
|
|
*ret_vm = NULL; |
1101 |
|
|
|
1102 |
|
|
if ((vm = vm_getbyname(vcp->vcp_name)) != NULL || |
1103 |
|
|
(vm = vm_getbyvmid(vcp->vcp_id)) != NULL) { |
1104 |
|
|
if (vm_checkperm(vm, uid) != 0 || vmc->vmc_flags != 0) { |
1105 |
|
|
errno = EPERM; |
1106 |
|
|
goto fail; |
1107 |
|
|
} |
1108 |
|
|
*ret_vm = vm; |
1109 |
|
|
errno = EALREADY; |
1110 |
|
|
goto fail; |
1111 |
|
|
} |
1112 |
|
|
|
1113 |
|
|
/* |
1114 |
|
|
* non-root users can only start existing VMs |
1115 |
|
|
* XXX there could be a mechanism to allow overriding some options |
1116 |
|
|
*/ |
1117 |
|
|
if (vm_checkperm(NULL, uid) != 0) { |
1118 |
|
|
errno = EPERM; |
1119 |
|
|
goto fail; |
1120 |
|
|
} |
1121 |
|
|
if (vmc->vmc_flags == 0) { |
1122 |
|
|
errno = ENOENT; |
1123 |
|
|
goto fail; |
1124 |
|
|
} |
1125 |
|
|
if (vcp->vcp_ncpus == 0) |
1126 |
|
|
vcp->vcp_ncpus = 1; |
1127 |
|
|
if (vcp->vcp_memranges[0].vmr_size == 0) |
1128 |
|
|
vcp->vcp_memranges[0].vmr_size = VM_DEFAULT_MEMORY; |
1129 |
|
|
if (vcp->vcp_ncpus > VMM_MAX_VCPUS_PER_VM) { |
1130 |
|
|
log_warnx("invalid number of CPUs"); |
1131 |
|
|
goto fail; |
1132 |
|
|
} else if (vcp->vcp_ndisks > VMM_MAX_DISKS_PER_VM) { |
1133 |
|
|
log_warnx("invalid number of disks"); |
1134 |
|
|
goto fail; |
1135 |
|
|
} else if (vcp->vcp_nnics > VMM_MAX_NICS_PER_VM) { |
1136 |
|
|
log_warnx("invalid number of interfaces"); |
1137 |
|
|
goto fail; |
1138 |
|
|
} else if (strlen(vcp->vcp_kernel) == 0 && vcp->vcp_ndisks == 0) { |
1139 |
|
|
log_warnx("no kernel or disk specified"); |
1140 |
|
|
goto fail; |
1141 |
|
|
} else if (strlen(vcp->vcp_name) == 0) { |
1142 |
|
|
log_warnx("invalid VM name"); |
1143 |
|
|
goto fail; |
1144 |
|
|
} else if (*vcp->vcp_name == '-' || *vcp->vcp_name == '.' || |
1145 |
|
|
*vcp->vcp_name == '_') { |
1146 |
|
|
log_warnx("Invalid VM name"); |
1147 |
|
|
goto fail; |
1148 |
|
|
} else { |
1149 |
|
|
for (s = vcp->vcp_name; *s != '\0'; ++s) { |
1150 |
|
|
if (!(isalnum(*s) || *s == '.' || *s == '-' || |
1151 |
|
|
*s == '_')) { |
1152 |
|
|
log_warnx("Invalid VM name"); |
1153 |
|
|
goto fail; |
1154 |
|
|
} |
1155 |
|
|
} |
1156 |
|
|
} |
1157 |
|
|
|
1158 |
|
|
if ((vm = calloc(1, sizeof(*vm))) == NULL) |
1159 |
|
|
goto fail; |
1160 |
|
|
|
1161 |
|
|
memcpy(&vm->vm_params, vmc, sizeof(vm->vm_params)); |
1162 |
|
|
vmc = &vm->vm_params; |
1163 |
|
|
vcp = &vmc->vmc_params; |
1164 |
|
|
vm->vm_pid = -1; |
1165 |
|
|
vm->vm_tty = -1; |
1166 |
|
|
vm->vm_receive_fd = -1; |
1167 |
|
|
vm->vm_paused = 0; |
1168 |
|
|
|
1169 |
|
|
for (i = 0; i < vcp->vcp_ndisks; i++) |
1170 |
|
|
vm->vm_disks[i] = -1; |
1171 |
|
|
for (i = 0; i < vcp->vcp_nnics; i++) { |
1172 |
|
|
vm->vm_ifs[i].vif_fd = -1; |
1173 |
|
|
|
1174 |
|
|
if ((sw = switch_getbyname(vmc->vmc_ifswitch[i])) != NULL) { |
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 |
|
|
/* |
1217 |
|
|
* vm_checkperm |
1218 |
|
|
* |
1219 |
|
|
* Checks if the user represented by the 'uid' parameter is allowed to |
1220 |
|
|
* manipulate the VM described by the 'vm' parameter (or connect to said VM's |
1221 |
|
|
* console.) |
1222 |
|
|
* |
1223 |
|
|
* Parameters: |
1224 |
|
|
* vm: the VM whose permission is to be checked |
1225 |
|
|
* uid: the user ID of the user making the request |
1226 |
|
|
* |
1227 |
|
|
* Return values: |
1228 |
|
|
* 0: the permission should be granted |
1229 |
|
|
* -1: the permission check failed (also returned if vm == null) |
1230 |
|
|
*/ |
1231 |
|
|
int |
1232 |
|
|
vm_checkperm(struct vmd_vm *vm, uid_t uid) |
1233 |
|
|
{ |
1234 |
|
|
struct group *gr; |
1235 |
|
|
struct passwd *pw; |
1236 |
|
|
char **grmem; |
1237 |
|
|
|
1238 |
|
|
/* root has no restrictions */ |
1239 |
|
|
if (uid == 0) |
1240 |
|
|
return (0); |
1241 |
|
|
|
1242 |
|
|
if (vm == NULL) |
1243 |
|
|
return (-1); |
1244 |
|
|
|
1245 |
|
|
/* check supplementary groups */ |
1246 |
|
|
if (vm->vm_params.vmc_gid != -1 && |
1247 |
|
|
(pw = getpwuid(uid)) != NULL && |
1248 |
|
|
(gr = getgrgid(vm->vm_params.vmc_gid)) != NULL) { |
1249 |
|
|
for (grmem = gr->gr_mem; *grmem; grmem++) |
1250 |
|
|
if (strcmp(*grmem, pw->pw_name) == 0) |
1251 |
|
|
return (0); |
1252 |
|
|
} |
1253 |
|
|
|
1254 |
|
|
/* check user */ |
1255 |
|
|
if ((vm->vm_running && vm->vm_uid == uid) || |
1256 |
|
|
(!vm->vm_running && vm->vm_params.vmc_uid == uid)) |
1257 |
|
|
return (0); |
1258 |
|
|
|
1259 |
|
|
return (-1); |
1260 |
|
|
} |
1261 |
|
|
|
1262 |
|
|
int |
1263 |
|
|
vm_opentty(struct vmd_vm *vm) |
1264 |
|
|
{ |
1265 |
|
|
struct ptmget ptm; |
1266 |
|
|
struct stat st; |
1267 |
|
|
struct group *gr; |
1268 |
|
|
uid_t uid; |
1269 |
|
|
gid_t gid; |
1270 |
|
|
mode_t mode; |
1271 |
|
|
|
1272 |
|
|
/* |
1273 |
|
|
* Open tty with pre-opened PTM fd |
1274 |
|
|
*/ |
1275 |
|
|
if ((ioctl(env->vmd_ptmfd, PTMGET, &ptm) == -1)) |
1276 |
|
|
return (-1); |
1277 |
|
|
|
1278 |
|
|
vm->vm_tty = ptm.cfd; |
1279 |
|
|
close(ptm.sfd); |
1280 |
|
|
if ((vm->vm_ttyname = strdup(ptm.sn)) == NULL) |
1281 |
|
|
goto fail; |
1282 |
|
|
|
1283 |
|
|
uid = vm->vm_uid; |
1284 |
|
|
gid = vm->vm_params.vmc_gid; |
1285 |
|
|
|
1286 |
|
|
if (vm->vm_params.vmc_gid != -1) { |
1287 |
|
|
mode = 0660; |
1288 |
|
|
} else if ((gr = getgrnam("tty")) != NULL) { |
1289 |
|
|
gid = gr->gr_gid; |
1290 |
|
|
mode = 0620; |
1291 |
|
|
} else { |
1292 |
|
|
mode = 0600; |
1293 |
|
|
gid = 0; |
1294 |
|
|
} |
1295 |
|
|
|
1296 |
|
|
log_debug("%s: vm %s tty %s uid %d gid %d mode %o", |
1297 |
|
|
__func__, vm->vm_params.vmc_params.vcp_name, |
1298 |
|
|
vm->vm_ttyname, uid, gid, mode); |
1299 |
|
|
|
1300 |
|
|
/* |
1301 |
|
|
* Change ownership and mode of the tty as required. |
1302 |
|
|
* Loosely based on the implementation of sshpty.c |
1303 |
|
|
*/ |
1304 |
|
|
if (stat(vm->vm_ttyname, &st) == -1) |
1305 |
|
|
goto fail; |
1306 |
|
|
|
1307 |
|
|
if (st.st_uid != uid || st.st_gid != gid) { |
1308 |
|
|
if (chown(vm->vm_ttyname, uid, gid) == -1) { |
1309 |
|
|
log_warn("chown %s %d %d failed, uid %d", |
1310 |
|
|
vm->vm_ttyname, uid, gid, getuid()); |
1311 |
|
|
|
1312 |
|
|
/* Ignore failure on read-only filesystems */ |
1313 |
|
|
if (!((errno == EROFS) && |
1314 |
|
|
(st.st_uid == uid || st.st_uid == 0))) |
1315 |
|
|
goto fail; |
1316 |
|
|
} |
1317 |
|
|
} |
1318 |
|
|
|
1319 |
|
|
if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) { |
1320 |
|
|
if (chmod(vm->vm_ttyname, mode) == -1) { |
1321 |
|
|
log_warn("chmod %s %o failed, uid %d", |
1322 |
|
|
vm->vm_ttyname, mode, getuid()); |
1323 |
|
|
|
1324 |
|
|
/* Ignore failure on read-only filesystems */ |
1325 |
|
|
if (!((errno == EROFS) && |
1326 |
|
|
(st.st_uid == uid || st.st_uid == 0))) |
1327 |
|
|
goto fail; |
1328 |
|
|
} |
1329 |
|
|
} |
1330 |
|
|
|
1331 |
|
|
return (0); |
1332 |
|
|
fail: |
1333 |
|
|
vm_closetty(vm); |
1334 |
|
|
return (-1); |
1335 |
|
|
} |
1336 |
|
|
|
1337 |
|
|
void |
1338 |
|
|
vm_closetty(struct vmd_vm *vm) |
1339 |
|
|
{ |
1340 |
|
|
if (vm->vm_tty != -1) { |
1341 |
|
|
/* Release and close the tty */ |
1342 |
|
|
if (fchown(vm->vm_tty, 0, 0) == -1) |
1343 |
|
|
log_warn("chown %s 0 0 failed", vm->vm_ttyname); |
1344 |
|
|
if (fchmod(vm->vm_tty, 0666) == -1) |
1345 |
|
|
log_warn("chmod %s 0666 failed", vm->vm_ttyname); |
1346 |
|
|
close(vm->vm_tty); |
1347 |
|
|
vm->vm_tty = -1; |
1348 |
|
|
} |
1349 |
|
|
free(vm->vm_ttyname); |
1350 |
|
|
vm->vm_ttyname = NULL; |
1351 |
|
|
} |
1352 |
|
|
|
1353 |
|
|
void |
1354 |
|
|
switch_remove(struct vmd_switch *vsw) |
1355 |
|
|
{ |
1356 |
|
|
if (vsw == NULL) |
1357 |
|
|
return; |
1358 |
|
|
|
1359 |
|
|
TAILQ_REMOVE(env->vmd_switches, vsw, sw_entry); |
1360 |
|
|
|
1361 |
|
|
free(vsw->sw_group); |
1362 |
|
|
free(vsw->sw_name); |
1363 |
|
|
free(vsw); |
1364 |
|
|
} |
1365 |
|
|
|
1366 |
|
|
struct vmd_switch * |
1367 |
|
|
switch_getbyname(const char *name) |
1368 |
|
|
{ |
1369 |
|
|
struct vmd_switch *vsw; |
1370 |
|
|
|
1371 |
|
|
if (name == NULL) |
1372 |
|
|
return (NULL); |
1373 |
|
|
TAILQ_FOREACH(vsw, env->vmd_switches, sw_entry) { |
1374 |
|
|
if (strcmp(vsw->sw_name, name) == 0) |
1375 |
|
|
return (vsw); |
1376 |
|
|
} |
1377 |
|
|
|
1378 |
|
|
return (NULL); |
1379 |
|
|
} |
1380 |
|
|
|
1381 |
|
|
char * |
1382 |
|
|
get_string(uint8_t *ptr, size_t len) |
1383 |
|
|
{ |
1384 |
|
|
size_t i; |
1385 |
|
|
|
1386 |
|
|
for (i = 0; i < len; i++) |
1387 |
|
|
if (!isprint(ptr[i])) |
1388 |
|
|
break; |
1389 |
|
|
|
1390 |
|
|
return strndup(ptr, i); |
1391 |
|
|
} |
1392 |
|
|
|
1393 |
|
|
uint32_t |
1394 |
|
|
prefixlen2mask(uint8_t prefixlen) |
1395 |
|
|
{ |
1396 |
|
|
if (prefixlen == 0) |
1397 |
|
|
return (0); |
1398 |
|
|
|
1399 |
|
|
if (prefixlen > 32) |
1400 |
|
|
prefixlen = 32; |
1401 |
|
|
|
1402 |
|
|
return (htonl(0xffffffff << (32 - prefixlen))); |
1403 |
|
|
} |