summaryrefslogtreecommitdiff
path: root/src/core/nm-sleep-monitor.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/nm-sleep-monitor.c')
-rw-r--r--src/core/nm-sleep-monitor.c389
1 files changed, 389 insertions, 0 deletions
diff --git a/src/core/nm-sleep-monitor.c b/src/core/nm-sleep-monitor.c
new file mode 100644
index 0000000000..cc3d72ee67
--- /dev/null
+++ b/src/core/nm-sleep-monitor.c
@@ -0,0 +1,389 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2012 - 2016 Red Hat, Inc.
+ * Author: Matthias Clasen <mclasen@redhat.com>
+ */
+
+#include "nm-default.h"
+
+#include "nm-sleep-monitor.h"
+
+#include <sys/stat.h>
+#include <gio/gunixfdlist.h>
+
+#include "nm-core-internal.h"
+#include "NetworkManagerUtils.h"
+
+#if defined(SUSPEND_RESUME_UPOWER)
+
+ #define SUSPEND_DBUS_NAME "org.freedesktop.UPower"
+ #define SUSPEND_DBUS_PATH "/org/freedesktop/UPower"
+ #define SUSPEND_DBUS_INTERFACE "org.freedesktop.UPower"
+ #define USE_UPOWER 1
+ #define _NMLOG_PREFIX_NAME "sleep-monitor-up"
+
+#elif defined(SUSPEND_RESUME_SYSTEMD) || defined(SUSPEND_RESUME_ELOGIND)
+
+ #define SUSPEND_DBUS_NAME "org.freedesktop.login1"
+ #define SUSPEND_DBUS_PATH "/org/freedesktop/login1"
+ #define SUSPEND_DBUS_INTERFACE "org.freedesktop.login1.Manager"
+ #define USE_UPOWER 0
+ #if defined(SUSPEND_RESUME_SYSTEMD)
+ #define _NMLOG_PREFIX_NAME "sleep-monitor-sd"
+ #else
+ #define _NMLOG_PREFIX_NAME "sleep-monitor-el"
+ #endif
+
+#elif defined(SUSPEND_RESUME_CONSOLEKIT)
+
+/* ConsoleKit2 has added the same suspend/resume DBUS API that Systemd
+ * uses. http://consolekit2.github.io/ConsoleKit2/#Manager.Inhibit
+ */
+
+ #define SUSPEND_DBUS_NAME "org.freedesktop.ConsoleKit"
+ #define SUSPEND_DBUS_PATH "/org/freedesktop/ConsoleKit/Manager"
+ #define SUSPEND_DBUS_INTERFACE "org.freedesktop.ConsoleKit.Manager"
+ #define USE_UPOWER 0
+ #define _NMLOG_PREFIX_NAME "sleep-monitor-ck"
+
+#else
+
+ #error define one of SUSPEND_RESUME_SYSTEMD, SUSPEND_RESUME_ELOGIND, SUSPEND_RESUME_CONSOLEKIT, or SUSPEND_RESUME_UPOWER
+
+#endif
+
+/*****************************************************************************/
+
+enum {
+ SLEEPING,
+ LAST_SIGNAL,
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+struct _NMSleepMonitor {
+ GObject parent;
+
+ GDBusProxy *proxy;
+
+ /* used both during construction of proxy and during Inhibit call. */
+ GCancellable *cancellable;
+
+ int inhibit_fd;
+ GSList *handles_active;
+ GSList *handles_stale;
+
+ gulong sig_id_1;
+ gulong sig_id_2;
+};
+
+struct _NMSleepMonitorClass {
+ GObjectClass parent;
+};
+
+G_DEFINE_TYPE(NMSleepMonitor, nm_sleep_monitor, G_TYPE_OBJECT);
+
+/*****************************************************************************/
+
+#define _NMLOG_DOMAIN LOGD_SUSPEND
+#define _NMLOG(level, ...) __NMLOG_DEFAULT(level, _NMLOG_DOMAIN, _NMLOG_PREFIX_NAME, __VA_ARGS__)
+
+/*****************************************************************************/
+
+static void sleep_signal(NMSleepMonitor *self, gboolean is_about_to_suspend);
+
+/*****************************************************************************/
+
+#if USE_UPOWER
+
+static void
+upower_sleeping_cb(GDBusProxy *proxy, gpointer user_data)
+{
+ sleep_signal(user_data, TRUE);
+}
+
+static void
+upower_resuming_cb(GDBusProxy *proxy, gpointer user_data)
+{
+ sleep_signal(user_data, FALSE);
+}
+
+#else /* USE_UPOWER */
+
+static void
+drop_inhibitor(NMSleepMonitor *self, gboolean force)
+{
+ if (!force && self->handles_active)
+ return;
+
+ if (self->inhibit_fd >= 0) {
+ _LOGD("inhibit: dropping sleep inhibitor %d", self->inhibit_fd);
+ nm_close(self->inhibit_fd);
+ self->inhibit_fd = -1;
+ }
+
+ if (self->handles_active) {
+ self->handles_stale = g_slist_concat(self->handles_stale, self->handles_active);
+ self->handles_active = NULL;
+ }
+
+ nm_clear_g_cancellable(&self->cancellable);
+}
+
+static void
+inhibit_done(GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ GDBusProxy * proxy = G_DBUS_PROXY(source);
+ NMSleepMonitor *self = user_data;
+ gs_free_error GError *error = NULL;
+ gs_unref_variant GVariant *res = NULL;
+ gs_unref_object GUnixFDList *fd_list = NULL;
+
+ res = g_dbus_proxy_call_with_unix_fd_list_finish(proxy, &fd_list, result, &error);
+ if (!res) {
+ if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_clear_object(&self->cancellable);
+ _LOGW("inhibit: failed (%s)", error->message);
+ }
+ return;
+ }
+
+ g_clear_object(&self->cancellable);
+
+ if (!fd_list || g_unix_fd_list_get_length(fd_list) != 1) {
+ _LOGW("inhibit: didn't get a single fd back");
+ return;
+ }
+
+ self->inhibit_fd = g_unix_fd_list_get(fd_list, 0, NULL);
+ _LOGD("inhibit: inhibitor fd is %d", self->inhibit_fd);
+}
+
+static void
+take_inhibitor(NMSleepMonitor *self)
+{
+ g_return_if_fail(NM_IS_SLEEP_MONITOR(self));
+ g_return_if_fail(G_IS_DBUS_PROXY(self->proxy));
+
+ drop_inhibitor(self, TRUE);
+
+ _LOGD("inhibit: taking sleep inhibitor...");
+ self->cancellable = g_cancellable_new();
+ g_dbus_proxy_call_with_unix_fd_list(self->proxy,
+ "Inhibit",
+ g_variant_new("(ssss)",
+ "sleep",
+ "NetworkManager",
+ "NetworkManager needs to turn off networks",
+ "delay"),
+ 0,
+ G_MAXINT,
+ NULL,
+ self->cancellable,
+ inhibit_done,
+ self);
+}
+
+static void
+prepare_for_sleep_cb(GDBusProxy *proxy, gboolean is_about_to_suspend, gpointer data)
+{
+ sleep_signal(data, is_about_to_suspend);
+}
+
+static void
+name_owner_cb(GObject *object, GParamSpec *pspec, gpointer user_data)
+{
+ GDBusProxy * proxy = G_DBUS_PROXY(object);
+ NMSleepMonitor *self = NM_SLEEP_MONITOR(user_data);
+ char * owner;
+
+ g_assert(proxy == self->proxy);
+
+ owner = g_dbus_proxy_get_name_owner(proxy);
+ if (owner)
+ take_inhibitor(self);
+ else
+ drop_inhibitor(self, TRUE);
+ g_free(owner);
+}
+#endif /* USE_UPOWER */
+
+static void
+sleep_signal(NMSleepMonitor *self, gboolean is_about_to_suspend)
+{
+ g_return_if_fail(NM_IS_SLEEP_MONITOR(self));
+
+ _LOGD("received %s signal", is_about_to_suspend ? "SLEEP" : "RESUME");
+
+#if !USE_UPOWER
+ if (!is_about_to_suspend)
+ take_inhibitor(self);
+#endif
+
+ g_signal_emit(self, signals[SLEEPING], 0, is_about_to_suspend);
+
+#if !USE_UPOWER
+ if (is_about_to_suspend)
+ drop_inhibitor(self, FALSE);
+#endif
+}
+
+/**
+ * nm_sleep_monitor_inhibit_take:
+ * @self: the #NMSleepMonitor instance
+ *
+ * Prevent the release of inhibitor lock
+ *
+ * Returns: an inhibitor handle that must be returned via
+ * nm_sleep_monitor_inhibit_release().
+ **/
+NMSleepMonitorInhibitorHandle *
+nm_sleep_monitor_inhibit_take(NMSleepMonitor *self)
+{
+ g_return_val_if_fail(NM_IS_SLEEP_MONITOR(self), NULL);
+
+ self->handles_active = g_slist_prepend(self->handles_active, NULL);
+ return (NMSleepMonitorInhibitorHandle *) self->handles_active;
+}
+
+/**
+ * nm_sleep_monitor_inhibit_release:
+ * @self: the #NMSleepMonitor instance
+ * @handle: the #NMSleepMonitorInhibitorHandle inhibitor handle.
+ *
+ * Allow again the release of inhibitor lock
+ **/
+void
+nm_sleep_monitor_inhibit_release(NMSleepMonitor *self, NMSleepMonitorInhibitorHandle *handle)
+{
+ GSList *l;
+
+ g_return_if_fail(NM_IS_SLEEP_MONITOR(self));
+ g_return_if_fail(handle);
+
+ l = (GSList *) handle;
+
+ if (g_slist_position(self->handles_active, l) < 0) {
+ if (g_slist_position(self->handles_stale, l) < 0)
+ g_return_if_reached();
+ self->handles_stale = g_slist_delete_link(self->handles_stale, l);
+ return;
+ }
+
+ self->handles_active = g_slist_delete_link(self->handles_active, l);
+
+#if !USE_UPOWER
+ drop_inhibitor(self, FALSE);
+#endif
+}
+
+static void
+on_proxy_acquired(GObject *object, GAsyncResult *res, NMSleepMonitor *self)
+{
+ GError * error = NULL;
+ GDBusProxy *proxy;
+
+ proxy = g_dbus_proxy_new_for_bus_finish(res, &error);
+ if (!proxy) {
+ if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ _LOGW("failed to acquire D-Bus proxy: %s", error->message);
+ g_clear_error(&error);
+ return;
+ }
+ self->proxy = proxy;
+ g_clear_object(&self->cancellable);
+
+#if USE_UPOWER
+ self->sig_id_1 = _nm_dbus_signal_connect(self->proxy,
+ "Sleeping",
+ NULL,
+ G_CALLBACK(upower_sleeping_cb),
+ self);
+ self->sig_id_2 = _nm_dbus_signal_connect(self->proxy,
+ "Resuming",
+ NULL,
+ G_CALLBACK(upower_resuming_cb),
+ self);
+#else
+ self->sig_id_1 =
+ g_signal_connect(self->proxy, "notify::g-name-owner", G_CALLBACK(name_owner_cb), self);
+ self->sig_id_2 = _nm_dbus_signal_connect(self->proxy,
+ "PrepareForSleep",
+ G_VARIANT_TYPE("(b)"),
+ G_CALLBACK(prepare_for_sleep_cb),
+ self);
+ {
+ gs_free char *owner = NULL;
+
+ owner = g_dbus_proxy_get_name_owner(self->proxy);
+ if (owner)
+ take_inhibitor(self);
+ }
+#endif
+}
+
+/*****************************************************************************/
+
+static void
+nm_sleep_monitor_init(NMSleepMonitor *self)
+{
+ self->inhibit_fd = -1;
+ self->cancellable = g_cancellable_new();
+ g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START
+ | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+ NULL,
+ SUSPEND_DBUS_NAME,
+ SUSPEND_DBUS_PATH,
+ SUSPEND_DBUS_INTERFACE,
+ self->cancellable,
+ (GAsyncReadyCallback) on_proxy_acquired,
+ self);
+}
+
+NMSleepMonitor *
+nm_sleep_monitor_new(void)
+{
+ return g_object_new(NM_TYPE_SLEEP_MONITOR, NULL);
+}
+
+static void
+dispose(GObject *object)
+{
+ NMSleepMonitor *self = NM_SLEEP_MONITOR(object);
+
+#if !USE_UPOWER
+ drop_inhibitor(self, TRUE);
+#endif
+
+ nm_clear_g_cancellable(&self->cancellable);
+
+ if (self->proxy) {
+ nm_clear_g_signal_handler(self->proxy, &self->sig_id_1);
+ nm_clear_g_signal_handler(self->proxy, &self->sig_id_2);
+ g_clear_object(&self->proxy);
+ }
+
+ G_OBJECT_CLASS(nm_sleep_monitor_parent_class)->dispose(object);
+}
+
+static void
+nm_sleep_monitor_class_init(NMSleepMonitorClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS(klass);
+
+ gobject_class->dispose = dispose;
+
+ signals[SLEEPING] = g_signal_new(NM_SLEEP_MONITOR_SLEEPING,
+ NM_TYPE_SLEEP_MONITOR,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_BOOLEAN);
+}