GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/vmctl/main.c Lines: 0 332 0.0 %
Date: 2017-11-13 Branches: 0 237 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: main.c,v 1.33 2017/10/07 19:48:30 guenther 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 flock cpath wpath", 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
		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 flock", 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 flock rpath cpath wpath", 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 flock rpath cpath wpath", 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
}