Bug Summary

File:src/usr.sbin/vmctl/main.c
Warning:line 719, column 8
Although the value stored to 'rlen' is used in the enclosing expression, the value is never actually read from 'rlen'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name main.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.sbin/vmctl/obj -resource-dir /usr/local/llvm16/lib/clang/16 -I /usr/src/usr.sbin/vmctl -I /usr/src/usr.sbin/vmctl/../vmd -internal-isystem /usr/local/llvm16/lib/clang/16/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/vmctl/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fcf-protection=branch -fno-jump-tables -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/scan/2024-01-11-140451-98009-1 -x c /usr/src/usr.sbin/vmctl/main.c
1/* $OpenBSD: main.c,v 1.75 2023/04/28 19:46:41 dv 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(*__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" "raw"
43#define QCOW2_FMT"qcow2" "qcow2"
44
45static const char *socket_name = SOCKET_NAME"/var/run/vmd.sock";
46static int ctl_sock = -1;
47static int tty_autoconnect = 0;
48
49__dead__attribute__((__noreturn__)) void usage(void);
50__dead__attribute__((__noreturn__)) void ctl_usage(struct ctl_command *);
51
52int vmm_action(struct parse_result *);
53
54int ctl_console(struct parse_result *, int, char *[]);
55int ctl_convert(const char *, const char *, int, size_t);
56int ctl_create(struct parse_result *, int, char *[]);
57int ctl_load(struct parse_result *, int, char *[]);
58int ctl_log(struct parse_result *, int, char *[]);
59int ctl_reload(struct parse_result *, int, char *[]);
60int ctl_reset(struct parse_result *, int, char *[]);
61int ctl_start(struct parse_result *, int, char *[]);
62int ctl_status(struct parse_result *, int, char *[]);
63int ctl_stop(struct parse_result *, int, char *[]);
64int ctl_waitfor(struct parse_result *, int, char *[]);
65int ctl_pause(struct parse_result *, int, char *[]);
66int ctl_unpause(struct parse_result *, int, char *[]);
67int ctl_send(struct parse_result *, int, char *[]);
68int ctl_receive(struct parse_result *, int, char *[]);
69
70struct ctl_command ctl_commands[] = {
71 { "console", CMD_CONSOLE, ctl_console, "id" },
72 { "create", CMD_CREATE, ctl_create,
73 "[-b base | -i disk] [-s size] disk", 1 },
74 { "load", CMD_LOAD, ctl_load, "filename" },
75 { "log", CMD_LOG, ctl_log, "[brief | verbose]" },
76 { "pause", CMD_PAUSE, ctl_pause, "id" },
77 { "receive", CMD_RECEIVE, ctl_receive, "name" , 1},
78 { "reload", CMD_RELOAD, ctl_reload, "" },
79 { "reset", CMD_RESET, ctl_reset, "[all | switches | vms]" },
80 { "send", CMD_SEND, ctl_send, "id", 1},
81 { "show", CMD_STATUS, ctl_status, "[id]" },
82 { "start", CMD_START, ctl_start,
83 "[-cL] [-B device] [-b path] [-d disk] [-i count]\n"
84 "\t\t[-m size] [-n switch] [-r path] [-t name] id | name", 1},
85 { "status", CMD_STATUS, ctl_status, "[id]" },
86 { "stop", CMD_STOP, ctl_stop, "[-fw] [id | -a]" },
87 { "unpause", CMD_UNPAUSE, ctl_unpause, "id" },
88 { "wait", CMD_WAITFOR, ctl_waitfor, "id" },
89 { NULL((void *)0) }
90};
91
92__dead__attribute__((__noreturn__)) void
93usage(void)
94{
95 extern char *__progname;
96
97 fprintf(stderr(&__sF[2]), "usage:\t%s [-v] command [arg ...]\n", __progname);
98
99 exit(1);
100}
101
102__dead__attribute__((__noreturn__)) void
103ctl_usage(struct ctl_command *ctl)
104{
105 extern char *__progname;
106
107 fprintf(stderr(&__sF[2]), "usage:\t%s [-v] %s %s\n", __progname,
108 ctl->name, ctl->usage);
109 exit(1);
110}
111
112int
113main(int argc, char *argv[])
114{
115 int ch, verbose = 1;
116
117 while ((ch = getopt(argc, argv, "v")) != -1) {
118 switch (ch) {
119 case 'v':
120 verbose = 2;
121 break;
122 default:
123 usage();
124 /* NOTREACHED */
125 }
126 }
127 argc -= optind;
128 argv += optind;
129 optreset = 1;
130 optind = 1;
131
132 if (argc < 1)
133 usage();
134
135 log_init(verbose, LOG_DAEMON(3<<3));
136
137 return (parse(argc, argv));
138}
139
140int
141parse(int argc, char *argv[])
142{
143 struct ctl_command *ctl = NULL((void *)0);
144 struct parse_result res;
145 int i;
146
147 memset(&res, 0, sizeof(res));
148 res.nifs = -1;
149
150 for (i = 0; ctl_commands[i].name != NULL((void *)0); i++) {
151 if (strncmp(ctl_commands[i].name,
152 argv[0], strlen(argv[0])) == 0) {
153 if (ctl != NULL((void *)0)) {
154 fprintf(stderr(&__sF[2]),
155 "ambiguous argument: %s\n", argv[0]);
156 usage();
157 }
158 ctl = &ctl_commands[i];
159 }
160 }
161
162 if (ctl == NULL((void *)0)) {
163 fprintf(stderr(&__sF[2]), "unknown argument: %s\n", argv[0]);
164 usage();
165 }
166
167 res.action = ctl->action;
168 res.ctl = ctl;
169
170 if (!ctl->has_pledge) {
171 /* pledge(2) default if command doesn't have its own pledge */
172 if (pledge("stdio rpath exec unix getpw unveil", NULL((void *)0)) == -1)
173 err(1, "pledge");
174 }
175 if (ctl->main(&res, argc, argv) != 0)
176 exit(1);
177
178 if (ctl_sock != -1) {
179 close(ibuf->fd);
180 free(ibuf);
181 }
182
183 return (0);
184}
185
186int
187vmmaction(struct parse_result *res)
188{
189 struct sockaddr_un sun;
190 struct imsg imsg;
191 int done = 0;
192 int n;
193 int ret, action;
194 unsigned int flags;
195
196 if (ctl_sock == -1) {
197 if (unveil(SOCKET_NAME"/var/run/vmd.sock", "w") == -1)
198 err(1, "unveil %s", SOCKET_NAME"/var/run/vmd.sock");
199 if ((ctl_sock = socket(AF_UNIX1,
200 SOCK_STREAM1|SOCK_CLOEXEC0x8000, 0)) == -1)
201 err(1, "socket");
202
203 memset(&sun, 0, sizeof(sun));
204 sun.sun_family = AF_UNIX1;
205 strlcpy(sun.sun_path, socket_name, sizeof(sun.sun_path));
206
207 if (connect(ctl_sock,
208 (struct sockaddr *)&sun, sizeof(sun)) == -1)
209 err(1, "connect: %s", socket_name);
210
211 if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL((void *)0))
212 err(1, "malloc");
213 imsg_init(ibuf, ctl_sock);
214 }
215
216 switch (res->action) {
217 case CMD_START:
218 ret = vm_start(res->id, res->name, res->size, res->nifs,
219 res->nets, res->ndisks, res->disks, res->disktypes,
220 res->path, res->isopath, res->instance, res->bootdevice);
221 if (ret) {
222 errno(*__errno()) = ret;
223 err(1, "start VM operation failed");
224 }
225 break;
226 case CMD_STOP:
227 terminate_vm(res->id, res->name, res->flags);
228 break;
229 case CMD_STATUS:
230 case CMD_CONSOLE:
231 case CMD_STOPALL:
232 get_info_vm(res->id, res->name, res->action, res->flags);
233 break;
234 case CMD_LOAD:
235 imsg_compose(ibuf, IMSG_VMDOP_LOAD, 0, 0, -1,
236 res->path, strlen(res->path) + 1);
237 break;
238 case CMD_LOG:
239 imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1,
240 &res->verbose, sizeof(res->verbose));
241 break;
242 case CMD_RELOAD:
243 imsg_compose(ibuf, IMSG_VMDOP_RELOAD, 0, 0, -1, NULL((void *)0), 0);
244 break;
245 case CMD_RESET:
246 imsg_compose(ibuf, IMSG_CTL_RESET, 0, 0, -1,
247 &res->mode, sizeof(res->mode));
248 break;
249 case CMD_WAITFOR:
250 waitfor_vm(res->id, res->name);
251 break;
252 case CMD_PAUSE:
253 pause_vm(res->id, res->name);
254 break;
255 case CMD_UNPAUSE:
256 unpause_vm(res->id, res->name);
257 break;
258 case CMD_SEND:
259 send_vm(res->id, res->name);
260 done = 1;
261 ret = 0;
262 break;
263 case CMD_RECEIVE:
264 vm_receive(res->id, res->name);
265 break;
266 case CMD_CREATE:
267 case NONE:
268 /* The action is not expected here */
269 errx(1, "invalid action %u", res->action);
270 break;
271 }
272
273 action = res->action;
274 flags = res->flags;
275 parse_free(res);
276
277 while (ibuf->w.queued)
278 if (msgbuf_write(&ibuf->w) <= 0 && errno(*__errno()) != EAGAIN35)
279 err(1, "write error");
280
281 while (!done) {
282 if ((n = imsg_read(ibuf)) == -1 && errno(*__errno()) != EAGAIN35)
283 errx(1, "imsg_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)((&imsg)->hdr.len - sizeof(struct imsg_hdr)) == sizeof(ret))
295 memcpy(&ret, imsg.data, sizeof(ret));
296 else
297 ret = 0;
298 if (ret != 0) {
299 errno(*__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_WAIT0x02;
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
347void
348parse_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
363int
364parse_ifs(struct parse_result *res, char *word, int val)
365{
366 const char *error;
367
368 if (word != NULL((void *)0)) {
369 val = strtonum(word, 1, INT_MAX0x7fffffff, &error);
370 if (error != NULL((void *)0)) {
371 warnx("count is %s: %s", error, word);
372 return (-1);
373 }
374 }
375 res->nifs = val;
376
377 return (0);
378}
379
380int
381parse_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((void *)0)) {
388 warn("reallocarray");
389 return (-1);
390 }
391 if ((s = strdup(word)) == NULL((void *)0)) {
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
402void
403parse_size(struct parse_result *res, char *word, const char *type)
404{
405 char result[FMT_SCALED_STRSIZE7];
406 long long val = 0;
407
408 if (word != NULL((void *)0)) {
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_SIZE32L * 1024 * 1024 * 1024) {
417 if (fmt_scaled(VMM_MAX_VM_MEM_SIZE32L * 1024 * 1024 * 1024, 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
434int
435parse_disktype(const char *s, const char **ret)
436{
437 char buf[BUFSIZ1024];
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"raw") == s && *(s + strlen(RAW_FMT"raw")) == ':') {
446 *ret = s + strlen(RAW_FMT"raw") + 1;
447 return (VMDF_RAW0x01);
448 }
449 if (strstr(s, QCOW2_FMT"qcow2") == s && *(s + strlen(QCOW2_FMT"qcow2")) == ':') {
450 *ret = s + strlen(QCOW2_FMT"qcow2") + 1;
451 return (VMDF_QCOW20x02);
452 }
453
454 /* Or try to derive the format from the file signature */
455 if ((fd = open(s, O_RDONLY0x0000)) != -1) {
456 len = read(fd, buf, sizeof(buf));
457 close(fd);
458
459 if (len >= (ssize_t)strlen(VM_MAGIC_QCOW"QFI\xfb") &&
460 strncmp(buf, VM_MAGIC_QCOW"QFI\xfb",
461 strlen(VM_MAGIC_QCOW"QFI\xfb")) == 0) {
462 /* Return qcow2, the version will be checked later */
463 return (VMDF_QCOW20x02);
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((void *)0) && *(++ext) != '\0') {
473 if (strcasecmp(ext, RAW_FMT"raw") == 0)
474 return (VMDF_RAW0x01);
475 else if (strcasecmp(ext, QCOW2_FMT"qcow2") == 0)
476 return (VMDF_QCOW20x02);
477 }
478
479 /* Fallback to raw */
480 return (VMDF_RAW0x01);
481}
482
483int
484parse_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((void *)0)) {
492 warn("reallocarray");
493 return (-1);
494 }
495 if ((disktypes = reallocarray(res->disktypes, res->ndisks + 1,
496 sizeof(int))) == NULL((void *)0)) {
497 warn("reallocarray");
498 return -1;
499 }
500 if ((s = strdup(word)) == NULL((void *)0)) {
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
513int
514parse_vmid(struct parse_result *res, char *word, int needname)
515{
516 const char *error;
517 uint32_t id;
518
519 if (word == NULL((void *)0)) {
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_MAX0xffffffffU, &error);
528 if (error == NULL((void *)0)) {
529 if (needname) {
530 warnx("invalid vm name");
531 return (-1);
532 } else {
533 res->id = id;
534 res->name = NULL((void *)0);
535 }
536 } else {
537 if (strlen(word) >= VMM_MAX_NAME_LEN64) {
538 warnx("name too long");
539 return (-1);
540 }
541 res->id = 0;
542 if ((res->name = strdup(word)) == NULL((void *)0))
543 errx(1, "strdup");
544 }
545
546 return (0);
547}
548
549int
550parse_instance(struct parse_result *res, char *word)
551{
552 if (strlen(word) >= VMM_MAX_NAME_LEN64) {
553 warnx("instance vm name too long");
554 return (-1);
555 }
556 res->id = 0;
557 if ((res->instance = strdup(word)) == NULL((void *)0))
558 errx(1, "strdup");
559
560 return (0);
561}
562
563int
564ctl_create(struct parse_result *res, int argc, char *argv[])
565{
566 int ch, ret, type;
567 const char *disk, *format, *base = NULL((void *)0), *input = NULL((void *)0);
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((void *)0)) == -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_QCOW20x02)
603 errx(1, "base images require qcow2 disk format");
604 if (res->size == 0 && !base) {
605 fprintf(stderr(&__sF[2]), "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(*__errno()) = ret;
612 err(1, "create imagefile operation failed");
613 } else
614 warnx("%s imagefile created", format);
615
616 return (0);
617}
618
619int
620ctl_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((void *)0);
631 uint8_t *buf = NULL((void *)0), *zerobuf = NULL((void *)0);
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_RDONLY0x0000,
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 else
652 dstsize *= 1048576;
653 if (dstsize < (size_t)src.size) {
654 errstr = "size cannot be smaller than input disk size";
655 goto done;
656 }
657
658 /* align to megabytes */
659 dst.size = ALIGNSZ(dstsize, 1048576)((dstsize + 1048576 - 1) & ~(1048576 - 1));
660
661 if ((ret = create_imagefile(dst.type, dst.disk, NULL((void *)0), dst.size,
662 &format)) != 0) {
663 errstr = "failed to create destination image file";
664 goto done;
665 }
666
667 if ((dst.fd = open_imagefile(dst.type, dst.disk, O_RDWR0x0002,
668 &dst.file, &dst.size)) == -1) {
669 errstr = "failed to open destination image file";
670 goto done;
671 }
672
673 if (pledge("stdio", NULL((void *)0)) == -1)
674 err(1, "pledge");
675
676 /*
677 * Use 64k buffers by default. This could also be adjusted to
678 * the backend cluster size.
679 */
680 buflen = 1 << 16;
681 if ((buf = calloc(1, buflen)) == NULL((void *)0) ||
682 (zerobuf = calloc(1, buflen)) == NULL((void *)0)) {
683 errstr = "failed to allocated buffers";
684 goto done;
685 }
686
687 for (off = 0; off < dst.size; off += len) {
688 /* Read input from the source image */
689 if (off < src.size) {
690 len = MIN((off_t)buflen, src.size - off)((((off_t)buflen)<(src.size - off))?((off_t)buflen):(src.size
- off))
;
691 if ((rlen = src.file.pread(src.file.p,
692 buf, (size_t)len, off)) != len) {
693 errno(*__errno()) = EIO5;
694 errstr = "failed to read from source";
695 goto done;
696 }
697 } else
698 len = 0;
699
700 /* and pad the remaining bytes */
701 if (len < (ssize_t)buflen) {
702 log_debug("%s: padding %zd zero bytes at offset %lld",
703 format, buflen - len, off + len);
704 memset(buf + len, 0, buflen - len);
705 len = buflen;
706 }
707
708 /*
709 * No need to copy empty buffers. This allows the backend,
710 * sparse files or QCOW2 images, to save space in the
711 * destination file.
712 */
713 if (memcmp(buf, zerobuf, buflen) == 0)
714 continue;
715
716 log_debug("%s: writing %zd of %lld bytes at offset %lld",
717 format, len, dst.size, off);
718
719 if ((rlen = dst.file.pwrite(dst.file.p,
Although the value stored to 'rlen' is used in the enclosing expression, the value is never actually read from 'rlen'
720 buf, (size_t)len, off)) != len) {
721 errno(*__errno()) = EIO5;
722 errstr = "failed to write to destination";
723 goto done;
724 }
725 }
726
727 if (dstsize < (size_t)dst.size)
728 warnx("destination size rounded to %lld megabytes",
729 dst.size / 1048576);
730
731 done:
732 free(buf);
733 free(zerobuf);
734 if (src.file.p != NULL((void *)0))
735 src.file.close(src.file.p, 0);
736 if (dst.file.p != NULL((void *)0))
737 dst.file.close(dst.file.p, 0);
738 if (errstr != NULL((void *)0))
739 errx(1, "%s", errstr);
740 else
741 warnx("%s imagefile created", format);
742
743 return (0);
744}
745
746int
747ctl_status(struct parse_result *res, int argc, char *argv[])
748{
749 if (argc == 2) {
750 if (parse_vmid(res, argv[1], 0) == -1)
751 errx(1, "invalid id: %s", argv[1]);
752 } else if (argc > 2)
753 ctl_usage(res->ctl);
754
755 return (vmmaction(res));
756}
757
758int
759ctl_load(struct parse_result *res, int argc, char *argv[])
760{
761 if (argc != 2)
762 ctl_usage(res->ctl);
763
764 if ((res->path = strdup(argv[1])) == NULL((void *)0))
765 err(1, "strdup");
766
767 return (vmmaction(res));
768}
769
770int
771ctl_log(struct parse_result *res, int argc, char *argv[])
772{
773 if (argc != 2)
774 ctl_usage(res->ctl);
775
776 if (strncasecmp("brief", argv[1], strlen(argv[1])) == 0)
777 res->verbose = 0;
778 else if (strncasecmp("verbose", argv[1], strlen(argv[1])) == 0)
779 res->verbose = 2;
780 else
781 ctl_usage(res->ctl);
782
783 return (vmmaction(res));
784}
785
786int
787ctl_reload(struct parse_result *res, int argc, char *argv[])
788{
789 if (argc != 1)
790 ctl_usage(res->ctl);
791
792 return (vmmaction(res));
793}
794
795int
796ctl_reset(struct parse_result *res, int argc, char *argv[])
797{
798 if (argc == 2) {
799 if (strcasecmp("all", argv[1]) == 0)
800 res->mode = CONFIG_ALL0xff;
801 else if (strcasecmp("vms", argv[1]) == 0)
802 res->mode = CONFIG_VMS0x01;
803 else if (strcasecmp("switches", argv[1]) == 0)
804 res->mode = CONFIG_SWITCHES0x02;
805 else
806 ctl_usage(res->ctl);
807 } else if (argc > 2)
808 ctl_usage(res->ctl);
809
810 if (res->mode == 0)
811 res->mode = CONFIG_ALL0xff;
812
813 return (vmmaction(res));
814}
815
816int
817ctl_start(struct parse_result *res, int argc, char *argv[])
818{
819 int ch, i, type;
820 char path[PATH_MAX1024];
821 const char *s;
822
823 /* We may require sendfd */
824 if (pledge("stdio rpath exec unix getpw unveil sendfd", NULL((void *)0)) == -1)
825 err(1, "pledge");
826
827 while ((ch = getopt(argc, argv, "b:B:cd:i:Lm:n:r:t:")) != -1) {
828 switch (ch) {
829 case 'b':
830 if (res->path)
831 errx(1, "boot image specified multiple times");
832 if (realpath(optarg, path) == NULL((void *)0))
833 err(1, "invalid boot image path");
834 if ((res->path = strdup(path)) == NULL((void *)0))
835 errx(1, "strdup");
836 break;
837 case 'B':
838 if (res->bootdevice)
839 errx(1, "boot device specified multiple times");
840 if (strcmp("disk", optarg) == 0)
841 res->bootdevice = VMBOOTDEV_DISK1;
842 else if (strcmp("cdrom", optarg) == 0)
843 res->bootdevice = VMBOOTDEV_CDROM2;
844 else if (strcmp("net", optarg) == 0)
845 res->bootdevice = VMBOOTDEV_NET3;
846 else
847 errx(1, "unknown boot device %s", optarg);
848 break;
849 case 'r':
850 if (res->isopath)
851 errx(1, "iso image specified multiple times");
852 if (realpath(optarg, path) == NULL((void *)0))
853 err(1, "invalid iso image path");
854 if ((res->isopath = strdup(path)) == NULL((void *)0))
855 errx(1, "strdup");
856 break;
857 case 'c':
858 tty_autoconnect = 1;
859 break;
860 case 'L':
861 if (parse_network(res, ".") != 0)
862 errx(1, "invalid network: %s", optarg);
863 break;
864 case 'm':
865 if (res->size)
866 errx(1, "memory specified multiple times");
867 parse_size(res, optarg, "memory");
868 break;
869 case 'n':
870 if (parse_network(res, optarg) != 0)
871 errx(1, "invalid network: %s", optarg);
872 break;
873 case 'd':
874 type = parse_disktype(optarg, &s);
875 if (realpath(s, path) == NULL((void *)0))
876 err(1, "invalid disk path");
877 if (parse_disk(res, path, type) != 0)
878 errx(1, "invalid disk: %s", optarg);
879 break;
880 case 'i':
881 if (res->nifs != -1)
882 errx(1, "interfaces specified multiple times");
883 if (parse_ifs(res, optarg, 0) != 0)
884 errx(1, "invalid interface count: %s", optarg);
885 break;
886 case 't':
887 if (parse_instance(res, optarg) == -1)
888 errx(1, "invalid name: %s", optarg);
889 break;
890 default:
891 ctl_usage(res->ctl);
892 /* NOTREACHED */
893 }
894 }
895 argc -= optind;
896 argv += optind;
897
898 if (argc != 1)
899 ctl_usage(res->ctl);
900
901 if (parse_vmid(res, argv[0], 0) == -1)
902 errx(1, "invalid id: %s", argv[0]);
903
904 for (i = res->nnets; i < res->nifs; i++) {
905 /* Add interface that is not attached to a switch */
906 if (parse_network(res, "") == -1)
907 return (-1);
908 }
909 if (res->nnets > res->nifs)
910 res->nifs = res->nnets;
911
912 return (vmmaction(res));
913}
914
915int
916ctl_stop(struct parse_result *res, int argc, char *argv[])
917{
918 int ch;
919
920 while ((ch = getopt(argc, argv, "afw")) != -1) {
921 switch (ch) {
922 case 'f':
923 res->flags |= VMOP_FORCE0x01;
924 break;
925 case 'w':
926 res->flags |= VMOP_WAIT0x02;
927 break;
928 case 'a':
929 res->action = CMD_STOPALL;
930 break;
931 default:
932 ctl_usage(res->ctl);
933 /* NOTREACHED */
934 }
935 }
936 argc -= optind;
937 argv += optind;
938
939 if (res->action == CMD_STOPALL) {
940 if (argc != 0)
941 ctl_usage(res->ctl);
942 } else {
943 if (argc != 1)
944 ctl_usage(res->ctl);
945 if (parse_vmid(res, argv[0], 0) == -1)
946 errx(1, "invalid id: %s", argv[0]);
947 }
948
949 return (vmmaction(res));
950}
951
952int
953ctl_console(struct parse_result *res, int argc, char *argv[])
954{
955 if (argc == 2) {
956 if (parse_vmid(res, argv[1], 0) == -1)
957 errx(1, "invalid id: %s", argv[1]);
958 } else if (argc != 2)
959 ctl_usage(res->ctl);
960
961 return (vmmaction(res));
962}
963
964int
965ctl_waitfor(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_pause(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_unpause(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_send(struct parse_result *res, int argc, char *argv[])
1002{
1003 if (pledge("stdio unix sendfd unveil", NULL((void *)0)) == -1)
1004 err(1, "pledge");
1005 if (argc == 2) {
1006 if (parse_vmid(res, argv[1], 0) == -1)
1007 errx(1, "invalid id: %s", argv[1]);
1008 } else if (argc != 2)
1009 ctl_usage(res->ctl);
1010
1011 return (vmmaction(res));
1012}
1013
1014int
1015ctl_receive(struct parse_result *res, int argc, char *argv[])
1016{
1017 if (pledge("stdio unix sendfd unveil", NULL((void *)0)) == -1)
1018 err(1, "pledge");
1019 if (argc == 2) {
1020 if (parse_vmid(res, argv[1], 1) == -1)
1021 errx(1, "invalid id: %s", argv[1]);
1022 } else if (argc != 2)
1023 ctl_usage(res->ctl);
1024
1025 return (vmmaction(res));
1026}
1027
1028__dead__attribute__((__noreturn__)) void
1029ctl_openconsole(const char *name)
1030{
1031 closefrom(STDERR_FILENO2 + 1);
1032 if (unveil(VMCTL_CU"/usr/bin/cu", "x") == -1)
1033 err(1, "unveil %s", VMCTL_CU"/usr/bin/cu");
1034 execl(VMCTL_CU"/usr/bin/cu", VMCTL_CU"/usr/bin/cu", "-r", "-l", name, "-s", "115200",
1035 (char *)NULL((void *)0));
1036 err(1, "failed to open the console");
1037}