summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorMike Larkin <mlarkin@cvs.openbsd.org>2015-11-22 20:22:54 +0000
committerMike Larkin <mlarkin@cvs.openbsd.org>2015-11-22 20:22:54 +0000
commit3880dc1488e181c9a29da8cb02990f70a9f3e4d6 (patch)
treed249a6d1e2dfa017c3c10284e232c4db1de6a63e /usr.sbin
parentd21316b1592a57efd1bfc34d4465f5528d4ae5d9 (diff)
vmmctl(8) - vmm subsystem control
There is still a lot to be done, and fixed, in these userland components but I have received enough "it works, commit it" emails that it's time to finish those things in tree. discussed with many, tested by many.
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/vmmctl/Makefile24
-rw-r--r--usr.sbin/vmmctl/vmmctl.8125
-rw-r--r--usr.sbin/vmmctl/vmmctl.c835
3 files changed, 984 insertions, 0 deletions
diff --git a/usr.sbin/vmmctl/Makefile b/usr.sbin/vmmctl/Makefile
new file mode 100644
index 00000000000..2c0c1e878f1
--- /dev/null
+++ b/usr.sbin/vmmctl/Makefile
@@ -0,0 +1,24 @@
+
+.if ${MACHINE} == "amd64"
+
+PROG= vmmctl
+SRCS= vmmctl.c
+CFLAGS+= -Wall
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+CFLAGS+= -Wsign-compare
+CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../vmd
+LDADD+= -lutil
+DPADD+= ${LIBUTIL}
+
+.else
+
+NOPROG= yes
+
+.endif
+
+MAN= vmmctl.8
+MANSUBDIR=${MACHINE}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/vmmctl/vmmctl.8 b/usr.sbin/vmmctl/vmmctl.8
new file mode 100644
index 00000000000..3ece162b75d
--- /dev/null
+++ b/usr.sbin/vmmctl/vmmctl.8
@@ -0,0 +1,125 @@
+.\"
+.\"Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
+.\"
+.\"Permission to use, copy, modify, and distribute this software for any
+.\"purpose with or without fee is hereby granted, provided that the above
+.\"copyright notice and this permission notice appear in all copies.
+.\"
+.\"THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\"WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\"MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\"ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\"WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\"ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\"OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.Dd $Mdocdate: November 22 2015 $
+.Dt VMMCTL 8
+.Os
+.Sh NAME
+.Nm vmmctl
+.Nd control VMM subsystem
+.Sh SYNOPSIS
+.Nm
+.Op Fl de
+.Nm
+.Op Fl C
+.Op Fl i Ar imagefile path
+.Op Fl s Ar size in MB
+.Nm
+.Op Fl S
+.Op Fl m Ar memory size
+.Op Fl n Ar nr nics
+.Op Fl b Ar diskfile
+.Op Fl k Ar kernel
+.Nm
+.Op Fl T
+.Op id
+.Nm
+.Op Fl I
+.Op id
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to control the virtual machine monitor (VMM) subsystem.
+A VMM manages virtual machines (VMs) on a
+.Ar host .
+The VMM subsystem is responsible for creating, destroying, and executing
+VMs.
+
+.Sh VMM OPERATIONS
+The options are as follows:
+.Bl -tag -width Dsssigfile
+.It Fl d
+Disable the VMM subsystem. Virtual machines running on the host should be
+terminated first.
+.It Fl e
+Enable the VMM subsystem. The VMM subsystem must be enabled before VMs
+can be managed on the host.
+.El
+.Pp
+Generally, the
+.Nm
+utility is run with -e option during system startup to enable the VMM
+subsystem on boot. This can be automated via the
+.Xr rc 8
+and
+.Xr rc.conf 8
+facilities used during system startup.
+.Sh VM OPERATIONS
+The options are as follows:
+.Bl -tag -width Dsssignature
+.It Fl C
+Creates a VM disk image file with the specified pathname and size in MB.
+.It Fl S
+Starts a VM defined by the specified parameters.
+.It Fl m
+Memory size (in MB) of the VM
+.It Fl n
+Number of network interfaces to add to the VM
+.It Fl b
+Disk image file (may be specified multiple times to add multiple disk images).
+.It Fl k
+Kernel to load when booting the VM
+.It Fl T
+Terminates (stops) a VM defined by the specified VM ID.
+.It Fl I
+Lists VMs running on the host, optionally listing just the selected VM ID.
+.El
+
+.Bl -tag -width Dsssignature
+.Sh EXIT STATUS
+.Ex -std vmmctl
+.Nm
+may fail due to one of the following reasons:
+.Pp
+.Bl -bullet -compact
+.It
+The VMM subsystem could not be enabled or disabled as requested
+.It
+A requested VM-based operation could not be completed
+.El
+.Sh EXAMPLES
+Enable the VMM subsystem
+.Dl $ vmmctl -e
+.Pp
+Disable the VMM subsystem
+.Dl $ vmmctl -d
+.Pp
+Create a new VM with 512MB memory, 1 network interface, one disk image
+('disk.img') and boot from kernel '/bsd'.
+.Dl $ vmmctl -S -m 512 -n 1 -b disk.img -k /bsd
+.Pp
+Terminate VM number 1
+.Dl $ vmmctl -T1
+.Pp
+.Sh SEE ALSO
+.Xr vmm 4 ,
+.Xr vmd 8 ,
+.Xr rc.conf 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Ox 5.9 .
+.Sh AUTHORS
+.An Mike Larkin Aq Mt mlarkin@openbsd.org
diff --git a/usr.sbin/vmmctl/vmmctl.c b/usr.sbin/vmmctl/vmmctl.c
new file mode 100644
index 00000000000..967641a425f
--- /dev/null
+++ b/usr.sbin/vmmctl/vmmctl.c
@@ -0,0 +1,835 @@
+/*
+ * Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * vmmctl(8) - control VMM subsystem
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <machine/vmmvar.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <imsg.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "vmd.h"
+
+__dead void usage(void);
+int main(int, char **);
+int create_imagefile(char *, long);
+void enable_vmm(void);
+int enable_vmm_complete(struct imsg *, int *);
+void disable_vmm(void);
+int disable_vmm_complete(struct imsg *, int *);
+int start_vm(int, int, int, char **, char *);
+int start_vm_complete(struct imsg *, int *);
+void terminate_vm(uint32_t);
+int terminate_vm_complete(struct imsg *, int *);
+void get_info_vm(void);
+int add_info(struct imsg *, int *);
+void print_vm_info(struct vm_info_result *, size_t);
+
+#define CMD_ENABLE 0x1
+#define CMD_DISABLE 0x2
+#define CMD_CREATE 0x4
+#define CMD_START 0x8
+#define CMD_TERMINATE 0x10
+#define CMD_INFO 0x20
+
+struct imsgbuf *ibuf;
+extern char *__progname;
+uint32_t info_id;
+
+/*
+ * usage
+ *
+ * print program usage. does not return
+ */
+__dead void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-de]\n"
+ " %s [-C] [-i imagefile path] [-s size in MB]\n"
+ " %s [-S] [-m memory size][-n nr nics][-b diskfile]"
+ "[-k kernel]\n"
+ " %s [-T[id]]\n"
+ " %s [-I[id]]\n",
+ __progname, __progname, __progname, __progname,
+ __progname);
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch, command, ret, fd, processed, n, memsize, nnics, ndisks, i;
+ const char *errstr;
+ char *imgfile, *sockname;
+ char *disks[VMM_MAX_DISKS_PER_VM];
+ char *kernel;
+ long imgsize;
+ struct sockaddr_un sun;
+ struct imsg imsg;
+ uint32_t terminate_id;
+
+ command = 0;
+ imgfile = NULL;
+ imgsize = 0;
+ info_id = 0;
+ terminate_id = 0;
+ memsize = 0;
+ nnics = 0;
+ ndisks = 0;
+ kernel = NULL;
+
+ for (i = 0 ; i < VMM_MAX_DISKS_PER_VM; i++) {
+ disks[i] = malloc(VMM_MAX_PATH_DISK);
+ if (disks[i] == NULL) {
+ fprintf(stderr, "memory allocation error\n");
+ exit(1);
+ }
+
+ bzero(disks[i], VMM_MAX_PATH_DISK);
+ }
+
+
+ while ((ch = getopt(argc, argv, "CST::I::dei:s:m:n:b:k:")) != -1) {
+ switch(ch) {
+ case 'C':
+ /* Create imagefile command */
+ if (command)
+ usage();
+
+ command = CMD_CREATE;
+ break;
+ case 'S':
+ /* Start command */
+ if (command)
+ usage();
+
+ command = CMD_START;
+ break;
+ case 'T':
+ /* Terminate command */
+ if (command)
+ usage();
+
+ command = CMD_TERMINATE;
+ if (optarg != NULL) {
+ terminate_id = strtonum(optarg, 1, UINT_MAX,
+ &errstr);
+ if (errstr) {
+ fprintf(stderr,
+ "%s: invalid VM ID (%s) specified: "
+ "%s\n", __progname, optarg, errstr);
+ usage();
+ }
+ } else {
+ fprintf(stderr, "%s: missing VM ID parameter\n",
+ __progname);
+ usage();
+ }
+
+ if (terminate_id == 0) {
+ fprintf(stderr, "%s: invalid vm ID supplied\n",
+ __progname);
+ usage();
+ }
+ break;
+ case 'I':
+ /* Info command */
+ if (command)
+ usage();
+
+ command = CMD_INFO;
+ if (optarg != NULL) {
+ info_id = strtonum(optarg, 1, UINT_MAX,
+ &errstr);
+ if (errstr) {
+ fprintf(stderr,
+ "%s: invalid VM ID (%s) specified: "
+ "%s\n", __progname, optarg, errstr);
+ usage();
+ }
+ }
+ break;
+ case 'd':
+ /* Disable VMM mode */
+ if (command)
+ usage();
+
+ command = CMD_DISABLE;
+ break;
+ case 'e':
+ /* Enable VMM mode */
+ if (command)
+ usage();
+
+ command = CMD_ENABLE;
+ break;
+ case 'i':
+ /* Imagefile name parameter */
+ if (imgfile)
+ usage();
+
+ imgfile = strdup(optarg);
+ break;
+ case 's':
+ /* Imagefile size parameter */
+ if (imgsize != 0)
+ usage();
+
+ imgsize = strtonum(optarg, 1, LONG_MAX, &errstr);
+ if (errstr) {
+ fprintf(stderr,
+ "%s: invalid image size (%s) specified: "
+ "%s\n", __progname, optarg, errstr);
+ usage();
+ }
+ break;
+ case 'm':
+ /* VM memory parameter */
+ if (memsize !=0)
+ usage();
+
+ memsize = strtonum(optarg, 1, VMM_MAX_VM_MEM_SIZE,
+ &errstr);
+
+ if (errstr) {
+ fprintf(stderr," %s: invalid memory size (%s) "
+ "specified: %s\n", __progname, optarg,
+ errstr);
+ }
+ break;
+ case 'n':
+ /* VM num nics parameter */
+ if (nnics !=0)
+ usage();
+
+ nnics = strtonum(optarg, 1, VMM_MAX_VM_MEM_SIZE,
+ &errstr);
+
+ if (errstr) {
+ fprintf(stderr," %s: invalid number of nics "
+ " (%s) specified: %s\n", __progname,
+ optarg, errstr);
+ }
+ break;
+ case 'b':
+ /* VM disk parameter */
+ if (ndisks < VMM_MAX_DISKS_PER_VM) {
+ strlcpy(disks[ndisks], optarg,
+ VMM_MAX_PATH_DISK);
+ ndisks++;
+ } else {
+ fprintf(stderr, "%s: maximum number of disks "
+ "reached, ignoring disk %s\n", __progname,
+ optarg);
+ }
+ break;
+ case 'k':
+ /* VM kernel parameter */
+ if (kernel != NULL)
+ usage();
+
+ kernel = malloc(VMM_MAX_KERNEL_PATH);
+ if (kernel == NULL) {
+ fprintf(stderr, "memory allocation error\n");
+ exit(1);
+ }
+
+ strlcpy(kernel, optarg, VMM_MAX_KERNEL_PATH);
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (!command)
+ usage();
+
+ /* Set up comms via imsg with vmd, unless CMD_CREATE (imgfile) */
+ if (command != CMD_CREATE) {
+ sockname = SOCKET_NAME;
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ err(1, "socket init failed");
+
+ bzero(&sun, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ if (strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path)) >=
+ sizeof(sun.sun_path))
+ errx(1, "socket name too long");
+
+ if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
+ err(1, "connect failed to vmd control socket");
+
+ if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
+ err(1, NULL);
+ imsg_init(ibuf, fd);
+ }
+
+ if (command == CMD_DISABLE)
+ disable_vmm();
+
+ if (command == CMD_ENABLE)
+ enable_vmm();
+
+ if (command == CMD_CREATE) {
+ if (imgfile == NULL) {
+ fprintf(stderr, "%s: missing -i (imagefile path) "
+ "argument\n", __progname);
+ exit(1);
+ }
+
+ if (imgsize <= 0) {
+ fprintf(stderr, "%s: missing/invalid -s (imgfile size) "
+ "argument\n", __progname);
+ exit(1);
+ }
+
+ ret = create_imagefile(imgfile, imgsize);
+ if (ret) {
+ fprintf(stderr, "%s: create imagefile operation failed "
+ "(%s)\n", __progname, strerror(ret));
+ exit(ret);
+ } else {
+ fprintf(stdout, "%s: imagefile created\n", __progname);
+ exit(0);
+ }
+ }
+
+ if (command == CMD_START) {
+ if (memsize == 0) {
+ fprintf(stderr, "%s: missing -m (memory size) "
+ "argument\n", __progname);
+ exit(1);
+ }
+
+ ret = start_vm(memsize, nnics, ndisks, disks, kernel);
+ if (ret) {
+ fprintf(stderr, "%s: start VM operation failed "
+ "(%s)\n", __progname, strerror(ret));
+ exit(ret);
+ }
+ }
+
+ if (command == CMD_TERMINATE)
+ terminate_vm(terminate_id);
+
+ if (command == CMD_INFO)
+ get_info_vm();
+
+ /* Send request message */
+ while (ibuf->w.queued)
+ if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
+ err(1, "write error");
+
+ /*
+ * We expect vmd to send us one reply message, and that the reply
+ * message we receive will be of the proper _REPLY type.
+ *
+ * The exception to this is the -I (get info) option, where vmd
+ * may send us arbitrary number of _REPLY messages followed by
+ * and _END message to indicate no more messages are forthcoming.
+ */
+ processed = 0;
+ while (!processed) {
+ if ((n = imsg_read(ibuf)) == -1)
+ err(1, "imsg_read error");
+ if (n == 0)
+ errx(1, "pipe closed");
+
+ while (!processed) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ err(1, "imsg_get error");
+ if (n == 0)
+ break;
+
+ switch (command) {
+ case CMD_DISABLE:
+ processed = disable_vmm_complete(&imsg,
+ &ret);
+ break;
+ case CMD_ENABLE:
+ processed = enable_vmm_complete(&imsg,
+ &ret);
+ break;
+ case CMD_START:
+ processed = start_vm_complete(&imsg,
+ &ret);
+ break;
+ case CMD_TERMINATE:
+ processed = terminate_vm_complete(&imsg,
+ &ret);
+ break;
+ case CMD_INFO:
+ processed = add_info(&imsg, &ret);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ }
+
+ return (ret);
+}
+
+/*
+ * enable_vmm
+ *
+ * Request vmd to enable VMM mode on the machine. This will result in the
+ * appropriate instruction sequence (eg, vmxon) being executed in order
+ * for the CPU to later service VMs.
+ */
+void
+enable_vmm(void)
+{
+ imsg_compose(ibuf, IMSG_VMDOP_ENABLE_VMM_REQUEST, 0, 0, -1, NULL, 0);
+}
+
+/*
+ * enable_vmm_complete
+ *
+ * Callback function invoked when we are expecting an
+ * IMSG_VMDOP_ENABLE_VMM_RESPONSE message indicating the completion of
+ * an enable vmm operation.
+ *
+ * Parameters:
+ * imsg : response imsg received from vmd
+ * ret : return value
+ *
+ * Return:
+ * Always 1 to indicate we have processed the return message (even if it
+ * was an incorrect/failure message).
+ *
+ * The function also sets 'ret' to the error code as follows:
+ * 0 : Message successfully processed
+ * EINVAL: Invalid or unexpected response from vmd
+ * EIO : enable_vmm command failed
+ */
+int
+enable_vmm_complete(struct imsg *imsg, int *ret)
+{
+ int res;
+
+ if (imsg->hdr.type == IMSG_VMDOP_ENABLE_VMM_RESPONSE) {
+ res = *(int *)imsg->data;
+ if (res) {
+ fprintf(stderr, "%s: enable VMM command failed (%d) - "
+ "%s\n", __progname, res, strerror(res));
+ *ret = EIO;
+ } else {
+ fprintf(stdout, "%s: enable VMM command successful\n",
+ __progname);
+ *ret = 0;
+ }
+ } else {
+ fprintf(stderr, "%s: unexpected response received from vmd\n",
+ __progname);
+ *ret = EINVAL;
+ }
+
+ return (1);
+}
+
+/*
+ * disable_vmm
+ *
+ * Request vmd to disable VMM mode on the machine.
+ */
+void
+disable_vmm(void)
+{
+ imsg_compose(ibuf, IMSG_VMDOP_DISABLE_VMM_REQUEST, 0, 0, -1, NULL, 0);
+}
+
+/*
+ * disable_vmm_complete
+ *
+ * Callback function invoked when we are expecting an
+ * IMSG_VMDOP_DISABLE_VMM_RESPONSE message indicating the completion of
+ * a disable vmm operation.
+ *
+ * Parameters:
+ * imsg : response imsg received from vmd
+ * ret : return value
+ *
+ * Return:
+ * Always 1 to indicate we have processed the return message (even if it
+ * was an incorrect/failure message)
+ *
+ * The function also sets 'ret' to the error code as follows:
+ * 0 : Message successfully processed
+ * EINVAL: Invalid or unexpected response from vmd
+ * EIO : disable_vmm command failed
+ */
+int
+disable_vmm_complete(struct imsg *imsg, int *ret)
+{
+ int res;
+
+ if (imsg->hdr.type == IMSG_VMDOP_DISABLE_VMM_RESPONSE) {
+ res = *(int *)imsg->data;
+ if (res) {
+ fprintf(stderr, "%s: disable VMM command failed (%d) - "
+ "%s\n", __progname, res, strerror(res));
+ *ret = EIO;
+ } else {
+ fprintf(stdout, "%s: disable VMM command successful\n",
+ __progname);
+ *ret = 0;
+ }
+ } else {
+ fprintf(stderr, "%s: unexpected response received from vmd\n",
+ __progname);
+ *ret = EINVAL;
+ }
+
+ return (1);
+}
+
+/*
+ * start_vm
+ *
+ * Request vmd to start the VM defined by the supplied parameters
+ *
+ * Parameters:
+ * memsize: memory size (MB) of the VM to create
+ * nnics: number of vionet network interfaces to create
+ * ndisks: number of disk images
+ * disks: disk image file names
+ * kernel: kernel image to load
+ *
+ * Return:
+ * 0 if the request to start the VM was sent successfully.
+ * ENOMEM if a memory allocation failure occurred.
+ */
+int
+start_vm(int memsize, int nnics, int ndisks, char **disks, char *kernel)
+{
+ struct vm_create_params *vcp;
+ int i;
+
+ vcp = malloc(sizeof(struct vm_create_params));
+ if (vcp == NULL)
+ return (ENOMEM);
+
+ bzero(vcp, sizeof(struct vm_create_params));
+
+ vcp->vcp_memory_size = memsize;
+ vcp->vcp_ncpus = 1;
+ vcp->vcp_ndisks = ndisks;
+
+ for (i = 0 ; i < ndisks; i++)
+ strlcpy(vcp->vcp_disks[i], disks[i], VMM_MAX_PATH_DISK);
+
+ strlcpy(vcp->vcp_kernel, kernel, VMM_MAX_KERNEL_PATH);
+ vcp->vcp_nnics = nnics;
+
+ imsg_compose(ibuf, IMSG_VMDOP_START_VM_REQUEST, 0, 0, -1,
+ vcp, sizeof(struct vm_create_params));
+
+ free(vcp);
+ return (0);
+}
+
+/*
+ * start_vm_complete
+ *
+ * Callback function invoked when we are expecting an
+ * IMSG_VMDOP_START_VMM_RESPONSE message indicating the completion of
+ * a start vm operation.
+ *
+ * Parameters:
+ * imsg : response imsg received from vmd
+ * ret : return value
+ *
+ * Return:
+ * Always 1 to indicate we have processed the return message (even if it
+ * was an incorrect/failure message)
+ *
+ * The function also sets 'ret' to the error code as follows:
+ * 0 : Message successfully processed
+ * EINVAL: Invalid or unexpected response from vmd
+ * EIO : start_vm command failed
+ */
+int
+start_vm_complete(struct imsg *imsg, int *ret)
+{
+ int res;
+
+ if (imsg->hdr.type == IMSG_VMDOP_START_VM_RESPONSE) {
+ res = *(int *)imsg->data;
+ if (res) {
+ fprintf(stderr, "%s: start VM command failed (%d) - "
+ "%s\n", __progname, res, strerror(res));
+ *ret = EIO;
+ } else {
+ fprintf(stdout, "%s: start VM command successful\n",
+ __progname);
+ *ret = 0;
+ }
+ } else {
+ fprintf(stderr, "%s: unexpected response received from vmd\n",
+ __progname);
+ *ret = EINVAL;
+ }
+
+ return (1);
+}
+
+/*
+ * terminate_vm
+ *
+ * Request vmd to stop the VM indicated
+ *
+ * Parameters:
+ * terminate_id: ID of the vm to be terminated
+ */
+void
+terminate_vm(uint32_t terminate_id)
+{
+ struct vm_terminate_params vtp;
+
+ bzero(&vtp, sizeof(struct vm_terminate_params));
+ vtp.vtp_vm_id = terminate_id;
+
+ imsg_compose(ibuf, IMSG_VMDOP_TERMINATE_VM_REQUEST, 0, 0, -1,
+ &vtp, sizeof(struct vm_terminate_params));
+}
+
+/*
+ * terminate_vm_complete
+ *
+ * Callback function invoked when we are expecting an
+ * IMSG_VMDOP_TERMINATE_VMM_RESPONSE message indicating the completion of
+ * a terminate vm operation.
+ *
+ * Parameters:
+ * imsg : response imsg received from vmd
+ * ret : return value
+ *
+ * Return:
+ * Always 1 to indicate we have processed the return message (even if it
+ * was an incorrect/failure message)
+ *
+ * The function also sets 'ret' to the error code as follows:
+ * 0 : Message successfully processed
+ * EINVAL: Invalid or unexpected response from vmd
+ * EIO : terminate_vm command failed
+ */
+int
+terminate_vm_complete(struct imsg *imsg, int *ret)
+{
+ int res;
+
+ if (imsg->hdr.type == IMSG_VMDOP_TERMINATE_VM_RESPONSE) {
+ res = *(int *)imsg->data;
+ if (res) {
+ fprintf(stderr, "%s: terminate VM command failed "
+ "(%d) - %s\n", __progname, res, strerror(res));
+ *ret = EIO;
+ } else {
+ fprintf(stderr, "%s: terminate VM command successful\n",
+ __progname);
+ *ret = 0;
+ }
+ } else {
+ fprintf(stderr, "%s: unexpected response received from vmd\n",
+ __progname);
+ *ret = EINVAL;
+ }
+
+ return (1);
+}
+
+/*
+ * get_info_vm
+ *
+ * Request a list of running VMs from vmd
+ */
+void
+get_info_vm(void)
+{
+ imsg_compose(ibuf, IMSG_VMDOP_GET_INFO_VM_REQUEST, 0, 0, -1, NULL, 0);
+}
+
+/*
+ * add_info
+ *
+ * Callback function invoked when we are expecting an
+ * IMSG_VMDOP_GET_INFO_VM_DATA message indicating the receipt of additional
+ * "list vm" data, or an IMSG_VMDOP_GET_INFO_VM_END_DATA message indicating
+ * that no additional "list vm" data will be forthcoming.
+ *
+ * Parameters:
+ * imsg : response imsg received from vmd
+ * ret : return value
+ *
+ * Return:
+ * 0 : the returned data was successfully added to the "list vm" data.
+ * The caller can expect more data.
+ * 1 : IMSG_VMDOP_GET_INFO_VM_END_DATA received (caller should not call
+ * add_info again), or an error occurred adding the returned data
+ * to the "list vm" data. The caller should check the value of
+ * 'ret' to determine which case occurred.
+ *
+ * The function also sets 'ret' to the error code as follows:
+ * 0 : Message successfully processed
+ * EINVAL: Invalid or unexpected response from vmd
+ * ENOMEM: memory allocation failure
+ */
+int
+add_info(struct imsg *imsg, int *ret)
+{
+ static size_t ct = 0;
+ static struct vm_info_result *vir = NULL;
+
+ if (imsg->hdr.type == IMSG_VMDOP_GET_INFO_VM_DATA) {
+ if (ct == 0) {
+ vir = malloc(sizeof(struct vm_info_result));
+ if (vir == NULL) {
+ *ret = ENOMEM;
+ return (1);
+ }
+ } else {
+ vir = reallocarray(vir, ct + 1,
+ sizeof(struct vm_info_result));
+ if (vir == NULL) {
+ *ret = ENOMEM;
+ return (1);
+ }
+ }
+ bcopy(imsg->data, &vir[ct], sizeof(struct vm_info_result));
+ ct++;
+ *ret = 0;
+ return (0);
+ } else if (imsg->hdr.type == IMSG_VMDOP_GET_INFO_VM_END_DATA) {
+ print_vm_info(vir, ct);
+ free(vir);
+ *ret = 0;
+ return (1);
+ } else {
+ *ret = EINVAL;
+ return (1);
+ }
+}
+
+/*
+ * print_vm_info
+ *
+ * Prints the vm information returned from vmd in 'list' to stdout.
+ *
+ * Parameters
+ * list: the vm information (consolidated) returned from vmd via imsg
+ * ct : the size (number of elements in 'list') of the result
+ */
+void
+print_vm_info(struct vm_info_result *list, size_t ct)
+{
+ size_t i, j;
+ char *vcpu_state;
+
+ printf("%5s %5s %5s %7s %s\n", "ID", "PID", "VCPUS", "MAXMEM",
+ "NAME");
+ for (i = 0; i < ct; i++) {
+ if (info_id == 0 || info_id == list[i].vir_id)
+ printf("%5u %5u %5zd %7zdMB %s\n",
+ list[i].vir_id, list[i].vir_creator_pid,
+ list[i].vir_ncpus, list[i].vir_memory_size,
+ list[i].vir_name);
+ if (info_id == list[i].vir_id) {
+ for (j = 0; j < list[i].vir_ncpus; j++) {
+ if (list[i].vir_vcpu_state[j] ==
+ VCPU_STATE_STOPPED)
+ vcpu_state = "STOPPED";
+ else if (list[i].vir_vcpu_state[j] ==
+ VCPU_STATE_RUNNING)
+ vcpu_state = "RUNNING";
+ else
+ vcpu_state = "UNKNOWN";
+
+ printf(" VCPU: %2zd STATE: %s\n",
+ j, vcpu_state);
+ }
+ }
+ }
+}
+
+/*
+ * create_imagefile
+ *
+ * Create an empty imagefile with the specified path and size.
+ *
+ * Parameters:
+ * imgfile_path: path to the image file to create
+ * imgsize : size of the image file to create
+ *
+ * Return:
+ * EEXIST: The requested image file already exists
+ * 0 : Image file successfully created
+ * Exxxx : Various other Exxxx errno codes due to other I/O errors
+ */
+int
+create_imagefile(char *imgfile_path, long imgsize)
+{
+ int fd, ret;
+ struct stat sb;
+ off_t ofs;
+ char ch = '\0';
+
+ /* Refuse to overwrite an existing image */
+ bzero(&sb, sizeof(sb));
+ if (stat(imgfile_path, &sb) == 0) {
+ return (EEXIST);
+ }
+
+ fd = open(imgfile_path, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+
+ if (fd == -1) {
+ return (errno);
+ }
+
+ ofs = (imgsize * 1024 * 1024) - 1;
+
+ /* Set fd pos at desired size */
+ if (lseek(fd, ofs, SEEK_SET) == -1) {
+ ret = errno;
+ close(fd);
+ return (ret);
+ }
+
+ /* Write one byte to fill out the extent */
+ if (write(fd, &ch, 1) == -1) {
+ ret = errno;
+ close(fd);
+ return (ret);
+ }
+
+ ret = close(fd);
+ return (ret);
+}