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