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; }