about summary refs log tree commit diff
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 00000000..ffb4db4e
--- /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 "src/core/nm-default-daemon.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);
+}