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

Reply via email to