1/*	$OpenBSD: main.c,v 1.78 2024/05/18 06:45:00 jsg 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 <err.h>
25#include <errno.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <stdint.h>
29#include <limits.h>
30#include <string.h>
31#include <syslog.h>
32#include <unistd.h>
33#include <fcntl.h>
34#include <util.h>
35#include <imsg.h>
36
37#include "vmd.h"
38#include "virtio.h"
39#include "proc.h"
40#include "vmctl.h"
41
42#define RAW_FMT		"raw"
43#define QCOW2_FMT	"qcow2"
44
45static const char	*socket_name = SOCKET_NAME;
46static int		 ctl_sock = -1;
47static int		 tty_autoconnect = 0;
48int			 stat_rflag;
49
50__dead void	 usage(void);
51__dead void	 ctl_usage(struct ctl_command *);
52
53int		 ctl_console(struct parse_result *, int, char *[]);
54int		 ctl_convert(const char *, const char *, int, size_t);
55int		 ctl_create(struct parse_result *, int, char *[]);
56int		 ctl_load(struct parse_result *, int, char *[]);
57int		 ctl_log(struct parse_result *, int, char *[]);
58int		 ctl_reload(struct parse_result *, int, char *[]);
59int		 ctl_reset(struct parse_result *, int, char *[]);
60int		 ctl_start(struct parse_result *, int, char *[]);
61int		 ctl_status(struct parse_result *, int, char *[]);
62int		 ctl_stop(struct parse_result *, int, char *[]);
63int		 ctl_waitfor(struct parse_result *, int, char *[]);
64int		 ctl_pause(struct parse_result *, int, char *[]);
65int		 ctl_unpause(struct parse_result *, int, char *[]);
66int		 ctl_send(struct parse_result *, int, char *[]);
67int		 ctl_receive(struct parse_result *, int, char *[]);
68
69struct ctl_command ctl_commands[] = {
70	{ "console",	CMD_CONSOLE,	ctl_console,	"id" },
71	{ "create",	CMD_CREATE,	ctl_create,
72		"[-b base | -i disk] [-s size] disk", 1 },
73	{ "load",	CMD_LOAD,	ctl_load,	"filename" },
74	{ "log",	CMD_LOG,	ctl_log,	"[brief | verbose]" },
75	{ "pause",	CMD_PAUSE,	ctl_pause,	"id" },
76	{ "receive",	CMD_RECEIVE,	ctl_receive,	"name" ,	1},
77	{ "reload",	CMD_RELOAD,	ctl_reload,	"" },
78	{ "reset",	CMD_RESET,	ctl_reset,	"[all | switches | vms]" },
79	{ "send",	CMD_SEND,	ctl_send,	"id",	1},
80	{ "show",	CMD_STATUS,	ctl_status,	"[id]" },
81	{ "start",	CMD_START,	ctl_start,
82	    "[-cL] [-B device] [-b path] [-d disk] [-i count]\n"
83	    "\t\t[-m size] [-n switch] [-r path] [-t name] id | name",	1},
84	{ "status",	CMD_STATUS,	ctl_status,	"[-r] [id]" },
85	{ "stop",	CMD_STOP,	ctl_stop,	"[-fw] [id | -a]" },
86	{ "unpause",	CMD_UNPAUSE,	ctl_unpause,	"id" },
87	{ "wait",	CMD_WAITFOR,	ctl_waitfor,	"id" },
88	{ NULL }
89};
90
91__dead void
92usage(void)
93{
94	extern char	*__progname;
95
96	fprintf(stderr, "usage:\t%s [-v] command [arg ...]\n", __progname);
97
98	exit(1);
99}
100
101__dead void
102ctl_usage(struct ctl_command *ctl)
103{
104	extern char	*__progname;
105
106	fprintf(stderr, "usage:\t%s [-v] %s %s\n", __progname,
107	    ctl->name, ctl->usage);
108	exit(1);
109}
110
111int
112main(int argc, char *argv[])
113{
114	int	 ch, verbose = 1;
115
116	while ((ch = getopt(argc, argv, "v")) != -1) {
117		switch (ch) {
118		case 'v':
119			verbose = 2;
120			break;
121		default:
122			usage();
123			/* NOTREACHED */
124		}
125	}
126	argc -= optind;
127	argv += optind;
128	optreset = 1;
129	optind = 1;
130
131	if (argc < 1)
132		usage();
133
134	log_init(verbose, LOG_DAEMON);
135
136	return (parse(argc, argv));
137}
138
139int
140parse(int argc, char *argv[])
141{
142	struct ctl_command	*ctl = NULL;
143	struct parse_result	 res;
144	int			 i;
145
146	memset(&res, 0, sizeof(res));
147	res.nifs = -1;
148
149	for (i = 0; ctl_commands[i].name != NULL; i++) {
150		if (strncmp(ctl_commands[i].name,
151		    argv[0], strlen(argv[0])) == 0) {
152			if (ctl != NULL) {
153				fprintf(stderr,
154				    "ambiguous argument: %s\n", argv[0]);
155				usage();
156			}
157			ctl = &ctl_commands[i];
158		}
159	}
160
161	if (ctl == NULL) {
162		fprintf(stderr, "unknown argument: %s\n", argv[0]);
163		usage();
164	}
165
166	res.action = ctl->action;
167	res.ctl = ctl;
168
169	if (!ctl->has_pledge) {
170		/* pledge(2) default if command doesn't have its own pledge */
171		if (pledge("stdio rpath exec unix getpw unveil", NULL) == -1)
172			err(1, "pledge");
173	}
174	if (ctl->main(&res, argc, argv) != 0)
175		exit(1);
176
177	if (ctl_sock != -1) {
178		close(ibuf->fd);
179		free(ibuf);
180	}
181
182	return (0);
183}
184
185int
186vmmaction(struct parse_result *res)
187{
188	struct sockaddr_un	 sun;
189	struct imsg		 imsg;
190	int			 done = 0;
191	int			 n;
192	int			 ret, action;
193	unsigned int		 flags;
194
195	if (ctl_sock == -1) {
196		if (unveil(SOCKET_NAME, "w") == -1)
197			err(1, "unveil %s", SOCKET_NAME);
198		if ((ctl_sock = socket(AF_UNIX,
199		    SOCK_STREAM|SOCK_CLOEXEC, 0)) == -1)
200			err(1, "socket");
201
202		memset(&sun, 0, sizeof(sun));
203		sun.sun_family = AF_UNIX;
204		strlcpy(sun.sun_path, socket_name, sizeof(sun.sun_path));
205
206		if (connect(ctl_sock,
207		    (struct sockaddr *)&sun, sizeof(sun)) == -1)
208			err(1, "connect: %s", socket_name);
209
210		if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
211			err(1, "malloc");
212		imsg_init(ibuf, ctl_sock);
213	}
214
215	switch (res->action) {
216	case CMD_START:
217		ret = vm_start(res->id, res->name, res->size, res->nifs,
218		    res->nets, res->ndisks, res->disks, res->disktypes,
219		    res->path, res->isopath, res->instance, res->bootdevice);
220		if (ret) {
221			errno = ret;
222			err(1, "start VM operation failed");
223		}
224		break;
225	case CMD_STOP:
226		terminate_vm(res->id, res->name, res->flags);
227		break;
228	case CMD_STATUS:
229	case CMD_CONSOLE:
230	case CMD_STOPALL:
231		get_info_vm(res->id, res->name, res->action, res->flags);
232		break;
233	case CMD_LOAD:
234		imsg_compose(ibuf, IMSG_VMDOP_LOAD, 0, 0, -1,
235		    res->path, strlen(res->path) + 1);
236		break;
237	case CMD_LOG:
238		imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1,
239		    &res->verbose, sizeof(res->verbose));
240		break;
241	case CMD_RELOAD:
242		imsg_compose(ibuf, IMSG_VMDOP_RELOAD, 0, 0, -1, NULL, 0);
243		break;
244	case CMD_RESET:
245		imsg_compose(ibuf, IMSG_CTL_RESET, 0, 0, -1,
246		    &res->mode, sizeof(res->mode));
247		break;
248	case CMD_WAITFOR:
249		waitfor_vm(res->id, res->name);
250		break;
251	case CMD_PAUSE:
252		pause_vm(res->id, res->name);
253		break;
254	case CMD_UNPAUSE:
255		unpause_vm(res->id, res->name);
256		break;
257	case CMD_SEND:
258		send_vm(res->id, res->name);
259		done = 1;
260		ret = 0;
261		break;
262	case CMD_RECEIVE:
263		vm_receive(res->id, res->name);
264		break;
265	case CMD_CREATE:
266	case NONE:
267		/* The action is not expected here */
268		errx(1, "invalid action %u", res->action);
269		break;
270	}
271
272	action = res->action;
273	flags = res->flags;
274	parse_free(res);
275
276	while (ibuf->w.queued)
277		if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
278			err(1, "write error");
279
280	while (!done) {
281		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
282			errx(1, "imsg_read error");
283		if (n == 0)
284			errx(1, "pipe closed");
285
286		while (!done) {
287			if ((n = imsg_get(ibuf, &imsg)) == -1)
288				errx(1, "imsg_get error");
289			if (n == 0)
290				break;
291
292			if (imsg.hdr.type == IMSG_CTL_FAIL) {
293				if (IMSG_DATA_SIZE(&imsg) == sizeof(ret))
294					memcpy(&ret, imsg.data, sizeof(ret));
295				else
296					ret = 0;
297				if (ret != 0) {
298					errno = ret;
299					err(1, "command failed");
300				} else
301					errx(1, "command failed");
302			}
303
304			ret = 0;
305			switch (action) {
306			case CMD_START:
307				done = vm_start_complete(&imsg, &ret,
308				    tty_autoconnect);
309				break;
310			case CMD_WAITFOR:
311				flags = VMOP_WAIT;
312				/* FALLTHROUGH */
313			case CMD_STOP:
314				done = terminate_vm_complete(&imsg, &ret,
315				    flags);
316				break;
317			case CMD_CONSOLE:
318			case CMD_STATUS:
319			case CMD_STOPALL:
320				done = add_info(&imsg, &ret);
321				break;
322			case CMD_PAUSE:
323				done = pause_vm_complete(&imsg, &ret);
324				break;
325			case CMD_RECEIVE:
326				done = vm_start_complete(&imsg, &ret, 0);
327				break;
328			case CMD_UNPAUSE:
329				done = unpause_vm_complete(&imsg, &ret);
330				break;
331			default:
332				done = 1;
333				break;
334			}
335
336			imsg_free(&imsg);
337		}
338	}
339
340	if (ret)
341		return (1);
342	else
343		return (0);
344}
345
346void
347parse_free(struct parse_result *res)
348{
349	size_t	 i;
350
351	free(res->name);
352	free(res->path);
353	free(res->isopath);
354	free(res->instance);
355	for (i = 0; i < res->ndisks; i++)
356		free(res->disks[i]);
357	free(res->disks);
358	free(res->disktypes);
359	memset(res, 0, sizeof(*res));
360}
361
362int
363parse_ifs(struct parse_result *res, char *word, int val)
364{
365	const char	*error;
366
367	if (word != NULL) {
368		val = strtonum(word, 1, INT_MAX, &error);
369		if (error != NULL)  {
370			warnx("count is %s: %s", error, word);
371			return (-1);
372		}
373	}
374	res->nifs = val;
375
376	return (0);
377}
378
379int
380parse_network(struct parse_result *res, char *word)
381{
382	char		**nets;
383	char		*s;
384
385	if ((nets = reallocarray(res->nets, res->nnets + 1,
386	    sizeof(char *))) == NULL) {
387		warn("reallocarray");
388		return (-1);
389	}
390	if ((s = strdup(word)) == NULL) {
391		warn("strdup");
392		return (-1);
393	}
394	nets[res->nnets] = s;
395	res->nets = nets;
396	res->nnets++;
397
398	return (0);
399}
400
401void
402parse_size(struct parse_result *res, char *word, const char *type)
403{
404	char		 result[FMT_SCALED_STRSIZE];
405	long long 	 val = 0;
406
407	if (word != NULL) {
408		if (scan_scaled(word, &val) != 0)
409			err(1, "invalid %s size: %s", type, word);
410	}
411
412	if (val < (1024 * 1024))
413		errx(1, "%s size must be at least 1MB", type);
414
415	if (strcmp("memory", type) == 0 && val > VMM_MAX_VM_MEM_SIZE) {
416		if (fmt_scaled(VMM_MAX_VM_MEM_SIZE, result) == 0)
417			errx(1, "memory size too large (limit is %s)", result);
418		else
419			errx(1, "memory size too large");
420	}
421
422	/* Round down to the megabyte. */
423	res->size = (val / (1024 * 1024)) * (1024 * 1024);
424
425	if (res->size != (size_t)val) {
426		if (fmt_scaled(res->size, result) == 0)
427			warnx("%s size rounded to %s", type, result);
428		else
429			warnx("%s size rounded to %zuB", type, res->size);
430	}
431}
432
433int
434parse_disktype(const char *s, const char **ret)
435{
436	char		 buf[BUFSIZ];
437	const char	*ext;
438	int		 fd;
439	ssize_t		 len;
440
441	*ret = s;
442
443	/* Try to parse the explicit format (qcow2:disk.qc2) */
444	if (strstr(s, RAW_FMT) == s && *(s + strlen(RAW_FMT)) == ':') {
445		*ret = s + strlen(RAW_FMT) + 1;
446		return (VMDF_RAW);
447	}
448	if (strstr(s, QCOW2_FMT) == s && *(s + strlen(QCOW2_FMT)) == ':') {
449		*ret = s + strlen(QCOW2_FMT) + 1;
450		return (VMDF_QCOW2);
451	}
452
453	/* Or try to derive the format from the file signature */
454	if ((fd = open(s, O_RDONLY)) != -1) {
455		len = read(fd, buf, sizeof(buf));
456		close(fd);
457
458		if (len >= (ssize_t)strlen(VM_MAGIC_QCOW) &&
459		    strncmp(buf, VM_MAGIC_QCOW,
460		    strlen(VM_MAGIC_QCOW)) == 0) {
461			/* Return qcow2, the version will be checked later */
462			return (VMDF_QCOW2);
463		}
464	}
465
466	/*
467	 * Use the extension as a last option.  This is needed for
468	 * 'vmctl create' as the file, and the signature, doesn't
469	 * exist yet.
470	 */
471	if ((ext = strrchr(s, '.')) != NULL && *(++ext) != '\0') {
472		if (strcasecmp(ext, RAW_FMT) == 0)
473			return (VMDF_RAW);
474		else if (strcasecmp(ext, QCOW2_FMT) == 0)
475			return (VMDF_QCOW2);
476	}
477
478	/* Fallback to raw */
479	return (VMDF_RAW);
480}
481
482int
483parse_disk(struct parse_result *res, char *word, int type)
484{
485	char		**disks;
486	int		*disktypes;
487	char		*s;
488
489	if ((disks = reallocarray(res->disks, res->ndisks + 1,
490	    sizeof(char *))) == NULL) {
491		warn("reallocarray");
492		return (-1);
493	}
494	if ((disktypes = reallocarray(res->disktypes, res->ndisks + 1,
495	    sizeof(int))) == NULL) {
496		warn("reallocarray");
497		return -1;
498	}
499	if ((s = strdup(word)) == NULL) {
500		warn("strdup");
501		return (-1);
502	}
503	disks[res->ndisks] = s;
504	disktypes[res->ndisks] = type;
505	res->disks = disks;
506	res->disktypes = disktypes;
507	res->ndisks++;
508
509	return (0);
510}
511
512int
513parse_vmid(struct parse_result *res, char *word, int needname)
514{
515	const char	*error;
516	uint32_t	 id;
517
518	if (word == NULL) {
519		warnx("missing vmid argument");
520		return (-1);
521	}
522	if (*word == '-') {
523		/* don't print a warning to allow command line options */
524		return (-1);
525	}
526	id = strtonum(word, 0, UINT32_MAX, &error);
527	if (error == NULL) {
528		if (needname) {
529			warnx("invalid vm name");
530			return (-1);
531		} else {
532			res->id = id;
533			res->name = NULL;
534		}
535	} else {
536		if (strlen(word) >= VMM_MAX_NAME_LEN) {
537			warnx("name too long");
538			return (-1);
539		}
540		res->id = 0;
541		if ((res->name = strdup(word)) == NULL)
542			errx(1, "strdup");
543	}
544
545	return (0);
546}
547
548int
549parse_instance(struct parse_result *res, char *word)
550{
551	if (strlen(word) >= VMM_MAX_NAME_LEN) {
552		warnx("instance vm name too long");
553		return (-1);
554	}
555	res->id = 0;
556	if ((res->instance = strdup(word)) == NULL)
557		errx(1, "strdup");
558
559	return (0);
560}
561
562int
563ctl_create(struct parse_result *res, int argc, char *argv[])
564{
565	int		 ch, ret, type;
566	const char	*disk, *format, *base = NULL, *input = NULL;
567
568	while ((ch = getopt(argc, argv, "b:i:s:")) != -1) {
569		switch (ch) {
570		case 'b':
571			base = optarg;
572			break;
573		case 'i':
574			input = optarg;
575			break;
576		case 's':
577			parse_size(res, optarg, "disk");
578			break;
579		default:
580			ctl_usage(res->ctl);
581			/* NOTREACHED */
582		}
583	}
584	argc -= optind;
585	argv += optind;
586
587	if (argc != 1)
588		ctl_usage(res->ctl);
589
590	type = parse_disktype(argv[0], &disk);
591
592	if (pledge("stdio rpath wpath cpath", NULL) == -1)
593		err(1, "pledge");
594
595	if (input) {
596		if (base && input)
597			errx(1, "conflicting -b and -i arguments");
598		return ctl_convert(input, disk, type, res->size);
599	}
600
601	if (base && type != VMDF_QCOW2)
602		errx(1, "base images require qcow2 disk format");
603	if (res->size == 0 && !base) {
604		fprintf(stderr, "could not create %s: missing size argument\n",
605		    disk);
606		ctl_usage(res->ctl);
607	}
608
609	if ((ret = create_imagefile(type, disk, base, res->size, &format)) != 0) {
610		errno = ret;
611		err(1, "create imagefile operation failed");
612	} else
613		warnx("%s imagefile created", format);
614
615	return (0);
616}
617
618int
619ctl_convert(const char *srcfile, const char *dstfile, int dsttype, size_t dstsize)
620{
621	struct {
622		int			 fd;
623		int			 type;
624		struct virtio_backing	 file;
625		const char		*disk;
626		off_t			 size;
627	}	 src, dst;
628	int		 ret;
629	const char	*format, *errstr = NULL;
630	uint8_t		*buf = NULL, *zerobuf = NULL;
631	size_t		 buflen;
632	ssize_t		 len, rlen;
633	off_t		 off;
634
635	memset(&src, 0, sizeof(src));
636	memset(&dst, 0, sizeof(dst));
637
638	src.type = parse_disktype(srcfile, &src.disk);
639	dst.type = dsttype;
640	dst.disk = dstfile;
641
642	if ((src.fd = open_imagefile(src.type, src.disk, O_RDONLY,
643	    &src.file, &src.size)) == -1) {
644		errstr = "failed to open source image file";
645		goto done;
646	}
647
648	if (dstsize == 0)
649		dstsize = src.size;
650	if (dstsize < (size_t)src.size) {
651		errstr = "size cannot be smaller than input disk size";
652		goto done;
653	}
654
655	/* align to megabytes */
656	dst.size = ALIGNSZ(dstsize, 1048576);
657
658	if ((ret = create_imagefile(dst.type, dst.disk, NULL, dst.size,
659	    &format)) != 0) {
660		errstr = "failed to create destination image file";
661		goto done;
662	}
663
664	if ((dst.fd = open_imagefile(dst.type, dst.disk, O_RDWR,
665	    &dst.file, &dst.size)) == -1) {
666		errstr = "failed to open destination image file";
667		goto done;
668	}
669
670	if (pledge("stdio", NULL) == -1)
671		err(1, "pledge");
672
673	/*
674	 * Use 64k buffers by default.  This could also be adjusted to
675	 * the backend cluster size.
676	 */
677	buflen = 1 << 16;
678	if ((buf = calloc(1, buflen)) == NULL ||
679	    (zerobuf = calloc(1, buflen)) == NULL) {
680		errstr = "failed to allocated buffers";
681		goto done;
682	}
683
684	for (off = 0; off < dst.size; off += len) {
685		/* Read input from the source image */
686		if (off < src.size) {
687			len = MIN((off_t)buflen, src.size - off);
688			if ((rlen = src.file.pread(src.file.p,
689			    buf, (size_t)len, off)) != len) {
690				errno = EIO;
691				errstr = "failed to read from source";
692				goto done;
693			}
694		} else
695			len = 0;
696
697		/* and pad the remaining bytes */
698		if (len < (ssize_t)buflen) {
699			log_debug("%s: padding %zd zero bytes at offset %lld",
700			    format, buflen - len, off + len);
701			memset(buf + len, 0, buflen - len);
702			len = buflen;
703		}
704
705		/*
706		 * No need to copy empty buffers.  This allows the backend,
707		 * sparse files or QCOW2 images, to save space in the
708		 * destination file.
709		 */
710		if (memcmp(buf, zerobuf, buflen) == 0)
711			continue;
712
713		log_debug("%s: writing %zd of %lld bytes at offset %lld",
714		    format, len, dst.size, off);
715
716		if ((rlen = dst.file.pwrite(dst.file.p,
717		    buf, (size_t)len, off)) != len) {
718			errno = EIO;
719			errstr = "failed to write to destination";
720			goto done;
721		}
722	}
723
724	if (dstsize < (size_t)dst.size)
725		warnx("destination size rounded to %lld megabytes",
726		    dst.size / 1048576);
727
728 done:
729	free(buf);
730	free(zerobuf);
731	if (src.file.p != NULL)
732		src.file.close(src.file.p, 0);
733	if (dst.file.p != NULL)
734		dst.file.close(dst.file.p, 0);
735	if (errstr != NULL)
736		errx(1, "%s", errstr);
737	else
738		warnx("%s imagefile created", format);
739
740	return (0);
741}
742
743int
744ctl_status(struct parse_result *res, int argc, char *argv[])
745{
746	char ch;
747
748	while ((ch = getopt(argc, argv, "r")) != -1) {
749		switch (ch) {
750		case 'r':
751			stat_rflag = 1;
752			break;
753		default:
754			ctl_usage(res->ctl);
755			/* NOTREACHED */
756		}
757	}
758	argc -= optind;
759	argv += optind;
760
761	if (argc == 1) {
762		if (parse_vmid(res, argv[0], 0) == -1)
763			errx(1, "invalid id: %s", argv[0]);
764	} else if (argc > 1)
765		ctl_usage(res->ctl);
766
767	return (vmmaction(res));
768}
769
770int
771ctl_load(struct parse_result *res, int argc, char *argv[])
772{
773	if (argc != 2)
774		ctl_usage(res->ctl);
775
776	if ((res->path = strdup(argv[1])) == NULL)
777		err(1, "strdup");
778
779	return (vmmaction(res));
780}
781
782int
783ctl_log(struct parse_result *res, int argc, char *argv[])
784{
785	if (argc != 2)
786		ctl_usage(res->ctl);
787
788	if (strncasecmp("brief", argv[1], strlen(argv[1])) == 0)
789		res->verbose = 0;
790	else if (strncasecmp("verbose", argv[1], strlen(argv[1])) == 0)
791		res->verbose = 2;
792	else
793		ctl_usage(res->ctl);
794
795	return (vmmaction(res));
796}
797
798int
799ctl_reload(struct parse_result *res, int argc, char *argv[])
800{
801	if (argc != 1)
802		ctl_usage(res->ctl);
803
804	return (vmmaction(res));
805}
806
807int
808ctl_reset(struct parse_result *res, int argc, char *argv[])
809{
810	if (argc == 2) {
811		if (strcasecmp("all", argv[1]) == 0)
812			res->mode = CONFIG_ALL;
813		else if (strcasecmp("vms", argv[1]) == 0)
814			res->mode = CONFIG_VMS;
815		else if (strcasecmp("switches", argv[1]) == 0)
816			res->mode = CONFIG_SWITCHES;
817		else
818			ctl_usage(res->ctl);
819	} else if (argc > 2)
820		ctl_usage(res->ctl);
821
822	if (res->mode == 0)
823		res->mode = CONFIG_ALL;
824
825	return (vmmaction(res));
826}
827
828int
829ctl_start(struct parse_result *res, int argc, char *argv[])
830{
831	int		 ch, i, type;
832	char		 path[PATH_MAX];
833	const char	*s;
834
835	/* We may require sendfd */
836	if (pledge("stdio rpath exec unix getpw unveil sendfd", NULL) == -1)
837		err(1, "pledge");
838
839	while ((ch = getopt(argc, argv, "b:B:cd:i:Lm:n:r:t:")) != -1) {
840		switch (ch) {
841		case 'b':
842			if (res->path)
843				errx(1, "boot image specified multiple times");
844			if (realpath(optarg, path) == NULL)
845				err(1, "invalid boot image path");
846			if ((res->path = strdup(path)) == NULL)
847				errx(1, "strdup");
848			break;
849		case 'B':
850			if (res->bootdevice)
851				errx(1, "boot device specified multiple times");
852			if (strcmp("disk", optarg) == 0)
853				res->bootdevice = VMBOOTDEV_DISK;
854			else if (strcmp("cdrom", optarg) == 0)
855				res->bootdevice = VMBOOTDEV_CDROM;
856			else if (strcmp("net", optarg) == 0)
857				res->bootdevice = VMBOOTDEV_NET;
858			else
859				errx(1, "unknown boot device %s", optarg);
860			break;
861		case 'r':
862			if (res->isopath)
863				errx(1, "iso image specified multiple times");
864			if (realpath(optarg, path) == NULL)
865				err(1, "invalid iso image path");
866			if ((res->isopath = strdup(path)) == NULL)
867				errx(1, "strdup");
868			break;
869		case 'c':
870			tty_autoconnect = 1;
871			break;
872		case 'L':
873			if (parse_network(res, ".") != 0)
874				errx(1, "invalid network: %s", optarg);
875			break;
876		case 'm':
877			if (res->size)
878				errx(1, "memory specified multiple times");
879			parse_size(res, optarg, "memory");
880			break;
881		case 'n':
882			if (parse_network(res, optarg) != 0)
883				errx(1, "invalid network: %s", optarg);
884			break;
885		case 'd':
886			type = parse_disktype(optarg, &s);
887			if (realpath(s, path) == NULL)
888				err(1, "invalid disk path");
889			if (parse_disk(res, path, type) != 0)
890				errx(1, "invalid disk: %s", optarg);
891			break;
892		case 'i':
893			if (res->nifs != -1)
894				errx(1, "interfaces specified multiple times");
895			if (parse_ifs(res, optarg, 0) != 0)
896				errx(1, "invalid interface count: %s", optarg);
897			break;
898		case 't':
899			if (parse_instance(res, optarg) == -1)
900				errx(1, "invalid name: %s", optarg);
901			break;
902		default:
903			ctl_usage(res->ctl);
904			/* NOTREACHED */
905		}
906	}
907	argc -= optind;
908	argv += optind;
909
910	if (argc != 1)
911		ctl_usage(res->ctl);
912
913	if (parse_vmid(res, argv[0], 0) == -1)
914		errx(1, "invalid id: %s", argv[0]);
915
916	for (i = res->nnets; i < res->nifs; i++) {
917		/* Add interface that is not attached to a switch */
918		if (parse_network(res, "") == -1)
919			return (-1);
920	}
921	if (res->nnets > res->nifs)
922		res->nifs = res->nnets;
923
924	return (vmmaction(res));
925}
926
927int
928ctl_stop(struct parse_result *res, int argc, char *argv[])
929{
930	int		 ch;
931
932	while ((ch = getopt(argc, argv, "afw")) != -1) {
933		switch (ch) {
934		case 'f':
935			res->flags |= VMOP_FORCE;
936			break;
937		case 'w':
938			res->flags |= VMOP_WAIT;
939			break;
940		case 'a':
941			res->action = CMD_STOPALL;
942			break;
943		default:
944			ctl_usage(res->ctl);
945			/* NOTREACHED */
946		}
947	}
948	argc -= optind;
949	argv += optind;
950
951	if (res->action == CMD_STOPALL) {
952		if (argc != 0)
953			ctl_usage(res->ctl);
954	} else {
955		if (argc != 1)
956			ctl_usage(res->ctl);
957		if (parse_vmid(res, argv[0], 0) == -1)
958			errx(1, "invalid id: %s", argv[0]);
959	}
960
961	return (vmmaction(res));
962}
963
964int
965ctl_console(struct parse_result *res, int argc, char *argv[])
966{
967	if (argc == 2) {
968		if (parse_vmid(res, argv[1], 0) == -1)
969			errx(1, "invalid id: %s", argv[1]);
970	} else if (argc != 2)
971		ctl_usage(res->ctl);
972
973	return (vmmaction(res));
974}
975
976int
977ctl_waitfor(struct parse_result *res, int argc, char *argv[])
978{
979	if (argc == 2) {
980		if (parse_vmid(res, argv[1], 0) == -1)
981			errx(1, "invalid id: %s", argv[1]);
982	} else if (argc != 2)
983		ctl_usage(res->ctl);
984
985	return (vmmaction(res));
986}
987
988int
989ctl_pause(struct parse_result *res, int argc, char *argv[])
990{
991	if (argc == 2) {
992		if (parse_vmid(res, argv[1], 0) == -1)
993			errx(1, "invalid id: %s", argv[1]);
994	} else if (argc != 2)
995		ctl_usage(res->ctl);
996
997	return (vmmaction(res));
998}
999
1000int
1001ctl_unpause(struct parse_result *res, int argc, char *argv[])
1002{
1003	if (argc == 2) {
1004		if (parse_vmid(res, argv[1], 0) == -1)
1005			errx(1, "invalid id: %s", argv[1]);
1006	} else if (argc != 2)
1007		ctl_usage(res->ctl);
1008
1009	return (vmmaction(res));
1010}
1011
1012int
1013ctl_send(struct parse_result *res, int argc, char *argv[])
1014{
1015	if (pledge("stdio unix sendfd unveil", NULL) == -1)
1016		err(1, "pledge");
1017	if (argc == 2) {
1018		if (parse_vmid(res, argv[1], 0) == -1)
1019			errx(1, "invalid id: %s", argv[1]);
1020	} else if (argc != 2)
1021		ctl_usage(res->ctl);
1022
1023	return (vmmaction(res));
1024}
1025
1026int
1027ctl_receive(struct parse_result *res, int argc, char *argv[])
1028{
1029	if (pledge("stdio unix sendfd unveil", NULL) == -1)
1030		err(1, "pledge");
1031	if (argc == 2) {
1032		if (parse_vmid(res, argv[1], 1) == -1)
1033			errx(1, "invalid id: %s", argv[1]);
1034	} else if (argc != 2)
1035		ctl_usage(res->ctl);
1036
1037	return (vmmaction(res));
1038}
1039
1040__dead void
1041ctl_openconsole(const char *name)
1042{
1043	closefrom(STDERR_FILENO + 1);
1044	if (unveil(VMCTL_CU, "x") == -1)
1045		err(1, "unveil %s", VMCTL_CU);
1046	execl(VMCTL_CU, VMCTL_CU, "-r", "-l", name, "-s", "115200",
1047	    (char *)NULL);
1048	err(1, "failed to open the console");
1049}
1050