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