2 * Copyright (C) 2012 Denis Yantarev
3 * Copyright (C) 2005-2013 Team XBMC
6 * This Program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
11 * This Program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with XBMC; see the file COPYING. If not, write to
18 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19 * http://www.gnu.org/copyleft/gpl.html
24 #include "LogindUPowerSyscall.h"
25 #include "utils/log.h"
29 // logind DBus interface specification:
30 // http://www.freedesktop.org/wiki/Software/Logind/logind
32 // Inhibitor Locks documentation:
33 // http://www.freedesktop.org/wiki/Software/Logind/inhibit/
35 #define LOGIND_DEST "org.freedesktop.login1"
36 #define LOGIND_PATH "/org/freedesktop/login1"
37 #define LOGIND_IFACE "org.freedesktop.login1.Manager"
39 CLogindUPowerSyscall::CLogindUPowerSyscall()
43 CLog::Log(LOGINFO, "Selected Logind/UPower as PowerSyscall");
45 // Check if we have UPower. If not, we avoid any battery related operations.
46 CDBusMessage message("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "EnumerateDevices");
47 m_hasUPower = message.SendSystem() != NULL;
50 CLog::Log(LOGINFO, "LogindUPowerSyscall - UPower not found, battery information will not be available");
52 m_canPowerdown = LogindCheckCapability("CanPowerOff");
53 m_canReboot = LogindCheckCapability("CanReboot");
54 m_canHibernate = LogindCheckCapability("CanHibernate");
55 m_canSuspend = LogindCheckCapability("CanSuspend");
64 dbus_error_init(&error);
65 m_connection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
67 if (dbus_error_is_set(&error))
69 CLog::Log(LOGERROR, "LogindUPowerSyscall: Failed to get dbus connection: %s", error.message);
70 dbus_connection_close(m_connection);
71 dbus_connection_unref(m_connection);
73 dbus_error_free(&error);
77 dbus_connection_set_exit_on_disconnect(m_connection, false);
78 dbus_bus_add_match(m_connection, "type='signal',interface='org.freedesktop.login1.Manager',member='PrepareForSleep'", NULL);
81 dbus_bus_add_match(m_connection, "type='signal',interface='org.freedesktop.UPower',member='DeviceChanged'", NULL);
83 dbus_connection_flush(m_connection);
84 dbus_error_free(&error);
87 CLogindUPowerSyscall::~CLogindUPowerSyscall()
91 dbus_connection_close(m_connection);
92 dbus_connection_unref(m_connection);
98 bool CLogindUPowerSyscall::Powerdown()
100 return LogindSetPowerState("PowerOff");
103 bool CLogindUPowerSyscall::Reboot()
105 return LogindSetPowerState("Reboot");
108 bool CLogindUPowerSyscall::Suspend()
110 return LogindSetPowerState("Suspend");
113 bool CLogindUPowerSyscall::Hibernate()
115 return LogindSetPowerState("Hibernate");
118 bool CLogindUPowerSyscall::CanPowerdown()
120 return m_canPowerdown;
123 bool CLogindUPowerSyscall::CanSuspend()
128 bool CLogindUPowerSyscall::CanHibernate()
130 return m_canHibernate;
133 bool CLogindUPowerSyscall::CanReboot()
138 bool CLogindUPowerSyscall::HasLogind()
140 // recommended method by systemd devs. The seats directory
141 // doesn't exist unless logind created it and therefore is running.
142 // see also https://mail.gnome.org/archives/desktop-devel-list/2013-March/msg00092.html
143 return (access("/run/systemd/seats/", F_OK) >= 0);
146 bool CLogindUPowerSyscall::LogindSetPowerState(const char *state)
148 CDBusMessage message(LOGIND_DEST, LOGIND_PATH, LOGIND_IFACE, state);
149 // The user_interaction boolean parameters can be used to control
150 // wether PolicyKit should interactively ask the user for authentication
151 // credentials if it needs to.
152 message.AppendArgument(false);
153 return message.SendSystem() != NULL;
156 bool CLogindUPowerSyscall::LogindCheckCapability(const char *capability)
159 CDBusMessage message(LOGIND_DEST, LOGIND_PATH, LOGIND_IFACE, capability);
160 DBusMessage *reply = message.SendSystem();
161 if(reply && dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID))
163 // Returns one of "yes", "no" or "challenge". If "challenge" is
164 // returned the operation is available, but only after authorization.
165 return (strcmp(arg, "yes") == 0);
170 int CLogindUPowerSyscall::BatteryLevel()
172 return m_batteryLevel;
175 void CLogindUPowerSyscall::UpdateBatteryLevel()
177 char** source = NULL;
179 double batteryLevelSum = 0;
180 int batteryCount = 0;
182 CDBusMessage message("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "EnumerateDevices");
183 DBusMessage *reply = message.SendSystem();
188 if (!dbus_message_get_args(reply, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &source, &length, DBUS_TYPE_INVALID))
190 CLog::Log(LOGWARNING, "LogindUPowerSyscall: failed to enumerate devices");
194 for (int i = 0; i < length; i++)
196 CVariant properties = CDBusUtil::GetAll("org.freedesktop.UPower", source[i], "org.freedesktop.UPower.Device");
197 bool isRechargeable = properties["IsRechargeable"].asBoolean();
202 batteryLevelSum += properties["Percentage"].asDouble();
206 dbus_free_string_array(source);
208 if (batteryCount > 0)
209 m_batteryLevel = (int)(batteryLevelSum / (double)batteryCount);
211 m_lowBattery = CDBusUtil::GetVariant("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "OnLowBattery").asBoolean();
214 bool CLogindUPowerSyscall::PumpPowerEvents(IPowerEventsCallback *callback)
217 bool releaseLock = false;
221 dbus_connection_read_write(m_connection, 0);
222 DBusMessage *msg = dbus_connection_pop_message(m_connection);
226 if (dbus_message_is_signal(msg, "org.freedesktop.login1.Manager", "PrepareForSleep"))
229 // the boolean argument defines whether we are going to sleep (true) or just woke up (false)
230 dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &arg, DBUS_TYPE_INVALID);
231 CLog::Log(LOGDEBUG, "LogindUPowerSyscall: Received PrepareForSleep with arg %i", (int)arg);
245 else if (dbus_message_is_signal(msg, "org.freedesktop.UPower", "DeviceChanged"))
247 bool lowBattery = m_lowBattery;
248 UpdateBatteryLevel();
249 if (m_lowBattery && !lowBattery)
250 callback->OnLowBattery();
255 CLog::Log(LOGDEBUG, "LogindUPowerSyscall - Received unknown signal %s", dbus_message_get_member(msg));
257 dbus_message_unref(msg);
267 void CLogindUPowerSyscall::InhibitDelayLock()
269 #ifdef DBUS_TYPE_UNIX_FD
270 CDBusMessage message("org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "Inhibit");
271 message.AppendArgument("sleep"); // what to inhibit
272 message.AppendArgument("XBMC"); // who
273 message.AppendArgument(""); // reason
274 message.AppendArgument("delay"); // mode
276 DBusMessage *reply = message.SendSystem();
280 CLog::Log(LOGWARNING, "LogindUPowerSyscall - failed to inhibit sleep delay lock");
285 if (!dbus_message_get_args(reply, NULL, DBUS_TYPE_UNIX_FD, &m_delayLockFd, DBUS_TYPE_INVALID))
287 CLog::Log(LOGWARNING, "LogindUPowerSyscall - failed to get inhibit file descriptor");
292 CLog::Log(LOGDEBUG, "LogindUPowerSyscall - inhibit lock taken, fd %i", m_delayLockFd);
294 CLog::Log(LOGWARNING, "LogindUPowerSyscall - inhibit lock support not compiled in");
298 void CLogindUPowerSyscall::ReleaseDelayLock()
300 if (m_delayLockFd != -1)
302 close(m_delayLockFd);
304 CLog::Log(LOGDEBUG, "LogindUPowerSyscall - delay lock released");