[PATCH 09/23] virsh: Move 'cmdEvent' and all of it's machinery to virsh-domain-event.c

Peter Krempa pkrempa at redhat.com
Wed Mar 2 13:55:08 UTC 2022


'cmdEvent' along with all the helper functions it needs is ~950 LOC.
Move it out from virsh-domain.c to virsh-domain-event.c along with the
completer function so that the new module doesn't have to expose any new
types.

Semantically this creates a new category in 'virsh help' but all other
behaviour stays the same.

Signed-off-by: Peter Krempa <pkrempa at redhat.com>
---
 po/POTFILES.in                 |    1 +
 tools/meson.build              |    1 +
 tools/virsh-completer-domain.c |   19 -
 tools/virsh-completer-domain.h |    5 -
 tools/virsh-domain-event.c     | 1007 ++++++++++++++++++++++++++++++++
 tools/virsh-domain-event.h     |   23 +
 tools/virsh-domain.c           |  946 ------------------------------
 tools/virsh.c                  |    2 +
 tools/virsh.h                  |    1 +
 9 files changed, 1035 insertions(+), 970 deletions(-)
 create mode 100644 tools/virsh-domain-event.c
 create mode 100644 tools/virsh-domain-event.h

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 1fd3afdd6f..0d9adb0758 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -354,6 +354,7 @@
 @SRCDIR at tools/virsh-checkpoint.c
 @SRCDIR at tools/virsh-completer-host.c
 @SRCDIR at tools/virsh-console.c
+ at SRCDIR@tools/virsh-domain-event.c
 @SRCDIR at tools/virsh-domain-monitor.c
 @SRCDIR at tools/virsh-domain.c
 @SRCDIR at tools/virsh-edit.c
diff --git a/tools/meson.build b/tools/meson.build
index f4b4a16c29..ac714e6425 100644
--- a/tools/meson.build
+++ b/tools/meson.build
@@ -174,6 +174,7 @@ executable(
     'virsh-completer-volume.c',
     'virsh-console.c',
     'virsh-domain.c',
+    'virsh-domain-event.c',
     'virsh-domain-monitor.c',
     'virsh-host.c',
     'virsh-interface.c',
diff --git a/tools/virsh-completer-domain.c b/tools/virsh-completer-domain.c
index 321c47ef65..250dd8b21a 100644
--- a/tools/virsh-completer-domain.c
+++ b/tools/virsh-completer-domain.c
@@ -357,25 +357,6 @@ virshDomainBlockjobBaseTopCompleter(vshControl *ctl,
     return ret;
 }

-char **
-virshDomainEventNameCompleter(vshControl *ctl G_GNUC_UNUSED,
-                              const vshCmd *cmd G_GNUC_UNUSED,
-                              unsigned int flags)
-{
-    size_t i = 0;
-    g_auto(GStrv) tmp = NULL;
-
-    virCheckFlags(0, NULL);
-
-    tmp = g_new0(char *, VIR_DOMAIN_EVENT_ID_LAST + 1);
-
-    for (i = 0; i < VIR_DOMAIN_EVENT_ID_LAST; i++)
-        tmp[i] = g_strdup(virshDomainEventCallbacks[i].name);
-
-    return g_steal_pointer(&tmp);
-}
-
-
 char **
 virshDomainInterfaceStateCompleter(vshControl *ctl,
                                    const vshCmd *cmd,
diff --git a/tools/virsh-completer-domain.h b/tools/virsh-completer-domain.h
index 044c675842..27cf963912 100644
--- a/tools/virsh-completer-domain.h
+++ b/tools/virsh-completer-domain.h
@@ -41,11 +41,6 @@ virshDomainDiskTargetCompleter(vshControl *ctl,
                                const vshCmd *cmd,
                                unsigned int flags);

-char **
-virshDomainEventNameCompleter(vshControl *ctl,
-                              const vshCmd *cmd,
-                              unsigned int flags);
-
 char **
 virshDomainInterfaceStateCompleter(vshControl *ctl,
                                    const vshCmd *cmd,
diff --git a/tools/virsh-domain-event.c b/tools/virsh-domain-event.c
new file mode 100644
index 0000000000..51571dffad
--- /dev/null
+++ b/tools/virsh-domain-event.c
@@ -0,0 +1,1007 @@
+/*
+ * virsh-domain-event.c: Domain event listening commands
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include "virsh-domain-event.h"
+#include "virsh-util.h"
+
+#include "internal.h"
+#include "viralloc.h"
+#include "virenum.h"
+#include "virutil.h"
+#include "virtime.h"
+#include "virtypedparam.h"
+
+/*
+ * "event" command
+ */
+
+VIR_ENUM_DECL(virshDomainEvent);
+VIR_ENUM_IMPL(virshDomainEvent,
+              VIR_DOMAIN_EVENT_LAST,
+              N_("Defined"),
+              N_("Undefined"),
+              N_("Started"),
+              N_("Suspended"),
+              N_("Resumed"),
+              N_("Stopped"),
+              N_("Shutdown"),
+              N_("PMSuspended"),
+              N_("Crashed"));
+
+static const char *
+virshDomainEventToString(int event)
+{
+    const char *str = virshDomainEventTypeToString(event);
+    return str ? _(str) : _("unknown");
+}
+
+VIR_ENUM_DECL(virshDomainEventDefined);
+VIR_ENUM_IMPL(virshDomainEventDefined,
+              VIR_DOMAIN_EVENT_DEFINED_LAST,
+              N_("Added"),
+              N_("Updated"),
+              N_("Renamed"),
+              N_("Snapshot"));
+
+VIR_ENUM_DECL(virshDomainEventUndefined);
+VIR_ENUM_IMPL(virshDomainEventUndefined,
+              VIR_DOMAIN_EVENT_UNDEFINED_LAST,
+              N_("Removed"),
+              N_("Renamed"));
+
+VIR_ENUM_DECL(virshDomainEventStarted);
+VIR_ENUM_IMPL(virshDomainEventStarted,
+              VIR_DOMAIN_EVENT_STARTED_LAST,
+              N_("Booted"),
+              N_("Migrated"),
+              N_("Restored"),
+              N_("Snapshot"),
+              N_("Event wakeup"));
+
+VIR_ENUM_DECL(virshDomainEventSuspended);
+VIR_ENUM_IMPL(virshDomainEventSuspended,
+              VIR_DOMAIN_EVENT_SUSPENDED_LAST,
+              N_("Paused"),
+              N_("Migrated"),
+              N_("I/O Error"),
+              N_("Watchdog"),
+              N_("Restored"),
+              N_("Snapshot"),
+              N_("API error"),
+              N_("Post-copy"),
+              N_("Post-copy Error"));
+
+VIR_ENUM_DECL(virshDomainEventResumed);
+VIR_ENUM_IMPL(virshDomainEventResumed,
+              VIR_DOMAIN_EVENT_RESUMED_LAST,
+              N_("Unpaused"),
+              N_("Migrated"),
+              N_("Snapshot"),
+              N_("Post-copy"));
+
+VIR_ENUM_DECL(virshDomainEventStopped);
+VIR_ENUM_IMPL(virshDomainEventStopped,
+              VIR_DOMAIN_EVENT_STOPPED_LAST,
+              N_("Shutdown"),
+              N_("Destroyed"),
+              N_("Crashed"),
+              N_("Migrated"),
+              N_("Saved"),
+              N_("Failed"),
+              N_("Snapshot"));
+
+VIR_ENUM_DECL(virshDomainEventShutdown);
+VIR_ENUM_IMPL(virshDomainEventShutdown,
+              VIR_DOMAIN_EVENT_SHUTDOWN_LAST,
+              N_("Finished"),
+              N_("Finished after guest request"),
+              N_("Finished after host request"));
+
+VIR_ENUM_DECL(virshDomainEventPMSuspended);
+VIR_ENUM_IMPL(virshDomainEventPMSuspended,
+              VIR_DOMAIN_EVENT_PMSUSPENDED_LAST,
+              N_("Memory"),
+              N_("Disk"));
+
+VIR_ENUM_DECL(virshDomainEventCrashed);
+VIR_ENUM_IMPL(virshDomainEventCrashed,
+              VIR_DOMAIN_EVENT_CRASHED_LAST,
+              N_("Panicked"),
+              N_("Crashloaded"));
+
+static const char *
+virshDomainEventDetailToString(int event, int detail)
+{
+    const char *str = NULL;
+    switch ((virDomainEventType) event) {
+    case VIR_DOMAIN_EVENT_DEFINED:
+        str = virshDomainEventDefinedTypeToString(detail);
+        break;
+    case VIR_DOMAIN_EVENT_UNDEFINED:
+        str = virshDomainEventUndefinedTypeToString(detail);
+        break;
+    case VIR_DOMAIN_EVENT_STARTED:
+        str = virshDomainEventStartedTypeToString(detail);
+        break;
+    case VIR_DOMAIN_EVENT_SUSPENDED:
+        str = virshDomainEventSuspendedTypeToString(detail);
+        break;
+    case VIR_DOMAIN_EVENT_RESUMED:
+        str = virshDomainEventResumedTypeToString(detail);
+        break;
+    case VIR_DOMAIN_EVENT_STOPPED:
+        str = virshDomainEventStoppedTypeToString(detail);
+        break;
+    case VIR_DOMAIN_EVENT_SHUTDOWN:
+        str = virshDomainEventShutdownTypeToString(detail);
+        break;
+    case VIR_DOMAIN_EVENT_PMSUSPENDED:
+        str = virshDomainEventPMSuspendedTypeToString(detail);
+        break;
+    case VIR_DOMAIN_EVENT_CRASHED:
+        str = virshDomainEventCrashedTypeToString(detail);
+        break;
+    case VIR_DOMAIN_EVENT_LAST:
+        break;
+    }
+    return str ? _(str) : _("unknown");
+}
+
+VIR_ENUM_DECL(virshDomainEventWatchdog);
+VIR_ENUM_IMPL(virshDomainEventWatchdog,
+              VIR_DOMAIN_EVENT_WATCHDOG_LAST,
+              N_("none"),
+              N_("pause"),
+              N_("reset"),
+              N_("poweroff"),
+              N_("shutdown"),
+              N_("debug"),
+              N_("inject-nmi"));
+
+static const char *
+virshDomainEventWatchdogToString(int action)
+{
+    const char *str = virshDomainEventWatchdogTypeToString(action);
+    return str ? _(str) : _("unknown");
+}
+
+VIR_ENUM_DECL(virshDomainEventIOError);
+VIR_ENUM_IMPL(virshDomainEventIOError,
+              VIR_DOMAIN_EVENT_IO_ERROR_LAST,
+              N_("none"),
+              N_("pause"),
+              N_("report"));
+
+static const char *
+virshDomainEventIOErrorToString(int action)
+{
+    const char *str = virshDomainEventIOErrorTypeToString(action);
+    return str ? _(str) : _("unknown");
+}
+
+VIR_ENUM_DECL(virshGraphicsPhase);
+VIR_ENUM_IMPL(virshGraphicsPhase,
+              VIR_DOMAIN_EVENT_GRAPHICS_LAST,
+              N_("connect"),
+              N_("initialize"),
+              N_("disconnect"));
+
+static const char *
+virshGraphicsPhaseToString(int phase)
+{
+    const char *str = virshGraphicsPhaseTypeToString(phase);
+    return str ? _(str) : _("unknown");
+}
+
+VIR_ENUM_DECL(virshGraphicsAddress);
+VIR_ENUM_IMPL(virshGraphicsAddress,
+              VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_LAST,
+              N_("IPv4"),
+              N_("IPv6"),
+              N_("unix"));
+
+static const char *
+virshGraphicsAddressToString(int family)
+{
+    const char *str = virshGraphicsAddressTypeToString(family);
+    return str ? _(str) : _("unknown");
+}
+
+VIR_ENUM_DECL(virshDomainBlockJobStatus);
+VIR_ENUM_IMPL(virshDomainBlockJobStatus,
+              VIR_DOMAIN_BLOCK_JOB_LAST,
+              N_("completed"),
+              N_("failed"),
+              N_("canceled"),
+              N_("ready"));
+
+static const char *
+virshDomainBlockJobStatusToString(int status)
+{
+    const char *str = virshDomainBlockJobStatusTypeToString(status);
+    return str ? _(str) : _("unknown");
+}
+
+VIR_ENUM_DECL(virshDomainEventDiskChange);
+VIR_ENUM_IMPL(virshDomainEventDiskChange,
+              VIR_DOMAIN_EVENT_DISK_CHANGE_LAST,
+              N_("changed"),
+              N_("dropped"));
+
+static const char *
+virshDomainEventDiskChangeToString(int reason)
+{
+    const char *str = virshDomainEventDiskChangeTypeToString(reason);
+    return str ? _(str) : _("unknown");
+}
+
+VIR_ENUM_DECL(virshDomainEventTrayChange);
+VIR_ENUM_IMPL(virshDomainEventTrayChange,
+              VIR_DOMAIN_EVENT_TRAY_CHANGE_LAST,
+              N_("opened"),
+              N_("closed"));
+
+static const char *
+virshDomainEventTrayChangeToString(int reason)
+{
+    const char *str = virshDomainEventTrayChangeTypeToString(reason);
+    return str ? _(str) : _("unknown");
+}
+
+
+struct virshDomainEventCallback {
+    const char *name;
+    virConnectDomainEventGenericCallback cb;
+};
+typedef struct virshDomainEventCallback virshDomainEventCallback;
+
+
+struct virshDomEventData {
+    vshControl *ctl;
+    bool loop;
+    int *count;
+    bool timestamp;
+    virshDomainEventCallback *cb;
+    int id;
+};
+typedef struct virshDomEventData virshDomEventData;
+
+/**
+ * virshEventPrint:
+ *
+ * @data: opaque data passed to all event callbacks
+ * @buf: string buffer describing the event
+ *
+ * Print the event description found in @buf and update virshDomEventData.
+ *
+ * This function resets @buf and frees all memory consumed by its content.
+ */
+static void
+virshEventPrint(virshDomEventData *data,
+                virBuffer *buf)
+{
+    g_autofree char *msg = NULL;
+
+    if (!(msg = virBufferContentAndReset(buf)))
+        return;
+
+    if (!data->loop && *data->count)
+        return;
+
+    if (data->timestamp) {
+        char timestamp[VIR_TIME_STRING_BUFLEN];
+
+        if (virTimeStringNowRaw(timestamp) < 0)
+            timestamp[0] = '\0';
+
+        vshPrint(data->ctl, "%s: %s", timestamp, msg);
+    } else {
+        vshPrint(data->ctl, "%s", msg);
+    }
+
+    (*data->count)++;
+    if (!data->loop)
+        vshEventDone(data->ctl);
+}
+
+static void
+virshEventGenericPrint(virConnectPtr conn G_GNUC_UNUSED,
+                       virDomainPtr dom,
+                       void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf, _("event '%s' for domain '%s'\n"),
+                      ((virshDomEventData *) opaque)->cb->name,
+                      virDomainGetName(dom));
+    virshEventPrint(opaque, &buf);
+}
+
+static void
+virshEventLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED,
+                         virDomainPtr dom,
+                         int event,
+                         int detail,
+                         void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf, _("event 'lifecycle' for domain '%s': %s %s\n"),
+                      virDomainGetName(dom),
+                      virshDomainEventToString(event),
+                      virshDomainEventDetailToString(event, detail));
+    virshEventPrint(opaque, &buf);
+}
+
+static void
+virshEventRTCChangePrint(virConnectPtr conn G_GNUC_UNUSED,
+                         virDomainPtr dom,
+                         long long utcoffset,
+                         void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf, _("event 'rtc-change' for domain '%s': %lld\n"),
+                      virDomainGetName(dom),
+                      utcoffset);
+    virshEventPrint(opaque, &buf);
+}
+
+static void
+virshEventWatchdogPrint(virConnectPtr conn G_GNUC_UNUSED,
+                        virDomainPtr dom,
+                        int action,
+                        void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf, _("event 'watchdog' for domain '%s': %s\n"),
+                      virDomainGetName(dom),
+                      virshDomainEventWatchdogToString(action));
+    virshEventPrint(opaque, &buf);
+}
+
+static void
+virshEventIOErrorPrint(virConnectPtr conn G_GNUC_UNUSED,
+                       virDomainPtr dom,
+                       const char *srcPath,
+                       const char *devAlias,
+                       int action,
+                       void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf, _("event 'io-error' for domain '%s': %s (%s) %s\n"),
+                      virDomainGetName(dom),
+                      srcPath,
+                      devAlias,
+                      virshDomainEventIOErrorToString(action));
+    virshEventPrint(opaque, &buf);
+}
+
+static void
+virshEventGraphicsPrint(virConnectPtr conn G_GNUC_UNUSED,
+                        virDomainPtr dom,
+                        int phase,
+                        const virDomainEventGraphicsAddress *local,
+                        const virDomainEventGraphicsAddress *remote,
+                        const char *authScheme,
+                        const virDomainEventGraphicsSubject *subject,
+                        void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+    size_t i;
+
+    virBufferAsprintf(&buf, _("event 'graphics' for domain '%s': "
+                              "%s local[%s %s %s] remote[%s %s %s] %s\n"),
+                      virDomainGetName(dom),
+                      virshGraphicsPhaseToString(phase),
+                      virshGraphicsAddressToString(local->family),
+                      local->node,
+                      local->service,
+                      virshGraphicsAddressToString(remote->family),
+                      remote->node,
+                      remote->service,
+                      authScheme);
+    for (i = 0; i < subject->nidentity; i++) {
+        virBufferAsprintf(&buf, "\t%s=%s\n",
+                          subject->identities[i].type,
+                          subject->identities[i].name);
+    }
+    virshEventPrint(opaque, &buf);
+}
+
+static void
+virshEventIOErrorReasonPrint(virConnectPtr conn G_GNUC_UNUSED,
+                             virDomainPtr dom,
+                             const char *srcPath,
+                             const char *devAlias,
+                             int action,
+                             const char *reason,
+                             void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf, _("event 'io-error-reason' for domain '%s': "
+                              "%s (%s) %s due to %s\n"),
+                      virDomainGetName(dom),
+                      srcPath,
+                      devAlias,
+                      virshDomainEventIOErrorToString(action),
+                      reason);
+    virshEventPrint(opaque, &buf);
+}
+
+static void
+virshEventBlockJobPrint(virConnectPtr conn G_GNUC_UNUSED,
+                        virDomainPtr dom,
+                        const char *disk,
+                        int type,
+                        int status,
+                        void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf, _("event '%s' for domain '%s': %s for %s %s\n"),
+                      ((virshDomEventData *) opaque)->cb->name,
+                      virDomainGetName(dom),
+                      virshDomainBlockJobToString(type),
+                      disk,
+                      virshDomainBlockJobStatusToString(status));
+    virshEventPrint(opaque, &buf);
+}
+
+static void
+virshEventDiskChangePrint(virConnectPtr conn G_GNUC_UNUSED,
+                          virDomainPtr dom,
+                          const char *oldSrc,
+                          const char *newSrc,
+                          const char *alias,
+                          int reason,
+                          void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf, _("event 'disk-change' for domain '%s' disk %s: "
+                              "%s -> %s: %s\n"),
+                      virDomainGetName(dom),
+                      alias,
+                      NULLSTR(oldSrc),
+                      NULLSTR(newSrc),
+                      virshDomainEventDiskChangeToString(reason));
+    virshEventPrint(opaque, &buf);
+}
+
+static void
+virshEventTrayChangePrint(virConnectPtr conn G_GNUC_UNUSED,
+                          virDomainPtr dom,
+                          const char *alias,
+                          int reason,
+                          void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf, _("event 'tray-change' for domain '%s' disk %s: %s\n"),
+                      virDomainGetName(dom),
+                      alias,
+                      virshDomainEventTrayChangeToString(reason));
+    virshEventPrint(opaque, &buf);
+}
+
+static void
+virshEventPMChangePrint(virConnectPtr conn G_GNUC_UNUSED,
+                        virDomainPtr dom,
+                        int reason G_GNUC_UNUSED,
+                        void *opaque)
+{
+    /* As long as libvirt.h doesn't define any reasons, we might as
+     * well treat all PM state changes as generic events.  */
+    virshEventGenericPrint(conn, dom, opaque);
+}
+
+static void
+virshEventBalloonChangePrint(virConnectPtr conn G_GNUC_UNUSED,
+                             virDomainPtr dom,
+                             unsigned long long actual,
+                             void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf, _("event 'balloon-change' for domain '%s': %lluKiB\n"),
+                      virDomainGetName(dom),
+                      actual);
+    virshEventPrint(opaque, &buf);
+}
+
+static void
+virshEventDeviceRemovedPrint(virConnectPtr conn G_GNUC_UNUSED,
+                             virDomainPtr dom,
+                             const char *alias,
+                             void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf, _("event 'device-removed' for domain '%s': %s\n"),
+                      virDomainGetName(dom),
+                      alias);
+    virshEventPrint(opaque, &buf);
+}
+
+static void
+virshEventDeviceAddedPrint(virConnectPtr conn G_GNUC_UNUSED,
+                           virDomainPtr dom,
+                           const char *alias,
+                           void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf, _("event 'device-added' for domain '%s': %s\n"),
+                      virDomainGetName(dom),
+                      alias);
+    virshEventPrint(opaque, &buf);
+}
+
+static void
+virshEventTunablePrint(virConnectPtr conn G_GNUC_UNUSED,
+                       virDomainPtr dom,
+                       virTypedParameterPtr params,
+                       int nparams,
+                       void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+    size_t i;
+    char *value;
+
+    virBufferAsprintf(&buf, _("event 'tunable' for domain '%s':\n"),
+                      virDomainGetName(dom));
+    for (i = 0; i < nparams; i++) {
+        value = virTypedParameterToString(&params[i]);
+        if (value) {
+            virBufferAsprintf(&buf, "\t%s: %s\n", params[i].field, value);
+            VIR_FREE(value);
+        }
+    }
+    virshEventPrint(opaque, &buf);
+}
+
+VIR_ENUM_DECL(virshEventAgentLifecycleState);
+VIR_ENUM_IMPL(virshEventAgentLifecycleState,
+              VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_LAST,
+              N_("unknown"),
+              N_("connected"),
+              N_("disconnected"));
+
+VIR_ENUM_DECL(virshEventAgentLifecycleReason);
+VIR_ENUM_IMPL(virshEventAgentLifecycleReason,
+              VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_LAST,
+              N_("unknown"),
+              N_("domain started"),
+              N_("channel event"));
+
+#define UNKNOWNSTR(str) (str ? str : N_("unsupported value"))
+static void
+virshEventAgentLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED,
+                              virDomainPtr dom,
+                              int state,
+                              int reason,
+                              void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf, _("event 'agent-lifecycle' for domain '%s': state: "
+                              "'%s' reason: '%s'\n"),
+                      virDomainGetName(dom),
+                      UNKNOWNSTR(virshEventAgentLifecycleStateTypeToString(state)),
+                      UNKNOWNSTR(virshEventAgentLifecycleReasonTypeToString(reason)));
+    virshEventPrint(opaque, &buf);
+}
+
+static void
+virshEventMigrationIterationPrint(virConnectPtr conn G_GNUC_UNUSED,
+                                  virDomainPtr dom,
+                                  int iteration,
+                                  void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf, _("event 'migration-iteration' for domain '%s': "
+                              "iteration: '%d'\n"),
+                      virDomainGetName(dom),
+                      iteration);
+
+    virshEventPrint(opaque, &buf);
+}
+
+static void
+virshEventJobCompletedPrint(virConnectPtr conn G_GNUC_UNUSED,
+                            virDomainPtr dom,
+                            virTypedParameterPtr params,
+                            int nparams,
+                            void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+    size_t i;
+
+    virBufferAsprintf(&buf, _("event 'job-completed' for domain '%s':\n"),
+                      virDomainGetName(dom));
+    for (i = 0; i < nparams; i++) {
+        g_autofree char *value = virTypedParameterToString(&params[i]);
+        if (value)
+            virBufferAsprintf(&buf, "\t%s: %s\n", params[i].field, value);
+    }
+    virshEventPrint(opaque, &buf);
+}
+
+
+static void
+virshEventDeviceRemovalFailedPrint(virConnectPtr conn G_GNUC_UNUSED,
+                                   virDomainPtr dom,
+                                   const char *alias,
+                                   void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf, _("event 'device-removal-failed' for domain '%s': %s\n"),
+                      virDomainGetName(dom),
+                      alias);
+    virshEventPrint(opaque, &buf);
+}
+
+VIR_ENUM_DECL(virshEventMetadataChangeType);
+VIR_ENUM_IMPL(virshEventMetadataChangeType,
+              VIR_DOMAIN_METADATA_LAST,
+              N_("description"),
+              N_("title"),
+              N_("element"));
+
+static void
+virshEventMetadataChangePrint(virConnectPtr conn G_GNUC_UNUSED,
+                              virDomainPtr dom,
+                              int type,
+                              const char *nsuri,
+                              void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf, _("event 'metadata-change' for domain '%s': type %s, uri %s\n"),
+                      virDomainGetName(dom),
+                      UNKNOWNSTR(virshEventMetadataChangeTypeTypeToString(type)),
+                      NULLSTR(nsuri));
+    virshEventPrint(opaque, &buf);
+}
+
+
+static void
+virshEventBlockThresholdPrint(virConnectPtr conn G_GNUC_UNUSED,
+                              virDomainPtr dom,
+                              const char *dev,
+                              const char *path,
+                              unsigned long long threshold,
+                              unsigned long long excess,
+                              void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf, _("event 'block-threshold' for domain '%s': "
+                              "dev: %s(%s) %llu %llu\n"),
+                      virDomainGetName(dom),
+                      dev, NULLSTR(path), threshold, excess);
+    virshEventPrint(opaque, &buf);
+}
+
+
+VIR_ENUM_DECL(virshEventMemoryFailureRecipientType);
+VIR_ENUM_IMPL(virshEventMemoryFailureRecipientType,
+              VIR_DOMAIN_EVENT_MEMORY_FAILURE_RECIPIENT_LAST,
+              N_("hypervisor"),
+              N_("guest"));
+
+VIR_ENUM_DECL(virshEventMemoryFailureActionType);
+VIR_ENUM_IMPL(virshEventMemoryFailureActionType,
+              VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_LAST,
+              N_("ignore"),
+              N_("inject"),
+              N_("fatal"),
+              N_("reset"));
+
+static void
+virshEventMemoryFailurePrint(virConnectPtr conn G_GNUC_UNUSED,
+                             virDomainPtr dom,
+                             int recipient,
+                             int action,
+                             unsigned int flags,
+                             void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf, _("event 'memory-failure' for domain '%s':\n"
+                              "recipient: %s\naction: %s\n"),
+                      virDomainGetName(dom),
+                      UNKNOWNSTR(virshEventMemoryFailureRecipientTypeTypeToString(recipient)),
+                      UNKNOWNSTR(virshEventMemoryFailureActionTypeTypeToString(action)));
+    virBufferAsprintf(&buf, _("flags:\n"
+                              "\taction required: %d\n\trecursive: %d\n"),
+                      !!(flags & VIR_DOMAIN_MEMORY_FAILURE_ACTION_REQUIRED),
+                      !!(flags & VIR_DOMAIN_MEMORY_FAILURE_RECURSIVE));
+
+    virshEventPrint(opaque, &buf);
+}
+
+
+static void
+virshEventMemoryDeviceSizeChangePrint(virConnectPtr conn G_GNUC_UNUSED,
+                                      virDomainPtr dom,
+                                      const char *alias,
+                                      unsigned long long size,
+                                      void *opaque)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf,
+                      _("event 'memory-device-size-change' for domain '%s':\n"
+                        "alias: %s\nsize: %llu\n"),
+                      virDomainGetName(dom), alias, size);
+
+    virshEventPrint(opaque, &buf);
+}
+
+
+virshDomainEventCallback virshDomainEventCallbacks[] = {
+    { "lifecycle",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventLifecyclePrint), },
+    { "reboot", virshEventGenericPrint, },
+    { "rtc-change",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventRTCChangePrint), },
+    { "watchdog",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventWatchdogPrint), },
+    { "io-error",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventIOErrorPrint), },
+    { "graphics",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventGraphicsPrint), },
+    { "io-error-reason",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventIOErrorReasonPrint), },
+    { "control-error", virshEventGenericPrint, },
+    { "block-job",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockJobPrint), },
+    { "disk-change",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventDiskChangePrint), },
+    { "tray-change",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventTrayChangePrint), },
+    { "pm-wakeup",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), },
+    { "pm-suspend",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), },
+    { "balloon-change",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventBalloonChangePrint), },
+    { "pm-suspend-disk",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), },
+    { "device-removed",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovedPrint), },
+    { "block-job-2",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockJobPrint), },
+    { "tunable",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventTunablePrint), },
+    { "agent-lifecycle",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventAgentLifecyclePrint), },
+    { "device-added",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceAddedPrint), },
+    { "migration-iteration",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventMigrationIterationPrint), },
+    { "job-completed",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventJobCompletedPrint), },
+    { "device-removal-failed",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovalFailedPrint), },
+    { "metadata-change",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventMetadataChangePrint), },
+    { "block-threshold",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockThresholdPrint), },
+    { "memory-failure",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventMemoryFailurePrint), },
+    { "memory-device-size-change",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventMemoryDeviceSizeChangePrint), },
+};
+G_STATIC_ASSERT(VIR_DOMAIN_EVENT_ID_LAST == G_N_ELEMENTS(virshDomainEventCallbacks));
+
+
+static char **
+virshDomainEventNameCompleter(vshControl *ctl G_GNUC_UNUSED,
+                              const vshCmd *cmd G_GNUC_UNUSED,
+                              unsigned int flags)
+{
+    size_t i = 0;
+    g_auto(GStrv) tmp = NULL;
+
+    virCheckFlags(0, NULL);
+
+    tmp = g_new0(char *, VIR_DOMAIN_EVENT_ID_LAST + 1);
+
+    for (i = 0; i < VIR_DOMAIN_EVENT_ID_LAST; i++)
+        tmp[i] = g_strdup(virshDomainEventCallbacks[i].name);
+
+    return g_steal_pointer(&tmp);
+}
+
+
+static const vshCmdInfo info_event[] = {
+    {.name = "help",
+     .data = N_("Domain Events")
+    },
+    {.name = "desc",
+     .data = N_("List event types, or wait for domain events to occur")
+    },
+    {.name = NULL}
+};
+
+static const vshCmdOptDef opts_event[] = {
+    VIRSH_COMMON_OPT_DOMAIN_OT_STRING(N_("filter by domain name, id or uuid"),
+                                      0, 0),
+    {.name = "event",
+     .type = VSH_OT_STRING,
+     .completer = virshDomainEventNameCompleter,
+     .help = N_("which event type to wait for")
+    },
+    {.name = "all",
+     .type = VSH_OT_BOOL,
+     .help = N_("wait for all events instead of just one type")
+    },
+    {.name = "loop",
+     .type = VSH_OT_BOOL,
+     .help = N_("loop until timeout or interrupt, rather than one-shot")
+    },
+    {.name = "timeout",
+     .type = VSH_OT_INT,
+     .help = N_("timeout seconds")
+    },
+    {.name = "list",
+     .type = VSH_OT_BOOL,
+     .help = N_("list valid event types")
+    },
+    {.name = "timestamp",
+     .type = VSH_OT_BOOL,
+     .help = N_("show timestamp for each printed event")
+    },
+    {.name = NULL}
+};
+
+static bool
+cmdEvent(vshControl *ctl, const vshCmd *cmd)
+{
+    g_autoptr(virshDomain) dom = NULL;
+    bool ret = false;
+    int timeout = 0;
+    virshDomEventData *data = NULL;
+    size_t i;
+    const char *eventName = NULL;
+    int event = -1;
+    bool all = vshCommandOptBool(cmd, "all");
+    bool loop = vshCommandOptBool(cmd, "loop");
+    bool timestamp = vshCommandOptBool(cmd, "timestamp");
+    int count = 0;
+    virshControl *priv = ctl->privData;
+
+    VSH_EXCLUSIVE_OPTIONS("all", "event");
+    VSH_EXCLUSIVE_OPTIONS("list", "all");
+    VSH_EXCLUSIVE_OPTIONS("list", "event");
+
+    if (vshCommandOptBool(cmd, "list")) {
+        for (event = 0; event < VIR_DOMAIN_EVENT_ID_LAST; event++)
+            vshPrint(ctl, "%s\n", virshDomainEventCallbacks[event].name);
+        return true;
+    }
+
+    if (vshCommandOptStringReq(ctl, cmd, "event", &eventName) < 0)
+        return false;
+    if (eventName) {
+        for (event = 0; event < VIR_DOMAIN_EVENT_ID_LAST; event++)
+            if (STREQ(eventName, virshDomainEventCallbacks[event].name))
+                break;
+        if (event == VIR_DOMAIN_EVENT_ID_LAST) {
+            vshError(ctl, _("unknown event type %s"), eventName);
+            return false;
+        }
+    } else if (!all) {
+        vshError(ctl, "%s",
+                 _("one of --list, --all, or --event <type> is required"));
+        return false;
+    }
+
+    if (all) {
+        data = g_new0(virshDomEventData, VIR_DOMAIN_EVENT_ID_LAST);
+        for (i = 0; i < VIR_DOMAIN_EVENT_ID_LAST; i++) {
+            data[i].ctl = ctl;
+            data[i].loop = loop;
+            data[i].count = &count;
+            data[i].timestamp = timestamp;
+            data[i].cb = &virshDomainEventCallbacks[i];
+            data[i].id = -1;
+        }
+    } else {
+        data = g_new0(virshDomEventData, 1);
+        data[0].ctl = ctl;
+        data[0].loop = vshCommandOptBool(cmd, "loop");
+        data[0].count = &count;
+        data[0].timestamp = timestamp;
+        data[0].cb = &virshDomainEventCallbacks[event];
+        data[0].id = -1;
+    }
+    if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
+        goto cleanup;
+
+    if (vshCommandOptBool(cmd, "domain"))
+        if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
+            goto cleanup;
+
+    if (vshEventStart(ctl, timeout) < 0)
+        goto cleanup;
+
+    for (i = 0; i < (all ? VIR_DOMAIN_EVENT_ID_LAST : 1); i++) {
+        if ((data[i].id = virConnectDomainEventRegisterAny(priv->conn, dom,
+                                                           all ? i : event,
+                                                           data[i].cb->cb,
+                                                           &data[i],
+                                                           NULL)) < 0) {
+            /* When registering for all events: if the first
+             * registration succeeds, silently ignore failures on all
+             * later registrations on the assumption that the server
+             * is older and didn't know quite as many events.  */
+            if (i)
+                vshResetLibvirtError();
+            else
+                goto cleanup;
+        }
+    }
+    switch (vshEventWait(ctl)) {
+    case VSH_EVENT_INTERRUPT:
+        vshPrint(ctl, "%s", _("event loop interrupted\n"));
+        break;
+    case VSH_EVENT_TIMEOUT:
+        vshPrint(ctl, "%s", _("event loop timed out\n"));
+        break;
+    case VSH_EVENT_DONE:
+        break;
+    default:
+        goto cleanup;
+    }
+    vshPrint(ctl, _("events received: %d\n"), count);
+    if (count)
+        ret = true;
+
+ cleanup:
+    vshEventCleanup(ctl);
+    if (data) {
+        for (i = 0; i < (all ? VIR_DOMAIN_EVENT_ID_LAST : 1); i++) {
+            if (data[i].id >= 0 &&
+                virConnectDomainEventDeregisterAny(priv->conn, data[i].id) < 0)
+                ret = false;
+        }
+        VIR_FREE(data);
+    }
+    return ret;
+}
+
+
+const vshCmdDef domEventCmds[] = {
+    {.name = "event",
+     .handler = cmdEvent,
+     .opts = opts_event,
+     .info = info_event,
+     .flags = 0
+    },
+    {.name = NULL}
+};
diff --git a/tools/virsh-domain-event.h b/tools/virsh-domain-event.h
new file mode 100644
index 0000000000..dd96ef21ac
--- /dev/null
+++ b/tools/virsh-domain-event.h
@@ -0,0 +1,23 @@
+/*
+ * virsh-domain-event.h: Commands for domain events
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "virsh.h"
+
+extern const vshCmdDef domEventCmds[];
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
index 9c304dbf78..dc6e3b5020 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -12887,946 +12887,6 @@ cmdEdit(vshControl *ctl, const vshCmd *cmd)
 }


-/*
- * "event" command
- */
-VIR_ENUM_DECL(virshDomainEvent);
-VIR_ENUM_IMPL(virshDomainEvent,
-              VIR_DOMAIN_EVENT_LAST,
-              N_("Defined"),
-              N_("Undefined"),
-              N_("Started"),
-              N_("Suspended"),
-              N_("Resumed"),
-              N_("Stopped"),
-              N_("Shutdown"),
-              N_("PMSuspended"),
-              N_("Crashed"));
-
-static const char *
-virshDomainEventToString(int event)
-{
-    const char *str = virshDomainEventTypeToString(event);
-    return str ? _(str) : _("unknown");
-}
-
-VIR_ENUM_DECL(virshDomainEventDefined);
-VIR_ENUM_IMPL(virshDomainEventDefined,
-              VIR_DOMAIN_EVENT_DEFINED_LAST,
-              N_("Added"),
-              N_("Updated"),
-              N_("Renamed"),
-              N_("Snapshot"));
-
-VIR_ENUM_DECL(virshDomainEventUndefined);
-VIR_ENUM_IMPL(virshDomainEventUndefined,
-              VIR_DOMAIN_EVENT_UNDEFINED_LAST,
-              N_("Removed"),
-              N_("Renamed"));
-
-VIR_ENUM_DECL(virshDomainEventStarted);
-VIR_ENUM_IMPL(virshDomainEventStarted,
-              VIR_DOMAIN_EVENT_STARTED_LAST,
-              N_("Booted"),
-              N_("Migrated"),
-              N_("Restored"),
-              N_("Snapshot"),
-              N_("Event wakeup"));
-
-VIR_ENUM_DECL(virshDomainEventSuspended);
-VIR_ENUM_IMPL(virshDomainEventSuspended,
-              VIR_DOMAIN_EVENT_SUSPENDED_LAST,
-              N_("Paused"),
-              N_("Migrated"),
-              N_("I/O Error"),
-              N_("Watchdog"),
-              N_("Restored"),
-              N_("Snapshot"),
-              N_("API error"),
-              N_("Post-copy"),
-              N_("Post-copy Error"));
-
-VIR_ENUM_DECL(virshDomainEventResumed);
-VIR_ENUM_IMPL(virshDomainEventResumed,
-              VIR_DOMAIN_EVENT_RESUMED_LAST,
-              N_("Unpaused"),
-              N_("Migrated"),
-              N_("Snapshot"),
-              N_("Post-copy"));
-
-VIR_ENUM_DECL(virshDomainEventStopped);
-VIR_ENUM_IMPL(virshDomainEventStopped,
-              VIR_DOMAIN_EVENT_STOPPED_LAST,
-              N_("Shutdown"),
-              N_("Destroyed"),
-              N_("Crashed"),
-              N_("Migrated"),
-              N_("Saved"),
-              N_("Failed"),
-              N_("Snapshot"));
-
-VIR_ENUM_DECL(virshDomainEventShutdown);
-VIR_ENUM_IMPL(virshDomainEventShutdown,
-              VIR_DOMAIN_EVENT_SHUTDOWN_LAST,
-              N_("Finished"),
-              N_("Finished after guest request"),
-              N_("Finished after host request"));
-
-VIR_ENUM_DECL(virshDomainEventPMSuspended);
-VIR_ENUM_IMPL(virshDomainEventPMSuspended,
-              VIR_DOMAIN_EVENT_PMSUSPENDED_LAST,
-              N_("Memory"),
-              N_("Disk"));
-
-VIR_ENUM_DECL(virshDomainEventCrashed);
-VIR_ENUM_IMPL(virshDomainEventCrashed,
-              VIR_DOMAIN_EVENT_CRASHED_LAST,
-              N_("Panicked"),
-              N_("Crashloaded"));
-
-static const char *
-virshDomainEventDetailToString(int event, int detail)
-{
-    const char *str = NULL;
-    switch ((virDomainEventType) event) {
-    case VIR_DOMAIN_EVENT_DEFINED:
-        str = virshDomainEventDefinedTypeToString(detail);
-        break;
-    case VIR_DOMAIN_EVENT_UNDEFINED:
-        str = virshDomainEventUndefinedTypeToString(detail);
-        break;
-    case VIR_DOMAIN_EVENT_STARTED:
-        str = virshDomainEventStartedTypeToString(detail);
-        break;
-    case VIR_DOMAIN_EVENT_SUSPENDED:
-        str = virshDomainEventSuspendedTypeToString(detail);
-        break;
-    case VIR_DOMAIN_EVENT_RESUMED:
-        str = virshDomainEventResumedTypeToString(detail);
-        break;
-    case VIR_DOMAIN_EVENT_STOPPED:
-        str = virshDomainEventStoppedTypeToString(detail);
-        break;
-    case VIR_DOMAIN_EVENT_SHUTDOWN:
-        str = virshDomainEventShutdownTypeToString(detail);
-        break;
-    case VIR_DOMAIN_EVENT_PMSUSPENDED:
-        str = virshDomainEventPMSuspendedTypeToString(detail);
-        break;
-    case VIR_DOMAIN_EVENT_CRASHED:
-        str = virshDomainEventCrashedTypeToString(detail);
-        break;
-    case VIR_DOMAIN_EVENT_LAST:
-        break;
-    }
-    return str ? _(str) : _("unknown");
-}
-
-VIR_ENUM_DECL(virshDomainEventWatchdog);
-VIR_ENUM_IMPL(virshDomainEventWatchdog,
-              VIR_DOMAIN_EVENT_WATCHDOG_LAST,
-              N_("none"),
-              N_("pause"),
-              N_("reset"),
-              N_("poweroff"),
-              N_("shutdown"),
-              N_("debug"),
-              N_("inject-nmi"));
-
-static const char *
-virshDomainEventWatchdogToString(int action)
-{
-    const char *str = virshDomainEventWatchdogTypeToString(action);
-    return str ? _(str) : _("unknown");
-}
-
-VIR_ENUM_DECL(virshDomainEventIOError);
-VIR_ENUM_IMPL(virshDomainEventIOError,
-              VIR_DOMAIN_EVENT_IO_ERROR_LAST,
-              N_("none"),
-              N_("pause"),
-              N_("report"));
-
-static const char *
-virshDomainEventIOErrorToString(int action)
-{
-    const char *str = virshDomainEventIOErrorTypeToString(action);
-    return str ? _(str) : _("unknown");
-}
-
-VIR_ENUM_DECL(virshGraphicsPhase);
-VIR_ENUM_IMPL(virshGraphicsPhase,
-              VIR_DOMAIN_EVENT_GRAPHICS_LAST,
-              N_("connect"),
-              N_("initialize"),
-              N_("disconnect"));
-
-static const char *
-virshGraphicsPhaseToString(int phase)
-{
-    const char *str = virshGraphicsPhaseTypeToString(phase);
-    return str ? _(str) : _("unknown");
-}
-
-VIR_ENUM_DECL(virshGraphicsAddress);
-VIR_ENUM_IMPL(virshGraphicsAddress,
-              VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_LAST,
-              N_("IPv4"),
-              N_("IPv6"),
-              N_("unix"));
-
-static const char *
-virshGraphicsAddressToString(int family)
-{
-    const char *str = virshGraphicsAddressTypeToString(family);
-    return str ? _(str) : _("unknown");
-}
-
-VIR_ENUM_DECL(virshDomainBlockJobStatus);
-VIR_ENUM_IMPL(virshDomainBlockJobStatus,
-              VIR_DOMAIN_BLOCK_JOB_LAST,
-              N_("completed"),
-              N_("failed"),
-              N_("canceled"),
-              N_("ready"));
-
-static const char *
-virshDomainBlockJobStatusToString(int status)
-{
-    const char *str = virshDomainBlockJobStatusTypeToString(status);
-    return str ? _(str) : _("unknown");
-}
-
-VIR_ENUM_DECL(virshDomainEventDiskChange);
-VIR_ENUM_IMPL(virshDomainEventDiskChange,
-              VIR_DOMAIN_EVENT_DISK_CHANGE_LAST,
-              N_("changed"),
-              N_("dropped"));
-
-static const char *
-virshDomainEventDiskChangeToString(int reason)
-{
-    const char *str = virshDomainEventDiskChangeTypeToString(reason);
-    return str ? _(str) : _("unknown");
-}
-
-VIR_ENUM_DECL(virshDomainEventTrayChange);
-VIR_ENUM_IMPL(virshDomainEventTrayChange,
-              VIR_DOMAIN_EVENT_TRAY_CHANGE_LAST,
-              N_("opened"),
-              N_("closed"));
-
-static const char *
-virshDomainEventTrayChangeToString(int reason)
-{
-    const char *str = virshDomainEventTrayChangeTypeToString(reason);
-    return str ? _(str) : _("unknown");
-}
-
-struct virshDomEventData {
-    vshControl *ctl;
-    bool loop;
-    int *count;
-    bool timestamp;
-    virshDomainEventCallback *cb;
-    int id;
-};
-typedef struct virshDomEventData virshDomEventData;
-
-/**
- * virshEventPrint:
- *
- * @data: opaque data passed to all event callbacks
- * @buf: string buffer describing the event
- *
- * Print the event description found in @buf and update virshDomEventData.
- *
- * This function resets @buf and frees all memory consumed by its content.
- */
-static void
-virshEventPrint(virshDomEventData *data,
-                virBuffer *buf)
-{
-    g_autofree char *msg = NULL;
-
-    if (!(msg = virBufferContentAndReset(buf)))
-        return;
-
-    if (!data->loop && *data->count)
-        return;
-
-    if (data->timestamp) {
-        char timestamp[VIR_TIME_STRING_BUFLEN];
-
-        if (virTimeStringNowRaw(timestamp) < 0)
-            timestamp[0] = '\0';
-
-        vshPrint(data->ctl, "%s: %s", timestamp, msg);
-    } else {
-        vshPrint(data->ctl, "%s", msg);
-    }
-
-    (*data->count)++;
-    if (!data->loop)
-        vshEventDone(data->ctl);
-}
-
-static void
-virshEventGenericPrint(virConnectPtr conn G_GNUC_UNUSED,
-                       virDomainPtr dom,
-                       void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    virBufferAsprintf(&buf, _("event '%s' for domain '%s'\n"),
-                      ((virshDomEventData *) opaque)->cb->name,
-                      virDomainGetName(dom));
-    virshEventPrint(opaque, &buf);
-}
-
-static void
-virshEventLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED,
-                         virDomainPtr dom,
-                         int event,
-                         int detail,
-                         void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    virBufferAsprintf(&buf, _("event 'lifecycle' for domain '%s': %s %s\n"),
-                      virDomainGetName(dom),
-                      virshDomainEventToString(event),
-                      virshDomainEventDetailToString(event, detail));
-    virshEventPrint(opaque, &buf);
-}
-
-static void
-virshEventRTCChangePrint(virConnectPtr conn G_GNUC_UNUSED,
-                         virDomainPtr dom,
-                         long long utcoffset,
-                         void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    virBufferAsprintf(&buf, _("event 'rtc-change' for domain '%s': %lld\n"),
-                      virDomainGetName(dom),
-                      utcoffset);
-    virshEventPrint(opaque, &buf);
-}
-
-static void
-virshEventWatchdogPrint(virConnectPtr conn G_GNUC_UNUSED,
-                        virDomainPtr dom,
-                        int action,
-                        void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    virBufferAsprintf(&buf, _("event 'watchdog' for domain '%s': %s\n"),
-                      virDomainGetName(dom),
-                      virshDomainEventWatchdogToString(action));
-    virshEventPrint(opaque, &buf);
-}
-
-static void
-virshEventIOErrorPrint(virConnectPtr conn G_GNUC_UNUSED,
-                       virDomainPtr dom,
-                       const char *srcPath,
-                       const char *devAlias,
-                       int action,
-                       void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    virBufferAsprintf(&buf, _("event 'io-error' for domain '%s': %s (%s) %s\n"),
-                      virDomainGetName(dom),
-                      srcPath,
-                      devAlias,
-                      virshDomainEventIOErrorToString(action));
-    virshEventPrint(opaque, &buf);
-}
-
-static void
-virshEventGraphicsPrint(virConnectPtr conn G_GNUC_UNUSED,
-                        virDomainPtr dom,
-                        int phase,
-                        const virDomainEventGraphicsAddress *local,
-                        const virDomainEventGraphicsAddress *remote,
-                        const char *authScheme,
-                        const virDomainEventGraphicsSubject *subject,
-                        void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-    size_t i;
-
-    virBufferAsprintf(&buf, _("event 'graphics' for domain '%s': "
-                              "%s local[%s %s %s] remote[%s %s %s] %s\n"),
-                      virDomainGetName(dom),
-                      virshGraphicsPhaseToString(phase),
-                      virshGraphicsAddressToString(local->family),
-                      local->node,
-                      local->service,
-                      virshGraphicsAddressToString(remote->family),
-                      remote->node,
-                      remote->service,
-                      authScheme);
-    for (i = 0; i < subject->nidentity; i++) {
-        virBufferAsprintf(&buf, "\t%s=%s\n",
-                          subject->identities[i].type,
-                          subject->identities[i].name);
-    }
-    virshEventPrint(opaque, &buf);
-}
-
-static void
-virshEventIOErrorReasonPrint(virConnectPtr conn G_GNUC_UNUSED,
-                             virDomainPtr dom,
-                             const char *srcPath,
-                             const char *devAlias,
-                             int action,
-                             const char *reason,
-                             void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    virBufferAsprintf(&buf, _("event 'io-error-reason' for domain '%s': "
-                              "%s (%s) %s due to %s\n"),
-                      virDomainGetName(dom),
-                      srcPath,
-                      devAlias,
-                      virshDomainEventIOErrorToString(action),
-                      reason);
-    virshEventPrint(opaque, &buf);
-}
-
-static void
-virshEventBlockJobPrint(virConnectPtr conn G_GNUC_UNUSED,
-                        virDomainPtr dom,
-                        const char *disk,
-                        int type,
-                        int status,
-                        void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    virBufferAsprintf(&buf, _("event '%s' for domain '%s': %s for %s %s\n"),
-                      ((virshDomEventData *) opaque)->cb->name,
-                      virDomainGetName(dom),
-                      virshDomainBlockJobToString(type),
-                      disk,
-                      virshDomainBlockJobStatusToString(status));
-    virshEventPrint(opaque, &buf);
-}
-
-static void
-virshEventDiskChangePrint(virConnectPtr conn G_GNUC_UNUSED,
-                          virDomainPtr dom,
-                          const char *oldSrc,
-                          const char *newSrc,
-                          const char *alias,
-                          int reason,
-                          void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    virBufferAsprintf(&buf, _("event 'disk-change' for domain '%s' disk %s: "
-                              "%s -> %s: %s\n"),
-                      virDomainGetName(dom),
-                      alias,
-                      NULLSTR(oldSrc),
-                      NULLSTR(newSrc),
-                      virshDomainEventDiskChangeToString(reason));
-    virshEventPrint(opaque, &buf);
-}
-
-static void
-virshEventTrayChangePrint(virConnectPtr conn G_GNUC_UNUSED,
-                          virDomainPtr dom,
-                          const char *alias,
-                          int reason,
-                          void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    virBufferAsprintf(&buf, _("event 'tray-change' for domain '%s' disk %s: %s\n"),
-                      virDomainGetName(dom),
-                      alias,
-                      virshDomainEventTrayChangeToString(reason));
-    virshEventPrint(opaque, &buf);
-}
-
-static void
-virshEventPMChangePrint(virConnectPtr conn G_GNUC_UNUSED,
-                        virDomainPtr dom,
-                        int reason G_GNUC_UNUSED,
-                        void *opaque)
-{
-    /* As long as libvirt.h doesn't define any reasons, we might as
-     * well treat all PM state changes as generic events.  */
-    virshEventGenericPrint(conn, dom, opaque);
-}
-
-static void
-virshEventBalloonChangePrint(virConnectPtr conn G_GNUC_UNUSED,
-                             virDomainPtr dom,
-                             unsigned long long actual,
-                             void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    virBufferAsprintf(&buf, _("event 'balloon-change' for domain '%s': %lluKiB\n"),
-                      virDomainGetName(dom),
-                      actual);
-    virshEventPrint(opaque, &buf);
-}
-
-static void
-virshEventDeviceRemovedPrint(virConnectPtr conn G_GNUC_UNUSED,
-                             virDomainPtr dom,
-                             const char *alias,
-                             void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    virBufferAsprintf(&buf, _("event 'device-removed' for domain '%s': %s\n"),
-                      virDomainGetName(dom),
-                      alias);
-    virshEventPrint(opaque, &buf);
-}
-
-static void
-virshEventDeviceAddedPrint(virConnectPtr conn G_GNUC_UNUSED,
-                           virDomainPtr dom,
-                           const char *alias,
-                           void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    virBufferAsprintf(&buf, _("event 'device-added' for domain '%s': %s\n"),
-                      virDomainGetName(dom),
-                      alias);
-    virshEventPrint(opaque, &buf);
-}
-
-static void
-virshEventTunablePrint(virConnectPtr conn G_GNUC_UNUSED,
-                       virDomainPtr dom,
-                       virTypedParameterPtr params,
-                       int nparams,
-                       void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-    size_t i;
-    char *value;
-
-    virBufferAsprintf(&buf, _("event 'tunable' for domain '%s':\n"),
-                      virDomainGetName(dom));
-    for (i = 0; i < nparams; i++) {
-        value = virTypedParameterToString(&params[i]);
-        if (value) {
-            virBufferAsprintf(&buf, "\t%s: %s\n", params[i].field, value);
-            VIR_FREE(value);
-        }
-    }
-    virshEventPrint(opaque, &buf);
-}
-
-VIR_ENUM_DECL(virshEventAgentLifecycleState);
-VIR_ENUM_IMPL(virshEventAgentLifecycleState,
-              VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_LAST,
-              N_("unknown"),
-              N_("connected"),
-              N_("disconnected"));
-
-VIR_ENUM_DECL(virshEventAgentLifecycleReason);
-VIR_ENUM_IMPL(virshEventAgentLifecycleReason,
-              VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_LAST,
-              N_("unknown"),
-              N_("domain started"),
-              N_("channel event"));
-
-#define UNKNOWNSTR(str) (str ? str : N_("unsupported value"))
-static void
-virshEventAgentLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED,
-                              virDomainPtr dom,
-                              int state,
-                              int reason,
-                              void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    virBufferAsprintf(&buf, _("event 'agent-lifecycle' for domain '%s': state: "
-                              "'%s' reason: '%s'\n"),
-                      virDomainGetName(dom),
-                      UNKNOWNSTR(virshEventAgentLifecycleStateTypeToString(state)),
-                      UNKNOWNSTR(virshEventAgentLifecycleReasonTypeToString(reason)));
-    virshEventPrint(opaque, &buf);
-}
-
-static void
-virshEventMigrationIterationPrint(virConnectPtr conn G_GNUC_UNUSED,
-                                  virDomainPtr dom,
-                                  int iteration,
-                                  void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    virBufferAsprintf(&buf, _("event 'migration-iteration' for domain '%s': "
-                              "iteration: '%d'\n"),
-                      virDomainGetName(dom),
-                      iteration);
-
-    virshEventPrint(opaque, &buf);
-}
-
-static void
-virshEventJobCompletedPrint(virConnectPtr conn G_GNUC_UNUSED,
-                            virDomainPtr dom,
-                            virTypedParameterPtr params,
-                            int nparams,
-                            void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-    size_t i;
-
-    virBufferAsprintf(&buf, _("event 'job-completed' for domain '%s':\n"),
-                      virDomainGetName(dom));
-    for (i = 0; i < nparams; i++) {
-        g_autofree char *value = virTypedParameterToString(&params[i]);
-        if (value)
-            virBufferAsprintf(&buf, "\t%s: %s\n", params[i].field, value);
-    }
-    virshEventPrint(opaque, &buf);
-}
-
-
-static void
-virshEventDeviceRemovalFailedPrint(virConnectPtr conn G_GNUC_UNUSED,
-                                   virDomainPtr dom,
-                                   const char *alias,
-                                   void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    virBufferAsprintf(&buf, _("event 'device-removal-failed' for domain '%s': %s\n"),
-                      virDomainGetName(dom),
-                      alias);
-    virshEventPrint(opaque, &buf);
-}
-
-VIR_ENUM_DECL(virshEventMetadataChangeType);
-VIR_ENUM_IMPL(virshEventMetadataChangeType,
-              VIR_DOMAIN_METADATA_LAST,
-              N_("description"),
-              N_("title"),
-              N_("element"));
-
-static void
-virshEventMetadataChangePrint(virConnectPtr conn G_GNUC_UNUSED,
-                              virDomainPtr dom,
-                              int type,
-                              const char *nsuri,
-                              void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    virBufferAsprintf(&buf, _("event 'metadata-change' for domain '%s': type %s, uri %s\n"),
-                      virDomainGetName(dom),
-                      UNKNOWNSTR(virshEventMetadataChangeTypeTypeToString(type)),
-                      NULLSTR(nsuri));
-    virshEventPrint(opaque, &buf);
-}
-
-
-static void
-virshEventBlockThresholdPrint(virConnectPtr conn G_GNUC_UNUSED,
-                              virDomainPtr dom,
-                              const char *dev,
-                              const char *path,
-                              unsigned long long threshold,
-                              unsigned long long excess,
-                              void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    virBufferAsprintf(&buf, _("event 'block-threshold' for domain '%s': "
-                              "dev: %s(%s) %llu %llu\n"),
-                      virDomainGetName(dom),
-                      dev, NULLSTR(path), threshold, excess);
-    virshEventPrint(opaque, &buf);
-}
-
-
-VIR_ENUM_DECL(virshEventMemoryFailureRecipientType);
-VIR_ENUM_IMPL(virshEventMemoryFailureRecipientType,
-              VIR_DOMAIN_EVENT_MEMORY_FAILURE_RECIPIENT_LAST,
-              N_("hypervisor"),
-              N_("guest"));
-
-VIR_ENUM_DECL(virshEventMemoryFailureActionType);
-VIR_ENUM_IMPL(virshEventMemoryFailureActionType,
-              VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_LAST,
-              N_("ignore"),
-              N_("inject"),
-              N_("fatal"),
-              N_("reset"));
-
-static void
-virshEventMemoryFailurePrint(virConnectPtr conn G_GNUC_UNUSED,
-                             virDomainPtr dom,
-                             int recipient,
-                             int action,
-                             unsigned int flags,
-                             void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    virBufferAsprintf(&buf, _("event 'memory-failure' for domain '%s':\n"
-                              "recipient: %s\naction: %s\n"),
-                      virDomainGetName(dom),
-                      UNKNOWNSTR(virshEventMemoryFailureRecipientTypeTypeToString(recipient)),
-                      UNKNOWNSTR(virshEventMemoryFailureActionTypeTypeToString(action)));
-    virBufferAsprintf(&buf, _("flags:\n"
-                              "\taction required: %d\n\trecursive: %d\n"),
-                      !!(flags & VIR_DOMAIN_MEMORY_FAILURE_ACTION_REQUIRED),
-                      !!(flags & VIR_DOMAIN_MEMORY_FAILURE_RECURSIVE));
-
-    virshEventPrint(opaque, &buf);
-}
-
-
-static void
-virshEventMemoryDeviceSizeChangePrint(virConnectPtr conn G_GNUC_UNUSED,
-                                      virDomainPtr dom,
-                                      const char *alias,
-                                      unsigned long long size,
-                                      void *opaque)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    virBufferAsprintf(&buf,
-                      _("event 'memory-device-size-change' for domain '%s':\n"
-                        "alias: %s\nsize: %llu\n"),
-                      virDomainGetName(dom), alias, size);
-
-    virshEventPrint(opaque, &buf);
-}
-
-
-virshDomainEventCallback virshDomainEventCallbacks[] = {
-    { "lifecycle",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventLifecyclePrint), },
-    { "reboot", virshEventGenericPrint, },
-    { "rtc-change",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventRTCChangePrint), },
-    { "watchdog",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventWatchdogPrint), },
-    { "io-error",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventIOErrorPrint), },
-    { "graphics",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventGraphicsPrint), },
-    { "io-error-reason",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventIOErrorReasonPrint), },
-    { "control-error", virshEventGenericPrint, },
-    { "block-job",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockJobPrint), },
-    { "disk-change",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventDiskChangePrint), },
-    { "tray-change",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventTrayChangePrint), },
-    { "pm-wakeup",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), },
-    { "pm-suspend",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), },
-    { "balloon-change",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventBalloonChangePrint), },
-    { "pm-suspend-disk",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), },
-    { "device-removed",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovedPrint), },
-    { "block-job-2",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockJobPrint), },
-    { "tunable",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventTunablePrint), },
-    { "agent-lifecycle",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventAgentLifecyclePrint), },
-    { "device-added",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceAddedPrint), },
-    { "migration-iteration",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventMigrationIterationPrint), },
-    { "job-completed",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventJobCompletedPrint), },
-    { "device-removal-failed",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovalFailedPrint), },
-    { "metadata-change",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventMetadataChangePrint), },
-    { "block-threshold",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockThresholdPrint), },
-    { "memory-failure",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventMemoryFailurePrint), },
-    { "memory-device-size-change",
-      VIR_DOMAIN_EVENT_CALLBACK(virshEventMemoryDeviceSizeChangePrint), },
-};
-G_STATIC_ASSERT(VIR_DOMAIN_EVENT_ID_LAST == G_N_ELEMENTS(virshDomainEventCallbacks));
-
-static const vshCmdInfo info_event[] = {
-    {.name = "help",
-     .data = N_("Domain Events")
-    },
-    {.name = "desc",
-     .data = N_("List event types, or wait for domain events to occur")
-    },
-    {.name = NULL}
-};
-
-static const vshCmdOptDef opts_event[] = {
-    VIRSH_COMMON_OPT_DOMAIN_OT_STRING(N_("filter by domain name, id or uuid"),
-                                      0, 0),
-    {.name = "event",
-     .type = VSH_OT_STRING,
-     .completer = virshDomainEventNameCompleter,
-     .help = N_("which event type to wait for")
-    },
-    {.name = "all",
-     .type = VSH_OT_BOOL,
-     .help = N_("wait for all events instead of just one type")
-    },
-    {.name = "loop",
-     .type = VSH_OT_BOOL,
-     .help = N_("loop until timeout or interrupt, rather than one-shot")
-    },
-    {.name = "timeout",
-     .type = VSH_OT_INT,
-     .help = N_("timeout seconds")
-    },
-    {.name = "list",
-     .type = VSH_OT_BOOL,
-     .help = N_("list valid event types")
-    },
-    {.name = "timestamp",
-     .type = VSH_OT_BOOL,
-     .help = N_("show timestamp for each printed event")
-    },
-    {.name = NULL}
-};
-
-static bool
-cmdEvent(vshControl *ctl, const vshCmd *cmd)
-{
-    g_autoptr(virshDomain) dom = NULL;
-    bool ret = false;
-    int timeout = 0;
-    virshDomEventData *data = NULL;
-    size_t i;
-    const char *eventName = NULL;
-    int event = -1;
-    bool all = vshCommandOptBool(cmd, "all");
-    bool loop = vshCommandOptBool(cmd, "loop");
-    bool timestamp = vshCommandOptBool(cmd, "timestamp");
-    int count = 0;
-    virshControl *priv = ctl->privData;
-
-    VSH_EXCLUSIVE_OPTIONS("all", "event");
-    VSH_EXCLUSIVE_OPTIONS("list", "all");
-    VSH_EXCLUSIVE_OPTIONS("list", "event");
-
-    if (vshCommandOptBool(cmd, "list")) {
-        for (event = 0; event < VIR_DOMAIN_EVENT_ID_LAST; event++)
-            vshPrint(ctl, "%s\n", virshDomainEventCallbacks[event].name);
-        return true;
-    }
-
-    if (vshCommandOptStringReq(ctl, cmd, "event", &eventName) < 0)
-        return false;
-    if (eventName) {
-        for (event = 0; event < VIR_DOMAIN_EVENT_ID_LAST; event++)
-            if (STREQ(eventName, virshDomainEventCallbacks[event].name))
-                break;
-        if (event == VIR_DOMAIN_EVENT_ID_LAST) {
-            vshError(ctl, _("unknown event type %s"), eventName);
-            return false;
-        }
-    } else if (!all) {
-        vshError(ctl, "%s",
-                 _("one of --list, --all, or --event <type> is required"));
-        return false;
-    }
-
-    if (all) {
-        data = g_new0(virshDomEventData, VIR_DOMAIN_EVENT_ID_LAST);
-        for (i = 0; i < VIR_DOMAIN_EVENT_ID_LAST; i++) {
-            data[i].ctl = ctl;
-            data[i].loop = loop;
-            data[i].count = &count;
-            data[i].timestamp = timestamp;
-            data[i].cb = &virshDomainEventCallbacks[i];
-            data[i].id = -1;
-        }
-    } else {
-        data = g_new0(virshDomEventData, 1);
-        data[0].ctl = ctl;
-        data[0].loop = vshCommandOptBool(cmd, "loop");
-        data[0].count = &count;
-        data[0].timestamp = timestamp;
-        data[0].cb = &virshDomainEventCallbacks[event];
-        data[0].id = -1;
-    }
-    if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0)
-        goto cleanup;
-
-    if (vshCommandOptBool(cmd, "domain"))
-        if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
-            goto cleanup;
-
-    if (vshEventStart(ctl, timeout) < 0)
-        goto cleanup;
-
-    for (i = 0; i < (all ? VIR_DOMAIN_EVENT_ID_LAST : 1); i++) {
-        if ((data[i].id = virConnectDomainEventRegisterAny(priv->conn, dom,
-                                                           all ? i : event,
-                                                           data[i].cb->cb,
-                                                           &data[i],
-                                                           NULL)) < 0) {
-            /* When registering for all events: if the first
-             * registration succeeds, silently ignore failures on all
-             * later registrations on the assumption that the server
-             * is older and didn't know quite as many events.  */
-            if (i)
-                vshResetLibvirtError();
-            else
-                goto cleanup;
-        }
-    }
-    switch (vshEventWait(ctl)) {
-    case VSH_EVENT_INTERRUPT:
-        vshPrint(ctl, "%s", _("event loop interrupted\n"));
-        break;
-    case VSH_EVENT_TIMEOUT:
-        vshPrint(ctl, "%s", _("event loop timed out\n"));
-        break;
-    case VSH_EVENT_DONE:
-        break;
-    default:
-        goto cleanup;
-    }
-    vshPrint(ctl, _("events received: %d\n"), count);
-    if (count)
-        ret = true;
-
- cleanup:
-    vshEventCleanup(ctl);
-    if (data) {
-        for (i = 0; i < (all ? VIR_DOMAIN_EVENT_ID_LAST : 1); i++) {
-            if (data[i].id >= 0 &&
-                virConnectDomainEventDeregisterAny(priv->conn, data[i].id) < 0)
-                ret = false;
-        }
-        VIR_FREE(data);
-    }
-    return ret;
-}
-
-
 /*
  * "change-media" command
  */
@@ -14851,12 +13911,6 @@ const vshCmdDef domManagementCmds[] = {
      .info = info_edit,
      .flags = 0
     },
-    {.name = "event",
-     .handler = cmdEvent,
-     .opts = opts_event,
-     .info = info_event,
-     .flags = 0
-    },
     {.name = "get-user-sshkeys",
      .handler = cmdGetUserSSHKeys,
      .opts = opts_get_user_sshkeys,
diff --git a/tools/virsh.c b/tools/virsh.c
index 64e0700fcd..f7adb90be8 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -45,6 +45,7 @@
 #include "virsh-checkpoint.h"
 #include "virsh-console.h"
 #include "virsh-domain.h"
+#include "virsh-domain-event.h"
 #include "virsh-domain-monitor.h"
 #include "virsh-host.h"
 #include "virsh-interface.h"
@@ -814,6 +815,7 @@ static const vshCmdDef virshCmds[] = {
 static const vshCmdGrp cmdGroups[] = {
     {VIRSH_CMD_GRP_DOM_MANAGEMENT, "domain", domManagementCmds},
     {VIRSH_CMD_GRP_DOM_MONITORING, "monitor", domMonitoringCmds},
+    {VIRSH_CMD_GRP_DOM_EVENTS, "events", domEventCmds},
     {VIRSH_CMD_GRP_HOST_AND_HV, "host", hostAndHypervisorCmds},
     {VIRSH_CMD_GRP_CHECKPOINT, "checkpoint", checkpointCmds},
     {VIRSH_CMD_GRP_IFACE, "interface", ifaceCmds},
diff --git a/tools/virsh.h b/tools/virsh.h
index cacd54db57..d7a60b135d 100644
--- a/tools/virsh.h
+++ b/tools/virsh.h
@@ -41,6 +41,7 @@
 #define VIRSH_CMD_GRP_CHECKPOINT       "Checkpoint"
 #define VIRSH_CMD_GRP_DOM_MANAGEMENT   "Domain Management"
 #define VIRSH_CMD_GRP_DOM_MONITORING   "Domain Monitoring"
+#define VIRSH_CMD_GRP_DOM_EVENTS       "Domain Events"
 #define VIRSH_CMD_GRP_STORAGE_POOL     "Storage Pool"
 #define VIRSH_CMD_GRP_STORAGE_VOL      "Storage Volume"
 #define VIRSH_CMD_GRP_NETWORK          "Networking"
-- 
2.35.1




More information about the libvir-list mailing list