xref: /openbsd-src/usr.sbin/vmctl/main.c (revision 0a9d031fce78c0ebce0995b311938b1c87b1e208)
1*0a9d031fSclaudio /*	$OpenBSD: main.c,v 1.84 2024/11/21 13:39:34 claudio Exp $	*/
2eb8d1da1Sreyk 
3eb8d1da1Sreyk /*
4eb8d1da1Sreyk  * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
5eb8d1da1Sreyk  *
6eb8d1da1Sreyk  * Permission to use, copy, modify, and distribute this software for any
7eb8d1da1Sreyk  * purpose with or without fee is hereby granted, provided that the above
8eb8d1da1Sreyk  * copyright notice and this permission notice appear in all copies.
9eb8d1da1Sreyk  *
10eb8d1da1Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11eb8d1da1Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12eb8d1da1Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13eb8d1da1Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14eb8d1da1Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15eb8d1da1Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16eb8d1da1Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17eb8d1da1Sreyk  */
18eb8d1da1Sreyk 
19eb8d1da1Sreyk #include <sys/types.h>
20eb8d1da1Sreyk #include <sys/socket.h>
21eb8d1da1Sreyk #include <sys/queue.h>
22eb8d1da1Sreyk #include <sys/un.h>
23eb8d1da1Sreyk 
24eb8d1da1Sreyk #include <err.h>
25eb8d1da1Sreyk #include <errno.h>
26eb8d1da1Sreyk #include <stdio.h>
27eb8d1da1Sreyk #include <stdlib.h>
28eb8d1da1Sreyk #include <stdint.h>
29eb8d1da1Sreyk #include <limits.h>
30eb8d1da1Sreyk #include <string.h>
314d2a1fb2Sreyk #include <syslog.h>
32eb8d1da1Sreyk #include <unistd.h>
33e2ceadc1Sreyk #include <fcntl.h>
34eb8d1da1Sreyk #include <util.h>
35eb8d1da1Sreyk #include <imsg.h>
36eb8d1da1Sreyk 
37eb8d1da1Sreyk #include "vmd.h"
384d2a1fb2Sreyk #include "virtio.h"
398213a70aSreyk #include "proc.h"
40eb8d1da1Sreyk #include "vmctl.h"
41eb8d1da1Sreyk 
42e2ceadc1Sreyk #define RAW_FMT		"raw"
43e2ceadc1Sreyk #define QCOW2_FMT	"qcow2"
44e2ceadc1Sreyk 
45eb8d1da1Sreyk static const char	*socket_name = SOCKET_NAME;
46eb8d1da1Sreyk static int		 ctl_sock = -1;
47fe102934Sreyk static int		 tty_autoconnect = 0;
48986b002cSmlarkin int			 stat_rflag;
49eb8d1da1Sreyk 
50eb8d1da1Sreyk __dead void	 usage(void);
51eb8d1da1Sreyk __dead void	 ctl_usage(struct ctl_command *);
52eb8d1da1Sreyk 
5395009bb4Sreyk int		 ctl_console(struct parse_result *, int, char *[]);
544d2a1fb2Sreyk int		 ctl_convert(const char *, const char *, int, size_t);
55eb8d1da1Sreyk int		 ctl_create(struct parse_result *, int, char *[]);
56eb8d1da1Sreyk int		 ctl_load(struct parse_result *, int, char *[]);
573afb90b0Sreyk int		 ctl_log(struct parse_result *, int, char *[]);
581f7fe034Sreyk int		 ctl_reload(struct parse_result *, int, char *[]);
591f7fe034Sreyk int		 ctl_reset(struct parse_result *, int, char *[]);
60eb8d1da1Sreyk int		 ctl_start(struct parse_result *, int, char *[]);
61eb8d1da1Sreyk int		 ctl_status(struct parse_result *, int, char *[]);
62eb8d1da1Sreyk int		 ctl_stop(struct parse_result *, int, char *[]);
63583f6618Sclaudio int		 ctl_waitfor(struct parse_result *, int, char *[]);
6452e954a3Spd int		 ctl_pause(struct parse_result *, int, char *[]);
6552e954a3Spd int		 ctl_unpause(struct parse_result *, int, char *[]);
66eed20f3bSpd int		 ctl_send(struct parse_result *, int, char *[]);
67eed20f3bSpd int		 ctl_receive(struct parse_result *, int, char *[]);
68eb8d1da1Sreyk 
69eb8d1da1Sreyk struct ctl_command ctl_commands[] = {
707ad0becaSreyk 	{ "console",	CMD_CONSOLE,	ctl_console,	"id" },
7173613953Sreyk 	{ "create",	CMD_CREATE,	ctl_create,
72bd4b76f8Sreyk 		"[-b base | -i disk] [-s size] disk", 1 },
73cb84e044Sjmc 	{ "load",	CMD_LOAD,	ctl_load,	"filename" },
74cb84e044Sjmc 	{ "log",	CMD_LOG,	ctl_log,	"[brief | verbose]" },
75cb84e044Sjmc 	{ "pause",	CMD_PAUSE,	ctl_pause,	"id" },
76cb84e044Sjmc 	{ "receive",	CMD_RECEIVE,	ctl_receive,	"name" ,	1},
771f7fe034Sreyk 	{ "reload",	CMD_RELOAD,	ctl_reload,	"" },
78cb84e044Sjmc 	{ "reset",	CMD_RESET,	ctl_reset,	"[all | switches | vms]" },
79cb84e044Sjmc 	{ "send",	CMD_SEND,	ctl_send,	"id",	1},
808c44bfcaSphessler 	{ "show",	CMD_STATUS,	ctl_status,	"[id]" },
81bd4b76f8Sreyk 	{ "start",	CMD_START,	ctl_start,
82cb84e044Sjmc 	    "[-cL] [-B device] [-b path] [-d disk] [-i count]\n"
83b848b186Sdv 	    "\t\t[-m size] [-n switch] [-r path] [-t name] id | name",	1},
84986b002cSmlarkin 	{ "status",	CMD_STATUS,	ctl_status,	"[-r] [id]" },
85bd4b76f8Sreyk 	{ "stop",	CMD_STOP,	ctl_stop,	"[-fw] [id | -a]" },
8652e954a3Spd 	{ "unpause",	CMD_UNPAUSE,	ctl_unpause,	"id" },
87cb84e044Sjmc 	{ "wait",	CMD_WAITFOR,	ctl_waitfor,	"id" },
88eb8d1da1Sreyk 	{ NULL }
89eb8d1da1Sreyk };
90eb8d1da1Sreyk 
91eb8d1da1Sreyk __dead void
92eb8d1da1Sreyk usage(void)
93eb8d1da1Sreyk {
94eb8d1da1Sreyk 	extern char	*__progname;
95eb8d1da1Sreyk 
962a9bad78Stb 	fprintf(stderr, "usage:\t%s [-v] command [arg ...]\n", __progname);
97409e257aSjmc 
98eb8d1da1Sreyk 	exit(1);
99eb8d1da1Sreyk }
100eb8d1da1Sreyk 
101eb8d1da1Sreyk __dead void
102eb8d1da1Sreyk ctl_usage(struct ctl_command *ctl)
103eb8d1da1Sreyk {
104eb8d1da1Sreyk 	extern char	*__progname;
105eb8d1da1Sreyk 
1064d2a1fb2Sreyk 	fprintf(stderr, "usage:\t%s [-v] %s %s\n", __progname,
107eb8d1da1Sreyk 	    ctl->name, ctl->usage);
108eb8d1da1Sreyk 	exit(1);
109eb8d1da1Sreyk }
110eb8d1da1Sreyk 
111eb8d1da1Sreyk int
112eb8d1da1Sreyk main(int argc, char *argv[])
113eb8d1da1Sreyk {
1144d2a1fb2Sreyk 	int	 ch, verbose = 1;
115eb8d1da1Sreyk 
1164d2a1fb2Sreyk 	while ((ch = getopt(argc, argv, "v")) != -1) {
117eb8d1da1Sreyk 		switch (ch) {
1184d2a1fb2Sreyk 		case 'v':
1194d2a1fb2Sreyk 			verbose = 2;
1204d2a1fb2Sreyk 			break;
121eb8d1da1Sreyk 		default:
122eb8d1da1Sreyk 			usage();
123eb8d1da1Sreyk 			/* NOTREACHED */
124eb8d1da1Sreyk 		}
125eb8d1da1Sreyk 	}
126eb8d1da1Sreyk 	argc -= optind;
127eb8d1da1Sreyk 	argv += optind;
128eb8d1da1Sreyk 	optreset = 1;
1298ff019a1Sreyk 	optind = 1;
130eb8d1da1Sreyk 
131eb8d1da1Sreyk 	if (argc < 1)
132eb8d1da1Sreyk 		usage();
133eb8d1da1Sreyk 
1344d2a1fb2Sreyk 	log_init(verbose, LOG_DAEMON);
1354d2a1fb2Sreyk 
136eb8d1da1Sreyk 	return (parse(argc, argv));
137eb8d1da1Sreyk }
138eb8d1da1Sreyk 
139eb8d1da1Sreyk int
140eb8d1da1Sreyk parse(int argc, char *argv[])
141eb8d1da1Sreyk {
142eb8d1da1Sreyk 	struct ctl_command	*ctl = NULL;
143eb8d1da1Sreyk 	struct parse_result	 res;
144eb8d1da1Sreyk 	int			 i;
145eb8d1da1Sreyk 
146eb8d1da1Sreyk 	memset(&res, 0, sizeof(res));
147eb8d1da1Sreyk 	res.nifs = -1;
148eb8d1da1Sreyk 
149eb8d1da1Sreyk 	for (i = 0; ctl_commands[i].name != NULL; i++) {
150eb8d1da1Sreyk 		if (strncmp(ctl_commands[i].name,
151eb8d1da1Sreyk 		    argv[0], strlen(argv[0])) == 0) {
152eb8d1da1Sreyk 			if (ctl != NULL) {
153eb8d1da1Sreyk 				fprintf(stderr,
154eb8d1da1Sreyk 				    "ambiguous argument: %s\n", argv[0]);
155eb8d1da1Sreyk 				usage();
156eb8d1da1Sreyk 			}
157eb8d1da1Sreyk 			ctl = &ctl_commands[i];
158eb8d1da1Sreyk 		}
159eb8d1da1Sreyk 	}
160eb8d1da1Sreyk 
161eb8d1da1Sreyk 	if (ctl == NULL) {
162eb8d1da1Sreyk 		fprintf(stderr, "unknown argument: %s\n", argv[0]);
163eb8d1da1Sreyk 		usage();
164eb8d1da1Sreyk 	}
165eb8d1da1Sreyk 
166eb8d1da1Sreyk 	res.action = ctl->action;
167eb8d1da1Sreyk 	res.ctl = ctl;
168eb8d1da1Sreyk 
169eb8d1da1Sreyk 	if (!ctl->has_pledge) {
170eb8d1da1Sreyk 		/* pledge(2) default if command doesn't have its own pledge */
1716955bc4dSccardenas 		if (pledge("stdio rpath exec unix getpw unveil", NULL) == -1)
172eb8d1da1Sreyk 			err(1, "pledge");
173eb8d1da1Sreyk 	}
174eb8d1da1Sreyk 	if (ctl->main(&res, argc, argv) != 0)
175583f6618Sclaudio 		exit(1);
176eb8d1da1Sreyk 
177eb8d1da1Sreyk 	if (ctl_sock != -1) {
178eb8d1da1Sreyk 		close(ibuf->fd);
179eb8d1da1Sreyk 		free(ibuf);
180eb8d1da1Sreyk 	}
181eb8d1da1Sreyk 
182eb8d1da1Sreyk 	return (0);
183eb8d1da1Sreyk }
184eb8d1da1Sreyk 
185eb8d1da1Sreyk int
186eb8d1da1Sreyk vmmaction(struct parse_result *res)
187eb8d1da1Sreyk {
188eb8d1da1Sreyk 	struct sockaddr_un	 sun;
189eb8d1da1Sreyk 	struct imsg		 imsg;
190eb8d1da1Sreyk 	int			 done = 0;
191eb8d1da1Sreyk 	int			 n;
192fe102934Sreyk 	int			 ret, action;
1933be9785fSreyk 	unsigned int		 flags;
194eb8d1da1Sreyk 
195eb8d1da1Sreyk 	if (ctl_sock == -1) {
1962d650259Sbenno 		if (unveil(SOCKET_NAME, "w") == -1)
197bc5a8259Sbeck 			err(1, "unveil %s", SOCKET_NAME);
198fe102934Sreyk 		if ((ctl_sock = socket(AF_UNIX,
199fe102934Sreyk 		    SOCK_STREAM|SOCK_CLOEXEC, 0)) == -1)
200eb8d1da1Sreyk 			err(1, "socket");
201eb8d1da1Sreyk 
2025eaf1adbSguenther 		memset(&sun, 0, sizeof(sun));
203eb8d1da1Sreyk 		sun.sun_family = AF_UNIX;
204eb8d1da1Sreyk 		strlcpy(sun.sun_path, socket_name, sizeof(sun.sun_path));
205eb8d1da1Sreyk 
206eb8d1da1Sreyk 		if (connect(ctl_sock,
207eb8d1da1Sreyk 		    (struct sockaddr *)&sun, sizeof(sun)) == -1)
208eb8d1da1Sreyk 			err(1, "connect: %s", socket_name);
209eb8d1da1Sreyk 
210eb8d1da1Sreyk 		if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
211eb8d1da1Sreyk 			err(1, "malloc");
212*0a9d031fSclaudio 		if (imsgbuf_init(ibuf, ctl_sock) == -1)
213*0a9d031fSclaudio 			err(1, "imsgbuf_init");
214*0a9d031fSclaudio 		imsgbuf_allow_fdpass(ibuf);
215eb8d1da1Sreyk 	}
216eb8d1da1Sreyk 
217eb8d1da1Sreyk 	switch (res->action) {
218eb8d1da1Sreyk 	case CMD_START:
219eb1cd41dSreyk 		ret = vm_start(res->id, res->name, res->size, res->nifs,
220f224f92aSccardenas 		    res->nets, res->ndisks, res->disks, res->disktypes,
221917458a3Sclaudio 		    res->path, res->isopath, res->instance, res->bootdevice);
222eb8d1da1Sreyk 		if (ret) {
223eb8d1da1Sreyk 			errno = ret;
224eb8d1da1Sreyk 			err(1, "start VM operation failed");
225eb8d1da1Sreyk 		}
226eb8d1da1Sreyk 		break;
227eb8d1da1Sreyk 	case CMD_STOP:
2283be9785fSreyk 		terminate_vm(res->id, res->name, res->flags);
229eb8d1da1Sreyk 		break;
230eb8d1da1Sreyk 	case CMD_STATUS:
23195009bb4Sreyk 	case CMD_CONSOLE:
232e0b12962Sreyk 	case CMD_STOPALL:
233e0b12962Sreyk 		get_info_vm(res->id, res->name, res->action, res->flags);
234eb8d1da1Sreyk 		break;
235eb8d1da1Sreyk 	case CMD_LOAD:
236008065a5Sreyk 		imsg_compose(ibuf, IMSG_VMDOP_LOAD, 0, 0, -1,
2371f7fe034Sreyk 		    res->path, strlen(res->path) + 1);
2381f7fe034Sreyk 		break;
2393afb90b0Sreyk 	case CMD_LOG:
2403afb90b0Sreyk 		imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1,
2413afb90b0Sreyk 		    &res->verbose, sizeof(res->verbose));
2423afb90b0Sreyk 		break;
2431f7fe034Sreyk 	case CMD_RELOAD:
2441f7fe034Sreyk 		imsg_compose(ibuf, IMSG_VMDOP_RELOAD, 0, 0, -1, NULL, 0);
2451f7fe034Sreyk 		break;
2461f7fe034Sreyk 	case CMD_RESET:
2471f7fe034Sreyk 		imsg_compose(ibuf, IMSG_CTL_RESET, 0, 0, -1,
2481f7fe034Sreyk 		    &res->mode, sizeof(res->mode));
249008065a5Sreyk 		break;
250583f6618Sclaudio 	case CMD_WAITFOR:
251583f6618Sclaudio 		waitfor_vm(res->id, res->name);
252583f6618Sclaudio 		break;
25352e954a3Spd 	case CMD_PAUSE:
25452e954a3Spd 		pause_vm(res->id, res->name);
25552e954a3Spd 		break;
25652e954a3Spd 	case CMD_UNPAUSE:
25752e954a3Spd 		unpause_vm(res->id, res->name);
25852e954a3Spd 		break;
259eed20f3bSpd 	case CMD_SEND:
260eed20f3bSpd 		send_vm(res->id, res->name);
261eed20f3bSpd 		done = 1;
2620410d4edSpd 		ret = 0;
263eed20f3bSpd 		break;
264eed20f3bSpd 	case CMD_RECEIVE:
265eed20f3bSpd 		vm_receive(res->id, res->name);
266eed20f3bSpd 		break;
267008065a5Sreyk 	case CMD_CREATE:
268eb8d1da1Sreyk 	case NONE:
2694d2a1fb2Sreyk 		/* The action is not expected here */
2704d2a1fb2Sreyk 		errx(1, "invalid action %u", res->action);
271eb8d1da1Sreyk 		break;
272eb8d1da1Sreyk 	}
273eb8d1da1Sreyk 
274fe102934Sreyk 	action = res->action;
2753be9785fSreyk 	flags = res->flags;
276fe102934Sreyk 	parse_free(res);
277fe102934Sreyk 
278dd7efffeSclaudio 	if (imsgbuf_flush(ibuf) == -1)
279eb8d1da1Sreyk 		err(1, "write error");
280eb8d1da1Sreyk 
281eb8d1da1Sreyk 	while (!done) {
282668e5ba9Sclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
283ef2e27a1Sclaudio 			err(1, "read error");
284eb8d1da1Sreyk 		if (n == 0)
285eb8d1da1Sreyk 			errx(1, "pipe closed");
286eb8d1da1Sreyk 
287eb8d1da1Sreyk 		while (!done) {
288eb8d1da1Sreyk 			if ((n = imsg_get(ibuf, &imsg)) == -1)
289eb8d1da1Sreyk 				errx(1, "imsg_get error");
290eb8d1da1Sreyk 			if (n == 0)
291eb8d1da1Sreyk 				break;
292eb8d1da1Sreyk 
293eb8d1da1Sreyk 			if (imsg.hdr.type == IMSG_CTL_FAIL) {
294d1866b5cSreyk 				if (IMSG_DATA_SIZE(&imsg) == sizeof(ret))
295d1866b5cSreyk 					memcpy(&ret, imsg.data, sizeof(ret));
296d1866b5cSreyk 				else
297d1866b5cSreyk 					ret = 0;
298d1866b5cSreyk 				if (ret != 0) {
299eb8d1da1Sreyk 					errno = ret;
300b55cbc1dSreyk 					err(1, "command failed");
301b55cbc1dSreyk 				} else
302b55cbc1dSreyk 					errx(1, "command failed");
303eb8d1da1Sreyk 			}
304eb8d1da1Sreyk 
305eb8d1da1Sreyk 			ret = 0;
306fe102934Sreyk 			switch (action) {
307eb8d1da1Sreyk 			case CMD_START:
30876031b44Sreyk 				done = vm_start_complete(&imsg, &ret,
309fe102934Sreyk 				    tty_autoconnect);
310eb8d1da1Sreyk 				break;
311583f6618Sclaudio 			case CMD_WAITFOR:
312583f6618Sclaudio 				flags = VMOP_WAIT;
313583f6618Sclaudio 				/* FALLTHROUGH */
314eb8d1da1Sreyk 			case CMD_STOP:
3153be9785fSreyk 				done = terminate_vm_complete(&imsg, &ret,
3163be9785fSreyk 				    flags);
317eb8d1da1Sreyk 				break;
31895009bb4Sreyk 			case CMD_CONSOLE:
319eb8d1da1Sreyk 			case CMD_STATUS:
320e0b12962Sreyk 			case CMD_STOPALL:
321eb8d1da1Sreyk 				done = add_info(&imsg, &ret);
322eb8d1da1Sreyk 				break;
32352e954a3Spd 			case CMD_PAUSE:
32452e954a3Spd 				done = pause_vm_complete(&imsg, &ret);
32552e954a3Spd 				break;
326eed20f3bSpd 			case CMD_RECEIVE:
327eed20f3bSpd 				done = vm_start_complete(&imsg, &ret, 0);
328eed20f3bSpd 				break;
32952e954a3Spd 			case CMD_UNPAUSE:
33052e954a3Spd 				done = unpause_vm_complete(&imsg, &ret);
33152e954a3Spd 				break;
332eb8d1da1Sreyk 			default:
333eb8d1da1Sreyk 				done = 1;
334eb8d1da1Sreyk 				break;
335eb8d1da1Sreyk 			}
336eb8d1da1Sreyk 
337eb8d1da1Sreyk 			imsg_free(&imsg);
338eb8d1da1Sreyk 		}
339eb8d1da1Sreyk 	}
340eb8d1da1Sreyk 
341583f6618Sclaudio 	if (ret)
342583f6618Sclaudio 		return (1);
343583f6618Sclaudio 	else
344eb8d1da1Sreyk 		return (0);
345eb8d1da1Sreyk }
346eb8d1da1Sreyk 
347eb8d1da1Sreyk void
348eb8d1da1Sreyk parse_free(struct parse_result *res)
349eb8d1da1Sreyk {
350eb8d1da1Sreyk 	size_t	 i;
351eb8d1da1Sreyk 
352eb8d1da1Sreyk 	free(res->name);
353eb8d1da1Sreyk 	free(res->path);
35495ab188fSccardenas 	free(res->isopath);
3556429e633Sreyk 	free(res->instance);
356eb8d1da1Sreyk 	for (i = 0; i < res->ndisks; i++)
357eb8d1da1Sreyk 		free(res->disks[i]);
358eb8d1da1Sreyk 	free(res->disks);
359f224f92aSccardenas 	free(res->disktypes);
360eb8d1da1Sreyk 	memset(res, 0, sizeof(*res));
361eb8d1da1Sreyk }
362eb8d1da1Sreyk 
363eb8d1da1Sreyk int
364eb8d1da1Sreyk parse_ifs(struct parse_result *res, char *word, int val)
365eb8d1da1Sreyk {
366eb8d1da1Sreyk 	const char	*error;
367eb8d1da1Sreyk 
368eb8d1da1Sreyk 	if (word != NULL) {
3690f0fb1d3Skn 		val = strtonum(word, 1, INT_MAX, &error);
370eb8d1da1Sreyk 		if (error != NULL)  {
3710f0fb1d3Skn 			warnx("count is %s: %s", error, word);
372eb8d1da1Sreyk 			return (-1);
373eb8d1da1Sreyk 		}
374eb8d1da1Sreyk 	}
375eb8d1da1Sreyk 	res->nifs = val;
376b657d36cSreyk 
377b657d36cSreyk 	return (0);
378b657d36cSreyk }
379b657d36cSreyk 
380b657d36cSreyk int
381b657d36cSreyk parse_network(struct parse_result *res, char *word)
382b657d36cSreyk {
383b657d36cSreyk 	char		**nets;
384b657d36cSreyk 	char		*s;
385b657d36cSreyk 
386b657d36cSreyk 	if ((nets = reallocarray(res->nets, res->nnets + 1,
387b657d36cSreyk 	    sizeof(char *))) == NULL) {
388b657d36cSreyk 		warn("reallocarray");
389b657d36cSreyk 		return (-1);
390b657d36cSreyk 	}
391b657d36cSreyk 	if ((s = strdup(word)) == NULL) {
392b657d36cSreyk 		warn("strdup");
393b657d36cSreyk 		return (-1);
394b657d36cSreyk 	}
395b657d36cSreyk 	nets[res->nnets] = s;
396b657d36cSreyk 	res->nets = nets;
397b657d36cSreyk 	res->nnets++;
398b657d36cSreyk 
399eb8d1da1Sreyk 	return (0);
400eb8d1da1Sreyk }
401eb8d1da1Sreyk 
402ead1b146Sdv void
403ead1b146Sdv parse_size(struct parse_result *res, char *word, const char *type)
404eb8d1da1Sreyk {
405e545c54cSdv 	char		 result[FMT_SCALED_STRSIZE];
4064a2cfa82Skn 	long long 	 val = 0;
4074a2cfa82Skn 
408eb8d1da1Sreyk 	if (word != NULL) {
409ead1b146Sdv 		if (scan_scaled(word, &val) != 0)
410ead1b146Sdv 			err(1, "invalid %s size: %s", type, word);
411eb8d1da1Sreyk 	}
412eb8d1da1Sreyk 
413ead1b146Sdv 	if (val < (1024 * 1024))
414ead1b146Sdv 		errx(1, "%s size must be at least 1MB", type);
415eb8d1da1Sreyk 
416ead1b146Sdv 	if (strcmp("memory", type) == 0 && val > VMM_MAX_VM_MEM_SIZE) {
417e545c54cSdv 		if (fmt_scaled(VMM_MAX_VM_MEM_SIZE, result) == 0)
418ead1b146Sdv 			errx(1, "memory size too large (limit is %s)", result);
419e545c54cSdv 		else
420ead1b146Sdv 			errx(1, "memory size too large");
421e545c54cSdv 	}
422e545c54cSdv 
423e545c54cSdv 	/* Round down to the megabyte. */
424e545c54cSdv 	res->size = (val / (1024 * 1024)) * (1024 * 1024);
425e545c54cSdv 
426e545c54cSdv 	if (res->size != (size_t)val) {
427e545c54cSdv 		if (fmt_scaled(res->size, result) == 0)
428ead1b146Sdv 			warnx("%s size rounded to %s", type, result);
429e545c54cSdv 		else
430ead1b146Sdv 			warnx("%s size rounded to %zuB", type, res->size);
431e545c54cSdv 	}
432eb8d1da1Sreyk }
433eb8d1da1Sreyk 
434eb8d1da1Sreyk int
435e2ceadc1Sreyk parse_disktype(const char *s, const char **ret)
436f224f92aSccardenas {
437e2ceadc1Sreyk 	char		 buf[BUFSIZ];
438e2ceadc1Sreyk 	const char	*ext;
439e2ceadc1Sreyk 	int		 fd;
440e2ceadc1Sreyk 	ssize_t		 len;
441e2ceadc1Sreyk 
442f224f92aSccardenas 	*ret = s;
443e2ceadc1Sreyk 
444e2ceadc1Sreyk 	/* Try to parse the explicit format (qcow2:disk.qc2) */
445e2ceadc1Sreyk 	if (strstr(s, RAW_FMT) == s && *(s + strlen(RAW_FMT)) == ':') {
446e2ceadc1Sreyk 		*ret = s + strlen(RAW_FMT) + 1;
447e2ceadc1Sreyk 		return (VMDF_RAW);
448f224f92aSccardenas 	}
449e2ceadc1Sreyk 	if (strstr(s, QCOW2_FMT) == s && *(s + strlen(QCOW2_FMT)) == ':') {
450e2ceadc1Sreyk 		*ret = s + strlen(QCOW2_FMT) + 1;
451e2ceadc1Sreyk 		return (VMDF_QCOW2);
452f224f92aSccardenas 	}
453e2ceadc1Sreyk 
454e2ceadc1Sreyk 	/* Or try to derive the format from the file signature */
455e2ceadc1Sreyk 	if ((fd = open(s, O_RDONLY)) != -1) {
456e2ceadc1Sreyk 		len = read(fd, buf, sizeof(buf));
457e2ceadc1Sreyk 		close(fd);
458e2ceadc1Sreyk 
459e2ceadc1Sreyk 		if (len >= (ssize_t)strlen(VM_MAGIC_QCOW) &&
460e2ceadc1Sreyk 		    strncmp(buf, VM_MAGIC_QCOW,
461e2ceadc1Sreyk 		    strlen(VM_MAGIC_QCOW)) == 0) {
462e2ceadc1Sreyk 			/* Return qcow2, the version will be checked later */
463e2ceadc1Sreyk 			return (VMDF_QCOW2);
464e2ceadc1Sreyk 		}
465e2ceadc1Sreyk 	}
466e2ceadc1Sreyk 
467e2ceadc1Sreyk 	/*
468e2ceadc1Sreyk 	 * Use the extension as a last option.  This is needed for
469e2ceadc1Sreyk 	 * 'vmctl create' as the file, and the signature, doesn't
470e2ceadc1Sreyk 	 * exist yet.
471e2ceadc1Sreyk 	 */
472e2ceadc1Sreyk 	if ((ext = strrchr(s, '.')) != NULL && *(++ext) != '\0') {
473e2ceadc1Sreyk 		if (strcasecmp(ext, RAW_FMT) == 0)
474e2ceadc1Sreyk 			return (VMDF_RAW);
475e2ceadc1Sreyk 		else if (strcasecmp(ext, QCOW2_FMT) == 0)
476e2ceadc1Sreyk 			return (VMDF_QCOW2);
477e2ceadc1Sreyk 	}
478e2ceadc1Sreyk 
479e2ceadc1Sreyk 	/* Fallback to raw */
480e2ceadc1Sreyk 	return (VMDF_RAW);
481f224f92aSccardenas }
482f224f92aSccardenas 
483f224f92aSccardenas int
484f224f92aSccardenas parse_disk(struct parse_result *res, char *word, int type)
485eb8d1da1Sreyk {
486eb8d1da1Sreyk 	char		**disks;
487f224f92aSccardenas 	int		*disktypes;
488eb8d1da1Sreyk 	char		*s;
489eb8d1da1Sreyk 
490eb8d1da1Sreyk 	if ((disks = reallocarray(res->disks, res->ndisks + 1,
491eb8d1da1Sreyk 	    sizeof(char *))) == NULL) {
492eb8d1da1Sreyk 		warn("reallocarray");
493eb8d1da1Sreyk 		return (-1);
494eb8d1da1Sreyk 	}
495f224f92aSccardenas 	if ((disktypes = reallocarray(res->disktypes, res->ndisks + 1,
496f224f92aSccardenas 	    sizeof(int))) == NULL) {
497f224f92aSccardenas 		warn("reallocarray");
498f224f92aSccardenas 		return -1;
499f224f92aSccardenas 	}
500eb8d1da1Sreyk 	if ((s = strdup(word)) == NULL) {
501eb8d1da1Sreyk 		warn("strdup");
502eb8d1da1Sreyk 		return (-1);
503eb8d1da1Sreyk 	}
504eb8d1da1Sreyk 	disks[res->ndisks] = s;
505f224f92aSccardenas 	disktypes[res->ndisks] = type;
506eb8d1da1Sreyk 	res->disks = disks;
507f224f92aSccardenas 	res->disktypes = disktypes;
508eb8d1da1Sreyk 	res->ndisks++;
509eb8d1da1Sreyk 
510eb8d1da1Sreyk 	return (0);
511eb8d1da1Sreyk }
512eb8d1da1Sreyk 
513eb8d1da1Sreyk int
5144100d478Sjasper parse_vmid(struct parse_result *res, char *word, int needname)
515eb8d1da1Sreyk {
516eb8d1da1Sreyk 	const char	*error;
51700b52b3dSreyk 	uint32_t	 id;
518eb8d1da1Sreyk 
51900b52b3dSreyk 	if (word == NULL) {
52000b52b3dSreyk 		warnx("missing vmid argument");
52100b52b3dSreyk 		return (-1);
52200b52b3dSreyk 	}
523e0b12962Sreyk 	if (*word == '-') {
524e0b12962Sreyk 		/* don't print a warning to allow command line options */
525e0b12962Sreyk 		return (-1);
526e0b12962Sreyk 	}
527eb8d1da1Sreyk 	id = strtonum(word, 0, UINT32_MAX, &error);
52859fb7762Sreyk 	if (error == NULL) {
5294100d478Sjasper 		if (needname) {
530eed20f3bSpd 			warnx("invalid vm name");
531eed20f3bSpd 			return (-1);
532eed20f3bSpd 		} else {
5334100d478Sjasper 			res->id = id;
5344100d478Sjasper 			res->name = NULL;
5354100d478Sjasper 		}
5364100d478Sjasper 	} else {
537eed20f3bSpd 		if (strlen(word) >= VMM_MAX_NAME_LEN) {
538eed20f3bSpd 			warnx("name too long");
539eed20f3bSpd 			return (-1);
540eed20f3bSpd 		}
541eed20f3bSpd 		res->id = 0;
542eed20f3bSpd 		if ((res->name = strdup(word)) == NULL)
543eed20f3bSpd 			errx(1, "strdup");
544eed20f3bSpd 	}
545eed20f3bSpd 
546eed20f3bSpd 	return (0);
547eed20f3bSpd }
548eed20f3bSpd 
549eed20f3bSpd int
5506429e633Sreyk parse_instance(struct parse_result *res, char *word)
5516429e633Sreyk {
5526429e633Sreyk 	if (strlen(word) >= VMM_MAX_NAME_LEN) {
5536429e633Sreyk 		warnx("instance vm name too long");
5546429e633Sreyk 		return (-1);
5556429e633Sreyk 	}
5566429e633Sreyk 	res->id = 0;
5576429e633Sreyk 	if ((res->instance = strdup(word)) == NULL)
5586429e633Sreyk 		errx(1, "strdup");
5596429e633Sreyk 
5606429e633Sreyk 	return (0);
5616429e633Sreyk }
5626429e633Sreyk 
5636429e633Sreyk int
564eb8d1da1Sreyk ctl_create(struct parse_result *res, int argc, char *argv[])
565eb8d1da1Sreyk {
566e2ceadc1Sreyk 	int		 ch, ret, type;
567bd4b76f8Sreyk 	const char	*disk, *format, *base = NULL, *input = NULL;
568eb8d1da1Sreyk 
5694d2a1fb2Sreyk 	while ((ch = getopt(argc, argv, "b:i:s:")) != -1) {
570eb8d1da1Sreyk 		switch (ch) {
57173613953Sreyk 		case 'b':
57273613953Sreyk 			base = optarg;
57373613953Sreyk 			break;
5744d2a1fb2Sreyk 		case 'i':
5754d2a1fb2Sreyk 			input = optarg;
5764d2a1fb2Sreyk 			break;
5774d2a1fb2Sreyk 		case 's':
578ead1b146Sdv 			parse_size(res, optarg, "disk");
5794d2a1fb2Sreyk 			break;
580eb8d1da1Sreyk 		default:
581eb8d1da1Sreyk 			ctl_usage(res->ctl);
582eb8d1da1Sreyk 			/* NOTREACHED */
583eb8d1da1Sreyk 		}
584eb8d1da1Sreyk 	}
585c1e58e1aSkn 	argc -= optind;
586c1e58e1aSkn 	argv += optind;
5874d2a1fb2Sreyk 
5882b3f5770Skn 	if (argc != 1)
589b0b31ffcSkn 		ctl_usage(res->ctl);
590b0b31ffcSkn 
591bd4b76f8Sreyk 	type = parse_disktype(argv[0], &disk);
592bd4b76f8Sreyk 
5933538c82eSdv 	if (pledge("stdio rpath wpath cpath", NULL) == -1)
594bd4b76f8Sreyk 		err(1, "pledge");
595bd4b76f8Sreyk 
5964d2a1fb2Sreyk 	if (input) {
5974d2a1fb2Sreyk 		if (base && input)
5984d2a1fb2Sreyk 			errx(1, "conflicting -b and -i arguments");
5994d2a1fb2Sreyk 		return ctl_convert(input, disk, type, res->size);
6004d2a1fb2Sreyk 	}
6014d2a1fb2Sreyk 
60273613953Sreyk 	if (base && type != VMDF_QCOW2)
60373613953Sreyk 		errx(1, "base images require qcow2 disk format");
60473613953Sreyk 	if (res->size == 0 && !base) {
60573613953Sreyk 		fprintf(stderr, "could not create %s: missing size argument\n",
60673613953Sreyk 		    disk);
607eb8d1da1Sreyk 		ctl_usage(res->ctl);
608eb8d1da1Sreyk 	}
609e2ceadc1Sreyk 
6104d2a1fb2Sreyk 	if ((ret = create_imagefile(type, disk, base, res->size, &format)) != 0) {
611eb8d1da1Sreyk 		errno = ret;
612eb8d1da1Sreyk 		err(1, "create imagefile operation failed");
613eb8d1da1Sreyk 	} else
614e2ceadc1Sreyk 		warnx("%s imagefile created", format);
615e2ceadc1Sreyk 
616eb8d1da1Sreyk 	return (0);
617eb8d1da1Sreyk }
618eb8d1da1Sreyk 
619eb8d1da1Sreyk int
6204d2a1fb2Sreyk ctl_convert(const char *srcfile, const char *dstfile, int dsttype, size_t dstsize)
6214d2a1fb2Sreyk {
6224d2a1fb2Sreyk 	struct {
6234d2a1fb2Sreyk 		int			 fd;
6244d2a1fb2Sreyk 		int			 type;
6254d2a1fb2Sreyk 		struct virtio_backing	 file;
6264d2a1fb2Sreyk 		const char		*disk;
6274d2a1fb2Sreyk 		off_t			 size;
6284d2a1fb2Sreyk 	}	 src, dst;
6294d2a1fb2Sreyk 	int		 ret;
6304d2a1fb2Sreyk 	const char	*format, *errstr = NULL;
6314d2a1fb2Sreyk 	uint8_t		*buf = NULL, *zerobuf = NULL;
6324d2a1fb2Sreyk 	size_t		 buflen;
6334d2a1fb2Sreyk 	ssize_t		 len, rlen;
6344d2a1fb2Sreyk 	off_t		 off;
6354d2a1fb2Sreyk 
6364d2a1fb2Sreyk 	memset(&src, 0, sizeof(src));
6374d2a1fb2Sreyk 	memset(&dst, 0, sizeof(dst));
6384d2a1fb2Sreyk 
6394d2a1fb2Sreyk 	src.type = parse_disktype(srcfile, &src.disk);
6404d2a1fb2Sreyk 	dst.type = dsttype;
6414d2a1fb2Sreyk 	dst.disk = dstfile;
6424d2a1fb2Sreyk 
6434d2a1fb2Sreyk 	if ((src.fd = open_imagefile(src.type, src.disk, O_RDONLY,
6444d2a1fb2Sreyk 	    &src.file, &src.size)) == -1) {
6454d2a1fb2Sreyk 		errstr = "failed to open source image file";
6464d2a1fb2Sreyk 		goto done;
6474d2a1fb2Sreyk 	}
6484d2a1fb2Sreyk 
6494d2a1fb2Sreyk 	if (dstsize == 0)
6504d2a1fb2Sreyk 		dstsize = src.size;
6514d2a1fb2Sreyk 	if (dstsize < (size_t)src.size) {
6524d2a1fb2Sreyk 		errstr = "size cannot be smaller than input disk size";
6534d2a1fb2Sreyk 		goto done;
6544d2a1fb2Sreyk 	}
6554d2a1fb2Sreyk 
6564d2a1fb2Sreyk 	/* align to megabytes */
65762df93eeSreyk 	dst.size = ALIGNSZ(dstsize, 1048576);
6584d2a1fb2Sreyk 
6597ac3f975Syasuoka 	if ((ret = create_imagefile(dst.type, dst.disk, NULL, dst.size,
6607ac3f975Syasuoka 	    &format)) != 0) {
6614d2a1fb2Sreyk 		errstr = "failed to create destination image file";
6624d2a1fb2Sreyk 		goto done;
6634d2a1fb2Sreyk 	}
6644d2a1fb2Sreyk 
6654d2a1fb2Sreyk 	if ((dst.fd = open_imagefile(dst.type, dst.disk, O_RDWR,
6664d2a1fb2Sreyk 	    &dst.file, &dst.size)) == -1) {
6674d2a1fb2Sreyk 		errstr = "failed to open destination image file";
6684d2a1fb2Sreyk 		goto done;
6694d2a1fb2Sreyk 	}
6704d2a1fb2Sreyk 
6714d2a1fb2Sreyk 	if (pledge("stdio", NULL) == -1)
6724d2a1fb2Sreyk 		err(1, "pledge");
6734d2a1fb2Sreyk 
6744d2a1fb2Sreyk 	/*
6754d2a1fb2Sreyk 	 * Use 64k buffers by default.  This could also be adjusted to
6764d2a1fb2Sreyk 	 * the backend cluster size.
6774d2a1fb2Sreyk 	 */
6784d2a1fb2Sreyk 	buflen = 1 << 16;
6794d2a1fb2Sreyk 	if ((buf = calloc(1, buflen)) == NULL ||
6804d2a1fb2Sreyk 	    (zerobuf = calloc(1, buflen)) == NULL) {
6814d2a1fb2Sreyk 		errstr = "failed to allocated buffers";
6824d2a1fb2Sreyk 		goto done;
6834d2a1fb2Sreyk 	}
6844d2a1fb2Sreyk 
6854d2a1fb2Sreyk 	for (off = 0; off < dst.size; off += len) {
6864d2a1fb2Sreyk 		/* Read input from the source image */
6874d2a1fb2Sreyk 		if (off < src.size) {
6884d2a1fb2Sreyk 			len = MIN((off_t)buflen, src.size - off);
6894d2a1fb2Sreyk 			if ((rlen = src.file.pread(src.file.p,
6904d2a1fb2Sreyk 			    buf, (size_t)len, off)) != len) {
6914d2a1fb2Sreyk 				errno = EIO;
6924d2a1fb2Sreyk 				errstr = "failed to read from source";
6934d2a1fb2Sreyk 				goto done;
6944d2a1fb2Sreyk 			}
6954d2a1fb2Sreyk 		} else
6964d2a1fb2Sreyk 			len = 0;
6974d2a1fb2Sreyk 
6984d2a1fb2Sreyk 		/* and pad the remaining bytes */
6994d2a1fb2Sreyk 		if (len < (ssize_t)buflen) {
7004d2a1fb2Sreyk 			log_debug("%s: padding %zd zero bytes at offset %lld",
7014d2a1fb2Sreyk 			    format, buflen - len, off + len);
7024d2a1fb2Sreyk 			memset(buf + len, 0, buflen - len);
7034d2a1fb2Sreyk 			len = buflen;
7044d2a1fb2Sreyk 		}
7054d2a1fb2Sreyk 
7064d2a1fb2Sreyk 		/*
7074d2a1fb2Sreyk 		 * No need to copy empty buffers.  This allows the backend,
7084d2a1fb2Sreyk 		 * sparse files or QCOW2 images, to save space in the
7094d2a1fb2Sreyk 		 * destination file.
7104d2a1fb2Sreyk 		 */
7114d2a1fb2Sreyk 		if (memcmp(buf, zerobuf, buflen) == 0)
7124d2a1fb2Sreyk 			continue;
7134d2a1fb2Sreyk 
7144d2a1fb2Sreyk 		log_debug("%s: writing %zd of %lld bytes at offset %lld",
7154d2a1fb2Sreyk 		    format, len, dst.size, off);
7164d2a1fb2Sreyk 
7174d2a1fb2Sreyk 		if ((rlen = dst.file.pwrite(dst.file.p,
7184d2a1fb2Sreyk 		    buf, (size_t)len, off)) != len) {
7194d2a1fb2Sreyk 			errno = EIO;
7204d2a1fb2Sreyk 			errstr = "failed to write to destination";
7214d2a1fb2Sreyk 			goto done;
7224d2a1fb2Sreyk 		}
7234d2a1fb2Sreyk 	}
7244d2a1fb2Sreyk 
7254d2a1fb2Sreyk 	if (dstsize < (size_t)dst.size)
7264d2a1fb2Sreyk 		warnx("destination size rounded to %lld megabytes",
7274d2a1fb2Sreyk 		    dst.size / 1048576);
7284d2a1fb2Sreyk 
7294d2a1fb2Sreyk  done:
7304d2a1fb2Sreyk 	free(buf);
7314d2a1fb2Sreyk 	free(zerobuf);
7324d2a1fb2Sreyk 	if (src.file.p != NULL)
7334d2a1fb2Sreyk 		src.file.close(src.file.p, 0);
7344d2a1fb2Sreyk 	if (dst.file.p != NULL)
7354d2a1fb2Sreyk 		dst.file.close(dst.file.p, 0);
7364d2a1fb2Sreyk 	if (errstr != NULL)
7374d2a1fb2Sreyk 		errx(1, "%s", errstr);
7384d2a1fb2Sreyk 	else
7394d2a1fb2Sreyk 		warnx("%s imagefile created", format);
7404d2a1fb2Sreyk 
7414d2a1fb2Sreyk 	return (0);
7424d2a1fb2Sreyk }
7434d2a1fb2Sreyk 
7444d2a1fb2Sreyk int
745eb8d1da1Sreyk ctl_status(struct parse_result *res, int argc, char *argv[])
746eb8d1da1Sreyk {
74722b23f34Smlarkin 	int ch;
748986b002cSmlarkin 
749986b002cSmlarkin 	while ((ch = getopt(argc, argv, "r")) != -1) {
750986b002cSmlarkin 		switch (ch) {
751986b002cSmlarkin 		case 'r':
752986b002cSmlarkin 			stat_rflag = 1;
753986b002cSmlarkin 			break;
754986b002cSmlarkin 		default:
755986b002cSmlarkin 			ctl_usage(res->ctl);
756986b002cSmlarkin 			/* NOTREACHED */
757986b002cSmlarkin 		}
758986b002cSmlarkin 	}
759986b002cSmlarkin 	argc -= optind;
760986b002cSmlarkin 	argv += optind;
761986b002cSmlarkin 
762986b002cSmlarkin 	if (argc == 1) {
763986b002cSmlarkin 		if (parse_vmid(res, argv[0], 0) == -1)
764986b002cSmlarkin 			errx(1, "invalid id: %s", argv[0]);
765986b002cSmlarkin 	} else if (argc > 1)
766eb8d1da1Sreyk 		ctl_usage(res->ctl);
767eb8d1da1Sreyk 
768eb8d1da1Sreyk 	return (vmmaction(res));
769eb8d1da1Sreyk }
770eb8d1da1Sreyk 
771eb8d1da1Sreyk int
772eb8d1da1Sreyk ctl_load(struct parse_result *res, int argc, char *argv[])
773eb8d1da1Sreyk {
7741f7fe034Sreyk 	if (argc != 2)
775eb8d1da1Sreyk 		ctl_usage(res->ctl);
776eb8d1da1Sreyk 
7771f7fe034Sreyk 	if ((res->path = strdup(argv[1])) == NULL)
778008065a5Sreyk 		err(1, "strdup");
779008065a5Sreyk 
780008065a5Sreyk 	return (vmmaction(res));
781eb8d1da1Sreyk }
782eb8d1da1Sreyk 
783eb8d1da1Sreyk int
7843afb90b0Sreyk ctl_log(struct parse_result *res, int argc, char *argv[])
7853afb90b0Sreyk {
7863afb90b0Sreyk 	if (argc != 2)
7873afb90b0Sreyk 		ctl_usage(res->ctl);
7883afb90b0Sreyk 
7893afb90b0Sreyk 	if (strncasecmp("brief", argv[1], strlen(argv[1])) == 0)
7903afb90b0Sreyk 		res->verbose = 0;
7913afb90b0Sreyk 	else if (strncasecmp("verbose", argv[1], strlen(argv[1])) == 0)
7923afb90b0Sreyk 		res->verbose = 2;
7933afb90b0Sreyk 	else
7943afb90b0Sreyk 		ctl_usage(res->ctl);
7953afb90b0Sreyk 
7963afb90b0Sreyk 	return (vmmaction(res));
7973afb90b0Sreyk }
7983afb90b0Sreyk 
7993afb90b0Sreyk int
8001f7fe034Sreyk ctl_reload(struct parse_result *res, int argc, char *argv[])
8011f7fe034Sreyk {
8021f7fe034Sreyk 	if (argc != 1)
8031f7fe034Sreyk 		ctl_usage(res->ctl);
8041f7fe034Sreyk 
8051f7fe034Sreyk 	return (vmmaction(res));
8061f7fe034Sreyk }
8071f7fe034Sreyk 
8081f7fe034Sreyk int
8091f7fe034Sreyk ctl_reset(struct parse_result *res, int argc, char *argv[])
8101f7fe034Sreyk {
8111f7fe034Sreyk 	if (argc == 2) {
8121f7fe034Sreyk 		if (strcasecmp("all", argv[1]) == 0)
8131f7fe034Sreyk 			res->mode = CONFIG_ALL;
8141f7fe034Sreyk 		else if (strcasecmp("vms", argv[1]) == 0)
8151f7fe034Sreyk 			res->mode = CONFIG_VMS;
8161f7fe034Sreyk 		else if (strcasecmp("switches", argv[1]) == 0)
8171f7fe034Sreyk 			res->mode = CONFIG_SWITCHES;
8181f7fe034Sreyk 		else
8191f7fe034Sreyk 			ctl_usage(res->ctl);
8201f7fe034Sreyk 	} else if (argc > 2)
8211f7fe034Sreyk 		ctl_usage(res->ctl);
8221f7fe034Sreyk 
8231f7fe034Sreyk 	if (res->mode == 0)
8241f7fe034Sreyk 		res->mode = CONFIG_ALL;
8251f7fe034Sreyk 
8261f7fe034Sreyk 	return (vmmaction(res));
8271f7fe034Sreyk }
8281f7fe034Sreyk 
8291f7fe034Sreyk int
830eb8d1da1Sreyk ctl_start(struct parse_result *res, int argc, char *argv[])
831eb8d1da1Sreyk {
832f224f92aSccardenas 	int		 ch, i, type;
833e2ceadc1Sreyk 	char		 path[PATH_MAX];
834e2ceadc1Sreyk 	const char	*s;
835eb8d1da1Sreyk 
836b848b186Sdv 	/* We may require sendfd */
837b848b186Sdv 	if (pledge("stdio rpath exec unix getpw unveil sendfd", NULL) == -1)
838b848b186Sdv 		err(1, "pledge");
839b848b186Sdv 
840917458a3Sclaudio 	while ((ch = getopt(argc, argv, "b:B:cd:i:Lm:n:r:t:")) != -1) {
841eb8d1da1Sreyk 		switch (ch) {
84238d0e0c3Sreyk 		case 'b':
843eb8d1da1Sreyk 			if (res->path)
84438d0e0c3Sreyk 				errx(1, "boot image specified multiple times");
8455f82d0b6Sreyk 			if (realpath(optarg, path) == NULL)
84638d0e0c3Sreyk 				err(1, "invalid boot image path");
8475f82d0b6Sreyk 			if ((res->path = strdup(path)) == NULL)
848eb8d1da1Sreyk 				errx(1, "strdup");
849eb8d1da1Sreyk 			break;
850917458a3Sclaudio 		case 'B':
851917458a3Sclaudio 			if (res->bootdevice)
852917458a3Sclaudio 				errx(1, "boot device specified multiple times");
853bdbc1844Sclaudio 			if (strcmp("disk", optarg) == 0)
854bdbc1844Sclaudio 				res->bootdevice = VMBOOTDEV_DISK;
855bdbc1844Sclaudio 			else if (strcmp("cdrom", optarg) == 0)
856bdbc1844Sclaudio 				res->bootdevice = VMBOOTDEV_CDROM;
857bdbc1844Sclaudio 			else if (strcmp("net", optarg) == 0)
858917458a3Sclaudio 				res->bootdevice = VMBOOTDEV_NET;
859bdbc1844Sclaudio 			else
860bdbc1844Sclaudio 				errx(1, "unknown boot device %s", optarg);
861917458a3Sclaudio 			break;
86295ab188fSccardenas 		case 'r':
86395ab188fSccardenas 			if (res->isopath)
86495ab188fSccardenas 				errx(1, "iso image specified multiple times");
86595ab188fSccardenas 			if (realpath(optarg, path) == NULL)
86695ab188fSccardenas 				err(1, "invalid iso image path");
86795ab188fSccardenas 			if ((res->isopath = strdup(path)) == NULL)
86895ab188fSccardenas 				errx(1, "strdup");
86995ab188fSccardenas 			break;
87038d0e0c3Sreyk 		case 'c':
87138d0e0c3Sreyk 			tty_autoconnect = 1;
87238d0e0c3Sreyk 			break;
873470adcf5Sreyk 		case 'L':
874470adcf5Sreyk 			if (parse_network(res, ".") != 0)
875470adcf5Sreyk 				errx(1, "invalid network: %s", optarg);
876470adcf5Sreyk 			break;
877eb8d1da1Sreyk 		case 'm':
878eb8d1da1Sreyk 			if (res->size)
879eb8d1da1Sreyk 				errx(1, "memory specified multiple times");
880ead1b146Sdv 			parse_size(res, optarg, "memory");
881eb8d1da1Sreyk 			break;
882b657d36cSreyk 		case 'n':
883b657d36cSreyk 			if (parse_network(res, optarg) != 0)
884b657d36cSreyk 				errx(1, "invalid network: %s", optarg);
885b657d36cSreyk 			break;
886eb8d1da1Sreyk 		case 'd':
887f224f92aSccardenas 			type = parse_disktype(optarg, &s);
888f224f92aSccardenas 			if (realpath(s, path) == NULL)
8895f82d0b6Sreyk 				err(1, "invalid disk path");
890f224f92aSccardenas 			if (parse_disk(res, path, type) != 0)
8915f82d0b6Sreyk 				errx(1, "invalid disk: %s", optarg);
892eb8d1da1Sreyk 			break;
893eb8d1da1Sreyk 		case 'i':
894eb8d1da1Sreyk 			if (res->nifs != -1)
895eb8d1da1Sreyk 				errx(1, "interfaces specified multiple times");
896eb8d1da1Sreyk 			if (parse_ifs(res, optarg, 0) != 0)
897eb8d1da1Sreyk 				errx(1, "invalid interface count: %s", optarg);
898eb8d1da1Sreyk 			break;
899508a6e39Sreyk 		case 't':
900508a6e39Sreyk 			if (parse_instance(res, optarg) == -1)
901508a6e39Sreyk 				errx(1, "invalid name: %s", optarg);
902508a6e39Sreyk 			break;
903eb8d1da1Sreyk 		default:
904eb8d1da1Sreyk 			ctl_usage(res->ctl);
905eb8d1da1Sreyk 			/* NOTREACHED */
906eb8d1da1Sreyk 		}
907eb8d1da1Sreyk 	}
908c1e58e1aSkn 	argc -= optind;
909c1e58e1aSkn 	argv += optind;
910eb8d1da1Sreyk 
911bd4b76f8Sreyk 	if (argc != 1)
912b0b31ffcSkn 		ctl_usage(res->ctl);
913b0b31ffcSkn 
914bd4b76f8Sreyk 	if (parse_vmid(res, argv[0], 0) == -1)
915bd4b76f8Sreyk 		errx(1, "invalid id: %s", argv[0]);
916bd4b76f8Sreyk 
917b657d36cSreyk 	for (i = res->nnets; i < res->nifs; i++) {
918b657d36cSreyk 		/* Add interface that is not attached to a switch */
919b657d36cSreyk 		if (parse_network(res, "") == -1)
920b657d36cSreyk 			return (-1);
921b657d36cSreyk 	}
922b657d36cSreyk 	if (res->nnets > res->nifs)
923b657d36cSreyk 		res->nifs = res->nnets;
924b657d36cSreyk 
925eb8d1da1Sreyk 	return (vmmaction(res));
926eb8d1da1Sreyk }
927eb8d1da1Sreyk 
928eb8d1da1Sreyk int
929eb8d1da1Sreyk ctl_stop(struct parse_result *res, int argc, char *argv[])
930eb8d1da1Sreyk {
931a261be2aStb 	int		 ch;
932f6e5c9ebSreyk 
933e0b12962Sreyk 	while ((ch = getopt(argc, argv, "afw")) != -1) {
934f6e5c9ebSreyk 		switch (ch) {
935f6e5c9ebSreyk 		case 'f':
9363be9785fSreyk 			res->flags |= VMOP_FORCE;
9373be9785fSreyk 			break;
9383be9785fSreyk 		case 'w':
9393be9785fSreyk 			res->flags |= VMOP_WAIT;
940f6e5c9ebSreyk 			break;
941e0b12962Sreyk 		case 'a':
942e0b12962Sreyk 			res->action = CMD_STOPALL;
943e0b12962Sreyk 			break;
944f6e5c9ebSreyk 		default:
945eb8d1da1Sreyk 			ctl_usage(res->ctl);
946f6e5c9ebSreyk 			/* NOTREACHED */
947f6e5c9ebSreyk 		}
948f6e5c9ebSreyk 	}
949c1e58e1aSkn 	argc -= optind;
950c1e58e1aSkn 	argv += optind;
951eb8d1da1Sreyk 
952a261be2aStb 	if (res->action == CMD_STOPALL) {
953a261be2aStb 		if (argc != 0)
954f460dd9fSmlarkin 			ctl_usage(res->ctl);
955a261be2aStb 	} else {
956a261be2aStb 		if (argc != 1)
957b0b31ffcSkn 			ctl_usage(res->ctl);
958a261be2aStb 		if (parse_vmid(res, argv[0], 0) == -1)
959a261be2aStb 			errx(1, "invalid id: %s", argv[0]);
960a261be2aStb 	}
961e0b12962Sreyk 
962eb8d1da1Sreyk 	return (vmmaction(res));
963eb8d1da1Sreyk }
96495009bb4Sreyk 
96595009bb4Sreyk int
96695009bb4Sreyk ctl_console(struct parse_result *res, int argc, char *argv[])
96795009bb4Sreyk {
96895009bb4Sreyk 	if (argc == 2) {
9694100d478Sjasper 		if (parse_vmid(res, argv[1], 0) == -1)
97095009bb4Sreyk 			errx(1, "invalid id: %s", argv[1]);
97195009bb4Sreyk 	} else if (argc != 2)
97295009bb4Sreyk 		ctl_usage(res->ctl);
97395009bb4Sreyk 
97495009bb4Sreyk 	return (vmmaction(res));
97595009bb4Sreyk }
97695009bb4Sreyk 
97752e954a3Spd int
978583f6618Sclaudio ctl_waitfor(struct parse_result *res, int argc, char *argv[])
979583f6618Sclaudio {
980583f6618Sclaudio 	if (argc == 2) {
981583f6618Sclaudio 		if (parse_vmid(res, argv[1], 0) == -1)
982583f6618Sclaudio 			errx(1, "invalid id: %s", argv[1]);
983583f6618Sclaudio 	} else if (argc != 2)
984583f6618Sclaudio 		ctl_usage(res->ctl);
985583f6618Sclaudio 
986583f6618Sclaudio 	return (vmmaction(res));
987583f6618Sclaudio }
988583f6618Sclaudio 
989583f6618Sclaudio int
99052e954a3Spd ctl_pause(struct parse_result *res, int argc, char *argv[])
99152e954a3Spd {
99252e954a3Spd 	if (argc == 2) {
9934100d478Sjasper 		if (parse_vmid(res, argv[1], 0) == -1)
99452e954a3Spd 			errx(1, "invalid id: %s", argv[1]);
99552e954a3Spd 	} else if (argc != 2)
99652e954a3Spd 		ctl_usage(res->ctl);
99752e954a3Spd 
99852e954a3Spd 	return (vmmaction(res));
99952e954a3Spd }
100052e954a3Spd 
100152e954a3Spd int
100252e954a3Spd ctl_unpause(struct parse_result *res, int argc, char *argv[])
100352e954a3Spd {
100452e954a3Spd 	if (argc == 2) {
10054100d478Sjasper 		if (parse_vmid(res, argv[1], 0) == -1)
100652e954a3Spd 			errx(1, "invalid id: %s", argv[1]);
100752e954a3Spd 	} else if (argc != 2)
100852e954a3Spd 		ctl_usage(res->ctl);
100952e954a3Spd 
101052e954a3Spd 	return (vmmaction(res));
101152e954a3Spd }
101252e954a3Spd 
1013eed20f3bSpd int
1014eed20f3bSpd ctl_send(struct parse_result *res, int argc, char *argv[])
1015eed20f3bSpd {
10166955bc4dSccardenas 	if (pledge("stdio unix sendfd unveil", NULL) == -1)
1017eed20f3bSpd 		err(1, "pledge");
1018eed20f3bSpd 	if (argc == 2) {
10194100d478Sjasper 		if (parse_vmid(res, argv[1], 0) == -1)
1020eed20f3bSpd 			errx(1, "invalid id: %s", argv[1]);
1021eed20f3bSpd 	} else if (argc != 2)
1022eed20f3bSpd 		ctl_usage(res->ctl);
1023eed20f3bSpd 
1024eed20f3bSpd 	return (vmmaction(res));
1025eed20f3bSpd }
1026eed20f3bSpd 
1027eed20f3bSpd int
1028eed20f3bSpd ctl_receive(struct parse_result *res, int argc, char *argv[])
1029eed20f3bSpd {
10306955bc4dSccardenas 	if (pledge("stdio unix sendfd unveil", NULL) == -1)
1031eed20f3bSpd 		err(1, "pledge");
1032eed20f3bSpd 	if (argc == 2) {
10334100d478Sjasper 		if (parse_vmid(res, argv[1], 1) == -1)
1034eed20f3bSpd 			errx(1, "invalid id: %s", argv[1]);
1035eed20f3bSpd 	} else if (argc != 2)
1036eed20f3bSpd 		ctl_usage(res->ctl);
1037eed20f3bSpd 
1038eed20f3bSpd 	return (vmmaction(res));
1039eed20f3bSpd }
1040eed20f3bSpd 
104195009bb4Sreyk __dead void
104295009bb4Sreyk ctl_openconsole(const char *name)
104395009bb4Sreyk {
104495009bb4Sreyk 	closefrom(STDERR_FILENO + 1);
10456955bc4dSccardenas 	if (unveil(VMCTL_CU, "x") == -1)
1046bc5a8259Sbeck 		err(1, "unveil %s", VMCTL_CU);
10475154e7dcSkn 	execl(VMCTL_CU, VMCTL_CU, "-r", "-l", name, "-s", "115200",
10485154e7dcSkn 	    (char *)NULL);
104995009bb4Sreyk 	err(1, "failed to open the console");
105095009bb4Sreyk }
1051