From 43b17dd4444c0d22f71c04db89a53d4236d0270f Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 6 Feb 2014 14:46:52 -0700 Subject: [PATCH] qemu: allow filtering events by regex 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.c (virDomainQemuMonitorEventStateRegisterID): Add flags. (virDomainQemuMonitorEventFilter): Handle regex, and optimize client side. (virDomainQemuMonitorEventCleanup): Clean up regex. Signed-off-by: Eric Blake --- include/libvirt/libvirt-qemu.h | 10 ++++++++ src/conf/domain_event.c | 45 ++++++++++++++++++++++++++++++---- src/libvirt-qemu.c | 11 ++++++--- tools/virsh-domain.c | 13 ++++++++++ tools/virsh.pod | 5 +++- 5 files changed, 74 insertions(+), 10 deletions(-) diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h index ed6d3d2abe..0c5d650cc8 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 88d04fd82d..a9b2533221 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -24,6 +24,8 @@ #include +#include + #include "domain_event.h" #include "object_event.h" #include "object_event_private.h" @@ -1390,6 +1392,8 @@ error: * deregisters. */ struct virDomainQemuMonitorEventData { char *event; + regex_t regex; + unsigned int flags; void *opaque; virFreeCallback freecb; }; @@ -1609,6 +1613,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); } @@ -1619,6 +1629,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); @@ -1634,7 +1646,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, or set of registration flags on server + * @flags: -1 for client, valid virConnectDomainQemuMonitorEventRegisterFlags + * for server * @callbackID: filled with callback ID * * Register the function @cb with connection @conn, from @state, for @@ -1660,12 +1673,34 @@ virDomainQemuMonitorEventStateRegisterID(virConnectPtr conn, return -1; if (flags != -1) - virCheckFlags(0, -1); + virCheckFlags(VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_REGEX | + VIR_CONNECT_DOMAIN_QEMU_MONITOR_EVENT_REGISTER_NOCASE, + -1); 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/libvirt-qemu.c b/src/libvirt-qemu.c index a75a725eb1..37afdfe459 100644 --- a/src/libvirt-qemu.c +++ b/src/libvirt-qemu.c @@ -227,7 +227,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. @@ -242,9 +242,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/tools/virsh-domain.c b/tools/virsh-domain.c index fe15dbfad7..eb3397df56 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -7983,6 +7983,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} }; @@ -7997,6 +8005,11 @@ cmdQemuMonitorEvent(vshControl *ctl, const vshCmd *cmd) const char *event = NULL; vshQemuEventData 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 791c66f7af..4645be5c46 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -3385,12 +3385,15 @@ failed. And when I<--block> is given, the command waits forever with blocking timeout. =item B [I] [I<--event> I] [I<--loop>] -[I<--timeout> I] [I<--pretty>] +[I<--timeout> I] [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 or I. The 'query-events' QMP command can be used via I to learn what events are supported. +If I<--regex> is used, I is a basic regular expression +instead of a literal string. If I<--no-case> is used, I +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) to quit immediately.