diff --git a/po/POTFILES b/po/POTFILES index 3514aa3dca..c71e439fe3 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -293,6 +293,7 @@ src/util/virhostcpu.c src/util/virhostmem.c src/util/virhostuptime.c src/util/viridentity.c +src/util/virinhibitor.c src/util/virinitctl.c src/util/viriscsi.c src/util/virjson.c diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index c931003fad..adc3e3064f 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2608,6 +2608,13 @@ virIdentitySetUserName; virIdentitySetX509DName; +# util/virinhibitor.h +virInhibitorFree; +virInhibitorHold; +virInhibitorNew; +virInhibitorRelease; + + # util/virinitctl.h virInitctlFifos; virInitctlSetRunLevel; diff --git a/src/util/meson.build b/src/util/meson.build index 30f71b0227..69ef49139a 100644 --- a/src/util/meson.build +++ b/src/util/meson.build @@ -45,6 +45,7 @@ util_sources = [ 'virhostmem.c', 'virhostuptime.c', 'viridentity.c', + 'virinhibitor.c', 'virinitctl.c', 'viriscsi.c', 'virjson.c', diff --git a/src/util/virinhibitor.c b/src/util/virinhibitor.c new file mode 100644 index 0000000000..647bdc9fbb --- /dev/null +++ b/src/util/virinhibitor.c @@ -0,0 +1,214 @@ +/* + * virinhibitor.c: helper APIs for inhibiting host actions + * + * Copyright (C) 2024 Red Hat, Inc. + * + * 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 + * . + */ + +#include + +#include "virinhibitor.h" +#include "virgdbus.h" +#include "virsystemd.h" +#include "virfile.h" +#include "virlog.h" +#include "virenum.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +VIR_LOG_INIT("util.inhibitor"); + +struct _virInhibitor { + GMutex lock; + size_t count; + int fd; + + char *what; + char *who; + char *why; + const char *mode; + + virInhibitorAction action; + void *actionData; +}; + +VIR_ENUM_DECL(virInhibitorMode); + +VIR_ENUM_IMPL(virInhibitorMode, + VIR_INHIBITOR_MODE_LAST, + "block", "delay"); + +#ifdef G_OS_UNIX +/* As per: https://www.freedesktop.org/wiki/Software/systemd/inhibit */ +static int +virInhibitorAcquire(const char *what, + const char *who, + const char *why, + const char *mode, + int *inhibitorFD) +{ + g_autoptr(GVariant) reply = NULL; + g_autoptr(GUnixFDList) replyFD = NULL; + g_autoptr(GVariant) message = NULL; + GDBusConnection *systemBus; + int fd; + int rc; + + VIR_DEBUG("what=%s who=%s why=%s mode=%s", + NULLSTR(what), NULLSTR(who), NULLSTR(why), NULLSTR(mode)); + + if (!(systemBus = virGDBusGetSystemBus())) { + VIR_DEBUG("system dbus not available, skipping system inhibitor"); + return 0; + } + + if (virSystemdHasLogind() < 0) { + VIR_DEBUG("logind not available, skipping system inhibitor"); + return 0; + } + + message = g_variant_new("(ssss)", what, who, why, mode); + + rc = virGDBusCallMethodWithFD(systemBus, + &reply, + G_VARIANT_TYPE("(h)"), + &replyFD, + NULL, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "Inhibit", + message, + NULL); + + if (rc < 0) + return -1; + + if (g_unix_fd_list_get_length(replyFD) <= 0) { + VIR_DEBUG("Missing inhibitor FD in logind reply"); + return -1; + } + + fd = g_unix_fd_list_get(replyFD, 0, NULL); + if (fd < 0) { + VIR_DEBUG("Unable to get inhibitor FD from logind reply"); + return -1; + } + + *inhibitorFD = fd; + VIR_DEBUG("Got inhibitor FD %d", fd); + return 0; +} +#endif + + +static char * +virInhibitorWhatFormat(virInhibitorWhat what) +{ + const char *whatstr[] = { + "sleep", + "shutdown", + "idle", + "handle-power-key", + "handle-suspend-key", + "handle-hibernate-key", + "handle-lid-switch", + }; + GString *str = g_string_new(""); + size_t i; + + for (i = 0; i < G_N_ELEMENTS(whatstr); i++) { + if (what & (1 << i)) { + if (str->len) + g_string_append(str, ":"); + g_string_append(str, whatstr[i]); + } + } + + return g_string_free(str, FALSE); +} + + +virInhibitor *virInhibitorNew(virInhibitorWhat what, + const char *who, + const char *why, + virInhibitorMode mode, + virInhibitorAction action, + void *actionData) +{ + virInhibitor *inhibitor = g_new0(virInhibitor, 1); + + inhibitor->fd = -1; + inhibitor->what = virInhibitorWhatFormat(what); + inhibitor->who = g_strdup(who); + inhibitor->why = g_strdup(why); + inhibitor->mode = virInhibitorModeTypeToString(mode); + inhibitor->action = action; + inhibitor->actionData = actionData; + + return inhibitor; +} + +void virInhibitorHold(virInhibitor *inhibitor) +{ + g_mutex_lock(&inhibitor->lock); + + if (inhibitor->count == 0) { + if (inhibitor->action) { + inhibitor->action(true, inhibitor->actionData); + } +#ifdef G_OS_UNIX + if (virInhibitorAcquire( + inhibitor->what, inhibitor->who, inhibitor->why, + inhibitor->mode, &inhibitor->fd) < 0) { + VIR_ERROR(_("Failed to acquire inhibitor: %1$s"), + virGetLastErrorMessage()); + virResetLastError(); + } +#else + VIR_DEBUG("No inhibitor implementation on non-UNIX platforms"); +#endif + } + inhibitor->count++; + g_mutex_unlock(&inhibitor->lock); +} + + +void virInhibitorRelease(virInhibitor *inhibitor) +{ + g_mutex_lock(&inhibitor->lock); + inhibitor->count--; + if (inhibitor->count == 0) { + VIR_FORCE_CLOSE(inhibitor->fd); + if (inhibitor->action) { + inhibitor->action(false, inhibitor->actionData); + } + } + g_mutex_unlock(&inhibitor->lock); +} + + +void virInhibitorFree(virInhibitor *inhibitor) +{ + if (!inhibitor) + return; + + g_free(inhibitor->what); + g_free(inhibitor->who); + g_free(inhibitor->why); + VIR_FORCE_CLOSE(inhibitor->fd); + g_free(inhibitor); +} diff --git a/src/util/virinhibitor.h b/src/util/virinhibitor.h new file mode 100644 index 0000000000..0a1c445d41 --- /dev/null +++ b/src/util/virinhibitor.h @@ -0,0 +1,58 @@ +/* + * virinhibitor.h: helper APIs for inhibiting host actions + * + * Copyright (C) 2024 Red Hat, Inc. + * + * 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 + * . + */ + +#pragma once + +#include "internal.h" + +typedef struct _virInhibitor virInhibitor; + +typedef enum { + VIR_INHIBITOR_WHAT_NONE = 0, + VIR_INHIBITOR_WHAT_SLEEP = (1 << 1), + VIR_INHIBITOR_WHAT_SHUTDOWN = (1 << 2), + VIR_INHIBITOR_WHAT_IDLE = (1 << 3), + VIR_INHIBITOR_WHAT_POWER_KEY = (1 << 4), + VIR_INHIBITOR_WHAT_SUSPEND_KEY = (1 << 5), + VIR_INHIBITOR_WHAT_HIBERNATE_KEY = (1 << 6), + VIR_INHIBITOR_WHAT_LID_SWITCH = (1 << 7), +} virInhibitorWhat; + +typedef enum { + VIR_INHIBITOR_MODE_BLOCK, + VIR_INHIBITOR_MODE_DELAY, + + VIR_INHIBITOR_MODE_LAST +} virInhibitorMode; + +typedef void (*virInhibitorAction)(bool inhibited, + void *opaque); + +virInhibitor *virInhibitorNew(virInhibitorWhat what, + const char *who, + const char *why, + virInhibitorMode mode, + virInhibitorAction action, + void *actionData); + +void virInhibitorHold(virInhibitor *inhibitor); +void virInhibitorRelease(virInhibitor *inhibitor); + +void virInhibitorFree(virInhibitor *inhibitor);