blob: c5aae5aaf92ca28a6e517b072860e1a41e1761be [file] [log] [blame]
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* (C) Copyright 2012 Red Hat, Inc.
* Author: Matthias Clasen <mclasen@redhat.com>
*/
#include "config.h"
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
#include <gio/gunixfdlist.h>
#include "mm-log.h"
#include "mm-utils.h"
#include "mm-sleep-monitor.h"
#define SD_NAME "org.freedesktop.login1"
#define SD_PATH "/org/freedesktop/login1"
#define SD_INTERFACE "org.freedesktop.login1.Manager"
struct _MMSleepMonitor {
GObject parent_instance;
GDBusProxy *sd_proxy;
gint inhibit_fd;
};
struct _MMSleepMonitorClass {
GObjectClass parent_class;
void (*sleeping) (MMSleepMonitor *monitor);
void (*resuming) (MMSleepMonitor *monitor);
};
enum {
SLEEPING,
RESUMING,
LAST_SIGNAL,
};
static guint signals[LAST_SIGNAL] = {0};
G_DEFINE_TYPE (MMSleepMonitor, mm_sleep_monitor, G_TYPE_OBJECT);
/********************************************************************/
static gboolean
drop_inhibitor (MMSleepMonitor *self)
{
if (self->inhibit_fd >= 0) {
mm_dbg ("[sleep-monitor] dropping systemd sleep inhibitor");
close (self->inhibit_fd);
self->inhibit_fd = -1;
return TRUE;
}
return FALSE;
}
static void
inhibit_done (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GDBusProxy *sd_proxy = G_DBUS_PROXY (source);
MMSleepMonitor *self = user_data;
GError *error = NULL;
GVariant *res;
GUnixFDList *fd_list;
res = g_dbus_proxy_call_with_unix_fd_list_finish (sd_proxy, &fd_list, result, &error);
if (!res) {
mm_warn ("[sleep-monitor] inhibit failed: %s", error->message);
g_error_free (error);
} else {
if (!fd_list || g_unix_fd_list_get_length (fd_list) != 1)
mm_warn ("[sleep-monitor] didn't get a single fd back");
self->inhibit_fd = g_unix_fd_list_get (fd_list, 0, NULL);
mm_dbg ("[sleep-monitor] inhibitor fd is %d", self->inhibit_fd);
g_object_unref (fd_list);
g_variant_unref (res);
}
}
static void
take_inhibitor (MMSleepMonitor *self)
{
g_assert (self->inhibit_fd == -1);
mm_dbg ("[sleep-monitor] taking systemd sleep inhibitor");
g_dbus_proxy_call_with_unix_fd_list (self->sd_proxy,
"Inhibit",
g_variant_new ("(ssss)",
"sleep",
"ModemManager",
_("ModemManager needs to reset devices"),
"delay"),
0,
G_MAXINT,
NULL,
NULL,
inhibit_done,
self);
}
static void
signal_cb (GDBusProxy *proxy,
const gchar *sendername,
const gchar *signalname,
GVariant *args,
gpointer data)
{
MMSleepMonitor *self = data;
gboolean is_about_to_suspend;
if (strcmp (signalname, "PrepareForSleep") != 0)
return;
g_variant_get (args, "(b)", &is_about_to_suspend);
mm_dbg ("[sleep-monitor] received PrepareForSleep signal: %d", is_about_to_suspend);
if (is_about_to_suspend) {
g_signal_emit (self, signals[SLEEPING], 0);
drop_inhibitor (self);
} else {
take_inhibitor (self);
g_signal_emit (self, signals[RESUMING], 0);
}
}
static void
name_owner_cb (GObject *object,
GParamSpec *pspec,
gpointer user_data)
{
GDBusProxy *proxy = G_DBUS_PROXY (object);
MMSleepMonitor *self = MM_SLEEP_MONITOR (user_data);
char *owner;
g_assert (proxy == self->sd_proxy);
owner = g_dbus_proxy_get_name_owner (proxy);
if (owner)
take_inhibitor (self);
else
drop_inhibitor (self);
g_free (owner);
}
static void
on_proxy_acquired (GObject *object,
GAsyncResult *res,
MMSleepMonitor *self)
{
GError *error = NULL;
char *owner;
self->sd_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
if (!self->sd_proxy) {
mm_warn ("[sleep-monitor] failed to acquire logind proxy: %s", error->message);
g_clear_error (&error);
return;
}
g_signal_connect (self->sd_proxy, "notify::g-name-owner", G_CALLBACK (name_owner_cb), self);
g_signal_connect (self->sd_proxy, "g-signal", G_CALLBACK (signal_cb), self);
owner = g_dbus_proxy_get_name_owner (self->sd_proxy);
if (owner)
take_inhibitor (self);
g_free (owner);
}
static void
mm_sleep_monitor_init (MMSleepMonitor *self)
{
self->inhibit_fd = -1;
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,
SD_NAME, SD_PATH, SD_INTERFACE,
NULL,
(GAsyncReadyCallback) on_proxy_acquired, self);
}
static void
finalize (GObject *object)
{
MMSleepMonitor *self = MM_SLEEP_MONITOR (object);
drop_inhibitor (self);
if (self->sd_proxy)
g_object_unref (self->sd_proxy);
if (G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize != NULL)
G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize (object);
}
static void
mm_sleep_monitor_class_init (MMSleepMonitorClass *klass)
{
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = finalize;
signals[SLEEPING] = g_signal_new (MM_SLEEP_MONITOR_SLEEPING,
MM_TYPE_SLEEP_MONITOR,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MMSleepMonitorClass, sleeping),
NULL, /* accumulator */
NULL, /* accumulator data */
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[RESUMING] = g_signal_new (MM_SLEEP_MONITOR_RESUMING,
MM_TYPE_SLEEP_MONITOR,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MMSleepMonitorClass, resuming),
NULL, /* accumulator */
NULL, /* accumulator data */
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
MM_DEFINE_SINGLETON_GETTER (MMSleepMonitor, mm_sleep_monitor_get, MM_TYPE_SLEEP_MONITOR);
/* ---------------------------------------------------------------------------------------------------- */