[libvirt] [PATCH 7/6] qemu: allow filtering events by regex

Eric Blake eblake at redhat.com
Thu Feb 6 23:19:53 UTC 2014


When listening for a subset of monitor events, it can be tedious
to register for each event name in series; nicer is to register
for multiple events in one go.  Implement a flag to use regex
interpretation of the event filter.

While at it, prove how much I hate the shift key, by adding a
way to filter for 'shutdown' instead of 'SHUTDOWN'. :)

* include/libvirt/libvirt-qemu.h
(virConnectDomainQemuMonitorEventRegisterFlags): New enum.
* src/libvirt-qemu.c (virConnectDomainQemuMonitorEventRegister):
Document flags.
* tools/virsh-domain.c (cmdQemuMonitorEvent): Expose them.
* tools/virsh.pod (qemu-monitor-event): Document this.
* src/conf/domain_event.h
(virDomainQemuMonitorEventStateRegisterID): Adjust signature.
* src/conf/domain_event.c
(virDomainQemuMonitorEventStateRegisterID): Add flags.
(virDomainQemuMonitorEventFilter): Handle regex, and optimize
client side.
(virDomainQemuMonitorEventCleanup): Clean up regex.
* src/qemu/qemu_driver.c
(qemuConnectDomainQemuMonitorEventRegister): Adjust caller.
* src/remote/remote_driver.c
(remoteConnectDomainQemuMonitorEventRegister): New flags can
always be safely handled server side.

Signed-off-by: Eric Blake <eblake at redhat.com>
---
 include/libvirt/libvirt-qemu.h | 10 ++++++++++
 src/conf/domain_event.c        | 41 ++++++++++++++++++++++++++++++++++++++---
 src/conf/domain_event.h        |  3 ++-
 src/libvirt-qemu.c             | 11 +++++++----
 src/qemu/qemu_driver.c         |  7 +++++--
 src/remote/remote_driver.c     |  8 +-------
 tools/virsh-domain.c           | 13 +++++++++++++
 tools/virsh.pod                |  5 ++++-
 8 files changed, 80 insertions(+), 18 deletions(-)

diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h
index ed6d3d2..652926e 100644
--- a/include/libvirt/libvirt-qemu.h
+++ b/include/libvirt/libvirt-qemu.h
@@ -76,6 +76,16 @@ typedef void (*virConnectDomainQemuMonitorEventCallback)(virConnectPtr conn,
                                                          const char *details,
                                                          void *opaque);

+
+typedef enum {
+    /* Event filter is a regex rather than a literal string */
+    VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_REGEX = (1 << 0),
+
+    /* Event filter is case insensitive */
+    VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_NOCASE = (1 << 1),
+} virConnectDomainQemuMonitorEventRegisterFlags;
+
+
 int virConnectDomainQemuMonitorEventRegister(virConnectPtr conn,
                                              virDomainPtr dom,
                                              const char *event,
diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c
index 1fb5243..ab468c9 100644
--- a/src/conf/domain_event.c
+++ b/src/conf/domain_event.c
@@ -24,6 +24,8 @@

 #include <config.h>

+#include <regex.h>
+
 #include "domain_event.h"
 #include "object_event.h"
 #include "object_event_private.h"
@@ -1389,6 +1391,8 @@ error:
  * deregisters.  */
 struct virDomainQemuMonitorEventData {
     char *event;
+    regex_t regex;
+    unsigned int flags;
     void *opaque;
     virFreeCallback freecb;
 };
@@ -1608,6 +1612,12 @@ virDomainQemuMonitorEventFilter(virConnectPtr conn ATTRIBUTE_UNUSED,

     monitorEvent = (virDomainQemuMonitorEventPtr) event;

+    if (data->flags == -1)
+        return true;
+    if (data->flags & VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_REGEX)
+        return regexec(&data->regex, monitorEvent->event, 0, NULL, 0) == 0;
+    if (data->flags & VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_NOCASE)
+        return STRCASEEQ(monitorEvent->event, data->event);
     return STREQ(monitorEvent->event, data->event);
 }

@@ -1618,6 +1628,8 @@ virDomainQemuMonitorEventCleanup(void *opaque)
     virDomainQemuMonitorEventData *data = opaque;

     VIR_FREE(data->event);
+    if (data->flags & VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_REGEX)
+        regfree(&data->regex);
     if (data->freecb)
         (data->freecb)(data->opaque);
     VIR_FREE(data);
@@ -1633,6 +1645,8 @@ virDomainQemuMonitorEventCleanup(void *opaque)
  * @cb: function to invoke when event occurs
  * @opaque: data blob to pass to callback
  * @freecb: callback to free @opaque
+ * @flags: -1 for client, valid virConnectDomainQemuMonitorEventRegisterFlags
+ *         for server
  * @callbackID: filled with callback ID
  *
  * Register the function @cb with connection @conn, from @state, for
@@ -1648,6 +1662,7 @@ virDomainQemuMonitorEventStateRegisterID(virConnectPtr conn,
                                          virConnectDomainQemuMonitorEventCallback cb,
                                          void *opaque,
                                          virFreeCallback freecb,
+                                         unsigned int flags,
                                          int *callbackID)
 {
     virDomainQemuMonitorEventData *data = NULL;
@@ -1658,9 +1673,29 @@ virDomainQemuMonitorEventStateRegisterID(virConnectPtr conn,

     if (VIR_ALLOC(data) < 0)
         return -1;
-    if (VIR_STRDUP(data->event, event) < 0) {
-        VIR_FREE(data);
-        return -1;
+    data->flags = flags;
+    if (flags != -1) {
+        int rflags = REG_NOSUB;
+
+        if (flags & VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_NOCASE)
+            rflags |= REG_ICASE;
+        if (flags & VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_REGEX) {
+            int err = regcomp(&data->regex, event, rflags);
+
+            if (err) {
+                char error[100];
+                regerror(err, &data->regex, error, sizeof(error));
+                virReportError(VIR_ERR_INVALID_ARG,
+                               _("failed to compile regex '%s': %s"),
+                               event, error);
+                regfree(&data->regex);
+                VIR_FREE(data);
+                return -1;
+            }
+        } else if (VIR_STRDUP(data->event, event) < 0) {
+            VIR_FREE(data);
+            return -1;
+        }
     }
     data->opaque = opaque;
     data->freecb = freecb;
diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h
index eb5183e..9c41090 100644
--- a/src/conf/domain_event.h
+++ b/src/conf/domain_event.h
@@ -227,9 +227,10 @@ virDomainQemuMonitorEventStateRegisterID(virConnectPtr conn,
                                          virConnectDomainQemuMonitorEventCallback cb,
                                          void *opaque,
                                          virFreeCallback freecb,
+                                         unsigned int flags,
                                          int *callbackID)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(5)
-    ATTRIBUTE_NONNULL(8);
+    ATTRIBUTE_NONNULL(9);

 virObjectEventPtr
 virDomainQemuMonitorEventNew(int id,
diff --git a/src/libvirt-qemu.c b/src/libvirt-qemu.c
index e5c6311..0876d52 100644
--- a/src/libvirt-qemu.c
+++ b/src/libvirt-qemu.c
@@ -225,7 +225,7 @@ error:
  * @cb: callback to the function handling monitor events
  * @opaque: opaque data to pass on to the callback
  * @freecb: optional function to deallocate opaque when not used anymore
- * @flags: extra flags; not used yet, so callers should always pass 0
+ * @flags: bitwise-OR of virConnectDomainQemuMonitorEventRegisterFlags
  *
  * This API is QEMU specific, so it will only work with hypervisor
  * connections to the QEMU driver.
@@ -240,9 +240,12 @@ error:
  * is non-NULL, then only the specific domain will be monitored.
  *
  * If @event is NULL, then all monitor events will be reported. If @event is
- * non-NULL, then only the specific monitor event will be reported.  @flags
- * is currently unused, but in the future may support a flag for passing
- * @event as a glob instead of a literal name to match a category of events.
+ * non-NULL, then only specific monitor events will be reported.  @flags
+ * controls how the filtering is performed: 0 requests an exact match, while
+ * VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_REGEX states that @event
+ * is a basic regular expression.  Additionally, including
+ * VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_NOCASE lets @event match
+ * case-insensitively.
  *
  * The virDomainPtr object handle passed into the callback upon delivery
  * of an event is only valid for the duration of execution of the callback.
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 6b5076d..1639e53 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -16322,7 +16322,9 @@ qemuConnectDomainQemuMonitorEventRegister(virConnectPtr conn,
     virQEMUDriverPtr driver = conn->privateData;
     int ret = -1;

-    virCheckFlags(0, -1);
+    virCheckFlags(VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_REGEX |
+                  VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_NOCASE,
+                  -1);

     if (virConnectDomainQemuMonitorEventRegisterEnsureACL(conn) < 0)
         goto cleanup;
@@ -16330,7 +16332,8 @@ qemuConnectDomainQemuMonitorEventRegister(virConnectPtr conn,
     if (virDomainQemuMonitorEventStateRegisterID(conn,
                                                  driver->domainEventState,
                                                  dom, event, callback,
-                                                 opaque, freecb, &ret) < 0)
+                                                 opaque, freecb, flags,
+                                                 &ret) < 0)
         ret = -1;

 cleanup:
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index d38b883..1d3d13a 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -3205,16 +3205,10 @@ remoteConnectDomainQemuMonitorEventRegister(virConnectPtr conn,

     remoteDriverLock(priv);

-    /* While most remote functions don't check flags, so that they can
-     * control a newer server that does understand the flag, this
-     * particular function may use flags to modify how 'event' is
-     * interpreted client side.  */
-    virCheckFlags(0, -1);
-
     if ((count = virDomainQemuMonitorEventStateRegisterID(conn,
                                                           priv->eventState,
                                                           dom, event, callback,
-                                                          opaque, freecb,
+                                                          opaque, freecb, -1,
                                                           &callbackID)) < 0)
         goto done;

diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
index a6dc80a..fd04361 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -8016,6 +8016,14 @@ static const vshCmdOptDef opts_qemu_monitor_event[] = {
      .type = VSH_OT_INT,
      .help = N_("timeout seconds")
     },
+    {.name = "regex",
+     .type = VSH_OT_BOOL,
+     .help = N_("treat event as a regex rather than literal filter")
+    },
+    {.name = "no-case",
+     .type = VSH_OT_BOOL,
+     .help = N_("treat event case-insensitively")
+    },
     {.name = NULL}
 };

@@ -8036,6 +8044,11 @@ cmdQemuMonitorEvent(vshControl *ctl, const vshCmd *cmd)
     char buf = '\0';
     vshEventData data;

+    if (vshCommandOptBool(cmd, "regex"))
+        flags |= VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_REGEX;
+    if (vshCommandOptBool(cmd, "no-case"))
+        flags |= VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_NOCASE;
+
     data.ctl = ctl;
     data.loop = vshCommandOptBool(cmd, "loop");
     data.pretty = vshCommandOptBool(cmd, "pretty");
diff --git a/tools/virsh.pod b/tools/virsh.pod
index a8af75b..cc3aac2 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -3331,12 +3331,15 @@ failed. And when I<--block> is given, the command waits forever with blocking
 timeout.

 =item B<qemu-monitor-event> [I<domain>] [I<--event> I<event-name>] [I<--loop>]
-[I<--timeout> I<seconds>] [I<--pretty>]
+[I<--timeout> I<seconds>] [I<--pretty>] [I<--regex>] [I<--no-case>]

 Wait for arbitrary QEMU monitor events to occur, and print out the
 details of events as they happen.  The events can optionally be filtered
 by I<domain> or I<event-name>.  The 'query-events' QMP command can be
 used via I<qemu-monitor-command> to learn what events are supported.
+If I<--regex> is used, I<event-name> is a basic regular expression
+instead of a literal string.  If I<--no-case> is used, I<event-name>
+will match case-insensitively.

 By default, this command is one-shot, and returns success once an event
 occurs; you can send SIGINT (usually via C<Ctrl-C>) to quit immediately.
-- 
1.8.5.3




More information about the libvir-list mailing list