xref: /openbsd-src/usr.sbin/vmctl/main.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: main.c,v 1.17 2016/05/10 11:00:54 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/types.h>
20 #include <sys/socket.h>
21 #include <sys/queue.h>
22 #include <sys/un.h>
23 
24 #include <machine/vmmvar.h>
25 
26 #include <err.h>
27 #include <errno.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdint.h>
31 #include <limits.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <util.h>
35 #include <imsg.h>
36 
37 #include "vmd.h"
38 #include "proc.h"
39 #include "vmctl.h"
40 
41 static const char	*socket_name = SOCKET_NAME;
42 static int		 ctl_sock = -1;
43 static int		 tty_autoconnect = 0;
44 
45 __dead void	 usage(void);
46 __dead void	 ctl_usage(struct ctl_command *);
47 
48 int		 vmm_action(struct parse_result *);
49 
50 int		 ctl_console(struct parse_result *, int, char *[]);
51 int		 ctl_create(struct parse_result *, int, char *[]);
52 int		 ctl_load(struct parse_result *, int, char *[]);
53 int		 ctl_start(struct parse_result *, int, char *[]);
54 int		 ctl_status(struct parse_result *, int, char *[]);
55 int		 ctl_stop(struct parse_result *, int, char *[]);
56 
57 struct ctl_command ctl_commands[] = {
58 	{ "console",	CMD_CONSOLE,	ctl_console,	"id" },
59 	{ "create",	CMD_CREATE,	ctl_create,	"\"path\" -s size", 1 },
60 	{ "load",	CMD_LOAD,	ctl_load,	"[path]" },
61 	{ "reload",	CMD_RELOAD,	ctl_load,	"[path]" },
62 	{ "start",	CMD_START,	ctl_start,	"\"name\""
63 	    " [-c] -k kernel -m size [-i count] [-d disk]*" },
64 	{ "status",	CMD_STATUS,	ctl_status,	"[id]" },
65 	{ "stop",	CMD_STOP,	ctl_stop,	"id" },
66 	{ NULL }
67 };
68 
69 __dead void
70 usage(void)
71 {
72 	extern char	*__progname;
73 	int		 i;
74 
75 	fprintf(stderr, "usage:\t%s command [arg ...]\n",
76 	    __progname);
77 	for (i = 0; ctl_commands[i].name != NULL; i++) {
78 		fprintf(stderr, "\t%s %s %s\n", __progname,
79 		    ctl_commands[i].name, ctl_commands[i].usage);
80 	}
81 	exit(1);
82 }
83 
84 __dead void
85 ctl_usage(struct ctl_command *ctl)
86 {
87 	extern char	*__progname;
88 
89 	fprintf(stderr, "usage:\t%s %s %s\n", __progname,
90 	    ctl->name, ctl->usage);
91 	exit(1);
92 }
93 
94 int
95 main(int argc, char *argv[])
96 {
97 	int	 ch;
98 
99 	while ((ch = getopt(argc, argv, "")) != -1) {
100 		switch (ch) {
101 		default:
102 			usage();
103 			/* NOTREACHED */
104 		}
105 	}
106 	argc -= optind;
107 	argv += optind;
108 	optreset = 1;
109 
110 	if (argc < 1)
111 		usage();
112 
113 	return (parse(argc, argv));
114 }
115 
116 int
117 parse(int argc, char *argv[])
118 {
119 	struct ctl_command	*ctl = NULL;
120 	struct parse_result	 res;
121 	int			 i;
122 
123 	memset(&res, 0, sizeof(res));
124 	res.nifs = -1;
125 
126 	for (i = 0; ctl_commands[i].name != NULL; i++) {
127 		if (strncmp(ctl_commands[i].name,
128 		    argv[0], strlen(argv[0])) == 0) {
129 			if (ctl != NULL) {
130 				fprintf(stderr,
131 				    "ambiguous argument: %s\n", argv[0]);
132 				usage();
133 			}
134 			ctl = &ctl_commands[i];
135 		}
136 	}
137 
138 	if (ctl == NULL) {
139 		fprintf(stderr, "unknown argument: %s\n", argv[0]);
140 		usage();
141 	}
142 
143 	res.action = ctl->action;
144 	res.ctl = ctl;
145 
146 	if (!ctl->has_pledge) {
147 		/* pledge(2) default if command doesn't have its own pledge */
148 		if (pledge("stdio rpath exec unix", NULL) == -1)
149 			err(1, "pledge");
150 	}
151 	if (ctl->main(&res, argc, argv) != 0)
152 		err(1, "failed");
153 
154 	if (ctl_sock != -1) {
155 		close(ibuf->fd);
156 		free(ibuf);
157 	}
158 
159 	return (0);
160 }
161 
162 int
163 vmmaction(struct parse_result *res)
164 {
165 	struct sockaddr_un	 sun;
166 	struct imsg		 imsg;
167 	int			 done = 0;
168 	int			 n;
169 	int			 ret, action;
170 
171 	if (ctl_sock == -1) {
172 		if ((ctl_sock = socket(AF_UNIX,
173 		    SOCK_STREAM|SOCK_CLOEXEC, 0)) == -1)
174 			err(1, "socket");
175 
176 		bzero(&sun, sizeof(sun));
177 		sun.sun_family = AF_UNIX;
178 		strlcpy(sun.sun_path, socket_name, sizeof(sun.sun_path));
179 
180 		if (connect(ctl_sock,
181 		    (struct sockaddr *)&sun, sizeof(sun)) == -1)
182 			err(1, "connect: %s", socket_name);
183 
184 		if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
185 			err(1, "malloc");
186 		imsg_init(ibuf, ctl_sock);
187 	}
188 
189 	switch (res->action) {
190 	case CMD_START:
191 		ret = start_vm(res->name, res->size, res->nifs,
192 		    res->ndisks, res->disks, res->path);
193 		if (ret) {
194 			errno = ret;
195 			err(1, "start VM operation failed");
196 		}
197 		break;
198 	case CMD_STOP:
199 		terminate_vm(res->id, res->name);
200 		break;
201 	case CMD_STATUS:
202 		get_info_vm(res->id, res->name, 0);
203 		break;
204 	case CMD_CONSOLE:
205 		get_info_vm(res->id, res->name, 1);
206 		break;
207 	case CMD_RELOAD:
208 		imsg_compose(ibuf, IMSG_VMDOP_RELOAD, 0, 0, -1,
209 		    res->path, res->path == NULL ? 0 : strlen(res->path) + 1);
210 		done = 1;
211 		break;
212 	case CMD_LOAD:
213 		imsg_compose(ibuf, IMSG_VMDOP_LOAD, 0, 0, -1,
214 		    res->path, res->path == NULL ? 0 : strlen(res->path) + 1);
215 		done = 1;
216 		break;
217 	case CMD_CREATE:
218 	case NONE:
219 		break;
220 	}
221 
222 	action = res->action;
223 	parse_free(res);
224 
225 	while (ibuf->w.queued)
226 		if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
227 			err(1, "write error");
228 
229 	while (!done) {
230 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
231 			errx(1, "imsg_read error");
232 		if (n == 0)
233 			errx(1, "pipe closed");
234 
235 		while (!done) {
236 			if ((n = imsg_get(ibuf, &imsg)) == -1)
237 				errx(1, "imsg_get error");
238 			if (n == 0)
239 				break;
240 
241 			if (imsg.hdr.type == IMSG_CTL_FAIL) {
242 				if (IMSG_DATA_SIZE(&imsg) == sizeof(ret)) {
243 					memcpy(&ret, imsg.data, sizeof(ret));
244 					errno = ret;
245 					warn("command failed");
246 				} else {
247 					warnx("command failed");
248 				}
249 				done = 1;
250 				break;
251 			}
252 
253 			ret = 0;
254 			switch (action) {
255 			case CMD_START:
256 				done = start_vm_complete(&imsg, &ret,
257 				    tty_autoconnect);
258 				break;
259 			case CMD_STOP:
260 				done = terminate_vm_complete(&imsg, &ret);
261 				break;
262 			case CMD_CONSOLE:
263 			case CMD_STATUS:
264 				done = add_info(&imsg, &ret);
265 				break;
266 			default:
267 				done = 1;
268 				break;
269 			}
270 
271 			imsg_free(&imsg);
272 		}
273 	}
274 
275 	return (0);
276 }
277 
278 void
279 parse_free(struct parse_result *res)
280 {
281 	size_t	 i;
282 
283 	free(res->name);
284 	free(res->path);
285 	for (i = 0; i < res->ndisks; i++)
286 		free(res->disks[i]);
287 	free(res->disks);
288 	memset(res, 0, sizeof(*res));
289 }
290 
291 int
292 parse_ifs(struct parse_result *res, char *word, int val)
293 {
294 	const char	*error;
295 
296 	if (word != NULL) {
297 		val = strtonum(word, 0, INT_MAX, &error);
298 		if (error != NULL)  {
299 			warnx("invalid count \"%s\": %s", word, error);
300 			return (-1);
301 		}
302 	}
303 	res->nifs = val;
304 	return (0);
305 }
306 
307 int
308 parse_size(struct parse_result *res, char *word, long long val)
309 {
310 	if (word != NULL) {
311 		if (scan_scaled(word, &val) != 0) {
312 			warn("invalid size: %s", word);
313 			return (-1);
314 		}
315 	}
316 
317 	if (val < (1024 * 1024)) {
318 		warnx("size must be at least one megabyte");
319 		return (-1);
320 	} else
321 		res->size = val / 1024 / 1024;
322 
323 	if ((res->size * 1024 * 1024) != val)
324 		warnx("size rounded to %lld megabytes", res->size);
325 
326 	return (0);
327 }
328 
329 int
330 parse_disk(struct parse_result *res, char *word)
331 {
332 	char		**disks;
333 	char		*s;
334 
335 	if ((disks = reallocarray(res->disks, res->ndisks + 1,
336 	    sizeof(char *))) == NULL) {
337 		warn("reallocarray");
338 		return (-1);
339 	}
340 	if ((s = strdup(word)) == NULL) {
341 		warn("strdup");
342 		return (-1);
343 	}
344 	disks[res->ndisks] = s;
345 	res->disks = disks;
346 	res->ndisks++;
347 
348 	return (0);
349 }
350 
351 int
352 parse_vmid(struct parse_result *res, char *word)
353 {
354 	const char	*error;
355 	uint32_t	 id;
356 
357 	if (word == NULL) {
358 		warnx("missing vmid argument");
359 		return (-1);
360 	}
361 	id = strtonum(word, 0, UINT32_MAX, &error);
362 	if (error == NULL) {
363 		res->id = id;
364 		res->name = NULL;
365 	} else {
366 		if (strlen(word) >= VMM_MAX_NAME_LEN) {
367 			warnx("name too long");
368 			return (-1);
369 		}
370 		res->id = 0;
371 		if ((res->name = strdup(word)) == NULL)
372 			errx(1, "strdup");
373 	}
374 
375 	return (0);
376 }
377 
378 int
379 ctl_create(struct parse_result *res, int argc, char *argv[])
380 {
381 	int		 ch, ret;
382 	const char	*paths[2];
383 
384 	if (argc < 2)
385 		ctl_usage(res->ctl);
386 
387 	paths[0] = argv[1];
388 	paths[1] = NULL;
389 	if (pledge("stdio rpath wpath cpath", NULL) == -1)
390 		err(1, "pledge");
391 	argc--;
392 	argv++;
393 
394 	while ((ch = getopt(argc, argv, "s:")) != -1) {
395 		switch (ch) {
396 		case 's':
397 			if (parse_size(res, optarg, 0) != 0)
398 				errx(1, "invalid size: %s", optarg);
399 			break;
400 		default:
401 			ctl_usage(res->ctl);
402 			/* NOTREACHED */
403 		}
404 	}
405 
406 	if (res->size == 0) {
407 		fprintf(stderr, "missing size argument\n");
408 		ctl_usage(res->ctl);
409 	}
410 	ret = create_imagefile(paths[0], res->size);
411 	if (ret != 0) {
412 		errno = ret;
413 		err(1, "create imagefile operation failed");
414 	} else
415 		warnx("imagefile created");
416 	return (0);
417 }
418 
419 int
420 ctl_status(struct parse_result *res, int argc, char *argv[])
421 {
422 	if (argc == 2) {
423 		if (parse_vmid(res, argv[1]) == -1)
424 			errx(1, "invalid id: %s", argv[1]);
425 	} else if (argc > 2)
426 		ctl_usage(res->ctl);
427 
428 	return (vmmaction(res));
429 }
430 
431 int
432 ctl_load(struct parse_result *res, int argc, char *argv[])
433 {
434 	char	*config_file = NULL;
435 
436 	if (argc == 2)
437 		config_file = argv[1];
438 	else if (argc > 2)
439 		ctl_usage(res->ctl);
440 
441 	if (config_file != NULL &&
442 	    (res->path = strdup(config_file)) == NULL)
443 		err(1, "strdup");
444 
445 	return (vmmaction(res));
446 }
447 
448 int
449 ctl_start(struct parse_result *res, int argc, char *argv[])
450 {
451 	int		 ch;
452 	char		 path[PATH_MAX];
453 
454 	if (argc < 2)
455 		ctl_usage(res->ctl);
456 
457 	if ((res->name = strdup(argv[1])) == NULL)
458 		errx(1, "strdup");
459 	argc--;
460 	argv++;
461 
462 	while ((ch = getopt(argc, argv, "ck:m:d:i:")) != -1) {
463 		switch (ch) {
464 		case 'c':
465 			tty_autoconnect = 1;
466 			break;
467 		case 'k':
468 			if (res->path)
469 				errx(1, "kernel specified multiple times");
470 			if (realpath(optarg, path) == NULL)
471 				err(1, "invalid kernel path");
472 			if ((res->path = strdup(path)) == NULL)
473 				errx(1, "strdup");
474 			break;
475 		case 'm':
476 			if (res->size)
477 				errx(1, "memory specified multiple times");
478 			if (parse_size(res, optarg, 0) != 0)
479 				errx(1, "invalid memory size: %s", optarg);
480 			break;
481 		case 'd':
482 			if (realpath(optarg, path) == NULL)
483 				err(1, "invalid disk path");
484 			if (parse_disk(res, path) != 0)
485 				errx(1, "invalid disk: %s", optarg);
486 			break;
487 		case 'i':
488 			if (res->nifs != -1)
489 				errx(1, "interfaces specified multiple times");
490 			if (parse_ifs(res, optarg, 0) != 0)
491 				errx(1, "invalid interface count: %s", optarg);
492 			break;
493 		default:
494 			ctl_usage(res->ctl);
495 			/* NOTREACHED */
496 		}
497 	}
498 
499 	return (vmmaction(res));
500 }
501 
502 int
503 ctl_stop(struct parse_result *res, int argc, char *argv[])
504 {
505 	if (argc == 2) {
506 		if (parse_vmid(res, argv[1]) == -1)
507 			errx(1, "invalid id: %s", argv[1]);
508 	} else if (argc != 2)
509 		ctl_usage(res->ctl);
510 
511 	return (vmmaction(res));
512 }
513 
514 int
515 ctl_console(struct parse_result *res, int argc, char *argv[])
516 {
517 	if (argc == 2) {
518 		if (parse_vmid(res, argv[1]) == -1)
519 			errx(1, "invalid id: %s", argv[1]);
520 	} else if (argc != 2)
521 		ctl_usage(res->ctl);
522 
523 	return (vmmaction(res));
524 }
525 
526 __dead void
527 ctl_openconsole(const char *name)
528 {
529 	closefrom(STDERR_FILENO + 1);
530 	execl(VMCTL_CU, VMCTL_CU, "-l", name, "-s", "9600", (char *)NULL);
531 	err(1, "failed to open the console");
532 }
533