xref: /openbsd-src/usr.sbin/vmctl/main.c (revision f763167468dba5339ed4b14b7ecaca2a397ab0f6)
1 /*	$OpenBSD: main.c,v 1.32 2017/08/15 15:51:54 jasper 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] [-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 		bzero(&sun, 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 		if (ret) {
208 			errno = ret;
209 			err(1, "start VM operation failed");
210 		}
211 		break;
212 	case CMD_STOP:
213 		terminate_vm(res->id, res->name);
214 		break;
215 	case CMD_STATUS:
216 		get_info_vm(res->id, res->name, 0);
217 		break;
218 	case CMD_CONSOLE:
219 		get_info_vm(res->id, res->name, 1);
220 		break;
221 	case CMD_LOAD:
222 		imsg_compose(ibuf, IMSG_VMDOP_LOAD, 0, 0, -1,
223 		    res->path, strlen(res->path) + 1);
224 		break;
225 	case CMD_LOG:
226 		imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1,
227 		    &res->verbose, sizeof(res->verbose));
228 		break;
229 	case CMD_RELOAD:
230 		imsg_compose(ibuf, IMSG_VMDOP_RELOAD, 0, 0, -1, NULL, 0);
231 		break;
232 	case CMD_RESET:
233 		imsg_compose(ibuf, IMSG_CTL_RESET, 0, 0, -1,
234 		    &res->mode, sizeof(res->mode));
235 		break;
236 	case CMD_PAUSE:
237 		pause_vm(res->id, res->name);
238 		break;
239 	case CMD_UNPAUSE:
240 		unpause_vm(res->id, res->name);
241 		break;
242 	case CMD_SEND:
243 		send_vm(res->id, res->name);
244 		done = 1;
245 		break;
246 	case CMD_RECEIVE:
247 		vm_receive(res->id, res->name);
248 		break;
249 	case CMD_CREATE:
250 	case NONE:
251 		break;
252 	}
253 
254 	action = res->action;
255 	parse_free(res);
256 
257 	while (ibuf->w.queued)
258 		if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
259 			err(1, "write error");
260 
261 	while (!done) {
262 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
263 			errx(1, "imsg_read error");
264 		if (n == 0)
265 			errx(1, "pipe closed");
266 
267 		while (!done) {
268 			if ((n = imsg_get(ibuf, &imsg)) == -1)
269 				errx(1, "imsg_get error");
270 			if (n == 0)
271 				break;
272 
273 			if (imsg.hdr.type == IMSG_CTL_FAIL) {
274 				if (IMSG_DATA_SIZE(&imsg) == sizeof(ret))
275 					memcpy(&ret, imsg.data, sizeof(ret));
276 				else
277 					ret = 0;
278 				if (ret != 0) {
279 					memcpy(&ret, imsg.data, sizeof(ret));
280 					errno = ret;
281 					err(1, "command failed");
282 				} else
283 					errx(1, "command failed");
284 			}
285 
286 			ret = 0;
287 			switch (action) {
288 			case CMD_START:
289 				done = vm_start_complete(&imsg, &ret,
290 				    tty_autoconnect);
291 				break;
292 			case CMD_STOP:
293 				done = terminate_vm_complete(&imsg, &ret);
294 				break;
295 			case CMD_CONSOLE:
296 			case CMD_STATUS:
297 				done = add_info(&imsg, &ret);
298 				break;
299 			case CMD_PAUSE:
300 				done = pause_vm_complete(&imsg, &ret);
301 				break;
302 			case CMD_RECEIVE:
303 				done = vm_start_complete(&imsg, &ret, 0);
304 				break;
305 			case CMD_UNPAUSE:
306 				done = unpause_vm_complete(&imsg, &ret);
307 				break;
308 			default:
309 				done = 1;
310 				break;
311 			}
312 
313 			imsg_free(&imsg);
314 		}
315 	}
316 
317 	return (0);
318 }
319 
320 void
321 parse_free(struct parse_result *res)
322 {
323 	size_t	 i;
324 
325 	free(res->name);
326 	free(res->path);
327 	for (i = 0; i < res->ndisks; i++)
328 		free(res->disks[i]);
329 	free(res->disks);
330 	memset(res, 0, sizeof(*res));
331 }
332 
333 int
334 parse_ifs(struct parse_result *res, char *word, int val)
335 {
336 	const char	*error;
337 
338 	if (word != NULL) {
339 		val = strtonum(word, 0, INT_MAX, &error);
340 		if (error != NULL)  {
341 			warnx("invalid count \"%s\": %s", word, error);
342 			return (-1);
343 		}
344 	}
345 	res->nifs = val;
346 
347 	return (0);
348 }
349 
350 int
351 parse_network(struct parse_result *res, char *word)
352 {
353 	char		**nets;
354 	char		*s;
355 
356 	if ((nets = reallocarray(res->nets, res->nnets + 1,
357 	    sizeof(char *))) == NULL) {
358 		warn("reallocarray");
359 		return (-1);
360 	}
361 	if ((s = strdup(word)) == NULL) {
362 		warn("strdup");
363 		return (-1);
364 	}
365 	nets[res->nnets] = s;
366 	res->nets = nets;
367 	res->nnets++;
368 
369 	return (0);
370 }
371 
372 int
373 parse_size(struct parse_result *res, char *word, long long val)
374 {
375 	if (word != NULL) {
376 		if (scan_scaled(word, &val) != 0) {
377 			warn("invalid size: %s", word);
378 			return (-1);
379 		}
380 	}
381 
382 	if (val < (1024 * 1024)) {
383 		warnx("size must be at least one megabyte");
384 		return (-1);
385 	} else
386 		res->size = val / 1024 / 1024;
387 
388 	if ((res->size * 1024 * 1024) != val)
389 		warnx("size rounded to %lld megabytes", res->size);
390 
391 	return (0);
392 }
393 
394 int
395 parse_disk(struct parse_result *res, char *word)
396 {
397 	char		**disks;
398 	char		*s;
399 
400 	if ((disks = reallocarray(res->disks, res->ndisks + 1,
401 	    sizeof(char *))) == NULL) {
402 		warn("reallocarray");
403 		return (-1);
404 	}
405 	if ((s = strdup(word)) == NULL) {
406 		warn("strdup");
407 		return (-1);
408 	}
409 	disks[res->ndisks] = s;
410 	res->disks = disks;
411 	res->ndisks++;
412 
413 	return (0);
414 }
415 
416 int
417 parse_vmid(struct parse_result *res, char *word, int needname)
418 {
419 	const char	*error;
420 	uint32_t	 id;
421 
422 	if (word == NULL) {
423 		warnx("missing vmid argument");
424 		return (-1);
425 	}
426 	id = strtonum(word, 0, UINT32_MAX, &error);
427 	if (error == NULL) {
428 		if (needname) {
429 			warnx("invalid vm name");
430 			return (-1);
431 		} else {
432 			res->id = id;
433 			res->name = NULL;
434 		}
435 	} else {
436 		if (strlen(word) >= VMM_MAX_NAME_LEN) {
437 			warnx("name too long");
438 			return (-1);
439 		}
440 		res->id = 0;
441 		if ((res->name = strdup(word)) == NULL)
442 			errx(1, "strdup");
443 	}
444 
445 	return (0);
446 }
447 
448 int
449 ctl_create(struct parse_result *res, int argc, char *argv[])
450 {
451 	int		 ch, ret;
452 	const char	*paths[2];
453 
454 	if (argc < 2)
455 		ctl_usage(res->ctl);
456 
457 	paths[0] = argv[1];
458 	paths[1] = NULL;
459 	if (pledge("stdio rpath wpath cpath", NULL) == -1)
460 		err(1, "pledge");
461 	argc--;
462 	argv++;
463 
464 	while ((ch = getopt(argc, argv, "s:")) != -1) {
465 		switch (ch) {
466 		case 's':
467 			if (parse_size(res, optarg, 0) != 0)
468 				errx(1, "invalid size: %s", optarg);
469 			break;
470 		default:
471 			ctl_usage(res->ctl);
472 			/* NOTREACHED */
473 		}
474 	}
475 
476 	if (res->size == 0) {
477 		fprintf(stderr, "missing size argument\n");
478 		ctl_usage(res->ctl);
479 	}
480 	ret = create_imagefile(paths[0], res->size);
481 	if (ret != 0) {
482 		errno = ret;
483 		err(1, "create imagefile operation failed");
484 	} else
485 		warnx("imagefile created");
486 	return (0);
487 }
488 
489 int
490 ctl_status(struct parse_result *res, int argc, char *argv[])
491 {
492 	if (argc == 2) {
493 		if (parse_vmid(res, argv[1], 0) == -1)
494 			errx(1, "invalid id: %s", argv[1]);
495 	} else if (argc > 2)
496 		ctl_usage(res->ctl);
497 
498 	return (vmmaction(res));
499 }
500 
501 int
502 ctl_load(struct parse_result *res, int argc, char *argv[])
503 {
504 	if (argc != 2)
505 		ctl_usage(res->ctl);
506 
507 	if ((res->path = strdup(argv[1])) == NULL)
508 		err(1, "strdup");
509 
510 	return (vmmaction(res));
511 }
512 
513 int
514 ctl_log(struct parse_result *res, int argc, char *argv[])
515 {
516 	if (argc != 2)
517 		ctl_usage(res->ctl);
518 
519 	if (strncasecmp("brief", argv[1], strlen(argv[1])) == 0)
520 		res->verbose = 0;
521 	else if (strncasecmp("verbose", argv[1], strlen(argv[1])) == 0)
522 		res->verbose = 2;
523 	else
524 		ctl_usage(res->ctl);
525 
526 	return (vmmaction(res));
527 }
528 
529 int
530 ctl_reload(struct parse_result *res, int argc, char *argv[])
531 {
532 	if (argc != 1)
533 		ctl_usage(res->ctl);
534 
535 	return (vmmaction(res));
536 }
537 
538 int
539 ctl_reset(struct parse_result *res, int argc, char *argv[])
540 {
541 	if (argc == 2) {
542 		if (strcasecmp("all", argv[1]) == 0)
543 			res->mode = CONFIG_ALL;
544 		else if (strcasecmp("vms", argv[1]) == 0)
545 			res->mode = CONFIG_VMS;
546 		else if (strcasecmp("switches", argv[1]) == 0)
547 			res->mode = CONFIG_SWITCHES;
548 		else
549 			ctl_usage(res->ctl);
550 	} else if (argc > 2)
551 		ctl_usage(res->ctl);
552 
553 	if (res->mode == 0)
554 		res->mode = CONFIG_ALL;
555 
556 	return (vmmaction(res));
557 }
558 
559 int
560 ctl_start(struct parse_result *res, int argc, char *argv[])
561 {
562 	int		 ch, i;
563 	char		 path[PATH_MAX];
564 
565 	if (argc < 2)
566 		ctl_usage(res->ctl);
567 
568 	if (parse_vmid(res, argv[1], 0) == -1)
569 		errx(1, "invalid id: %s", argv[1]);
570 
571 	argc--;
572 	argv++;
573 
574 	while ((ch = getopt(argc, argv, "b:cLm:n:d:i:")) != -1) {
575 		switch (ch) {
576 		case 'b':
577 			if (res->path)
578 				errx(1, "boot image specified multiple times");
579 			if (realpath(optarg, path) == NULL)
580 				err(1, "invalid boot image path");
581 			if ((res->path = strdup(path)) == NULL)
582 				errx(1, "strdup");
583 			break;
584 		case 'c':
585 			tty_autoconnect = 1;
586 			break;
587 		case 'L':
588 			if (parse_network(res, ".") != 0)
589 				errx(1, "invalid network: %s", optarg);
590 			break;
591 		case 'm':
592 			if (res->size)
593 				errx(1, "memory specified multiple times");
594 			if (parse_size(res, optarg, 0) != 0)
595 				errx(1, "invalid memory size: %s", optarg);
596 			break;
597 		case 'n':
598 			if (parse_network(res, optarg) != 0)
599 				errx(1, "invalid network: %s", optarg);
600 			break;
601 		case 'd':
602 			if (realpath(optarg, path) == NULL)
603 				err(1, "invalid disk path");
604 			if (parse_disk(res, path) != 0)
605 				errx(1, "invalid disk: %s", optarg);
606 			break;
607 		case 'i':
608 			if (res->nifs != -1)
609 				errx(1, "interfaces specified multiple times");
610 			if (parse_ifs(res, optarg, 0) != 0)
611 				errx(1, "invalid interface count: %s", optarg);
612 			break;
613 		default:
614 			ctl_usage(res->ctl);
615 			/* NOTREACHED */
616 		}
617 	}
618 
619 	for (i = res->nnets; i < res->nifs; i++) {
620 		/* Add interface that is not attached to a switch */
621 		if (parse_network(res, "") == -1)
622 			return (-1);
623 	}
624 	if (res->nnets > res->nifs)
625 		res->nifs = res->nnets;
626 
627 	return (vmmaction(res));
628 }
629 
630 int
631 ctl_stop(struct parse_result *res, int argc, char *argv[])
632 {
633 	if (argc == 2) {
634 		if (parse_vmid(res, argv[1], 0) == -1)
635 			errx(1, "invalid id: %s", argv[1]);
636 	} else if (argc != 2)
637 		ctl_usage(res->ctl);
638 
639 	return (vmmaction(res));
640 }
641 
642 int
643 ctl_console(struct parse_result *res, int argc, char *argv[])
644 {
645 	if (argc == 2) {
646 		if (parse_vmid(res, argv[1], 0) == -1)
647 			errx(1, "invalid id: %s", argv[1]);
648 	} else if (argc != 2)
649 		ctl_usage(res->ctl);
650 
651 	return (vmmaction(res));
652 }
653 
654 int
655 ctl_pause(struct parse_result *res, int argc, char *argv[])
656 {
657 	if (argc == 2) {
658 		if (parse_vmid(res, argv[1], 0) == -1)
659 			errx(1, "invalid id: %s", argv[1]);
660 	} else if (argc != 2)
661 		ctl_usage(res->ctl);
662 
663 	return (vmmaction(res));
664 }
665 
666 int
667 ctl_unpause(struct parse_result *res, int argc, char *argv[])
668 {
669 	if (argc == 2) {
670 		if (parse_vmid(res, argv[1], 0) == -1)
671 			errx(1, "invalid id: %s", argv[1]);
672 	} else if (argc != 2)
673 		ctl_usage(res->ctl);
674 
675 	return (vmmaction(res));
676 }
677 
678 int
679 ctl_send(struct parse_result *res, int argc, char *argv[])
680 {
681 	if (pledge("stdio unix sendfd", NULL) == -1)
682 		err(1, "pledge");
683 	if (argc == 2) {
684 		if (parse_vmid(res, argv[1], 0) == -1)
685 			errx(1, "invalid id: %s", argv[1]);
686 	} else if (argc != 2)
687 		ctl_usage(res->ctl);
688 
689 	return (vmmaction(res));
690 }
691 
692 int
693 ctl_receive(struct parse_result *res, int argc, char *argv[])
694 {
695 	if (pledge("stdio unix sendfd", NULL) == -1)
696 		err(1, "pledge");
697 	if (argc == 2) {
698 		if (parse_vmid(res, argv[1], 1) == -1)
699 			errx(1, "invalid id: %s", argv[1]);
700 	} else if (argc != 2)
701 		ctl_usage(res->ctl);
702 
703 	return (vmmaction(res));
704 }
705 
706 __dead void
707 ctl_openconsole(const char *name)
708 {
709 	closefrom(STDERR_FILENO + 1);
710 	execl(VMCTL_CU, VMCTL_CU, "-l", name, "-s", "115200", (char *)NULL);
711 	err(1, "failed to open the console");
712 }
713