on 39aae47 help sign in
unity8/plugins/Unity/Session/dbusunitysessionservice.cpp
468 lines 14 kB history raw
1/*
2 * Copyright (C) 2014, 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
17// local
18#include "dbusunitysessionservice.h"
20// system
21#include <sys/types.h>
22#include <unistd.h>
23#include <pwd.h>
25// Qt
26#include <QDebug>
27#include <QDBusPendingCall>
28#include <QDBusReply>
29#include <QElapsedTimer>
30#include <QDateTime>
32#define LOGIN1_SERVICE QStringLiteral("org.freedesktop.login1")
33#define LOGIN1_PATH QStringLiteral("/org/freedesktop/login1")
34#define LOGIN1_IFACE QStringLiteral("org.freedesktop.login1.Manager")
35#define LOGIN1_SESSION_IFACE QStringLiteral("org.freedesktop.login1.Session")
37#define ACTIVE_KEY QStringLiteral("Active")
38#define IDLE_SINCE_KEY QStringLiteral("IdleSinceHint")
40class DBusUnitySessionServicePrivate: public QObject
42 Q_OBJECT
43public:
44 QString logindSessionPath;
45 bool isSessionActive = true;
46 QElapsedTimer screensaverActiveTimer;
48 DBusUnitySessionServicePrivate(): QObject() {
49 init();
50 checkActive();
51 }
53 void init()
54 {
55 // get our logind session path
56 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
57 LOGIN1_PATH,
58 LOGIN1_IFACE,
59 "GetSessionByPID");
60 msg << (quint32) getpid();
62 QDBusReply<QDBusObjectPath> reply = QDBusConnection::systemBus().asyncCall(msg);
63 if (reply.isValid()) {
64 logindSessionPath = reply.value().path();
66 // start watching the Active property
67 QDBusConnection::systemBus().connect(LOGIN1_SERVICE, logindSessionPath, "org.freedesktop.DBus.Properties", "PropertiesChanged",
68 this, SLOT(onPropertiesChanged(QString,QVariantMap,QStringList)));
69 } else {
70 qWarning() << "Failed to get logind session path" << reply.error().message();
71 }
72 }
74 bool checkLogin1Call(const QString &method) const
75 {
76 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, method);
77 QDBusReply<QString> reply = QDBusConnection::systemBus().asyncCall(msg);
78 return reply.isValid() && (reply == QStringLiteral("yes") || reply == QStringLiteral("challenge"));
79 }
81 void makeLogin1Call(const QString &method, const QVariantList &args)
82 {
83 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
84 LOGIN1_PATH,
85 LOGIN1_IFACE,
86 method);
87 msg.setArguments(args);
88 QDBusConnection::systemBus().asyncCall(msg);
89 }
91 void checkActive()
92 {
93 if (logindSessionPath.isEmpty()) {
94 qWarning() << "Invalid session path";
95 return;
96 }
98 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
99 logindSessionPath,
100 "org.freedesktop.DBus.Properties",
101 "Get");
102 msg << LOGIN1_SESSION_IFACE;
103 msg << ACTIVE_KEY;
105 QDBusReply<QVariant> reply = QDBusConnection::systemBus().asyncCall(msg);
106 if (reply.isValid()) {
107 isSessionActive = reply.value().toBool();
108 qDebug() << "Session" << logindSessionPath << "is active:" << isSessionActive;
109 } else {
110 qWarning() << "Failed to get Active property" << reply.error().message();
111 }
112 }
114 quint32 screensaverActiveTime() const
115 {
116 if (!isSessionActive && screensaverActiveTimer.isValid()) {
117 return screensaverActiveTimer.elapsed() / 1000;
118 }
120 return 0;
121 }
123 quint64 idleSinceUSecTimestamp() const
124 {
125 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
126 logindSessionPath,
127 "org.freedesktop.DBus.Properties",
128 "Get");
129 msg << LOGIN1_SESSION_IFACE;
130 msg << IDLE_SINCE_KEY;
132 QDBusReply<QVariant> reply = QDBusConnection::systemBus().asyncCall(msg);
133 if (reply.isValid()) {
134 return reply.value().value<quint64>();
135 } else {
136 qWarning() << "Failed to get IdleSinceHint property" << reply.error().message();
137 }
139 return 0;
140 }
142 void setIdleHint(bool idle)
143 {
144 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
145 logindSessionPath,
146 LOGIN1_SESSION_IFACE,
147 "SetIdleHint");
148 msg << idle;
149 QDBusConnection::systemBus().asyncCall(msg);
150 }
152private Q_SLOTS:
153 void onPropertiesChanged(const QString &iface, const QVariantMap &changedProps, const QStringList &invalidatedProps)
154 {
155 Q_UNUSED(iface)
157 if (changedProps.contains(ACTIVE_KEY) || invalidatedProps.contains(ACTIVE_KEY)) {
158 if (changedProps.value(ACTIVE_KEY).isValid()) {
159 isSessionActive = changedProps.value(ACTIVE_KEY).toBool();
160 } else {
161 checkActive();
162 }
164 Q_EMIT screensaverActiveChanged(!isSessionActive);
166 if (isSessionActive) {
167 screensaverActiveTimer.invalidate();
168 setIdleHint(false);
169 } else {
170 screensaverActiveTimer.start();
171 setIdleHint(true);
172 }
173 }
174 }
176Q_SIGNALS:
177 void screensaverActiveChanged(bool active);
178};
180Q_GLOBAL_STATIC(DBusUnitySessionServicePrivate, d)
182DBusUnitySessionService::DBusUnitySessionService()
183 : UnityDBusObject("/com/canonical/Unity/Session", "com.canonical.Unity")
185 if (!d->logindSessionPath.isEmpty()) {
186 // connect our Lock() slot to the logind's session Lock() signal
187 QDBusConnection::systemBus().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, "Lock", this, SLOT(Lock()));
188 // ... and our Unlocked() signal to the logind's session Unlock() signal
189 // (lightdm handles the unlocking by calling logind's Unlock method which in turn emits this signal we connect to)
190 QDBusConnection::systemBus().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, "Unlock", this, SIGNAL(Unlocked()));
191 } else {
192 qWarning() << "Failed to connect to logind's session Lock/Unlock signals";
193 }
196void DBusUnitySessionService::Logout()
198 // TODO ask the apps to quit and then emit the signal
199 Q_EMIT LogoutReady();
202void DBusUnitySessionService::EndSession()
204 const QDBusMessage msg = QDBusMessage::createMethodCall("com.ubuntu.Upstart",
205 "/com/ubuntu/Upstart",
206 "com.ubuntu.Upstart0_6",
207 "EndSession");
208 QDBusConnection::sessionBus().asyncCall(msg);
211bool DBusUnitySessionService::CanHibernate() const
213 return d->checkLogin1Call("CanHibernate");
216bool DBusUnitySessionService::CanSuspend() const
218 return d->checkLogin1Call("CanSuspend");
221bool DBusUnitySessionService::CanHybridSleep() const
223 return d->checkLogin1Call("CanHybridSleep");
226bool DBusUnitySessionService::CanReboot() const
228 return d->checkLogin1Call("CanReboot");
231bool DBusUnitySessionService::CanShutdown() const
233 return d->checkLogin1Call("CanPowerOff");
236bool DBusUnitySessionService::CanLock() const
238 return true; // FIXME
241QString DBusUnitySessionService::UserName() const
243 struct passwd *p = getpwuid(geteuid());
244 if (p) {
245 return QString::fromUtf8(p->pw_name);
246 }
248 return QString();
251QString DBusUnitySessionService::RealName() const
253 struct passwd *p = getpwuid(geteuid());
254 if (p) {
255 const QString gecos = QString::fromLocal8Bit(p->pw_gecos);
256 if (!gecos.isEmpty()) {
257 return gecos.split(QLatin1Char(',')).first();
258 }
259 }
261 return QString();
264QString DBusUnitySessionService::HostName() const
266 char hostName[512];
267 if (gethostname(hostName, sizeof(hostName)) == -1) {
268 qWarning() << "Could not determine local hostname";
269 return QString();
270 }
271 hostName[sizeof(hostName) - 1] = '\0';
272 return QString::fromLocal8Bit(hostName);
275void DBusUnitySessionService::PromptLock()
277 Q_EMIT LockRequested();
280void DBusUnitySessionService::Lock()
282 // lock the session using the org.freedesktop.DisplayManager system DBUS service
283 const QString sessionPath = QString::fromLocal8Bit(qgetenv("XDG_SESSION_PATH"));
284 QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.DisplayManager",
285 sessionPath,
286 "org.freedesktop.DisplayManager.Session",
287 "Lock");
288 qDebug() << "Locking session" << msg.path();
289 QDBusReply<void> reply = QDBusConnection::systemBus().asyncCall(msg);
290 if (!reply.isValid()) {
291 qWarning() << "Lock call failed" << reply.error().message();
292 } else {
293 // emit Locked when the call succeeds
294 Q_EMIT Locked();
295 }
298bool DBusUnitySessionService::IsLocked() const
300 return !d->isSessionActive;
303void DBusUnitySessionService::RequestLogout()
305 Q_EMIT LogoutRequested(false);
308void DBusUnitySessionService::Reboot()
310 d->makeLogin1Call("Reboot", {false});
313void DBusUnitySessionService::RequestReboot()
315 Q_EMIT RebootRequested(false);
318void DBusUnitySessionService::Shutdown()
320 d->makeLogin1Call("PowerOff", {false});
323void DBusUnitySessionService::Suspend()
325 d->makeLogin1Call("Suspend", {false});
328void DBusUnitySessionService::Hibernate()
330 d->makeLogin1Call("Hibernate", {false});
333void DBusUnitySessionService::HybridSleep()
335 d->makeLogin1Call("HybridSleep", {false});
338void DBusUnitySessionService::RequestShutdown()
340 Q_EMIT ShutdownRequested(false);
343enum class Action : unsigned
345 LOGOUT = 0,
346 SHUTDOWN,
347 REBOOT,
348 NONE
349};
352void performAsyncUnityCall(const QString &method)
354 const QDBusMessage msg = QDBusMessage::createMethodCall("com.canonical.Unity",
355 "/com/canonical/Unity/Session",
356 "com.canonical.Unity.Session",
357 method);
358 QDBusConnection::sessionBus().asyncCall(msg);
362DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
363 : UnityDBusObject("/org/gnome/SessionManager/EndSessionDialog", "com.canonical.Unity")
367void DBusGnomeSessionManagerWrapper::Open(const unsigned type, const unsigned arg_1, const unsigned max_wait, const QList<QDBusObjectPath> &inhibitors)
369 Q_UNUSED(arg_1);
370 Q_UNUSED(max_wait);
371 Q_UNUSED(inhibitors);
373 switch (static_cast<Action>(type))
374 {
375 case Action::LOGOUT:
376 performAsyncUnityCall("RequestLogout");
377 break;
379 case Action::REBOOT:
380 performAsyncUnityCall("RequestReboot");
381 break;
383 case Action::SHUTDOWN:
384 performAsyncUnityCall("RequestShutdown");
385 break;
387 default:
388 break;
389 }
393DBusGnomeScreensaverWrapper::DBusGnomeScreensaverWrapper()
394 : UnityDBusObject("/org/gnome/ScreenSaver", "org.gnome.ScreenSaver")
396 connect(d, &DBusUnitySessionServicePrivate::screensaverActiveChanged, this, &DBusGnomeScreensaverWrapper::ActiveChanged);
399bool DBusGnomeScreensaverWrapper::GetActive() const
401 return !d->isSessionActive; // return whether the session is not active
404void DBusGnomeScreensaverWrapper::SetActive(bool lock)
406 if (lock) {
407 Lock();
408 }
411void DBusGnomeScreensaverWrapper::Lock()
413 performAsyncUnityCall("Lock");
416quint32 DBusGnomeScreensaverWrapper::GetActiveTime() const
418 return d->screensaverActiveTime();
421void DBusGnomeScreensaverWrapper::SimulateUserActivity()
423 d->setIdleHint(false);
427DBusScreensaverWrapper::DBusScreensaverWrapper()
428 : UnityDBusObject("/org/freedesktop/ScreenSaver", "org.freedesktop.ScreenSaver")
430 QDBusConnection::sessionBus().registerObject("/ScreenSaver", this, QDBusConnection::ExportScriptableContents); // compat path, also register here
431 connect(d, &DBusUnitySessionServicePrivate::screensaverActiveChanged, this, &DBusScreensaverWrapper::ActiveChanged);
434bool DBusScreensaverWrapper::GetActive() const
436 return !d->isSessionActive; // return whether the session is not active
439bool DBusScreensaverWrapper::SetActive(bool lock)
441 if (lock) {
442 Lock();
443 return true;
444 }
445 return false;
448void DBusScreensaverWrapper::Lock()
450 performAsyncUnityCall("Lock");
453quint32 DBusScreensaverWrapper::GetActiveTime() const
455 return d->screensaverActiveTime();
458quint32 DBusScreensaverWrapper::GetSessionIdleTime() const
460 return QDateTime::fromMSecsSinceEpoch(d->idleSinceUSecTimestamp()/1000).secsTo(QDateTime::currentDateTime());
463void DBusScreensaverWrapper::SimulateUserActivity()
465 d->setIdleHint(false);
468#include "dbusunitysessionservice.moc"