diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h index ed6d3d2abe005a9b8c9140e961793c960a5af322..0c5d650cc8dd2318fef98378aa0336515dac1fe5 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 88d04fd82d3c440240f08a59038f88d9cc24f2ad..a9b2533221618eb49986566ea4221128f1fc9dbe 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 a75a725eb18b9dbb31cecc53c964f54880199039..37afdfe4593592c315fd4f1dfa8c888d4ce293e8 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 fe15dbfad75242a37914574d1dbaa65f8a944c0d..eb3397df565ed575479b2d06f5b9cf9233ce742e 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 791c66f7af31638141a02034a92c9fef80b177b8..4645be5c4606a8db23d1d123b231cfc9daaa8c79 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.