[PATCH 00/23] virsh: various small cleanups

A collection of mostly small patches caused by the geopolitically induced attention span shortening. Peter Krempa (23): virsh: cmdBlockcopy: Use virXMLFormatElement virsh: cmdStart: Rewrite ternary operator use to standard if conditions virsh: doSave: Use if-else instead of ternary operator virsh: cmdRestore: Use if-else instead of ternary operator virsh: virshVcpuinfoPrintAffinity: Use if-else instead of ternary operator virsh: Use NULLSTR_EMPTY instead of ternary operator virshEventPrint: Use automatic memory clearing virsh: Move 'virshDomainBlockJobToString' to virsh-util virsh: Move 'cmdEvent' and all of it's machinery to virsh-domain-event.c virsh: cmdEvent: Rewrite questionable event registration virsh: cmdDomDisplay: Extract loop body fetching display URIs into 'virshGetOneDisplay' virsh: cmdDomDisplay: Remove unneeded 'cleanup' label virshGetOneDisplay: Automaticaly free extracted data virshGetOneDisplay: Don't reuse 'xpath' variable virshGetOneDisplay: Refactor formatting of URI params virsh: cmdSchedinfo: Add separate variable for holding flags used for query virsh: cmdDesc: Use separate flags variable for getters vsh: Add helper for auto-removing temporary file virsh: cmdDesc: Use 'vshTempFile' type to simplify cleanup virsh: cmdDesc: Automatically free memory virsh: cmdDesc: Remove unneeded 'cleanup' virsh: domain: Don't use ternaries inside vshPrint/vshError functions virsh: cmdDesc: Fix logic when '-edit' is used along with 'desc' argument 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 | 1623 +++++++------------------------- tools/virsh-util.c | 19 + tools/virsh-util.h | 5 + tools/virsh.c | 2 + tools/virsh.h | 1 + tools/vsh.c | 25 +- tools/vsh.h | 3 + 13 files changed, 1412 insertions(+), 1322 deletions(-) create mode 100644 tools/virsh-domain-event.c create mode 100644 tools/virsh-domain-event.h -- 2.35.1

Rewrite the formatting of the block copy target xml using virXMLFormatElement. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 9b1b14cdc2..6f0c063438 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -2475,14 +2475,20 @@ cmdBlockcopy(vshControl *ctl, const vshCmd *cmd) if (!xmlstr) { g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - virBufferAsprintf(&buf, "<disk type='%s'>\n", - blockdev ? "block" : "file"); - virBufferAdjustIndent(&buf, 2); - virBufferAsprintf(&buf, "<source %s", blockdev ? "dev" : "file"); - virBufferEscapeString(&buf, "='%s'/>\n", dest); + g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) childBuf = VIR_BUFFER_INITIALIZER; + + if (blockdev) { + virBufferAddLit(&attrBuf, " type='block'"); + virBufferAddLit(&childBuf, "<source dev="); + } else { + virBufferAddLit(&buf, " type='file'"); + virBufferAddLit(&childBuf, "<source file="); + } + + virBufferEscapeString(&buf, "'%s'/>\n", dest); virBufferEscapeString(&buf, "<driver type='%s'/>\n", format); - virBufferAdjustIndent(&buf, -2); - virBufferAddLit(&buf, "</disk>\n"); + virXMLFormatElement(&buf, "disk", &attrBuf, &childBuf); xmlstr = virBufferContentAndReset(&buf); } -- 2.35.1

Rewrite the invocation of the virDomainCreate(WithFiles/Flags) APIs based on the arguments into if-else instead of (nested) ternary operators. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 6f0c063438..4c90f40f86 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -4104,10 +4104,15 @@ cmdStart(vshControl *ctl, const vshCmd *cmd) /* We can emulate force boot, even for older servers that reject it. */ if (flags & VIR_DOMAIN_START_FORCE_BOOT) { - if ((nfds ? - virDomainCreateWithFiles(dom, nfds, fds, flags) : - virDomainCreateWithFlags(dom, flags)) == 0) + if (nfds > 0) { + rc = virDomainCreateWithFiles(dom, nfds, fds, flags); + } else { + rc = virDomainCreateWithFlags(dom, flags); + } + + if (rc == 0) goto started; + if (last_error->code != VIR_ERR_NO_SUPPORT && last_error->code != VIR_ERR_INVALID_ARG) { vshReportError(ctl); @@ -4128,9 +4133,15 @@ cmdStart(vshControl *ctl, const vshCmd *cmd) } /* Prefer older API unless we have to pass a flag. */ - if ((nfds ? virDomainCreateWithFiles(dom, nfds, fds, flags) : - (flags ? virDomainCreateWithFlags(dom, flags) - : virDomainCreate(dom))) < 0) { + if (nfds > 0) { + rc = virDomainCreateWithFiles(dom, nfds, fds, flags); + } else if (flags != 0) { + rc = virDomainCreateWithFlags(dom, flags); + } else { + rc = virDomainCreate(dom); + } + + if (rc < 0) { vshError(ctl, _("Failed to start domain '%s'"), virDomainGetName(dom)); return false; } -- 2.35.1

Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 4c90f40f86..607eb973ac 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -4213,6 +4213,7 @@ doSave(void *opaque) unsigned int flags = 0; const char *xmlfile = NULL; g_autofree char *xml = NULL; + int rc; #ifndef WIN32 sigset_t sigmask, oldsigmask; @@ -4244,9 +4245,13 @@ doSave(void *opaque) goto out; } - if (((flags || xml) - ? virDomainSaveFlags(dom, to, xml, flags) - : virDomainSave(dom, to)) < 0) { + if (flags || xml) { + rc = virDomainSaveFlags(dom, to, xml, flags); + } else { + rc = virDomainSave(dom, to); + } + + if (rc < 0) { vshError(ctl, _("Failed to save domain '%s' to %s"), name, to); goto out; } -- 2.35.1

Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 607eb973ac..732690ec44 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -5313,6 +5313,7 @@ cmdRestore(vshControl *ctl, const vshCmd *cmd) const char *xmlfile = NULL; g_autofree char *xml = NULL; virshControl *priv = ctl->privData; + int rc; if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0) return false; @@ -5333,9 +5334,13 @@ cmdRestore(vshControl *ctl, const vshCmd *cmd) virFileReadAll(xmlfile, VSH_MAX_XML_FILE, &xml) < 0) return false; - if (((flags || xml) - ? virDomainRestoreFlags(priv->conn, from, xml, flags) - : virDomainRestore(priv->conn, from)) < 0) { + if (flags || xml) { + rc = virDomainRestoreFlags(priv->conn, from, xml, flags); + } else { + rc = virDomainRestore(priv->conn, from); + } + + if (rc < 0) { vshError(ctl, _("Failed to restore domain from %s"), from); return false; } -- 2.35.1

Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 732690ec44..73f05ce7f9 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -6721,8 +6721,12 @@ virshVcpuinfoPrintAffinity(vshControl *ctl, return -1; vshPrint(ctl, _("%s (out of %d)"), str, maxcpu); } else { - for (i = 0; i < maxcpu; i++) - vshPrint(ctl, "%c", VIR_CPU_USED(cpumap, i) ? 'y' : '-'); + for (i = 0; i < maxcpu; i++) { + if (VIR_CPU_USED(cpumap, i)) + vshPrint(ctl, "y"); + else + vshPrint(ctl, "-"); + } } vshPrint(ctl, "\n"); -- 2.35.1

Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 73f05ce7f9..25097627ac 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -7601,7 +7601,7 @@ cmdIOThreadInfo(vshControl *ctl, const vshCmd *cmd) ignore_value(pinInfo = virBitmapDataFormat(info[i]->cpumap, info[i]->cpumaplen)); - if (vshTableRowAppend(table, iothreadIdStr, pinInfo ? pinInfo : "", NULL) < 0) + if (vshTableRowAppend(table, iothreadIdStr, NULLSTR_EMPTY(pinInfo), NULL) < 0) goto cleanup; } @@ -14211,7 +14211,7 @@ cmdDomFSInfo(vshControl *ctl, const vshCmd *cmd) info[i]->mountpoint, info[i]->name, info[i]->fstype, - targets ? targets : "", + NULLSTR_EMPTY(targets), NULL) < 0) goto cleanup; } -- 2.35.1

Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 25097627ac..33984618eb 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -13166,13 +13166,13 @@ static void virshEventPrint(virshDomEventData *data, virBuffer *buf) { - char *msg; + g_autofree char *msg = NULL; if (!(msg = virBufferContentAndReset(buf))) return; if (!data->loop && *data->count) - goto cleanup; + return; if (data->timestamp) { char timestamp[VIR_TIME_STRING_BUFLEN]; @@ -13188,9 +13188,6 @@ virshEventPrint(virshDomEventData *data, (*data->count)++; if (!data->loop) vshEventDone(data->ctl); - - cleanup: - VIR_FREE(msg); } static void -- 2.35.1

The helper function is used in virshBlockJobInfo and also in the callbacks of cmdEvent. Upcoming patch is going to move out the event code into a helper so this needs to be in a shared place. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 19 ------------------- tools/virsh-util.c | 19 +++++++++++++++++++ tools/virsh-util.h | 5 +++++ 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 33984618eb..9c304dbf78 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -2614,25 +2614,6 @@ static const vshCmdOptDef opts_blockjob[] = { {.name = NULL} }; -VIR_ENUM_DECL(virshDomainBlockJob); -VIR_ENUM_IMPL(virshDomainBlockJob, - VIR_DOMAIN_BLOCK_JOB_TYPE_LAST, - N_("Unknown job"), - N_("Block Pull"), - N_("Block Copy"), - N_("Block Commit"), - N_("Active Block Commit"), - N_("Backup"), -); - -static const char * -virshDomainBlockJobToString(int type) -{ - const char *str = virshDomainBlockJobTypeToString(type); - return str ? _(str) : _("Unknown job"); -} - - static bool virshBlockJobInfo(vshControl *ctl, virDomainPtr dom, diff --git a/tools/virsh-util.c b/tools/virsh-util.c index 8fb617fa3c..dc6ed7a86d 100644 --- a/tools/virsh-util.c +++ b/tools/virsh-util.c @@ -418,3 +418,22 @@ virshDomainGetXML(vshControl *ctl, return ret; } + + +VIR_ENUM_IMPL(virshDomainBlockJob, + VIR_DOMAIN_BLOCK_JOB_TYPE_LAST, + N_("Unknown job"), + N_("Block Pull"), + N_("Block Copy"), + N_("Block Commit"), + N_("Active Block Commit"), + N_("Backup"), +); + + +const char * +virshDomainBlockJobToString(int type) +{ + const char *str = virshDomainBlockJobTypeToString(type); + return str ? _(str) : _("Unknown job"); +} diff --git a/tools/virsh-util.h b/tools/virsh-util.h index 838935d5e8..4d4fe6c01e 100644 --- a/tools/virsh-util.h +++ b/tools/virsh-util.h @@ -151,3 +151,8 @@ virshDomainGetXML(vshControl *ctl, xmlXPathContextPtr *ctxt) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(5) G_GNUC_WARN_UNUSED_RESULT; + +VIR_ENUM_DECL(virshDomainBlockJob); + +const char * +virshDomainBlockJobToString(int type); -- 2.35.1

'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@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@tools/virsh-checkpoint.c @SRCDIR@tools/virsh-completer-host.c @SRCDIR@tools/virsh-console.c +@SRCDIR@tools/virsh-domain-event.c @SRCDIR@tools/virsh-domain-monitor.c @SRCDIR@tools/virsh-domain.c @SRCDIR@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(¶ms[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(¶ms[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(¶ms[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(¶ms[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

*its On a Wednesday in 2022, Peter Krempa wrote:
'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@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
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

The code registering the event handlers in 'cmdEvent' had too many blocks of code conditional on whether just one event is being listened to or all events. The code can be greatly simplified by uniting the code paths and having only one branch when filling the list of events we want to listen for. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain-event.c | 70 +++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/tools/virsh-domain-event.c b/tools/virsh-domain-event.c index 51571dffad..1a2f1cb6e0 100644 --- a/tools/virsh-domain-event.c +++ b/tools/virsh-domain-event.c @@ -274,6 +274,7 @@ typedef struct virshDomainEventCallback virshDomainEventCallback; struct virshDomEventData { vshControl *ctl; + int event; bool loop; int *count; bool timestamp; @@ -885,10 +886,10 @@ cmdEvent(vshControl *ctl, const vshCmd *cmd) g_autoptr(virshDomain) dom = NULL; bool ret = false; int timeout = 0; - virshDomEventData *data = NULL; + g_autofree virshDomEventData *data = NULL; + size_t ndata = 0; 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"); @@ -900,59 +901,59 @@ cmdEvent(vshControl *ctl, const vshCmd *cmd) 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); + for (i = 0; i < G_N_ELEMENTS(virshDomainEventCallbacks); i++) + vshPrint(ctl, "%s\n", virshDomainEventCallbacks[i].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) { + + if (!eventName && !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 { + if (eventName) 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; + else + data = g_new0(virshDomEventData, G_N_ELEMENTS(virshDomainEventCallbacks)); + + for (i = 0; i < G_N_ELEMENTS(virshDomainEventCallbacks); i++) { + if (eventName && + STRNEQ(eventName, virshDomainEventCallbacks[i].name)) + continue; + + data[i].event = 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; + ndata++; } + + if (ndata == 0) { + vshError(ctl, _("unknown event type %s"), eventName); + return false; + } + if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) goto cleanup; - if (vshCommandOptBool(cmd, "domain")) + 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++) { + for (i = 0; i < ndata; i++) { if ((data[i].id = virConnectDomainEventRegisterAny(priv->conn, dom, - all ? i : event, + data[i].event, data[i].cb->cb, &data[i], NULL)) < 0) { @@ -985,12 +986,11 @@ cmdEvent(vshControl *ctl, const vshCmd *cmd) cleanup: vshEventCleanup(ctl); if (data) { - for (i = 0; i < (all ? VIR_DOMAIN_EVENT_ID_LAST : 1); i++) { + for (i = 0; i < ndata; i++) { if (data[i].id >= 0 && virConnectDomainEventDeregisterAny(priv->conn, data[i].id) < 0) ret = false; } - VIR_FREE(data); } return ret; } -- 2.35.1

Separate the code so that the function is not as massive. Note that this is a minimal extraction which does not clean up the code meant for looping. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 352 ++++++++++++++++++++++--------------------- 1 file changed, 183 insertions(+), 169 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index dc6e3b5020..f82aa49745 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -11659,215 +11659,235 @@ static const vshCmdOptDef opts_domdisplay[] = { {.name = NULL} }; -static bool -cmdDomDisplay(vshControl *ctl, const vshCmd *cmd) + +static char * +virshGetOneDisplay(vshControl *ctl, + const char *scheme, + xmlXPathContext *ctxt) { - g_autoptr(xmlDoc) xml = NULL; - g_autoptr(xmlXPathContext) ctxt = NULL; - g_autoptr(virshDomain) dom = NULL; + const char *xpath_fmt = "string(/domain/devices/graphics[@type='%s']/%s)"; g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - bool ret = false; char *xpath = NULL; char *listen_addr = NULL; - int port, tls_port = 0; + int port = 0; + int tls_port = 0; char *type_conn = NULL; char *sockpath = NULL; char *passwd = NULL; - char *output = NULL; - const char *scheme[] = { "vnc", "spice", "rdp", NULL }; - const char *type = NULL; - int iter = 0; int tmp; - int flags = 0; bool params = false; - bool all = vshCommandOptBool(cmd, "all"); - const char *xpath_fmt = "string(/domain/devices/graphics[@type='%s']/%s)"; virSocketAddr addr; - VSH_EXCLUSIVE_OPTIONS("all", "type"); + /* Create our XPATH lookup for the current display's port */ + VIR_FREE(xpath); + xpath = g_strdup_printf(xpath_fmt, scheme, "@port"); - if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) - return false; + /* Attempt to get the port number for the current graphics scheme */ + tmp = virXPathInt(xpath, ctxt, &port); + VIR_FREE(xpath); - if (!virDomainIsActive(dom)) { - vshError(ctl, _("Domain is not running")); - goto cleanup; - } + /* If there is no port number for this type, then jump to the next + * scheme */ + if (tmp) + port = 0; - if (vshCommandOptBool(cmd, "include-password")) - flags |= VIR_DOMAIN_XML_SECURE; + /* Create our XPATH lookup for TLS Port (automatically skipped + * for unsupported schemes */ + xpath = g_strdup_printf(xpath_fmt, scheme, "@tlsPort"); - if (vshCommandOptStringReq(ctl, cmd, "type", &type) < 0) - goto cleanup; + /* Attempt to get the TLS port number */ + tmp = virXPathInt(xpath, ctxt, &tls_port); + VIR_FREE(xpath); + if (tmp) + tls_port = 0; - if (virshDomainGetXMLFromDom(ctl, dom, flags, &xml, &ctxt) < 0) - goto cleanup; + /* Create our XPATH lookup for the current display's address */ + xpath = g_strdup_printf(xpath_fmt, scheme, "@listen"); - /* Attempt to grab our display info */ - for (iter = 0; scheme[iter] != NULL; iter++) { - /* Particular scheme requested */ - if (!all && type && STRNEQ(type, scheme[iter])) - continue; + /* Attempt to get the listening addr if set for the current + * graphics scheme */ + VIR_FREE(listen_addr); + listen_addr = virXPathString(xpath, ctxt); + VIR_FREE(xpath); - /* Create our XPATH lookup for the current display's port */ - VIR_FREE(xpath); - xpath = g_strdup_printf(xpath_fmt, scheme[iter], "@port"); + /* Create our XPATH lookup for the current spice type. */ + xpath = g_strdup_printf(xpath_fmt, scheme, "listen/@type"); - /* Attempt to get the port number for the current graphics scheme */ - tmp = virXPathInt(xpath, ctxt, &port); - VIR_FREE(xpath); + /* Attempt to get the type of spice connection */ + VIR_FREE(type_conn); + type_conn = virXPathString(xpath, ctxt); + VIR_FREE(xpath); - /* If there is no port number for this type, then jump to the next - * scheme */ - if (tmp) - port = 0; + if (STREQ_NULLABLE(type_conn, "socket")) { + if (!sockpath) { + xpath = g_strdup_printf(xpath_fmt, scheme, "listen/@socket"); - /* Create our XPATH lookup for TLS Port (automatically skipped - * for unsupported schemes */ - xpath = g_strdup_printf(xpath_fmt, scheme[iter], "@tlsPort"); + sockpath = virXPathString(xpath, ctxt); - /* Attempt to get the TLS port number */ - tmp = virXPathInt(xpath, ctxt, &tls_port); - VIR_FREE(xpath); - if (tmp) - tls_port = 0; + VIR_FREE(xpath); + } + } + + if (!port && !tls_port && !sockpath) + goto cleanup; - /* Create our XPATH lookup for the current display's address */ - xpath = g_strdup_printf(xpath_fmt, scheme[iter], "@listen"); + if (!listen_addr) { + /* The subelement address - <listen address='xyz'/> - + * *should* have been automatically backfilled into its + * parent <graphics listen='xyz'> (which we just tried to + * retrieve into listen_addr above) but in some cases it + * isn't, so we also do an explicit check for the + * subelement (which, by the way, doesn't exist on libvirt + * < 0.9.4, so we really do need to check both places) + */ + xpath = g_strdup_printf(xpath_fmt, scheme, "listen/@address"); - /* Attempt to get the listening addr if set for the current - * graphics scheme */ - VIR_FREE(listen_addr); listen_addr = virXPathString(xpath, ctxt); VIR_FREE(xpath); + } else { + /* If listen_addr is 0.0.0.0 or [::] we should try to parse URI and set + * listen_addr based on current URI. */ + if (virSocketAddrParse(&addr, listen_addr, AF_UNSPEC) > 0 && + virSocketAddrIsWildcard(&addr)) { + + virConnectPtr conn = ((virshControl *)(ctl->privData))->conn; + char *uriStr = virConnectGetURI(conn); + virURI *uri = NULL; + + if (uriStr) { + uri = virURIParse(uriStr); + VIR_FREE(uriStr); + } - /* Create our XPATH lookup for the current spice type. */ - xpath = g_strdup_printf(xpath_fmt, scheme[iter], "listen/@type"); + /* It's safe to free the listen_addr even if parsing of URI + * fails, if there is no listen_addr we will print "localhost". */ + VIR_FREE(listen_addr); - /* Attempt to get the type of spice connection */ - VIR_FREE(type_conn); - type_conn = virXPathString(xpath, ctxt); - VIR_FREE(xpath); + if (uri) { + listen_addr = g_strdup(uri->server); + virURIFree(uri); + } + } + } - if (STREQ_NULLABLE(type_conn, "socket")) { - if (!sockpath) { - xpath = g_strdup_printf(xpath_fmt, scheme[iter], "listen/@socket"); + /* We can query this info for all the graphics types since we'll + * get nothing for the unsupported ones (just rdp for now). + * Also the parameter '--include-password' was already taken + * care of when getting the XML */ - sockpath = virXPathString(xpath, ctxt); + /* Create our XPATH lookup for the password */ + xpath = g_strdup_printf(xpath_fmt, scheme, "@passwd"); - VIR_FREE(xpath); - } + /* Attempt to get the password */ + VIR_FREE(passwd); + passwd = virXPathString(xpath, ctxt); + VIR_FREE(xpath); + + /* Build up the full URI, starting with the scheme */ + if (sockpath) + virBufferAsprintf(&buf, "%s+unix://", scheme); + else + virBufferAsprintf(&buf, "%s://", scheme); + + /* There is no user, so just append password if there's any */ + if (STREQ(scheme, "vnc") && passwd) + virBufferAsprintf(&buf, ":%s@", passwd); + + /* Then host name or IP */ + if (!listen_addr && !sockpath) + virBufferAddLit(&buf, "localhost"); + else if (!sockpath && strchr(listen_addr, ':')) + virBufferAsprintf(&buf, "[%s]", listen_addr); + else if (sockpath) + virBufferAsprintf(&buf, "%s", sockpath); + else + virBufferAsprintf(&buf, "%s", listen_addr); + + /* Free socket to prepare the pointer for the next iteration */ + VIR_FREE(sockpath); + + /* Add the port */ + if (port) { + if (STREQ(scheme, "vnc")) { + /* VNC protocol handlers take their port number as + * 'port' - 5900 */ + port -= 5900; } - if (!port && !tls_port && !sockpath) - continue; + virBufferAsprintf(&buf, ":%d", port); + } - if (!listen_addr) { - /* The subelement address - <listen address='xyz'/> - - * *should* have been automatically backfilled into its - * parent <graphics listen='xyz'> (which we just tried to - * retrieve into listen_addr above) but in some cases it - * isn't, so we also do an explicit check for the - * subelement (which, by the way, doesn't exist on libvirt - * < 0.9.4, so we really do need to check both places) - */ - xpath = g_strdup_printf(xpath_fmt, scheme[iter], "listen/@address"); - - listen_addr = virXPathString(xpath, ctxt); - VIR_FREE(xpath); - } else { - /* If listen_addr is 0.0.0.0 or [::] we should try to parse URI and set - * listen_addr based on current URI. */ - if (virSocketAddrParse(&addr, listen_addr, AF_UNSPEC) > 0 && - virSocketAddrIsWildcard(&addr)) { - - virConnectPtr conn = ((virshControl *)(ctl->privData))->conn; - char *uriStr = virConnectGetURI(conn); - virURI *uri = NULL; - - if (uriStr) { - uri = virURIParse(uriStr); - VIR_FREE(uriStr); - } + /* TLS Port */ + if (tls_port) { + virBufferAsprintf(&buf, + "?tls-port=%d", + tls_port); + params = true; + } - /* It's safe to free the listen_addr even if parsing of URI - * fails, if there is no listen_addr we will print "localhost". */ - VIR_FREE(listen_addr); + if (STREQ(scheme, "spice") && passwd) { + virBufferAsprintf(&buf, + "%spassword=%s", + params ? "&" : "?", + passwd); + params = true; + } - if (uri) { - listen_addr = g_strdup(uri->server); - virURIFree(uri); - } - } - } + cleanup: + VIR_FREE(xpath); + VIR_FREE(type_conn); + VIR_FREE(sockpath); + VIR_FREE(passwd); + VIR_FREE(listen_addr); - /* We can query this info for all the graphics types since we'll - * get nothing for the unsupported ones (just rdp for now). - * Also the parameter '--include-password' was already taken - * care of when getting the XML */ + return virBufferContentAndReset(&buf); +} - /* Create our XPATH lookup for the password */ - xpath = g_strdup_printf(xpath_fmt, scheme[iter], "@passwd"); - /* Attempt to get the password */ - VIR_FREE(passwd); - passwd = virXPathString(xpath, ctxt); - VIR_FREE(xpath); +static bool +cmdDomDisplay(vshControl *ctl, const vshCmd *cmd) +{ + g_autoptr(xmlDoc) xml = NULL; + g_autoptr(xmlXPathContext) ctxt = NULL; + g_autoptr(virshDomain) dom = NULL; + bool ret = false; + const char *scheme[] = { "vnc", "spice", "rdp", NULL }; + const char *type = NULL; + int iter = 0; + int flags = 0; + bool all = vshCommandOptBool(cmd, "all"); - /* Build up the full URI, starting with the scheme */ - if (sockpath) - virBufferAsprintf(&buf, "%s+unix://", scheme[iter]); - else - virBufferAsprintf(&buf, "%s://", scheme[iter]); - - /* There is no user, so just append password if there's any */ - if (STREQ(scheme[iter], "vnc") && passwd) - virBufferAsprintf(&buf, ":%s@", passwd); - - /* Then host name or IP */ - if (!listen_addr && !sockpath) - virBufferAddLit(&buf, "localhost"); - else if (!sockpath && strchr(listen_addr, ':')) - virBufferAsprintf(&buf, "[%s]", listen_addr); - else if (sockpath) - virBufferAsprintf(&buf, "%s", sockpath); - else - virBufferAsprintf(&buf, "%s", listen_addr); + VSH_EXCLUSIVE_OPTIONS("all", "type"); - /* Free socket to prepare the pointer for the next iteration */ - VIR_FREE(sockpath); + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) + return false; - /* Add the port */ - if (port) { - if (STREQ(scheme[iter], "vnc")) { - /* VNC protocol handlers take their port number as - * 'port' - 5900 */ - port -= 5900; - } + if (!virDomainIsActive(dom)) { + vshError(ctl, _("Domain is not running")); + goto cleanup; + } - virBufferAsprintf(&buf, ":%d", port); - } + if (vshCommandOptBool(cmd, "include-password")) + flags |= VIR_DOMAIN_XML_SECURE; - /* TLS Port */ - if (tls_port) { - virBufferAsprintf(&buf, - "?tls-port=%d", - tls_port); - params = true; - } + if (vshCommandOptStringReq(ctl, cmd, "type", &type) < 0) + goto cleanup; - if (STREQ(scheme[iter], "spice") && passwd) { - virBufferAsprintf(&buf, - "%spassword=%s", - params ? "&" : "?", - passwd); - params = true; - } + if (virshDomainGetXMLFromDom(ctl, dom, flags, &xml, &ctxt) < 0) + goto cleanup; - /* Print out our full URI */ - VIR_FREE(output); - output = virBufferContentAndReset(&buf); - vshPrint(ctl, "%s", output); + /* Attempt to grab our display info */ + for (iter = 0; scheme[iter] != NULL; iter++) { + g_autofree char *display = NULL; + + /* Particular scheme requested */ + if (!all && type && STRNEQ(type, scheme[iter])) + continue; + + if (!(display = virshGetOneDisplay(ctl, scheme[iter], ctxt))) + continue; + + vshPrint(ctl, "%s", display); /* We got what we came for so return successfully */ ret = true; @@ -11884,12 +11904,6 @@ cmdDomDisplay(vshControl *ctl, const vshCmd *cmd) } cleanup: - VIR_FREE(xpath); - VIR_FREE(type_conn); - VIR_FREE(sockpath); - VIR_FREE(passwd); - VIR_FREE(listen_addr); - VIR_FREE(output); return ret; } -- 2.35.1

Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index f82aa49745..7c06c3f80d 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -11864,17 +11864,17 @@ cmdDomDisplay(vshControl *ctl, const vshCmd *cmd) if (!virDomainIsActive(dom)) { vshError(ctl, _("Domain is not running")); - goto cleanup; + return false; } if (vshCommandOptBool(cmd, "include-password")) flags |= VIR_DOMAIN_XML_SECURE; if (vshCommandOptStringReq(ctl, cmd, "type", &type) < 0) - goto cleanup; + return false; if (virshDomainGetXMLFromDom(ctl, dom, flags, &xml, &ctxt) < 0) - goto cleanup; + return false; /* Attempt to grab our display info */ for (iter = 0; scheme[iter] != NULL; iter++) { @@ -11903,7 +11903,6 @@ cmdDomDisplay(vshControl *ctl, const vshCmd *cmd) vshError(ctl, _("No graphical display found")); } - cleanup: return ret; } -- 2.35.1

Use automatic memory freeing for the temporary variables hodling the data extracted from the XML. The code in this function was originally extracted from a loop so we can also drop pre-clearing of the pointers. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 49 ++++++++++++++------------------------------ 1 file changed, 15 insertions(+), 34 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 7c06c3f80d..d5157e4a63 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -11668,15 +11668,14 @@ virshGetOneDisplay(vshControl *ctl, const char *xpath_fmt = "string(/domain/devices/graphics[@type='%s']/%s)"; g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; char *xpath = NULL; - char *listen_addr = NULL; + g_autofree char *listen_addr = NULL; int port = 0; int tls_port = 0; - char *type_conn = NULL; - char *sockpath = NULL; - char *passwd = NULL; + g_autofree char *type_conn = NULL; + g_autofree char *sockpath = NULL; + g_autofree char *passwd = NULL; int tmp; bool params = false; - virSocketAddr addr; /* Create our XPATH lookup for the current display's port */ VIR_FREE(xpath); @@ -11706,7 +11705,6 @@ virshGetOneDisplay(vshControl *ctl, /* Attempt to get the listening addr if set for the current * graphics scheme */ - VIR_FREE(listen_addr); listen_addr = virXPathString(xpath, ctxt); VIR_FREE(xpath); @@ -11714,18 +11712,15 @@ virshGetOneDisplay(vshControl *ctl, xpath = g_strdup_printf(xpath_fmt, scheme, "listen/@type"); /* Attempt to get the type of spice connection */ - VIR_FREE(type_conn); type_conn = virXPathString(xpath, ctxt); VIR_FREE(xpath); if (STREQ_NULLABLE(type_conn, "socket")) { - if (!sockpath) { - xpath = g_strdup_printf(xpath_fmt, scheme, "listen/@socket"); + xpath = g_strdup_printf(xpath_fmt, scheme, "listen/@socket"); - sockpath = virXPathString(xpath, ctxt); + sockpath = virXPathString(xpath, ctxt); - VIR_FREE(xpath); - } + VIR_FREE(xpath); } if (!port && !tls_port && !sockpath) @@ -11745,28 +11740,22 @@ virshGetOneDisplay(vshControl *ctl, listen_addr = virXPathString(xpath, ctxt); VIR_FREE(xpath); } else { + virSocketAddr addr; + /* If listen_addr is 0.0.0.0 or [::] we should try to parse URI and set - * listen_addr based on current URI. */ + * listen_addr based on current URI. If that fails we'll print + * 'localhost' as the address as INADDR_ANY won't help the user. */ if (virSocketAddrParse(&addr, listen_addr, AF_UNSPEC) > 0 && virSocketAddrIsWildcard(&addr)) { virConnectPtr conn = ((virshControl *)(ctl->privData))->conn; - char *uriStr = virConnectGetURI(conn); - virURI *uri = NULL; + g_autofree char *uriStr = virConnectGetURI(conn); + g_autoptr(virURI) uri = NULL; - if (uriStr) { - uri = virURIParse(uriStr); - VIR_FREE(uriStr); - } - - /* It's safe to free the listen_addr even if parsing of URI - * fails, if there is no listen_addr we will print "localhost". */ - VIR_FREE(listen_addr); + g_clear_pointer(&listen_addr, g_free); - if (uri) { + if (uriStr && (uri = virURIParse(uriStr))) listen_addr = g_strdup(uri->server); - virURIFree(uri); - } } } @@ -11779,7 +11768,6 @@ virshGetOneDisplay(vshControl *ctl, xpath = g_strdup_printf(xpath_fmt, scheme, "@passwd"); /* Attempt to get the password */ - VIR_FREE(passwd); passwd = virXPathString(xpath, ctxt); VIR_FREE(xpath); @@ -11803,9 +11791,6 @@ virshGetOneDisplay(vshControl *ctl, else virBufferAsprintf(&buf, "%s", listen_addr); - /* Free socket to prepare the pointer for the next iteration */ - VIR_FREE(sockpath); - /* Add the port */ if (port) { if (STREQ(scheme, "vnc")) { @@ -11835,10 +11820,6 @@ virshGetOneDisplay(vshControl *ctl, cleanup: VIR_FREE(xpath); - VIR_FREE(type_conn); - VIR_FREE(sockpath); - VIR_FREE(passwd); - VIR_FREE(listen_addr); return virBufferContentAndReset(&buf); } -- 2.35.1

On a Wednesday in 2022, Peter Krempa wrote:
Use automatic memory freeing for the temporary variables hodling the
*holding
data extracted from the XML.
The code in this function was originally extracted from a loop so we can also drop pre-clearing of the pointers.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 49 ++++++++++++++------------------------------ 1 file changed, 15 insertions(+), 34 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Add autofreed per-xpath variables to simplify the code. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 74 +++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 49 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index d5157e4a63..d0f78798b5 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -11667,66 +11667,50 @@ virshGetOneDisplay(vshControl *ctl, { const char *xpath_fmt = "string(/domain/devices/graphics[@type='%s']/%s)"; g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - char *xpath = NULL; + g_autofree char *xpathPort = NULL; + g_autofree char *xpathPortTLS = NULL; + g_autofree char *xpathListen = NULL; + g_autofree char *xpathType = NULL; + g_autofree char *xpathPasswd = NULL; g_autofree char *listen_addr = NULL; int port = 0; int tls_port = 0; g_autofree char *type_conn = NULL; g_autofree char *sockpath = NULL; g_autofree char *passwd = NULL; - int tmp; bool params = false; - /* Create our XPATH lookup for the current display's port */ - VIR_FREE(xpath); - xpath = g_strdup_printf(xpath_fmt, scheme, "@port"); - /* Attempt to get the port number for the current graphics scheme */ - tmp = virXPathInt(xpath, ctxt, &port); - VIR_FREE(xpath); + xpathPort = g_strdup_printf(xpath_fmt, scheme, "@port"); - /* If there is no port number for this type, then jump to the next - * scheme */ - if (tmp) + if (virXPathInt(xpathPort, ctxt, &port) < 0) port = 0; - /* Create our XPATH lookup for TLS Port (automatically skipped - * for unsupported schemes */ - xpath = g_strdup_printf(xpath_fmt, scheme, "@tlsPort"); - /* Attempt to get the TLS port number */ - tmp = virXPathInt(xpath, ctxt, &tls_port); - VIR_FREE(xpath); - if (tmp) - tls_port = 0; + xpathPortTLS = g_strdup_printf(xpath_fmt, scheme, "@tlsPort"); - /* Create our XPATH lookup for the current display's address */ - xpath = g_strdup_printf(xpath_fmt, scheme, "@listen"); - - /* Attempt to get the listening addr if set for the current - * graphics scheme */ - listen_addr = virXPathString(xpath, ctxt); - VIR_FREE(xpath); + if (virXPathInt(xpathPortTLS, ctxt, &tls_port) < 0) + tls_port = 0; - /* Create our XPATH lookup for the current spice type. */ - xpath = g_strdup_printf(xpath_fmt, scheme, "listen/@type"); + /* Attempt to get the listening addr if set for the current graphics scheme */ + xpathListen = g_strdup_printf(xpath_fmt, scheme, "@listen"); + listen_addr = virXPathString(xpathListen, ctxt); /* Attempt to get the type of spice connection */ - type_conn = virXPathString(xpath, ctxt); - VIR_FREE(xpath); + xpathType = g_strdup_printf(xpath_fmt, scheme, "listen/@type"); + type_conn = virXPathString(xpathType, ctxt); if (STREQ_NULLABLE(type_conn, "socket")) { - xpath = g_strdup_printf(xpath_fmt, scheme, "listen/@socket"); - - sockpath = virXPathString(xpath, ctxt); + g_autofree char *xpathSockpath = g_strdup_printf(xpath_fmt, scheme, "listen/@socket"); - VIR_FREE(xpath); + sockpath = virXPathString(xpathSockpath, ctxt); } if (!port && !tls_port && !sockpath) - goto cleanup; + return NULL; if (!listen_addr) { + g_autofree char *xpathListenAddress = NULL; /* The subelement address - <listen address='xyz'/> - * *should* have been automatically backfilled into its * parent <graphics listen='xyz'> (which we just tried to @@ -11735,10 +11719,9 @@ virshGetOneDisplay(vshControl *ctl, * subelement (which, by the way, doesn't exist on libvirt * < 0.9.4, so we really do need to check both places) */ - xpath = g_strdup_printf(xpath_fmt, scheme, "listen/@address"); + xpathListenAddress = g_strdup_printf(xpath_fmt, scheme, "listen/@address"); - listen_addr = virXPathString(xpath, ctxt); - VIR_FREE(xpath); + listen_addr = virXPathString(xpathListenAddress, ctxt); } else { virSocketAddr addr; @@ -11759,17 +11742,13 @@ virshGetOneDisplay(vshControl *ctl, } } - /* We can query this info for all the graphics types since we'll + /* Attempt to get the password. + * We can query this info for all the graphics types since we'll * get nothing for the unsupported ones (just rdp for now). * Also the parameter '--include-password' was already taken * care of when getting the XML */ - - /* Create our XPATH lookup for the password */ - xpath = g_strdup_printf(xpath_fmt, scheme, "@passwd"); - - /* Attempt to get the password */ - passwd = virXPathString(xpath, ctxt); - VIR_FREE(xpath); + xpathPasswd = g_strdup_printf(xpath_fmt, scheme, "@passwd"); + passwd = virXPathString(xpathPasswd, ctxt); /* Build up the full URI, starting with the scheme */ if (sockpath) @@ -11818,9 +11797,6 @@ virshGetOneDisplay(vshControl *ctl, params = true; } - cleanup: - VIR_FREE(xpath); - return virBufferContentAndReset(&buf); } -- 2.35.1

Unconditionally format the start of the query ('?') and make delimiters ('&') part of the arguments. At the end we can trim off 1 char from the end of the buffer unconditionally. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index d0f78798b5..ca1145428f 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -11678,7 +11678,6 @@ virshGetOneDisplay(vshControl *ctl, g_autofree char *type_conn = NULL; g_autofree char *sockpath = NULL; g_autofree char *passwd = NULL; - bool params = false; /* Attempt to get the port number for the current graphics scheme */ xpathPort = g_strdup_printf(xpath_fmt, scheme, "@port"); @@ -11781,22 +11780,20 @@ virshGetOneDisplay(vshControl *ctl, virBufferAsprintf(&buf, ":%d", port); } + /* format the parameters part of the uri */ + virBufferAddLit(&buf, "?"); + /* TLS Port */ if (tls_port) { - virBufferAsprintf(&buf, - "?tls-port=%d", - tls_port); - params = true; + virBufferAsprintf(&buf, "tls-port=%d&", tls_port); } if (STREQ(scheme, "spice") && passwd) { - virBufferAsprintf(&buf, - "%spassword=%s", - params ? "&" : "?", - passwd); - params = true; + virBufferAsprintf(&buf, "password=%s&", passwd); } + virBufferTrimLen(&buf, 1); + return virBufferContentAndReset(&buf); } -- 2.35.1

Instead of having two ad-hoc places which decide whether the original flags can be used add another variable specifically for flags used for query. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index ca1145428f..2d1889c71e 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -5158,6 +5158,7 @@ cmdSchedinfo(vshControl *ctl, const vshCmd *cmd) size_t i; bool ret_val = false; unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; + unsigned int queryflags = VIR_DOMAIN_AFFECT_CURRENT; bool current = vshCommandOptBool(cmd, "current"); bool config = vshCommandOptBool(cmd, "config"); bool live = vshCommandOptBool(cmd, "live"); @@ -5170,6 +5171,14 @@ cmdSchedinfo(vshControl *ctl, const vshCmd *cmd) if (live) flags |= VIR_DOMAIN_AFFECT_LIVE; + /* We cannot query both live and config at once, so settle + on current in that case. If we are setting, then the two values should + match when we re-query; otherwise, we report the error later. */ + if (config && live) + queryflags = VIR_DOMAIN_AFFECT_CURRENT; + else + queryflags = flags; + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) return false; @@ -5188,12 +5197,7 @@ cmdSchedinfo(vshControl *ctl, const vshCmd *cmd) memset(params, 0, sizeof(*params) * nparams); if (flags || current) { - /* We cannot query both live and config at once, so settle - on current in that case. If we are setting, then the - two values should match when we re-query; otherwise, we - report the error later. */ - if (virDomainGetSchedulerParametersFlags(dom, params, &nparams, - ((live && config) ? 0 : flags)) == -1) + if (virDomainGetSchedulerParametersFlags(dom, params, &nparams, queryflags) == -1) goto cleanup; } else { if (virDomainGetSchedulerParameters(dom, params, &nparams) == -1) @@ -5212,8 +5216,7 @@ cmdSchedinfo(vshControl *ctl, const vshCmd *cmd) nupdates, flags) == -1) goto cleanup; - if (virDomainGetSchedulerParametersFlags(dom, params, &nparams, - ((live && config) ? 0 : flags)) == -1) + if (virDomainGetSchedulerParametersFlags(dom, params, &nparams, queryflags) == -1) goto cleanup; } else { if (virDomainSetSchedulerParameters(dom, updates, nupdates) == -1) -- 2.35.1

The getters have a different set of flags. Add a variable for the getter to avoid having to construct flags when calling the getter. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 2d1889c71e..cac50dba51 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -8341,12 +8341,15 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; bool ret = false; unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; + unsigned int queryflags = 0; VSH_EXCLUSIVE_OPTIONS_VAR(current, live); VSH_EXCLUSIVE_OPTIONS_VAR(current, config); - if (config) + if (config) { flags |= VIR_DOMAIN_AFFECT_CONFIG; + queryflags |= VIR_DOMAIN_XML_INACTIVE; + } if (live) flags |= VIR_DOMAIN_AFFECT_LIVE; @@ -8370,8 +8373,7 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) if (edit || desc) { if (!desc) { - desc = virshGetDomainDescription(ctl, dom, title, - config?VIR_DOMAIN_XML_INACTIVE:0); + desc = virshGetDomainDescription(ctl, dom, title, queryflags); if (!desc) goto cleanup; } @@ -8420,8 +8422,7 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) title ? _("Domain title updated successfully") : _("Domain description updated successfully")); } else { - desc = virshGetDomainDescription(ctl, dom, title, - config?VIR_DOMAIN_XML_INACTIVE:0); + desc = virshGetDomainDescription(ctl, dom, title, queryflags); if (!desc) goto cleanup; -- 2.35.1

The vsh helpers for user-editing of contents use temporary files. Introduce 'vshTempFile' type which automatically removes the file. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/vsh.c | 25 +++++++++++++++++++------ tools/vsh.h | 3 +++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/tools/vsh.c b/tools/vsh.c index 4ec5e54b5d..bbde594967 100644 --- a/tools/vsh.c +++ b/tools/vsh.c @@ -2378,34 +2378,47 @@ vshAskReedit(vshControl *ctl, #endif /* WIN32 */ +void +vshEditUnlinkTempfile(char *file) +{ + if (!file) + return; + + ignore_value(unlink(file)); + g_free(file); +} + + /* Common code for the edit / net-edit / pool-edit functions which follow. */ char * vshEditWriteToTempFile(vshControl *ctl, const char *doc) { - g_autofree char *ret = NULL; + g_autofree char *filename = NULL; + g_autoptr(vshTempFile) ret = NULL; const char *tmpdir; VIR_AUTOCLOSE fd = -1; tmpdir = getenv("TMPDIR"); - if (!tmpdir) tmpdir = "/tmp"; - ret = g_strdup_printf("%s/virshXXXXXX.xml", tmpdir); - fd = g_mkstemp_full(ret, O_RDWR | O_CLOEXEC, S_IRUSR | S_IWUSR); + if (!tmpdir) + tmpdir = "/tmp"; + filename = g_strdup_printf("%s/virshXXXXXX.xml", tmpdir); + fd = g_mkstemp_full(filename, O_RDWR | O_CLOEXEC, S_IRUSR | S_IWUSR); if (fd == -1) { vshError(ctl, _("g_mkstemp_full: failed to create temporary file: %s"), g_strerror(errno)); return NULL; } + ret = g_steal_pointer(&filename); + if (safewrite(fd, doc, strlen(doc)) == -1) { vshError(ctl, _("write: %s: failed to write to temporary file: %s"), ret, g_strerror(errno)); - unlink(ret); return NULL; } if (VIR_CLOSE(fd) < 0) { vshError(ctl, _("close: %s: failed to write or close temporary file: %s"), ret, g_strerror(errno)); - unlink(ret); return NULL; } diff --git a/tools/vsh.h b/tools/vsh.h index e208d957bb..663dc1ffce 100644 --- a/tools/vsh.h +++ b/tools/vsh.h @@ -341,6 +341,9 @@ void vshSaveLibvirtError(void); void vshSaveLibvirtHelperError(void); /* file handling */ +void vshEditUnlinkTempfile(char *file); +typedef char vshTempFile; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(vshTempFile, vshEditUnlinkTempfile); char *vshEditWriteToTempFile(vshControl *ctl, const char *doc); int vshEditFile(vshControl *ctl, const char *filename); char *vshEditReadBackFile(vshControl *ctl, const char *filename); -- 2.35.1

Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index cac50dba51..dcf0f712f6 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -8335,7 +8335,6 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) int type; char *desc = NULL; char *desc_edited = NULL; - char *tmp = NULL; char *tmpstr; const vshCmdOpt *opt = NULL; g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; @@ -8379,6 +8378,8 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) } if (edit) { + g_autoptr(vshTempFile) tmp = NULL; + /* Create and open the temporary file. */ if (!(tmp = vshEditWriteToTempFile(ctl, desc))) goto cleanup; @@ -8439,10 +8440,6 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) cleanup: VIR_FREE(desc_edited); VIR_FREE(desc); - if (tmp) { - unlink(tmp); - VIR_FREE(tmp); - } return ret; } -- 2.35.1

Decrease scope of variables and use automatic freeing. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index dcf0f712f6..4d9722f400 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -8333,9 +8333,7 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) int state; int type; - char *desc = NULL; - char *desc_edited = NULL; - char *tmpstr; + g_autofree char *desc = NULL; const vshCmdOpt *opt = NULL; g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; bool ret = false; @@ -8379,6 +8377,8 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) if (edit) { g_autoptr(vshTempFile) tmp = NULL; + g_autofree char *desc_edited = NULL; + char *tmpstr; /* Create and open the temporary file. */ if (!(tmp = vshEditWriteToTempFile(ctl, desc))) @@ -8438,8 +8438,6 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) ret = true; cleanup: - VIR_FREE(desc_edited); - VIR_FREE(desc); return ret; } -- 2.35.1

Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 4d9722f400..7b4f8638a9 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -8336,7 +8336,6 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) g_autofree char *desc = NULL; const vshCmdOpt *opt = NULL; g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - bool ret = false; unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; unsigned int queryflags = 0; @@ -8354,7 +8353,7 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) return false; if ((state = virshDomainState(ctl, dom, NULL)) < 0) - goto cleanup; + return false; if (title) type = VIR_DOMAIN_METADATA_TITLE; @@ -8372,7 +8371,7 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) if (!desc) { desc = virshGetDomainDescription(ctl, dom, title, queryflags); if (!desc) - goto cleanup; + return false; } if (edit) { @@ -8382,15 +8381,15 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) /* Create and open the temporary file. */ if (!(tmp = vshEditWriteToTempFile(ctl, desc))) - goto cleanup; + return false; /* Start the editor. */ if (vshEditFile(ctl, tmp) == -1) - goto cleanup; + return false; /* Read back the edited file. */ if (!(desc_edited = vshEditReadBackFile(ctl, tmp))) - goto cleanup; + return false; /* strip a possible newline at the end of file; some * editors enforce a newline, this makes editing the title @@ -8405,8 +8404,7 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) vshPrintExtra(ctl, "%s", title ? _("Domain title not changed\n") : _("Domain description not changed\n")); - ret = true; - goto cleanup; + return true; } VIR_FREE(desc); @@ -8417,7 +8415,7 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) vshError(ctl, "%s", title ? _("Failed to set new domain title") : _("Failed to set new domain description")); - goto cleanup; + return false; } vshPrintExtra(ctl, "%s", title ? _("Domain title updated successfully") : @@ -8425,7 +8423,7 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) } else { desc = virshGetDomainDescription(ctl, dom, title, queryflags); if (!desc) - goto cleanup; + return false; if (strlen(desc) > 0) vshPrint(ctl, "%s", desc); @@ -8436,9 +8434,7 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) virDomainGetName(dom)); } - ret = true; - cleanup: - return ret; + return true; } -- 2.35.1

Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 50 ++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 7b4f8638a9..89ad45dbf0 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -3929,11 +3929,13 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd) /* No way to emulate deletion of just snapshot metadata * without support for the newer flags. Oh well. */ if (has_snapshots_metadata) { - vshError(ctl, - snapshots_metadata ? - _("Unable to remove metadata of %d snapshots") : - _("Refusing to undefine while %d snapshots exist"), - has_snapshots_metadata); + if (snapshots_metadata) + vshError(ctl, _("Unable to remove metadata of %d snapshots"), + has_snapshots_metadata); + else + vshError(ctl, _("Refusing to undefine while %d snapshots exist"), + has_snapshots_metadata); + goto cleanup; } @@ -8401,9 +8403,11 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) /* Compare original XML with edited. Has it changed at all? */ if (STREQ(desc, desc_edited)) { - vshPrintExtra(ctl, "%s", - title ? _("Domain title not changed\n") : - _("Domain description not changed\n")); + if (title) + vshPrintExtra(ctl, "%s", _("Domain title not changed\n")); + else + vshPrintExtra(ctl, "%s", _("Domain description not changed\n")); + return true; } @@ -8412,26 +8416,32 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) } if (virDomainSetMetadata(dom, type, desc, NULL, NULL, flags) < 0) { - vshError(ctl, "%s", - title ? _("Failed to set new domain title") : - _("Failed to set new domain description")); + if (title) + vshError(ctl, "%s", _("Failed to set new domain title")); + else + vshError(ctl, "%s", _("Failed to set new domain description")); + return false; } - vshPrintExtra(ctl, "%s", - title ? _("Domain title updated successfully") : - _("Domain description updated successfully")); + + if (title) + vshPrintExtra(ctl, "%s", _("Domain title updated successfully")); + else + vshPrintExtra(ctl, "%s", _("Domain description updated successfully")); + } else { desc = virshGetDomainDescription(ctl, dom, title, queryflags); if (!desc) return false; - if (strlen(desc) > 0) + if (strlen(desc) > 0) { vshPrint(ctl, "%s", desc); - else - vshPrintExtra(ctl, - title ? _("No title for domain: %s") : - _("No description for domain: %s"), - virDomainGetName(dom)); + } else { + if (title) + vshPrintExtra(ctl, _("No title for domain: %s"), virDomainGetName(dom)); + else + vshPrintExtra(ctl, _("No description for domain: %s"), virDomainGetName(dom)); + } } return true; -- 2.35.1

Historically the use of the '-desc' multiple argument parameter was not forbidden toghether with '-edit', but use of both together has some unexpected behaviour. Specifically the editor is filled with the contents passed via '-desc' but if the user doesn't change the text in any way virsh will claim that the description was not chaged even if it differs from the currently set description. Similarly, when the user would edit the description provided via 'desc' so that it's identical with the one configured for the domain, virsh would claim that it was updated: # virsh desc cd No description for domain: cd # EDITOR=true virsh desc cd --edit "test desc" Domain description not changed After the fix: # virsh desc cd No description for domain: cd # EDITOR=true virsh desc cd --edit "test desc" Domain description updated successfully # EDITOR=true virsh desc cd --edit "test desc" Domain description not changed Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- tools/virsh-domain.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 89ad45dbf0..d5fd8be7c3 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -8335,7 +8335,7 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) int state; int type; - g_autofree char *desc = NULL; + g_autofree char *descArg = NULL; const vshCmdOpt *opt = NULL; g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; @@ -8367,14 +8367,17 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) virBufferTrim(&buf, " "); - desc = virBufferContentAndReset(&buf); + descArg = virBufferContentAndReset(&buf); - if (edit || desc) { - if (!desc) { - desc = virshGetDomainDescription(ctl, dom, title, queryflags); - if (!desc) - return false; - } + if (edit || descArg) { + g_autofree char *descDom = NULL; + g_autofree char *descNew = NULL; + + if (!(descDom = virshGetDomainDescription(ctl, dom, title, queryflags))) + return false; + + if (!descArg) + descArg = g_strdup(descDom); if (edit) { g_autoptr(vshTempFile) tmp = NULL; @@ -8382,7 +8385,7 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) char *tmpstr; /* Create and open the temporary file. */ - if (!(tmp = vshEditWriteToTempFile(ctl, desc))) + if (!(tmp = vshEditWriteToTempFile(ctl, descArg))) return false; /* Start the editor. */ @@ -8402,7 +8405,7 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) *tmpstr = '\0'; /* Compare original XML with edited. Has it changed at all? */ - if (STREQ(desc, desc_edited)) { + if (STREQ(descDom, desc_edited)) { if (title) vshPrintExtra(ctl, "%s", _("Domain title not changed\n")); else @@ -8411,11 +8414,12 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) return true; } - VIR_FREE(desc); - desc = g_steal_pointer(&desc_edited); + descNew = g_steal_pointer(&desc_edited); + } else { + descNew = g_steal_pointer(&descArg); } - if (virDomainSetMetadata(dom, type, desc, NULL, NULL, flags) < 0) { + if (virDomainSetMetadata(dom, type, descNew, NULL, NULL, flags) < 0) { if (title) vshError(ctl, "%s", _("Failed to set new domain title")); else @@ -8430,7 +8434,7 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd) vshPrintExtra(ctl, "%s", _("Domain description updated successfully")); } else { - desc = virshGetDomainDescription(ctl, dom, title, queryflags); + g_autofree char *desc = virshGetDomainDescription(ctl, dom, title, queryflags); if (!desc) return false; -- 2.35.1

On a Wednesday in 2022, Peter Krempa wrote:
A collection of mostly small patches
[...]
Peter Krempa (23): virsh: cmdBlockcopy: Use virXMLFormatElement virsh: cmdStart: Rewrite ternary operator use to standard if conditions virsh: doSave: Use if-else instead of ternary operator virsh: cmdRestore: Use if-else instead of ternary operator virsh: virshVcpuinfoPrintAffinity: Use if-else instead of ternary operator virsh: Use NULLSTR_EMPTY instead of ternary operator virshEventPrint: Use automatic memory clearing virsh: Move 'virshDomainBlockJobToString' to virsh-util virsh: Move 'cmdEvent' and all of it's machinery to virsh-domain-event.c virsh: cmdEvent: Rewrite questionable event registration virsh: cmdDomDisplay: Extract loop body fetching display URIs into 'virshGetOneDisplay' virsh: cmdDomDisplay: Remove unneeded 'cleanup' label virshGetOneDisplay: Automaticaly free extracted data virshGetOneDisplay: Don't reuse 'xpath' variable virshGetOneDisplay: Refactor formatting of URI params virsh: cmdSchedinfo: Add separate variable for holding flags used for query virsh: cmdDesc: Use separate flags variable for getters vsh: Add helper for auto-removing temporary file virsh: cmdDesc: Use 'vshTempFile' type to simplify cleanup virsh: cmdDesc: Automatically free memory virsh: cmdDesc: Remove unneeded 'cleanup' virsh: domain: Don't use ternaries inside vshPrint/vshError functions virsh: cmdDesc: Fix logic when '-edit' is used along with 'desc' argument
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 | 1623 +++++++------------------------- tools/virsh-util.c | 19 + tools/virsh-util.h | 5 + tools/virsh.c | 2 + tools/virsh.h | 1 + tools/vsh.c | 25 +- tools/vsh.h | 3 + 13 files changed, 1412 insertions(+), 1322 deletions(-) create mode 100644 tools/virsh-domain-event.c create mode 100644 tools/virsh-domain-event.h
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano
participants (2)
-
Ján Tomko
-
Peter Krempa