xref: /openbsd-src/usr.sbin/vmctl/main.c (revision 897fc685943471cf985a0fe38ba076ea6fe74fa5)
1 /*	$OpenBSD: main.c,v 1.35 2018/02/24 10:39:35 phessler 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_log(struct parse_result *, int, char *[]);
54 int		 ctl_reload(struct parse_result *, int, char *[]);
55 int		 ctl_reset(struct parse_result *, int, char *[]);
56 int		 ctl_start(struct parse_result *, int, char *[]);
57 int		 ctl_status(struct parse_result *, int, char *[]);
58 int		 ctl_stop(struct parse_result *, int, char *[]);
59 int		 ctl_pause(struct parse_result *, int, char *[]);
60 int		 ctl_unpause(struct parse_result *, int, char *[]);
61 int		 ctl_send(struct parse_result *, int, char *[]);
62 int		 ctl_receive(struct parse_result *, int, char *[]);
63 
64 struct ctl_command ctl_commands[] = {
65 	{ "console",	CMD_CONSOLE,	ctl_console,	"id" },
66 	{ "create",	CMD_CREATE,	ctl_create,	"\"path\" -s size", 1 },
67 	{ "load",	CMD_LOAD,	ctl_load,	"\"path\"" },
68 	{ "log",	CMD_LOG,	ctl_log,	"(verbose|brief)" },
69 	{ "reload",	CMD_RELOAD,	ctl_reload,	"" },
70 	{ "reset",	CMD_RESET,	ctl_reset,	"[all|vms|switches]" },
71 	{ "show",	CMD_STATUS,	ctl_status,	"[id]" },
72 	{ "start",	CMD_START,	ctl_start,	"\"name\""
73 	    " [-Lc] [-b image] [-r image] [-m size]\n"
74 	    "\t\t[-n switch] [-i count] [-d disk]*" },
75 	{ "status",	CMD_STATUS,	ctl_status,	"[id]" },
76 	{ "stop",	CMD_STOP,	ctl_stop,	"id" },
77 	{ "pause",	CMD_PAUSE,	ctl_pause,	"id" },
78 	{ "unpause",	CMD_UNPAUSE,	ctl_unpause,	"id" },
79 	{ "send",	CMD_SEND,	ctl_send,	"id",	1},
80 	{ "receive",	CMD_RECEIVE,	ctl_receive,	"id" ,	1},
81 	{ NULL }
82 };
83 
84 __dead void
85 usage(void)
86 {
87 	extern char	*__progname;
88 	int		 i;
89 
90 	fprintf(stderr, "usage:\t%s command [arg ...]\n",
91 	    __progname);
92 	for (i = 0; ctl_commands[i].name != NULL; i++) {
93 		fprintf(stderr, "\t%s %s %s\n", __progname,
94 		    ctl_commands[i].name, ctl_commands[i].usage);
95 	}
96 	exit(1);
97 }
98 
99 __dead void
100 ctl_usage(struct ctl_command *ctl)
101 {
102 	extern char	*__progname;
103 
104 	fprintf(stderr, "usage:\t%s %s %s\n", __progname,
105 	    ctl->name, ctl->usage);
106 	exit(1);
107 }
108 
109 int
110 main(int argc, char *argv[])
111 {
112 	int	 ch;
113 
114 	while ((ch = getopt(argc, argv, "")) != -1) {
115 		switch (ch) {
116 		default:
117 			usage();
118 			/* NOTREACHED */
119 		}
120 	}
121 	argc -= optind;
122 	argv += optind;
123 	optreset = 1;
124 
125 	if (argc < 1)
126 		usage();
127 
128 	return (parse(argc, argv));
129 }
130 
131 int
132 parse(int argc, char *argv[])
133 {
134 	struct ctl_command	*ctl = NULL;
135 	struct parse_result	 res;
136 	int			 i;
137 
138 	memset(&res, 0, sizeof(res));
139 	res.nifs = -1;
140 
141 	for (i = 0; ctl_commands[i].name != NULL; i++) {
142 		if (strncmp(ctl_commands[i].name,
143 		    argv[0], strlen(argv[0])) == 0) {
144 			if (ctl != NULL) {
145 				fprintf(stderr,
146 				    "ambiguous argument: %s\n", argv[0]);
147 				usage();
148 			}
149 			ctl = &ctl_commands[i];
150 		}
151 	}
152 
153 	if (ctl == NULL) {
154 		fprintf(stderr, "unknown argument: %s\n", argv[0]);
155 		usage();
156 	}
157 
158 	res.action = ctl->action;
159 	res.ctl = ctl;
160 
161 	if (!ctl->has_pledge) {
162 		/* pledge(2) default if command doesn't have its own pledge */
163 		if (pledge("stdio rpath exec unix getpw", NULL) == -1)
164 			err(1, "pledge");
165 	}
166 	if (ctl->main(&res, argc, argv) != 0)
167 		err(1, "failed");
168 
169 	if (ctl_sock != -1) {
170 		close(ibuf->fd);
171 		free(ibuf);
172 	}
173 
174 	return (0);
175 }
176 
177 int
178 vmmaction(struct parse_result *res)
179 {
180 	struct sockaddr_un	 sun;
181 	struct imsg		 imsg;
182 	int			 done = 0;
183 	int			 n;
184 	int			 ret, action;
185 
186 	if (ctl_sock == -1) {
187 		if ((ctl_sock = socket(AF_UNIX,
188 		    SOCK_STREAM|SOCK_CLOEXEC, 0)) == -1)
189 			err(1, "socket");
190 
191 		memset(&sun, 0, sizeof(sun));
192 		sun.sun_family = AF_UNIX;
193 		strlcpy(sun.sun_path, socket_name, sizeof(sun.sun_path));
194 
195 		if (connect(ctl_sock,
196 		    (struct sockaddr *)&sun, sizeof(sun)) == -1)
197 			err(1, "connect: %s", socket_name);
198 
199 		if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
200 			err(1, "malloc");
201 		imsg_init(ibuf, ctl_sock);
202 	}
203 
204 	switch (res->action) {
205 	case CMD_START:
206 		ret = vm_start(res->id, res->name, res->size, res->nifs,
207 		    res->nets, res->ndisks, res->disks, res->path,
208 		    res->isopath);
209 		if (ret) {
210 			errno = ret;
211 			err(1, "start VM operation failed");
212 		}
213 		break;
214 	case CMD_STOP:
215 		terminate_vm(res->id, res->name);
216 		break;
217 	case CMD_STATUS:
218 		get_info_vm(res->id, res->name, 0);
219 		break;
220 	case CMD_CONSOLE:
221 		get_info_vm(res->id, res->name, 1);
222 		break;
223 	case CMD_LOAD:
224 		imsg_compose(ibuf, IMSG_VMDOP_LOAD, 0, 0, -1,
225 		    res->path, strlen(res->path) + 1);
226 		break;
227 	case CMD_LOG:
228 		imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1,
229 		    &res->verbose, sizeof(res->verbose));
230 		break;
231 	case CMD_RELOAD:
232 		imsg_compose(ibuf, IMSG_VMDOP_RELOAD, 0, 0, -1, NULL, 0);
233 		break;
234 	case CMD_RESET:
235 		imsg_compose(ibuf, IMSG_CTL_RESET, 0, 0, -1,
236 		    &res->mode, sizeof(res->mode));
237 		break;
238 	case CMD_PAUSE:
239 		pause_vm(res->id, res->name);
240 		break;
241 	case CMD_UNPAUSE:
242 		unpause_vm(res->id, res->name);
243 		break;
244 	case CMD_SEND:
245 		send_vm(res->id, res->name);
246 		done = 1;
247 		break;
248 	case CMD_RECEIVE:
249 		vm_receive(res->id, res->name);
250 		break;
251 	case CMD_CREATE:
252 	case NONE:
253 		break;
254 	}
255 
256 	action = res->action;
257 	parse_free(res);
258 
259 	while (ibuf->w.queued)
260 		if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
261 			err(1, "write error");
262 
263 	while (!done) {
264 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
265 			errx(1, "imsg_read error");
266 		if (n == 0)
267 			errx(1, "pipe closed");
268 
269 		while (!done) {
270 			if ((n = imsg_get(ibuf, &imsg)) == -1)
271 				errx(1, "imsg_get error");
272 			if (n == 0)
273 				break;
274 
275 			if (imsg.hdr.type == IMSG_CTL_FAIL) {
276 				if (IMSG_DATA_SIZE(&imsg) == sizeof(ret))
277 					memcpy(&ret, imsg.data, sizeof(ret));
278 				else
279 					ret = 0;
280 				if (ret != 0) {
281 					memcpy(&ret, imsg.data, sizeof(ret));
282 					errno = ret;
283 					err(1, "command failed");
284 				} else
285 					errx(1, "command failed");
286 			}
287 
288 			ret = 0;
289 			switch (action) {
290 			case CMD_START:
291 				done = vm_start_complete(&imsg, &ret,
292 				    tty_autoconnect);
293 				break;
294 			case CMD_STOP:
295 				done = terminate_vm_complete(&imsg, &ret);
296 				break;
297 			case CMD_CONSOLE:
298 			case CMD_STATUS:
299 				done = add_info(&imsg, &ret);
300 				break;
301 			case CMD_PAUSE:
302 				done = pause_vm_complete(&imsg, &ret);
303 				break;
304 			case CMD_RECEIVE:
305 				done = vm_start_complete(&imsg, &ret, 0);
306 				break;
307 			case CMD_UNPAUSE:
308 				done = unpause_vm_complete(&imsg, &ret);
309 				break;
310 			default:
311 				done = 1;
312 				break;
313 			}
314 
315 			imsg_free(&imsg);
316 		}
317 	}
318 
319 	return (0);
320 }
321 
322 void
323 parse_free(struct parse_result *res)
324 {
325 	size_t	 i;
326 
327 	free(res->name);
328 	free(res->path);
329 	free(res->isopath);
330 	for (i = 0; i < res->ndisks; i++)
331 		free(res->disks[i]);
332 	free(res->disks);
333 	memset(res, 0, sizeof(*res));
334 }
335 
336 int
337 parse_ifs(struct parse_result *res, char *word, int val)
338 {
339 	const char	*error;
340 
341 	if (word != NULL) {
342 		val = strtonum(word, 0, INT_MAX, &error);
343 		if (error != NULL)  {
344 			warnx("invalid count \"%s\": %s", word, error);
345 			return (-1);
346 		}
347 	}
348 	res->nifs = val;
349 
350 	return (0);
351 }
352 
353 int
354 parse_network(struct parse_result *res, char *word)
355 {
356 	char		**nets;
357 	char		*s;
358 
359 	if ((nets = reallocarray(res->nets, res->nnets + 1,
360 	    sizeof(char *))) == NULL) {
361 		warn("reallocarray");
362 		return (-1);
363 	}
364 	if ((s = strdup(word)) == NULL) {
365 		warn("strdup");
366 		return (-1);
367 	}
368 	nets[res->nnets] = s;
369 	res->nets = nets;
370 	res->nnets++;
371 
372 	return (0);
373 }
374 
375 int
376 parse_size(struct parse_result *res, char *word, long long val)
377 {
378 	if (word != NULL) {
379 		if (scan_scaled(word, &val) != 0) {
380 			warn("invalid size: %s", word);
381 			return (-1);
382 		}
383 	}
384 
385 	if (val < (1024 * 1024)) {
386 		warnx("size must be at least one megabyte");
387 		return (-1);
388 	} else
389 		res->size = val / 1024 / 1024;
390 
391 	if ((res->size * 1024 * 1024) != val)
392 		warnx("size rounded to %lld megabytes", res->size);
393 
394 	return (0);
395 }
396 
397 int
398 parse_disk(struct parse_result *res, char *word)
399 {
400 	char		**disks;
401 	char		*s;
402 
403 	if ((disks = reallocarray(res->disks, res->ndisks + 1,
404 	    sizeof(char *))) == NULL) {
405 		warn("reallocarray");
406 		return (-1);
407 	}
408 	if ((s = strdup(word)) == NULL) {
409 		warn("strdup");
410 		return (-1);
411 	}
412 	disks[res->ndisks] = s;
413 	res->disks = disks;
414 	res->ndisks++;
415 
416 	return (0);
417 }
418 
419 int
420 parse_vmid(struct parse_result *res, char *word, int needname)
421 {
422 	const char	*error;
423 	uint32_t	 id;
424 
425 	if (word == NULL) {
426 		warnx("missing vmid argument");
427 		return (-1);
428 	}
429 	id = strtonum(word, 0, UINT32_MAX, &error);
430 	if (error == NULL) {
431 		if (needname) {
432 			warnx("invalid vm name");
433 			return (-1);
434 		} else {
435 			res->id = id;
436 			res->name = NULL;
437 		}
438 	} else {
439 		if (strlen(word) >= VMM_MAX_NAME_LEN) {
440 			warnx("name too long");
441 			return (-1);
442 		}
443 		res->id = 0;
444 		if ((res->name = strdup(word)) == NULL)
445 			errx(1, "strdup");
446 	}
447 
448 	return (0);
449 }
450 
451 int
452 ctl_create(struct parse_result *res, int argc, char *argv[])
453 {
454 	int		 ch, ret;
455 	const char	*paths[2];
456 
457 	if (argc < 2)
458 		ctl_usage(res->ctl);
459 
460 	paths[0] = argv[1];
461 	paths[1] = NULL;
462 	if (pledge("stdio rpath wpath cpath", NULL) == -1)
463 		err(1, "pledge");
464 	argc--;
465 	argv++;
466 
467 	while ((ch = getopt(argc, argv, "s:")) != -1) {
468 		switch (ch) {
469 		case 's':
470 			if (parse_size(res, optarg, 0) != 0)
471 				errx(1, "invalid size: %s", optarg);
472 			break;
473 		default:
474 			ctl_usage(res->ctl);
475 			/* NOTREACHED */
476 		}
477 	}
478 
479 	if (res->size == 0) {
480 		fprintf(stderr, "missing size argument\n");
481 		ctl_usage(res->ctl);
482 	}
483 	ret = create_imagefile(paths[0], res->size);
484 	if (ret != 0) {
485 		errno = ret;
486 		err(1, "create imagefile operation failed");
487 	} else
488 		warnx("imagefile created");
489 	return (0);
490 }
491 
492 int
493 ctl_status(struct parse_result *res, int argc, char *argv[])
494 {
495 	if (argc == 2) {
496 		if (parse_vmid(res, argv[1], 0) == -1)
497 			errx(1, "invalid id: %s", argv[1]);
498 	} else if (argc > 2)
499 		ctl_usage(res->ctl);
500 
501 	return (vmmaction(res));
502 }
503 
504 int
505 ctl_load(struct parse_result *res, int argc, char *argv[])
506 {
507 	if (argc != 2)
508 		ctl_usage(res->ctl);
509 
510 	if ((res->path = strdup(argv[1])) == NULL)
511 		err(1, "strdup");
512 
513 	return (vmmaction(res));
514 }
515 
516 int
517 ctl_log(struct parse_result *res, int argc, char *argv[])
518 {
519 	if (argc != 2)
520 		ctl_usage(res->ctl);
521 
522 	if (strncasecmp("brief", argv[1], strlen(argv[1])) == 0)
523 		res->verbose = 0;
524 	else if (strncasecmp("verbose", argv[1], strlen(argv[1])) == 0)
525 		res->verbose = 2;
526 	else
527 		ctl_usage(res->ctl);
528 
529 	return (vmmaction(res));
530 }
531 
532 int
533 ctl_reload(struct parse_result *res, int argc, char *argv[])
534 {
535 	if (argc != 1)
536 		ctl_usage(res->ctl);
537 
538 	return (vmmaction(res));
539 }
540 
541 int
542 ctl_reset(struct parse_result *res, int argc, char *argv[])
543 {
544 	if (argc == 2) {
545 		if (strcasecmp("all", argv[1]) == 0)
546 			res->mode = CONFIG_ALL;
547 		else if (strcasecmp("vms", argv[1]) == 0)
548 			res->mode = CONFIG_VMS;
549 		else if (strcasecmp("switches", argv[1]) == 0)
550 			res->mode = CONFIG_SWITCHES;
551 		else
552 			ctl_usage(res->ctl);
553 	} else if (argc > 2)
554 		ctl_usage(res->ctl);
555 
556 	if (res->mode == 0)
557 		res->mode = CONFIG_ALL;
558 
559 	return (vmmaction(res));
560 }
561 
562 int
563 ctl_start(struct parse_result *res, int argc, char *argv[])
564 {
565 	int		 ch, i;
566 	char		 path[PATH_MAX];
567 
568 	if (argc < 2)
569 		ctl_usage(res->ctl);
570 
571 	if (parse_vmid(res, argv[1], 0) == -1)
572 		errx(1, "invalid id: %s", argv[1]);
573 
574 	argc--;
575 	argv++;
576 
577 	while ((ch = getopt(argc, argv, "b:r:cLm:n:d:i:")) != -1) {
578 		switch (ch) {
579 		case 'b':
580 			if (res->path)
581 				errx(1, "boot image specified multiple times");
582 			if (realpath(optarg, path) == NULL)
583 				err(1, "invalid boot image path");
584 			if ((res->path = strdup(path)) == NULL)
585 				errx(1, "strdup");
586 			break;
587 		case 'r':
588 			if (res->isopath)
589 				errx(1, "iso image specified multiple times");
590 			if (realpath(optarg, path) == NULL)
591 				err(1, "invalid iso image path");
592 			if ((res->isopath = strdup(path)) == NULL)
593 				errx(1, "strdup");
594 			break;
595 		case 'c':
596 			tty_autoconnect = 1;
597 			break;
598 		case 'L':
599 			if (parse_network(res, ".") != 0)
600 				errx(1, "invalid network: %s", optarg);
601 			break;
602 		case 'm':
603 			if (res->size)
604 				errx(1, "memory specified multiple times");
605 			if (parse_size(res, optarg, 0) != 0)
606 				errx(1, "invalid memory size: %s", optarg);
607 			break;
608 		case 'n':
609 			if (parse_network(res, optarg) != 0)
610 				errx(1, "invalid network: %s", optarg);
611 			break;
612 		case 'd':
613 			if (realpath(optarg, path) == NULL)
614 				err(1, "invalid disk path");
615 			if (parse_disk(res, path) != 0)
616 				errx(1, "invalid disk: %s", optarg);
617 			break;
618 		case 'i':
619 			if (res->nifs != -1)
620 				errx(1, "interfaces specified multiple times");
621 			if (parse_ifs(res, optarg, 0) != 0)
622 				errx(1, "invalid interface count: %s", optarg);
623 			break;
624 		default:
625 			ctl_usage(res->ctl);
626 			/* NOTREACHED */
627 		}
628 	}
629 
630 	for (i = res->nnets; i < res->nifs; i++) {
631 		/* Add interface that is not attached to a switch */
632 		if (parse_network(res, "") == -1)
633 			return (-1);
634 	}
635 	if (res->nnets > res->nifs)
636 		res->nifs = res->nnets;
637 
638 	return (vmmaction(res));
639 }
640 
641 int
642 ctl_stop(struct parse_result *res, int argc, char *argv[])
643 {
644 	if (argc == 2) {
645 		if (parse_vmid(res, argv[1], 0) == -1)
646 			errx(1, "invalid id: %s", argv[1]);
647 	} else if (argc != 2)
648 		ctl_usage(res->ctl);
649 
650 	return (vmmaction(res));
651 }
652 
653 int
654 ctl_console(struct parse_result *res, int argc, char *argv[])
655 {
656 	if (argc == 2) {
657 		if (parse_vmid(res, argv[1], 0) == -1)
658 			errx(1, "invalid id: %s", argv[1]);
659 	} else if (argc != 2)
660 		ctl_usage(res->ctl);
661 
662 	return (vmmaction(res));
663 }
664 
665 int
666 ctl_pause(struct parse_result *res, int argc, char *argv[])
667 {
668 	if (argc == 2) {
669 		if (parse_vmid(res, argv[1], 0) == -1)
670 			errx(1, "invalid id: %s", argv[1]);
671 	} else if (argc != 2)
672 		ctl_usage(res->ctl);
673 
674 	return (vmmaction(res));
675 }
676 
677 int
678 ctl_unpause(struct parse_result *res, int argc, char *argv[])
679 {
680 	if (argc == 2) {
681 		if (parse_vmid(res, argv[1], 0) == -1)
682 			errx(1, "invalid id: %s", argv[1]);
683 	} else if (argc != 2)
684 		ctl_usage(res->ctl);
685 
686 	return (vmmaction(res));
687 }
688 
689 int
690 ctl_send(struct parse_result *res, int argc, char *argv[])
691 {
692 	if (pledge("stdio unix sendfd", NULL) == -1)
693 		err(1, "pledge");
694 	if (argc == 2) {
695 		if (parse_vmid(res, argv[1], 0) == -1)
696 			errx(1, "invalid id: %s", argv[1]);
697 	} else if (argc != 2)
698 		ctl_usage(res->ctl);
699 
700 	return (vmmaction(res));
701 }
702 
703 int
704 ctl_receive(struct parse_result *res, int argc, char *argv[])
705 {
706 	if (pledge("stdio unix sendfd", NULL) == -1)
707 		err(1, "pledge");
708 	if (argc == 2) {
709 		if (parse_vmid(res, argv[1], 1) == -1)
710 			errx(1, "invalid id: %s", argv[1]);
711 	} else if (argc != 2)
712 		ctl_usage(res->ctl);
713 
714 	return (vmmaction(res));
715 }
716 
717 __dead void
718 ctl_openconsole(const char *name)
719 {
720 	closefrom(STDERR_FILENO + 1);
721 	execl(VMCTL_CU, VMCTL_CU, "-l", name, "-s", "115200", (char *)NULL);
722 	err(1, "failed to open the console");
723 }
724