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@xxxxxxxxxx> --- 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list