on 33fa90d help sign in
unity8/plugins/Unity/Session/dbusunitysessionservice.cpp
538 lines 17 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>
31#include <QDBusUnixFileDescriptor>
33#define LOGIN1_SERVICE QStringLiteral("org.freedesktop.login1")
34#define LOGIN1_PATH QStringLiteral("/org/freedesktop/login1")
35#define LOGIN1_IFACE QStringLiteral("org.freedesktop.login1.Manager")
36#define LOGIN1_SESSION_IFACE QStringLiteral("org.freedesktop.login1.Session")
38#define ACTIVE_KEY QStringLiteral("Active")
39#define IDLE_SINCE_KEY QStringLiteral("IdleSinceHint")
41class DBusUnitySessionServicePrivate: public QObject
43 Q_OBJECT
44public:
45 QString logindSessionPath;
46 bool isSessionActive = true;
47 QElapsedTimer screensaverActiveTimer;
48 QDBusUnixFileDescriptor m_systemdInhibitFd;
50 DBusUnitySessionServicePrivate(): QObject() {
51 init();
52 checkActive();
53 }
55 void init()
56 {
57 // get our logind session path
58 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
59 LOGIN1_PATH,
60 LOGIN1_IFACE,
61 QStringLiteral("GetSessionByPID"));
62 msg << (quint32) getpid();
64 QDBusReply<QDBusObjectPath> reply = QDBusConnection::systemBus().call(msg);
65 if (reply.isValid()) {
66 logindSessionPath = reply.value().path();
68 // start watching the Active property
69 QDBusConnection::systemBus().connect(LOGIN1_SERVICE, logindSessionPath, QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("PropertiesChanged"),
70 this, SLOT(onPropertiesChanged(QString,QVariantMap,QStringList)));
72 setupSystemdInhibition();
74 // re-enable the inhibition upon resume from sleep
75 QDBusConnection::systemBus().connect(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, QStringLiteral("PrepareForSleep"),
76 this, SLOT(onResuming(bool)));
77 } else {
78 qWarning() << "Failed to get logind session path" << reply.error().message();
79 }
80 }
82 void setupSystemdInhibition()
83 {
84 if (m_systemdInhibitFd.isValid())
85 return;
87 // inhibit systemd handling of power/sleep/lid buttons
88 // http://www.freedesktop.org/wiki/Software/systemd/inhibit
90 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, QStringLiteral("Inhibit"));
91 msg << "handle-power-key:handle-suspend-key:handle-hibernate-key:handle-lid-switch"; // what
92 msg << "Unity"; // who
93 msg << "Unity8 handles power events"; // why
94 msg << "block"; // mode
96 QDBusPendingCall pendingCall = QDBusConnection::systemBus().asyncCall(msg);
97 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
98 connect(watcher, &QDBusPendingCallWatcher::finished,
99 this, [this](QDBusPendingCallWatcher* watcher) {
101 QDBusPendingReply<QDBusUnixFileDescriptor> reply = *watcher;
102 watcher->deleteLater();
103 if (reply.isError()) {
104 qWarning() << "Failed to inhibit systemd powersave handling" << reply.error().message();
105 return;
106 }
108 m_systemdInhibitFd = reply.value();
109 });
110 }
113 bool checkLogin1Call(const QString &method) const
114 {
115 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, method);
116 QDBusReply<QString> reply = QDBusConnection::systemBus().call(msg);
117 return reply.isValid() && (reply == QStringLiteral("yes") || reply == QStringLiteral("challenge"));
118 }
120 void makeLogin1Call(const QString &method, const QVariantList &args)
121 {
122 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
123 LOGIN1_PATH,
124 LOGIN1_IFACE,
125 method);
126 msg.setArguments(args);
127 QDBusConnection::systemBus().asyncCall(msg);
128 }
130 void setActive(bool active)
131 {
132 isSessionActive = active;
134 Q_EMIT screensaverActiveChanged(!isSessionActive);
136 if (isSessionActive) {
137 screensaverActiveTimer.invalidate();
138 setIdleHint(false);
139 } else {
140 screensaverActiveTimer.start();
141 setIdleHint(true);
142 }
143 }
145 void checkActive()
146 {
147 if (logindSessionPath.isEmpty()) {
148 qWarning() << "Invalid session path";
149 return;
150 }
152 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
153 logindSessionPath,
154 QStringLiteral("org.freedesktop.DBus.Properties"),
155 QStringLiteral("Get"));
156 msg << LOGIN1_SESSION_IFACE;
157 msg << ACTIVE_KEY;
159 QDBusPendingCall pendingCall = QDBusConnection::systemBus().asyncCall(msg);
160 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
161 connect(watcher, &QDBusPendingCallWatcher::finished,
162 this, [this](QDBusPendingCallWatcher* watcher) {
164 QDBusPendingReply<QVariant> reply = *watcher;
165 watcher->deleteLater();
166 if (reply.isError()) {
167 qWarning() << "Failed to get Active property" << reply.error().message();
168 return;
169 }
171 setActive(reply.value().toBool());
172 });
173 }
175 quint32 screensaverActiveTime() const
176 {
177 if (!isSessionActive && screensaverActiveTimer.isValid()) {
178 return screensaverActiveTimer.elapsed() / 1000;
179 }
181 return 0;
182 }
184 quint64 idleSinceUSecTimestamp() const
185 {
186 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
187 logindSessionPath,
188 QStringLiteral("org.freedesktop.DBus.Properties"),
189 QStringLiteral("Get"));
190 msg << LOGIN1_SESSION_IFACE;
191 msg << IDLE_SINCE_KEY;
193 QDBusReply<QVariant> reply = QDBusConnection::systemBus().call(msg);
194 if (reply.isValid()) {
195 return reply.value().value<quint64>();
196 } else {
197 qWarning() << "Failed to get IdleSinceHint property" << reply.error().message();
198 }
200 return 0;
201 }
203 void setIdleHint(bool idle)
204 {
205 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
206 logindSessionPath,
207 LOGIN1_SESSION_IFACE,
208 QStringLiteral("SetIdleHint"));
209 msg << idle;
210 QDBusConnection::systemBus().asyncCall(msg);
211 }
213private Q_SLOTS:
214 void onPropertiesChanged(const QString &iface, const QVariantMap &changedProps, const QStringList &invalidatedProps)
215 {
216 Q_UNUSED(iface)
218 if (changedProps.contains(ACTIVE_KEY)) {
219 setActive(changedProps.value(ACTIVE_KEY).toBool());
220 } else if (invalidatedProps.contains(ACTIVE_KEY)) {
221 checkActive();
222 }
223 }
225 void onResuming(bool active)
226 {
227 if (!active) {
228 setupSystemdInhibition();
229 }
230 }
232Q_SIGNALS:
233 void screensaverActiveChanged(bool active);
234};
236Q_GLOBAL_STATIC(DBusUnitySessionServicePrivate, d)
238DBusUnitySessionService::DBusUnitySessionService()
239 : UnityDBusObject(QStringLiteral("/com/canonical/Unity/Session"), QStringLiteral("com.canonical.Unity"))
241 if (!d->logindSessionPath.isEmpty()) {
242 // connect our Lock() slot to the logind's session Lock() signal
243 QDBusConnection::systemBus().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, QStringLiteral("Lock"), this, SLOT(Lock()));
244 // ... and our Unlocked() signal to the logind's session Unlock() signal
245 // (lightdm handles the unlocking by calling logind's Unlock method which in turn emits this signal we connect to)
246 QDBusConnection::systemBus().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, QStringLiteral("Unlock"), this, SIGNAL(Unlocked()));
247 } else {
248 qWarning() << "Failed to connect to logind's session Lock/Unlock signals";
249 }
252void DBusUnitySessionService::Logout()
254 // TODO ask the apps to quit and then emit the signal
255 Q_EMIT LogoutReady();
256 Q_EMIT logoutReady();
259void DBusUnitySessionService::EndSession()
261 const QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("com.ubuntu.Upstart"),
262 QStringLiteral("/com/ubuntu/Upstart"),
263 QStringLiteral("com.ubuntu.Upstart0_6"),
264 QStringLiteral("EndSession"));
265 QDBusConnection::sessionBus().asyncCall(msg);
268bool DBusUnitySessionService::CanHibernate() const
270 return d->checkLogin1Call(QStringLiteral("CanHibernate"));
273bool DBusUnitySessionService::CanSuspend() const
275 return d->checkLogin1Call(QStringLiteral("CanSuspend"));
278bool DBusUnitySessionService::CanHybridSleep() const
280 return d->checkLogin1Call(QStringLiteral("CanHybridSleep"));
283bool DBusUnitySessionService::CanReboot() const
285 return d->checkLogin1Call(QStringLiteral("CanReboot"));
288bool DBusUnitySessionService::CanShutdown() const
290 return d->checkLogin1Call(QStringLiteral("CanPowerOff"));
293bool DBusUnitySessionService::CanLock() const
295 return true; // FIXME
298QString DBusUnitySessionService::UserName() const
300 struct passwd *p = getpwuid(geteuid());
301 if (p) {
302 return QString::fromUtf8(p->pw_name);
303 }
305 return QString();
308QString DBusUnitySessionService::RealName() const
310 struct passwd *p = getpwuid(geteuid());
311 if (p) {
312 const QString gecos = QString::fromLocal8Bit(p->pw_gecos);
313 if (!gecos.isEmpty()) {
314 const QStringList splitGecos = gecos.split(QLatin1Char(','));
315 return splitGecos.first();
316 }
317 }
319 return QString();
322QString DBusUnitySessionService::HostName() const
324 char hostName[512];
325 if (gethostname(hostName, sizeof(hostName)) == -1) {
326 qWarning() << "Could not determine local hostname";
327 return QString();
328 }
329 hostName[sizeof(hostName) - 1] = '\0';
330 return QString::fromLocal8Bit(hostName);
333void DBusUnitySessionService::PromptLock()
335 Q_EMIT LockRequested();
336 Q_EMIT lockRequested();
339void DBusUnitySessionService::Lock()
341 // lock the session using the org.freedesktop.DisplayManager system DBUS service
342 const QString sessionPath = QString::fromLocal8Bit(qgetenv("XDG_SESSION_PATH"));
343 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DisplayManager"),
344 sessionPath,
345 QStringLiteral("org.freedesktop.DisplayManager.Session"),
346 QStringLiteral("Lock"));
348 QDBusPendingCall pendingCall = QDBusConnection::systemBus().asyncCall(msg);
349 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
350 connect(watcher, &QDBusPendingCallWatcher::finished,
351 this, [this](QDBusPendingCallWatcher* watcher) {
353 QDBusPendingReply<QVariant> reply = *watcher;
354 watcher->deleteLater();
355 if (reply.isError()) {
356 qWarning() << "Lock call failed" << reply.error().message();
357 return;
358 }
360 // emit Locked when the call succeeds
361 Q_EMIT Locked();
362 });
365bool DBusUnitySessionService::IsLocked() const
367 return !d->isSessionActive;
370void DBusUnitySessionService::RequestLogout()
372 Q_EMIT LogoutRequested(false);
373 Q_EMIT logoutRequested(false);
376void DBusUnitySessionService::Reboot()
378 d->makeLogin1Call(QStringLiteral("Reboot"), {false});
381void DBusUnitySessionService::RequestReboot()
383 Q_EMIT RebootRequested(false);
384 Q_EMIT rebootRequested(false);
387void DBusUnitySessionService::Shutdown()
389 d->makeLogin1Call(QStringLiteral("PowerOff"), {false});
392void DBusUnitySessionService::Suspend()
394 d->makeLogin1Call(QStringLiteral("Suspend"), {false});
397void DBusUnitySessionService::Hibernate()
399 d->makeLogin1Call(QStringLiteral("Hibernate"), {false});
402void DBusUnitySessionService::HybridSleep()
404 d->makeLogin1Call(QStringLiteral("HybridSleep"), {false});
407void DBusUnitySessionService::RequestShutdown()
409 Q_EMIT ShutdownRequested(false);
410 Q_EMIT shutdownRequested(false);
413enum class Action : unsigned
415 LOGOUT = 0,
416 SHUTDOWN,
417 REBOOT,
418 NONE
419};
422void performAsyncUnityCall(const QString &method)
424 const QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("com.canonical.Unity"),
425 QStringLiteral("/com/canonical/Unity/Session"),
426 QStringLiteral("com.canonical.Unity.Session"),
427 method);
428 QDBusConnection::sessionBus().asyncCall(msg);
432DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
433 : UnityDBusObject(QStringLiteral("/org/gnome/SessionManager/EndSessionDialog"), QStringLiteral("com.canonical.Unity"))
437void DBusGnomeSessionManagerWrapper::Open(const unsigned type, const unsigned arg_1, const unsigned max_wait, const QList<QDBusObjectPath> &inhibitors)
439 Q_UNUSED(arg_1);
440 Q_UNUSED(max_wait);
441 Q_UNUSED(inhibitors);
443 switch (static_cast<Action>(type))
444 {
445 case Action::LOGOUT:
446 performAsyncUnityCall(QStringLiteral("RequestLogout"));
447 break;
449 case Action::REBOOT:
450 performAsyncUnityCall(QStringLiteral("RequestReboot"));
451 break;
453 case Action::SHUTDOWN:
454 performAsyncUnityCall(QStringLiteral("RequestShutdown"));
455 break;
457 default:
458 break;
459 }
463DBusGnomeScreensaverWrapper::DBusGnomeScreensaverWrapper()
464 : UnityDBusObject(QStringLiteral("/org/gnome/ScreenSaver"), QStringLiteral("org.gnome.ScreenSaver"))
466 connect(d, &DBusUnitySessionServicePrivate::screensaverActiveChanged, this, &DBusGnomeScreensaverWrapper::ActiveChanged);
469bool DBusGnomeScreensaverWrapper::GetActive() const
471 return !d->isSessionActive; // return whether the session is not active
474void DBusGnomeScreensaverWrapper::SetActive(bool lock)
476 if (lock) {
477 Lock();
478 }
481void DBusGnomeScreensaverWrapper::Lock()
483 performAsyncUnityCall(QStringLiteral("Lock"));
486quint32 DBusGnomeScreensaverWrapper::GetActiveTime() const
488 return d->screensaverActiveTime();
491void DBusGnomeScreensaverWrapper::SimulateUserActivity()
493 d->setIdleHint(false);
497DBusScreensaverWrapper::DBusScreensaverWrapper()
498 : UnityDBusObject(QStringLiteral("/org/freedesktop/ScreenSaver"), QStringLiteral("org.freedesktop.ScreenSaver"))
500 QDBusConnection::sessionBus().registerObject(QStringLiteral("/ScreenSaver"), this, QDBusConnection::ExportScriptableContents); // compat path, also register here
501 connect(d, &DBusUnitySessionServicePrivate::screensaverActiveChanged, this, &DBusScreensaverWrapper::ActiveChanged);
504bool DBusScreensaverWrapper::GetActive() const
506 return !d->isSessionActive; // return whether the session is not active
509bool DBusScreensaverWrapper::SetActive(bool lock)
511 if (lock) {
512 Lock();
513 return true;
514 }
515 return false;
518void DBusScreensaverWrapper::Lock()
520 performAsyncUnityCall(QStringLiteral("Lock"));
523quint32 DBusScreensaverWrapper::GetActiveTime() const
525 return d->screensaverActiveTime();
528quint32 DBusScreensaverWrapper::GetSessionIdleTime() const
530 return QDateTime::fromMSecsSinceEpoch(d->idleSinceUSecTimestamp()/1000).secsTo(QDateTime::currentDateTime());
533void DBusScreensaverWrapper::SimulateUserActivity()
535 d->setIdleHint(false);
538#include "dbusunitysessionservice.moc"