This adds a feature to vmctl/vmd to wait for a VM to stop.
It is a feature usable in many situation where you wait for a VM to halt
after work is done. This is more or less vmctl stop <VM> -w without
sending the termination to the VM.
There is only one vmctl that can wait so if a second one comes in the
previous one will terminate with an error. This is why I modified the
error path a bit in vmctl.
--
:wq Claudio
Index: vmctl/main.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmctl/main.c,v
retrieving revision 1.48
diff -u -p -r1.48 main.c
--- vmctl/main.c 26 Nov 2018 10:39:30 -0000 1.48
+++ vmctl/main.c 3 Dec 2018 17:11:08 -0000
@@ -63,6 +63,7 @@ int ctl_reset(struct parse_result *, i
int ctl_start(struct parse_result *, int, char *[]);
int ctl_status(struct parse_result *, int, char *[]);
int ctl_stop(struct parse_result *, int, char *[]);
+int ctl_waitfor(struct parse_result *, int, char *[]);
int ctl_pause(struct parse_result *, int, char *[]);
int ctl_unpause(struct parse_result *, int, char *[]);
int ctl_send(struct parse_result *, int, char *[]);
@@ -82,6 +83,7 @@ struct ctl_command ctl_commands[] = {
"\t\t[-n switch] [-i count] [-d disk]* [-t name]" },
{ "status", CMD_STATUS, ctl_status, "[id]" },
{ "stop", CMD_STOP, ctl_stop, "[id|-a] [-fw]" },
+ { "wait", CMD_WAITFOR, ctl_waitfor, "id" },
{ "pause", CMD_PAUSE, ctl_pause, "id" },
{ "unpause", CMD_UNPAUSE, ctl_unpause, "id" },
{ "send", CMD_SEND, ctl_send, "id", 1},
@@ -178,7 +180,7 @@ parse(int argc, char *argv[])
err(1, "pledge");
}
if (ctl->main(&res, argc, argv) != 0)
- err(1, "failed");
+ exit(1);
if (ctl_sock != -1) {
close(ibuf->fd);
@@ -251,6 +253,9 @@ vmmaction(struct parse_result *res)
imsg_compose(ibuf, IMSG_CTL_RESET, 0, 0, -1,
&res->mode, sizeof(res->mode));
break;
+ case CMD_WAITFOR:
+ waitfor_vm(res->id, res->name);
+ break;
case CMD_PAUSE:
pause_vm(res->id, res->name);
break;
@@ -310,6 +315,9 @@ vmmaction(struct parse_result *res)
done = vm_start_complete(&imsg, &ret,
tty_autoconnect);
break;
+ case CMD_WAITFOR:
+ flags = VMOP_WAIT;
+ /* FALLTHROUGH */
case CMD_STOP:
done = terminate_vm_complete(&imsg, &ret,
flags);
@@ -337,7 +345,10 @@ vmmaction(struct parse_result *res)
}
}
- return (0);
+ if (ret)
+ return (1);
+ else
+ return (0);
}
void
@@ -941,6 +952,18 @@ ctl_stop(struct parse_result *res, int a
int
ctl_console(struct parse_result *res, int argc, char *argv[])
+{
+ if (argc == 2) {
+ if (parse_vmid(res, argv[1], 0) == -1)
+ errx(1, "invalid id: %s", argv[1]);
+ } else if (argc != 2)
+ ctl_usage(res->ctl);
+
+ return (vmmaction(res));
+}
+
+int
+ctl_waitfor(struct parse_result *res, int argc, char *argv[])
{
if (argc == 2) {
if (parse_vmid(res, argv[1], 0) == -1)
Index: vmctl/vmctl.8
===================================================================
RCS file: /cvs/src/usr.sbin/vmctl/vmctl.8,v
retrieving revision 1.54
diff -u -p -r1.54 vmctl.8
--- vmctl/vmctl.8 20 Nov 2018 12:48:16 -0000 1.54
+++ vmctl/vmctl.8 3 Dec 2018 17:11:08 -0000
@@ -234,6 +234,8 @@ Stop all running VMs.
.It Cm unpause Ar id
Unpause (resume from a paused state) a VM with the specified
.Ar id .
+.It Cm wait Ar id
+Wait until the specified VM has stopped.
.El
.Pp
If the
Index: vmctl/vmctl.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmctl/vmctl.c,v
retrieving revision 1.63
diff -u -p -r1.63 vmctl.c
--- vmctl/vmctl.c 26 Nov 2018 10:39:30 -0000 1.63
+++ vmctl/vmctl.c 3 Dec 2018 17:11:08 -0000
@@ -496,6 +496,10 @@ terminate_vm_complete(struct imsg *imsg,
fprintf(stderr, "vm not found\n");
*ret = EIO;
break;
+ case EINTR:
+ fprintf(stderr, "interrupted call\n");
+ *ret = EIO;
+ break;
default:
errno = res;
fprintf(stderr, "failed: %s\n",
@@ -557,6 +561,33 @@ terminate_all(struct vmop_info_result *l
vmmaction(&res);
}
+}
+
+/*
+ * waitfor_vm
+ *
+ * Wait until vmd stopped the indicated VM
+ *
+ * Parameters:
+ * terminate_id: ID of the vm to be terminated
+ * name: optional name of the VM to be terminated
+ */
+void
+waitfor_vm(uint32_t terminate_id, const char *name)
+{
+ struct vmop_id vid;
+
+ memset(&vid, 0, sizeof(vid));
+ vid.vid_id = terminate_id;
+ if (name != NULL) {
+ (void)strlcpy(vid.vid_name, name, sizeof(vid.vid_name));
+ fprintf(stderr, "waiting for vm %s: ", name);
+ } else {
+ fprintf(stderr, "waiting for vm: ");
+ }
+
+ imsg_compose(ibuf, IMSG_VMDOP_WAIT_VM_REQUEST,
+ 0, 0, -1, &vid, sizeof(vid));
}
/*
Index: vmctl/vmctl.h
===================================================================
RCS file: /cvs/src/usr.sbin/vmctl/vmctl.h,v
retrieving revision 1.28
diff -u -p -r1.28 vmctl.h
--- vmctl/vmctl.h 26 Nov 2018 10:39:30 -0000 1.28
+++ vmctl/vmctl.h 3 Dec 2018 17:11:08 -0000
@@ -33,6 +33,7 @@ enum actions {
CMD_STATUS,
CMD_STOP,
CMD_STOPALL,
+ CMD_WAITFOR,
CMD_PAUSE,
CMD_UNPAUSE,
CMD_SEND,
@@ -96,6 +97,7 @@ int vm_start(uint32_t, const char *, in
int vm_start_complete(struct imsg *, int *, int);
void terminate_vm(uint32_t, const char *, unsigned int);
int terminate_vm_complete(struct imsg *, int *, unsigned int);
+void waitfor_vm(uint32_t, const char *);
void pause_vm(uint32_t, const char *);
int pause_vm_complete(struct imsg *, int *);
void unpause_vm(uint32_t, const char *);
Index: vmd/control.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/control.c,v
retrieving revision 1.29
diff -u -p -r1.29 control.c
--- vmd/control.c 5 Aug 2018 08:20:54 -0000 1.29
+++ vmd/control.c 3 Dec 2018 17:11:08 -0000
@@ -341,6 +341,7 @@ control_dispatch_imsg(int fd, short even
switch (imsg.hdr.type) {
case IMSG_VMDOP_GET_INFO_VM_REQUEST:
+ case IMSG_VMDOP_WAIT_VM_REQUEST:
case IMSG_VMDOP_TERMINATE_VM_REQUEST:
case IMSG_VMDOP_START_VM_REQUEST:
case IMSG_VMDOP_PAUSE_VM:
@@ -399,6 +400,7 @@ control_dispatch_imsg(int fd, short even
return;
}
break;
+ case IMSG_VMDOP_WAIT_VM_REQUEST:
case IMSG_VMDOP_TERMINATE_VM_REQUEST:
if (IMSG_DATA_SIZE(&imsg) < sizeof(vid))
goto fail;
Index: vmd/vmd.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vmd.c,v
retrieving revision 1.106
diff -u -p -r1.106 vmd.c
--- vmd/vmd.c 26 Nov 2018 05:44:46 -0000 1.106
+++ vmd/vmd.c 3 Dec 2018 17:11:08 -0000
@@ -116,6 +116,7 @@ vmd_dispatch_control(int fd, struct priv
cmd = IMSG_VMDOP_START_VM_RESPONSE;
}
break;
+ case IMSG_VMDOP_WAIT_VM_REQUEST:
case IMSG_VMDOP_TERMINATE_VM_REQUEST:
IMSG_SIZE_CHECK(imsg, &vid);
memcpy(&vid, imsg->data, sizeof(vid));
Index: vmd/vmd.h
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vmd.h,v
retrieving revision 1.86
diff -u -p -r1.86 vmd.h
--- vmd/vmd.h 26 Nov 2018 05:44:46 -0000 1.86
+++ vmd/vmd.h 3 Dec 2018 17:11:08 -0000
@@ -98,6 +98,7 @@ enum imsg_type {
IMSG_VMDOP_RECEIVE_VM_REQUEST,
IMSG_VMDOP_RECEIVE_VM_RESPONSE,
IMSG_VMDOP_RECEIVE_VM_END,
+ IMSG_VMDOP_WAIT_VM_REQUEST,
IMSG_VMDOP_TERMINATE_VM_REQUEST,
IMSG_VMDOP_TERMINATE_VM_RESPONSE,
IMSG_VMDOP_TERMINATE_VM_EVENT,
Index: vmd/vmm.c
===================================================================
RCS file: /cvs/src/usr.sbin/vmd/vmm.c,v
retrieving revision 1.90
diff -u -p -r1.90 vmm.c
--- vmd/vmm.c 8 Oct 2018 16:32:01 -0000 1.90
+++ vmd/vmm.c 3 Dec 2018 17:11:08 -0000
@@ -109,7 +109,7 @@ vmm_dispatch_parent(int fd, struct privs
struct vmop_id vid;
struct vmop_result vmr;
struct vmop_create_params vmc;
- uint32_t id = 0;
+ uint32_t id = 0, peerid = imsg->hdr.peerid;
pid_t pid = 0;
unsigned int mode, flags;
@@ -149,6 +149,30 @@ vmm_dispatch_parent(int fd, struct privs
res = ENOENT;
cmd = IMSG_VMDOP_START_VM_RESPONSE;
break;
+ case IMSG_VMDOP_WAIT_VM_REQUEST:
+ IMSG_SIZE_CHECK(imsg, &vid);
+ memcpy(&vid, imsg->data, sizeof(vid));
+ id = vid.vid_id;
+
+ DPRINTF("%s: recv'ed WAIT_VM for %d", __func__, id);
+
+ cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE;
+ if (id == 0) {
+ res = ENOENT;
+ } else if ((vm = vm_getbyvmid(id)) != NULL) {
+ if (vm->vm_peerid != (uint32_t)-1) {
+ peerid = vm->vm_peerid;
+ res = EINTR;
+ } else
+ cmd = 0;
+ vm->vm_peerid = imsg->hdr.peerid;
+ } else {
+ /* vm doesn't exist, cannot stop vm */
+ log_debug("%s: cannot stop vm that is not running",
+ __func__);
+ res = VMD_VM_STOP_INVALID;
+ }
+ break;
case IMSG_VMDOP_TERMINATE_VM_REQUEST:
IMSG_SIZE_CHECK(imsg, &vid);
memcpy(&vid, imsg->data, sizeof(vid));
@@ -199,8 +223,12 @@ vmm_dispatch_parent(int fd, struct privs
}
if ((flags & VMOP_WAIT) &&
res == 0 && vm->vm_shutdown == 1) {
+ if (vm->vm_peerid != (uint32_t)-1) {
+ peerid = vm->vm_peerid;
+ res = EINTR;
+ } else
+ cmd = 0;
vm->vm_peerid = imsg->hdr.peerid;
- cmd = 0;
}
} else {
/* vm doesn't exist, cannot stop vm */
@@ -329,12 +357,12 @@ vmm_dispatch_parent(int fd, struct privs
vmr.vmr_id = id;
vmr.vmr_pid = pid;
if (proc_compose_imsg(ps, PROC_PARENT, -1, cmd,
- imsg->hdr.peerid, -1, &vmr, sizeof(vmr)) == -1)
+ peerid, -1, &vmr, sizeof(vmr)) == -1)
return (-1);
break;
default:
if (proc_compose_imsg(ps, PROC_PARENT, -1, cmd,
- imsg->hdr.peerid, -1, &res, sizeof(res)) == -1)
+ peerid, -1, &res, sizeof(res)) == -1)
return (-1);
break;
}