GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/vmctl/vmctl.c Lines: 0 283 0.0 %
Date: 2017-11-07 Branches: 0 167 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: vmctl.c,v 1.44 2017/09/08 07:08:49 mlarkin Exp $	*/
2
3
/*
4
 * Copyright (c) 2014 Mike Larkin <mlarkin@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/queue.h>
20
#include <sys/uio.h>
21
#include <sys/stat.h>
22
#include <sys/socket.h>
23
#include <sys/un.h>
24
25
#include <machine/vmmvar.h>
26
27
#include <ctype.h>
28
#include <err.h>
29
#include <errno.h>
30
#include <fcntl.h>
31
#include <imsg.h>
32
#include <limits.h>
33
#include <stdio.h>
34
#include <stdlib.h>
35
#include <string.h>
36
#include <unistd.h>
37
#include <util.h>
38
#include <pwd.h>
39
#include <grp.h>
40
41
#include "vmd.h"
42
#include "vmctl.h"
43
#include "atomicio.h"
44
45
extern char *__progname;
46
uint32_t info_id;
47
char info_name[VMM_MAX_NAME_LEN];
48
int info_console;
49
50
/*
51
 * vm_start
52
 *
53
 * Request vmd to start the VM defined by the supplied parameters
54
 *
55
 * Parameters:
56
 *  start_id: optional ID of the VM
57
 *  name: optional name of the VM
58
 *  memsize: memory size (MB) of the VM to create
59
 *  nnics: number of vionet network interfaces to create
60
 *  nics: switch names of the network interfaces to create
61
 *  ndisks: number of disk images
62
 *  disks: disk image file names
63
 *  kernel: kernel image to load
64
 *
65
 * Return:
66
 *  0 if the request to start the VM was sent successfully.
67
 *  ENOMEM if a memory allocation failure occurred.
68
 */
69
int
70
vm_start(uint32_t start_id, const char *name, int memsize, int nnics,
71
    char **nics, int ndisks, char **disks, char *kernel)
72
{
73
	struct vmop_create_params *vmc;
74
	struct vm_create_params *vcp;
75
	unsigned int flags = 0;
76
	int i;
77
	const char *s;
78
79
	if (memsize)
80
		flags |= VMOP_CREATE_MEMORY;
81
	if (nnics)
82
		flags |= VMOP_CREATE_NETWORK;
83
	if (ndisks)
84
		flags |= VMOP_CREATE_DISK;
85
	if (kernel)
86
		flags |= VMOP_CREATE_KERNEL;
87
	if (flags != 0) {
88
		if (memsize < 1)
89
			memsize = VM_DEFAULT_MEMORY;
90
		if (ndisks > VMM_MAX_DISKS_PER_VM)
91
			errx(1, "too many disks");
92
		else if (ndisks == 0)
93
			warnx("starting without disks");
94
		if (kernel == NULL && ndisks == 0)
95
			errx(1, "no kernel or disk specified");
96
		if (nnics == -1)
97
			nnics = 0;
98
		if (nnics > VMM_MAX_NICS_PER_VM)
99
			errx(1, "too many network interfaces");
100
		if (nnics == 0)
101
			warnx("starting without network interfaces");
102
	}
103
104
	vmc = calloc(1, sizeof(struct vmop_create_params));
105
	if (vmc == NULL)
106
		return (ENOMEM);
107
108
	vmc->vmc_flags = flags;
109
110
	/* vcp includes configuration that is shared with the kernel */
111
	vcp = &vmc->vmc_params;
112
113
	/*
114
	 * XXX: vmd(8) fills in the actual memory ranges. vmctl(8)
115
	 * just passes in the actual memory size in MB here.
116
	 */
117
	vcp->vcp_nmemranges = 1;
118
	vcp->vcp_memranges[0].vmr_size = memsize;
119
120
	vcp->vcp_ncpus = 1;
121
	vcp->vcp_ndisks = ndisks;
122
	vcp->vcp_nnics = nnics;
123
	vcp->vcp_id = start_id;
124
125
	for (i = 0 ; i < ndisks; i++)
126
		strlcpy(vcp->vcp_disks[i], disks[i], VMM_MAX_PATH_DISK);
127
	for (i = 0 ; i < nnics; i++) {
128
		vmc->vmc_ifflags[i] = VMIFF_UP;
129
130
		if (strcmp(".", nics[i]) == 0) {
131
			/* Add a "local" interface */
132
			strlcpy(vmc->vmc_ifswitch[i], "", IF_NAMESIZE);
133
			vmc->vmc_ifflags[i] |= VMIFF_LOCAL;
134
		} else {
135
			/* Add an interface to a switch */
136
			strlcpy(vmc->vmc_ifswitch[i], nics[i], IF_NAMESIZE);
137
		}
138
	}
139
	if (name != NULL) {
140
		/* Allow VMs names with alphanumeric characters, dot, hyphen
141
		 * and underscore. But disallow dot, hyphen and underscore at
142
		 * the start.
143
		 */
144
		if (*name == '-' || *name == '.' || *name == '_')
145
			errx(1, "Invalid VM name");
146
147
		for (s = name; *s != '\0'; ++s) {
148
			if (!(isalnum(*s) || *s == '.' || *s == '-' ||
149
			      *s == '_'))
150
				errx(1, "Invalid VM name");
151
		}
152
153
		strlcpy(vcp->vcp_name, name, VMM_MAX_NAME_LEN);
154
	}
155
	if (kernel != NULL)
156
		strlcpy(vcp->vcp_kernel, kernel, VMM_MAX_KERNEL_PATH);
157
158
	imsg_compose(ibuf, IMSG_VMDOP_START_VM_REQUEST, 0, 0, -1,
159
	    vmc, sizeof(struct vmop_create_params));
160
161
	free(vcp);
162
	return (0);
163
}
164
165
/*
166
 * vm_start_complete
167
 *
168
 * Callback function invoked when we are expecting an
169
 * IMSG_VMDOP_START_VMM_RESPONSE message indicating the completion of
170
 * a start vm operation.
171
 *
172
 * Parameters:
173
 *  imsg : response imsg received from vmd
174
 *  ret  : return value
175
 *  autoconnect : open the console after startup
176
 *
177
 * Return:
178
 *  Always 1 to indicate we have processed the return message (even if it
179
 *  was an incorrect/failure message)
180
 *
181
 *  The function also sets 'ret' to the error code as follows:
182
 *   0     : Message successfully processed
183
 *   EINVAL: Invalid or unexpected response from vmd
184
 *   EIO   : vm_start command failed
185
 *   ENOENT: a specified component of the VM could not be found (disk image,
186
 *    BIOS firmware image, etc)
187
 */
188
int
189
vm_start_complete(struct imsg *imsg, int *ret, int autoconnect)
190
{
191
	struct vmop_result *vmr;
192
	int res;
193
194
	if (imsg->hdr.type == IMSG_VMDOP_START_VM_RESPONSE) {
195
		vmr = (struct vmop_result *)imsg->data;
196
		res = vmr->vmr_result;
197
		if (res) {
198
			switch (res) {
199
			case VMD_BIOS_MISSING:
200
				warnx("vmm bios firmware file not found.");
201
				*ret = ENOENT;
202
				break;
203
			case VMD_DISK_MISSING:
204
				warnx("could not find specified disk image(s)");
205
				*ret = ENOENT;
206
				break;
207
			case VMD_DISK_INVALID:
208
				warnx("specified disk image(s) are "
209
				    "not regular files");
210
				*ret = ENOENT;
211
				break;
212
			default:
213
				errno = res;
214
				warn("start vm command failed");
215
				*ret = EIO;
216
			}
217
		} else if (autoconnect) {
218
			/* does not return */
219
			ctl_openconsole(vmr->vmr_ttyname);
220
		} else {
221
			warnx("started vm %d successfully, tty %s",
222
			    vmr->vmr_id, vmr->vmr_ttyname);
223
			*ret = 0;
224
		}
225
	} else {
226
		warnx("unexpected response received from vmd");
227
		*ret = EINVAL;
228
	}
229
230
	return (1);
231
}
232
233
void
234
send_vm(uint32_t id, const char *name)
235
{
236
	struct vmop_id vid;
237
	int fds[2], readn, writen;
238
	long pagesz;
239
	char *buf;
240
241
	pagesz = getpagesize();
242
	buf = malloc(pagesz);
243
	if (buf == NULL)
244
		errx(1, "%s: memory allocation failure", __func__);
245
246
	memset(&vid, 0, sizeof(vid));
247
	vid.vid_id = id;
248
	if (name != NULL)
249
		strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
250
	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) == -1) {
251
		warnx("%s: socketpair creation failed", __func__);
252
	} else {
253
		imsg_compose(ibuf, IMSG_VMDOP_SEND_VM_REQUEST, 0, 0, fds[0],
254
				&vid, sizeof(vid));
255
		imsg_flush(ibuf);
256
		while (1) {
257
			readn = atomicio(read, fds[1], buf, pagesz);
258
			if (!readn)
259
				break;
260
			writen = atomicio(vwrite, STDOUT_FILENO, buf,
261
					readn);
262
			if (writen != readn)
263
				break;
264
		}
265
		if (vid.vid_id)
266
			warnx("sent vm %d successfully", vid.vid_id);
267
		else
268
			warnx("sent vm %s successfully", vid.vid_name);
269
	}
270
271
	free(buf);
272
}
273
274
void
275
vm_receive(uint32_t id, const char *name)
276
{
277
	struct vmop_id vid;
278
	int fds[2], readn, writen;
279
	long pagesz;
280
	char *buf;
281
282
	pagesz = getpagesize();
283
	buf = malloc(pagesz);
284
	if (buf == NULL)
285
		errx(1, "%s: memory allocation failure", __func__);
286
287
	memset(&vid, 0, sizeof(vid));
288
	if (name != NULL)
289
		strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
290
	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) == -1) {
291
		warnx("%s: socketpair creation failed", __func__);
292
	} else {
293
		imsg_compose(ibuf, IMSG_VMDOP_RECEIVE_VM_REQUEST, 0, 0, fds[0],
294
		    &vid, sizeof(vid));
295
		imsg_flush(ibuf);
296
		while (1) {
297
			readn = atomicio(read, STDIN_FILENO, buf, pagesz);
298
			if (!readn) {
299
				close(fds[1]);
300
				break;
301
			}
302
			writen = atomicio(vwrite, fds[1], buf, readn);
303
			if (writen != readn)
304
				break;
305
		}
306
	}
307
308
	free(buf);
309
}
310
311
void
312
pause_vm(uint32_t pause_id, const char *name)
313
{
314
	struct vmop_id vid;
315
316
	memset(&vid, 0, sizeof(vid));
317
	vid.vid_id = pause_id;
318
	if (name != NULL)
319
		(void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
320
321
	imsg_compose(ibuf, IMSG_VMDOP_PAUSE_VM, 0, 0, -1,
322
	    &vid, sizeof(vid));
323
}
324
325
int
326
pause_vm_complete(struct imsg *imsg, int *ret)
327
{
328
	struct vmop_result *vmr;
329
	int res;
330
331
	if (imsg->hdr.type == IMSG_VMDOP_PAUSE_VM_RESPONSE) {
332
		vmr = (struct vmop_result *)imsg->data;
333
		res = vmr->vmr_result;
334
		if (res) {
335
			errno = res;
336
			warn("pause vm command failed");
337
			*ret = EIO;
338
		} else {
339
			warnx("paused vm %d successfully", vmr->vmr_id);
340
			*ret = 0;
341
		}
342
	} else {
343
		warnx("unexpected response received from vmd");
344
		*ret = EINVAL;
345
	}
346
347
	return (1);
348
}
349
350
void
351
unpause_vm(uint32_t pause_id, const char *name)
352
{
353
	struct vmop_id vid;
354
355
	memset(&vid, 0, sizeof(vid));
356
	vid.vid_id = pause_id;
357
	if (name != NULL)
358
		(void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
359
360
	imsg_compose(ibuf, IMSG_VMDOP_UNPAUSE_VM, 0, 0, -1,
361
	    &vid, sizeof(vid));
362
}
363
364
int
365
unpause_vm_complete(struct imsg *imsg, int *ret)
366
{
367
	struct vmop_result *vmr;
368
	int res;
369
370
	if (imsg->hdr.type == IMSG_VMDOP_UNPAUSE_VM_RESPONSE) {
371
		vmr = (struct vmop_result *)imsg->data;
372
		res = vmr->vmr_result;
373
		if (res) {
374
			errno = res;
375
			warn("unpause vm command failed");
376
			*ret = EIO;
377
		} else {
378
			warnx("unpaused vm %d successfully", vmr->vmr_id);
379
			*ret = 0;
380
		}
381
	} else {
382
		warnx("unexpected response received from vmd");
383
		*ret = EINVAL;
384
	}
385
386
	return (1);
387
}
388
389
/*
390
 * terminate_vm
391
 *
392
 * Request vmd to stop the VM indicated
393
 *
394
 * Parameters:
395
 *  terminate_id: ID of the vm to be terminated
396
 *  name: optional name of the VM to be terminated
397
 */
398
void
399
terminate_vm(uint32_t terminate_id, const char *name)
400
{
401
	struct vmop_id vid;
402
403
	memset(&vid, 0, sizeof(vid));
404
	vid.vid_id = terminate_id;
405
	if (name != NULL)
406
		(void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
407
408
	imsg_compose(ibuf, IMSG_VMDOP_TERMINATE_VM_REQUEST, 0, 0, -1,
409
	    &vid, sizeof(vid));
410
}
411
412
/*
413
 * terminate_vm_complete
414
 *
415
 * Callback function invoked when we are expecting an
416
 * IMSG_VMDOP_TERMINATE_VMM_RESPONSE message indicating the completion of
417
 * a terminate vm operation.
418
 *
419
 * Parameters:
420
 *  imsg : response imsg received from vmd
421
 *  ret  : return value
422
 *
423
 * Return:
424
 *  Always 1 to indicate we have processed the return message (even if it
425
 *  was an incorrect/failure message)
426
 *
427
 *  The function also sets 'ret' to the error code as follows:
428
 *   0     : Message successfully processed
429
 *   EINVAL: Invalid or unexpected response from vmd
430
 *   EIO   : terminate_vm command failed
431
 */
432
int
433
terminate_vm_complete(struct imsg *imsg, int *ret)
434
{
435
	struct vmop_result *vmr;
436
	int res;
437
438
	if (imsg->hdr.type == IMSG_VMDOP_TERMINATE_VM_RESPONSE) {
439
		vmr = (struct vmop_result *)imsg->data;
440
		res = vmr->vmr_result;
441
		if (res) {
442
			switch (res) {
443
			case VMD_VM_STOP_INVALID:
444
				warnx("cannot stop vm that is not running");
445
				*ret = EINVAL;
446
				break;
447
			case ENOENT:
448
				warnx("vm not found");
449
				*ret = EIO;
450
				break;
451
			default:
452
				warn("terminate vm command failed");
453
				*ret = EIO;
454
			}
455
		} else {
456
			warnx("sent request to terminate vm %d", vmr->vmr_id);
457
			*ret = 0;
458
		}
459
	} else {
460
		warnx("unexpected response received from vmd");
461
		*ret = EINVAL;
462
	}
463
	errno = *ret;
464
465
	return (1);
466
}
467
468
/*
469
 * get_info_vm
470
 *
471
 * Return the list of all running VMs or find a specific VM by ID or name.
472
 *
473
 * Parameters:
474
 *  id: optional ID of a VM to list
475
 *  name: optional name of a VM to list
476
 *  console: if true, open the console of the selected VM (by name or ID)
477
 *
478
 * Request a list of running VMs from vmd
479
 */
480
void
481
get_info_vm(uint32_t id, const char *name, int console)
482
{
483
	info_id = id;
484
	if (name != NULL)
485
		(void)strlcpy(info_name, name, sizeof(info_name));
486
	info_console = console;
487
	imsg_compose(ibuf, IMSG_VMDOP_GET_INFO_VM_REQUEST, 0, 0, -1, NULL, 0);
488
}
489
490
/*
491
 * check_info_id
492
 *
493
 * Check if requested name or ID of a VM matches specified arguments
494
 *
495
 * Parameters:
496
 *  name: name of the VM
497
 *  id: ID of the VM
498
 */
499
int
500
check_info_id(const char *name, uint32_t id)
501
{
502
	if (info_id == 0 && *info_name == '\0')
503
		return (-1);
504
	if (info_id != 0 && info_id == id)
505
		return (1);
506
	if (*info_name != '\0' && name && strcmp(info_name, name) == 0)
507
		return (1);
508
	return (0);
509
}
510
511
/*
512
 * add_info
513
 *
514
 * Callback function invoked when we are expecting an
515
 * IMSG_VMDOP_GET_INFO_VM_DATA message indicating the receipt of additional
516
 * "list vm" data, or an IMSG_VMDOP_GET_INFO_VM_END_DATA message indicating
517
 * that no additional "list vm" data will be forthcoming.
518
 *
519
 * Parameters:
520
 *  imsg : response imsg received from vmd
521
 *  ret  : return value
522
 *
523
 * Return:
524
 *  0     : the returned data was successfully added to the "list vm" data.
525
 *          The caller can expect more data.
526
 *  1     : IMSG_VMDOP_GET_INFO_VM_END_DATA received (caller should not call
527
 *          add_info again), or an error occurred adding the returned data
528
 *          to the "list vm" data. The caller should check the value of
529
 *          'ret' to determine which case occurred.
530
 *
531
 * This function does not return if a VM is found and info_console is set.
532
 *
533
 *  The function also sets 'ret' to the error code as follows:
534
 *   0     : Message successfully processed
535
 *   EINVAL: Invalid or unexpected response from vmd
536
 *   ENOMEM: memory allocation failure
537
 */
538
int
539
add_info(struct imsg *imsg, int *ret)
540
{
541
	static size_t ct = 0;
542
	static struct vmop_info_result *vir = NULL;
543
544
	if (imsg->hdr.type == IMSG_VMDOP_GET_INFO_VM_DATA) {
545
		vir = reallocarray(vir, ct + 1,
546
		    sizeof(struct vmop_info_result));
547
		if (vir == NULL) {
548
			*ret = ENOMEM;
549
			return (1);
550
		}
551
		memcpy(&vir[ct], imsg->data, sizeof(struct vmop_info_result));
552
		ct++;
553
		*ret = 0;
554
		return (0);
555
	} else if (imsg->hdr.type == IMSG_VMDOP_GET_INFO_VM_END_DATA) {
556
		if (info_console)
557
			vm_console(vir, ct);
558
		else
559
			print_vm_info(vir, ct);
560
		free(vir);
561
		*ret = 0;
562
		return (1);
563
	} else {
564
		*ret = EINVAL;
565
		return (1);
566
	}
567
}
568
569
/*
570
 * print_vm_info
571
 *
572
 * Prints the vm information returned from vmd in 'list' to stdout.
573
 *
574
 * Parameters
575
 *  list: the vm information (consolidated) returned from vmd via imsg
576
 *  ct  : the size (number of elements in 'list') of the result
577
 */
578
void
579
print_vm_info(struct vmop_info_result *list, size_t ct)
580
{
581
	struct vm_info_result *vir;
582
	struct vmop_info_result *vmi;
583
	size_t i, j;
584
	char *vcpu_state, *tty;
585
	char curmem[FMT_SCALED_STRSIZE];
586
	char maxmem[FMT_SCALED_STRSIZE];
587
	char user[16], group[16];
588
	struct passwd *pw;
589
	struct group *gr;
590
591
	printf("%5s %5s %5s %7s %7s %7s %12s %s\n", "ID", "PID", "VCPUS",
592
	    "MAXMEM", "CURMEM", "TTY", "OWNER", "NAME");
593
594
	for (i = 0; i < ct; i++) {
595
		vmi = &list[i];
596
		vir = &vmi->vir_info;
597
		if (check_info_id(vir->vir_name, vir->vir_id)) {
598
			/* get user name */
599
			if ((pw = getpwuid(vmi->vir_uid)) == NULL)
600
				(void)snprintf(user, sizeof(user),
601
				    "%d", vmi->vir_uid);
602
			else
603
				(void)strlcpy(user, pw->pw_name,
604
				    sizeof(user));
605
			/* get group name */
606
			if (vmi->vir_gid != -1) {
607
				if (vmi->vir_uid == 0)
608
					*user = '\0';
609
				if ((gr = getgrgid(vmi->vir_gid)) == NULL)
610
					(void)snprintf(group, sizeof(group),
611
					    ":%lld", vmi->vir_gid);
612
				else
613
					(void)snprintf(group, sizeof(group),
614
					    ":%s", gr->gr_name);
615
				(void)strlcat(user, group, sizeof(user));
616
			}
617
618
			(void)strlcpy(curmem, "-", sizeof(curmem));
619
			(void)strlcpy(maxmem, "-", sizeof(maxmem));
620
621
			(void)fmt_scaled(vir->vir_memory_size * 1024 * 1024,
622
			    maxmem);
623
624
			if (vir->vir_creator_pid != 0 && vir->vir_id != 0) {
625
				if (*vmi->vir_ttyname == '\0')
626
					tty = "-";
627
				/* get tty - skip /dev/ path */
628
				else if ((tty = strrchr(vmi->vir_ttyname,
629
				    '/')) == NULL || ++tty == '\0')
630
					tty = list[i].vir_ttyname;
631
632
				(void)fmt_scaled(vir->vir_used_size, curmem);
633
634
				/* running vm */
635
				printf("%5u %5u %5zd %7s %7s %7s %12s %s\n",
636
				    vir->vir_id, vir->vir_creator_pid,
637
				    vir->vir_ncpus, maxmem, curmem,
638
				    tty, user, vir->vir_name);
639
			} else {
640
				/* disabled vm */
641
				printf("%5u %5s %5zd %7s %7s %7s %12s %s\n",
642
				    vir->vir_id, "-",
643
				    vir->vir_ncpus, maxmem, curmem,
644
				    "-", user, vir->vir_name);
645
			}
646
		}
647
		if (check_info_id(vir->vir_name, vir->vir_id) > 0) {
648
			for (j = 0; j < vir->vir_ncpus; j++) {
649
				if (vir->vir_vcpu_state[j] ==
650
				    VCPU_STATE_STOPPED)
651
					vcpu_state = "STOPPED";
652
				else if (vir->vir_vcpu_state[j] ==
653
				    VCPU_STATE_RUNNING)
654
					vcpu_state = "RUNNING";
655
				else
656
					vcpu_state = "UNKNOWN";
657
658
				printf(" VCPU: %2zd STATE: %s\n",
659
				    j, vcpu_state);
660
			}
661
		}
662
	}
663
}
664
665
/*
666
 * vm_console
667
 *
668
 * Connects to the vm console returned from vmd in 'list'.
669
 *
670
 * Parameters
671
 *  list: the vm information (consolidated) returned from vmd via imsg
672
 *  ct  : the size (number of elements in 'list') of the result
673
 */
674
__dead void
675
vm_console(struct vmop_info_result *list, size_t ct)
676
{
677
	struct vmop_info_result *vir;
678
	size_t i;
679
680
	for (i = 0; i < ct; i++) {
681
		vir = &list[i];
682
		if ((check_info_id(vir->vir_info.vir_name,
683
		    vir->vir_info.vir_id) > 0) &&
684
			(vir->vir_ttyname[0] != '\0')) {
685
			/* does not return */
686
			ctl_openconsole(vir->vir_ttyname);
687
		}
688
	}
689
690
	errx(1, "console %d not found", info_id);
691
}
692
693
/*
694
 * create_imagefile
695
 *
696
 * Create an empty imagefile with the specified path and size.
697
 *
698
 * Parameters:
699
 *  imgfile_path: path to the image file to create
700
 *  imgsize     : size of the image file to create (in MB)
701
 *
702
 * Return:
703
 *  EEXIST: The requested image file already exists
704
 *  0     : Image file successfully created
705
 *  Exxxx : Various other Exxxx errno codes due to other I/O errors
706
 */
707
int
708
create_imagefile(const char *imgfile_path, long imgsize)
709
{
710
	int fd, ret;
711
712
	/* Refuse to overwrite an existing image */
713
	fd = open(imgfile_path, O_RDWR | O_CREAT | O_TRUNC | O_EXCL,
714
	    S_IRUSR | S_IWUSR);
715
	if (fd == -1)
716
		return (errno);
717
718
	/* Extend to desired size */
719
	if (ftruncate(fd, (off_t)imgsize * 1024 * 1024) == -1) {
720
		ret = errno;
721
		close(fd);
722
		unlink(imgfile_path);
723
		return (ret);
724
	}
725
726
	ret = close(fd);
727
	return (ret);
728
}