on 588cf2d help sign in
unity8/plugins/Unity/Session/dbusunitysessionservice.cpp
576 lines 19 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// Glib
34#include <glib.h>
36#define LOGIN1_SERVICE QStringLiteral("org.freedesktop.login1")
37#define LOGIN1_PATH QStringLiteral("/org/freedesktop/login1")
38#define LOGIN1_IFACE QStringLiteral("org.freedesktop.login1.Manager")
39#define LOGIN1_SESSION_IFACE QStringLiteral("org.freedesktop.login1.Session")
41#define ACTIVE_KEY QStringLiteral("Active")
42#define IDLE_SINCE_KEY QStringLiteral("IdleSinceHint")
44class DBusUnitySessionServicePrivate: public QObject
46 Q_OBJECT
47public:
48 QString logindSessionPath;
49 bool isSessionActive = true;
50 QElapsedTimer screensaverActiveTimer;
51 QDBusUnixFileDescriptor m_systemdInhibitFd;
53 DBusUnitySessionServicePrivate(): QObject() {
54 init();
55 checkActive();
56 }
58 void init()
59 {
60 // get our logind session path
61 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
62 LOGIN1_PATH,
63 LOGIN1_IFACE,
64 QStringLiteral("GetSessionByPID"));
65 msg << (quint32) getpid();
67 QDBusReply<QDBusObjectPath> reply = QDBusConnection::SM_BUSNAME().call(msg);
68 if (reply.isValid()) {
69 logindSessionPath = reply.value().path();
71 // start watching the Active property
72 QDBusConnection::SM_BUSNAME().connect(LOGIN1_SERVICE, logindSessionPath, QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("PropertiesChanged"),
73 this, SLOT(onPropertiesChanged(QString,QVariantMap,QStringList)));
75 setupSystemdInhibition();
77 // re-enable the inhibition upon resume from sleep
78 QDBusConnection::SM_BUSNAME().connect(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, QStringLiteral("PrepareForSleep"),
79 this, SLOT(onResuming(bool)));
80 } else {
81 qWarning() << "Failed to get logind session path" << reply.error().message();
82 }
83 }
85 void setupSystemdInhibition()
86 {
87 if (m_systemdInhibitFd.isValid())
88 return;
90 // inhibit systemd handling of power/sleep/hibernate buttons
91 // http://www.freedesktop.org/wiki/Software/systemd/inhibit
93 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, QStringLiteral("Inhibit"));
94 msg << "handle-power-key:handle-suspend-key:handle-hibernate-key"; // what
95 msg << "Unity"; // who
96 msg << "Unity8 handles power events"; // why
97 msg << "block"; // mode
99 QDBusPendingCall pendingCall = QDBusConnection::SM_BUSNAME().asyncCall(msg);
100 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
101 connect(watcher, &QDBusPendingCallWatcher::finished,
102 this, [this](QDBusPendingCallWatcher* watcher) {
103 QDBusPendingReply<QDBusUnixFileDescriptor> reply = *watcher;
104 watcher->deleteLater();
105 if (reply.isError()) {
106 qWarning() << "Failed to inhibit systemd powersave handling" << reply.error().message();
107 return;
108 }
110 m_systemdInhibitFd = reply.value();
111 });
112 }
114 bool checkLogin1Call(const QString &method) const
115 {
116 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, method);
117 QDBusReply<QString> reply = QDBusConnection::SM_BUSNAME().call(msg);
118 return reply.isValid() && (reply == QStringLiteral("yes") || reply == QStringLiteral("challenge"));
119 }
121 void makeLogin1Call(const QString &method, const QVariantList &args)
122 {
123 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
124 LOGIN1_PATH,
125 LOGIN1_IFACE,
126 method);
127 msg.setArguments(args);
128 QDBusConnection::SM_BUSNAME().asyncCall(msg);
129 }
131 void setActive(bool active)
132 {
133 isSessionActive = active;
135 Q_EMIT screensaverActiveChanged(!isSessionActive);
137 if (isSessionActive) {
138 screensaverActiveTimer.invalidate();
139 setIdleHint(false);
140 } else {
141 screensaverActiveTimer.start();
142 setIdleHint(true);
143 }
144 }
146 void checkActive()
147 {
148 if (logindSessionPath.isEmpty()) {
149 qWarning() << "Invalid session path";
150 return;
151 }
153 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
154 logindSessionPath,
155 QStringLiteral("org.freedesktop.DBus.Properties"),
156 QStringLiteral("Get"));
157 msg << LOGIN1_SESSION_IFACE;
158 msg << ACTIVE_KEY;
160 QDBusPendingCall pendingCall = QDBusConnection::SM_BUSNAME().asyncCall(msg);
161 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
162 connect(watcher, &QDBusPendingCallWatcher::finished,
163 this, [this](QDBusPendingCallWatcher* watcher) {
165 QDBusPendingReply<QVariant> reply = *watcher;
166 watcher->deleteLater();
167 if (reply.isError()) {
168 qWarning() << "Failed to get Active property" << reply.error().message();
169 return;
170 }
172 setActive(reply.value().toBool());
173 });
174 }
176 quint32 screensaverActiveTime() const
177 {
178 if (!isSessionActive && screensaverActiveTimer.isValid()) {
179 return screensaverActiveTimer.elapsed() / 1000;
180 }
182 return 0;
183 }
185 quint64 idleSinceUSecTimestamp() const
186 {
187 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
188 logindSessionPath,
189 QStringLiteral("org.freedesktop.DBus.Properties"),
190 QStringLiteral("Get"));
191 msg << LOGIN1_SESSION_IFACE;
192 msg << IDLE_SINCE_KEY;
194 QDBusReply<QVariant> reply = QDBusConnection::SM_BUSNAME().call(msg);
195 if (reply.isValid()) {
196 return reply.value().value<quint64>();
197 } else {
198 qWarning() << "Failed to get IdleSinceHint property" << reply.error().message();
199 }
201 return 0;
202 }
204 void setIdleHint(bool idle)
205 {
206 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
207 logindSessionPath,
208 LOGIN1_SESSION_IFACE,
209 QStringLiteral("SetIdleHint"));
210 msg << idle;
211 QDBusConnection::SM_BUSNAME().asyncCall(msg);
212 }
214private Q_SLOTS:
215 void onPropertiesChanged(const QString &iface, const QVariantMap &changedProps, const QStringList &invalidatedProps)
216 {
217 Q_UNUSED(iface)
219 if (changedProps.contains(ACTIVE_KEY)) {
220 setActive(changedProps.value(ACTIVE_KEY).toBool());
221 } else if (invalidatedProps.contains(ACTIVE_KEY)) {
222 checkActive();
223 }
224 }
226 void onResuming(bool active)
227 {
228 if (!active) {
229 setupSystemdInhibition();
230 } else {
231 Q_EMIT prepareForSleep();
232 }
233 }
235Q_SIGNALS:
236 void screensaverActiveChanged(bool active);
237 void prepareForSleep();
238};
240Q_GLOBAL_STATIC(DBusUnitySessionServicePrivate, d)
242DBusUnitySessionService::DBusUnitySessionService()
243 : UnityDBusObject(QStringLiteral("/com/canonical/Unity/Session"), QStringLiteral("com.canonical.Unity"))
245 if (!d->logindSessionPath.isEmpty()) {
246 // connect our PromptLock() slot to the logind's session Lock() signal
247 QDBusConnection::SM_BUSNAME().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, QStringLiteral("Lock"), this, SLOT(PromptLock()));
248 // ... and our Unlocked() signal to the logind's session Unlock() signal
249 // (lightdm handles the unlocking by calling logind's Unlock method which in turn emits this signal we connect to)
250 QDBusConnection::SM_BUSNAME().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, QStringLiteral("Unlock"), this, SLOT(doUnlock()));
251 connect(d, &DBusUnitySessionServicePrivate::prepareForSleep, this, &DBusUnitySessionService::PromptLock);
252 } else {
253 qWarning() << "Failed to connect to logind's session Lock/Unlock signals";
254 }
257void DBusUnitySessionService::Logout()
259 // TODO ask the apps to quit and then emit the signal
260 Q_EMIT LogoutReady();
261 Q_EMIT logoutReady();
264void DBusUnitySessionService::EndSession()
266 const QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("com.ubuntu.Upstart"),
267 QStringLiteral("/com/ubuntu/Upstart"),
268 QStringLiteral("com.ubuntu.Upstart0_6"),
269 QStringLiteral("EndSession"));
270 QDBusConnection::sessionBus().asyncCall(msg);
273bool DBusUnitySessionService::CanHibernate() const
275 return d->checkLogin1Call(QStringLiteral("CanHibernate"));
278bool DBusUnitySessionService::CanSuspend() const
280 return d->checkLogin1Call(QStringLiteral("CanSuspend"));
283bool DBusUnitySessionService::CanHybridSleep() const
285 return d->checkLogin1Call(QStringLiteral("CanHybridSleep"));
288bool DBusUnitySessionService::CanReboot() const
290 return d->checkLogin1Call(QStringLiteral("CanReboot"));
293bool DBusUnitySessionService::CanShutdown() const
295 return d->checkLogin1Call(QStringLiteral("CanPowerOff"));
298bool DBusUnitySessionService::CanLock() const
300 return true; // FIXME
303QString DBusUnitySessionService::UserName() const
305 return QString::fromUtf8(g_get_user_name());
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 // Prompt as in quick. No locking animation needed. Usually used by
336 // indicator-session in combination with a switch to greeter or other
337 // user session.
338 Q_EMIT LockRequested();
339 Q_EMIT lockRequested();
342void DBusUnitySessionService::Lock()
344 // Normal lock (with animation, as compared to PromptLock above). Usually
345 // used by indicator-session to lock the session in place.
346 //
347 // FIXME: We also -- as a bit of a hack around indicator-session not fully
348 // supporting a phone profile -- switch to greeter here. The unity7 flow is
349 // that the user chooses "Lock/Switch" from the indicator, and then can go
350 // to greeter by selecting "Switch" again from the indicator, which is now
351 // exposed by the desktop_lockscreen profile. But since in unity8, we try
352 // to expose most things all the time, we don't use the separate lockscreen
353 // profile. Instead, we just go directly to the greeter the first time
354 // a user presses "Lock/Switch". This isn't what this DBus call is
355 // supposed to do, but we can live with it for now.
356 //
357 // Here's a bug about indicator-session growing a converged Touch profile:
358 // https://launchpad.net/bugs/1557716
359 //
360 // We only do this here in the animated-lock call because that's the only
361 // time the indicator locks without also asking the display manager to
362 // switch sessions on us. And since we are switching screens, we also
363 // don't bother respecting the animate request, simply doing a PromptLock.
364 PromptLock();
365 switchToGreeter();
368void DBusUnitySessionService::switchToGreeter()
370 // lock the session using the org.freedesktop.DisplayManager system DBUS service
371 const QString sessionPath = QString::fromLocal8Bit(qgetenv("XDG_SESSION_PATH"));
372 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DisplayManager"),
373 sessionPath,
374 QStringLiteral("org.freedesktop.DisplayManager.Session"),
375 QStringLiteral("Lock"));
377 QDBusPendingCall pendingCall = QDBusConnection::SM_BUSNAME().asyncCall(msg);
378 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
379 connect(watcher, &QDBusPendingCallWatcher::finished,
380 this, [this](QDBusPendingCallWatcher* watcher) {
382 QDBusPendingReply<void> reply = *watcher;
383 watcher->deleteLater();
384 if (reply.isError()) {
385 qWarning() << "Lock call failed" << reply.error().message();
386 return;
387 }
389 // emit Locked when the call succeeds
390 Q_EMIT Locked();
391 });
394void DBusUnitySessionService::doUnlock()
396 Q_EMIT Unlocked();
397 Q_EMIT unlocked();
400bool DBusUnitySessionService::IsLocked() const
402 return !d->isSessionActive;
405void DBusUnitySessionService::RequestLogout()
407 Q_EMIT LogoutRequested(false);
408 Q_EMIT logoutRequested(false);
411void DBusUnitySessionService::Reboot()
413 d->makeLogin1Call(QStringLiteral("Reboot"), {false});
416void DBusUnitySessionService::RequestReboot()
418 Q_EMIT RebootRequested(false);
419 Q_EMIT rebootRequested(false);
422void DBusUnitySessionService::Shutdown()
424 d->makeLogin1Call(QStringLiteral("PowerOff"), {false});
427void DBusUnitySessionService::Suspend()
429 PromptLock();
430 d->makeLogin1Call(QStringLiteral("Suspend"), {false});
433void DBusUnitySessionService::Hibernate()
435 PromptLock();
436 d->makeLogin1Call(QStringLiteral("Hibernate"), {false});
439void DBusUnitySessionService::HybridSleep()
441 PromptLock();
442 d->makeLogin1Call(QStringLiteral("HybridSleep"), {false});
445void DBusUnitySessionService::RequestShutdown()
447 Q_EMIT ShutdownRequested(false);
448 Q_EMIT shutdownRequested(false);
451enum class Action : unsigned
453 LOGOUT = 0,
454 SHUTDOWN,
455 REBOOT,
456 NONE
457};
460void performAsyncUnityCall(const QString &method)
462 const QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("com.canonical.Unity"),
463 QStringLiteral("/com/canonical/Unity/Session"),
464 QStringLiteral("com.canonical.Unity.Session"),
465 method);
466 QDBusConnection::sessionBus().asyncCall(msg);
470DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
471 : UnityDBusObject(QStringLiteral("/org/gnome/SessionManager/EndSessionDialog"), QStringLiteral("com.canonical.Unity"))
475void DBusGnomeSessionManagerWrapper::Open(const unsigned type, const unsigned arg_1, const unsigned max_wait, const QList<QDBusObjectPath> &inhibitors)
477 Q_UNUSED(arg_1);
478 Q_UNUSED(max_wait);
479 Q_UNUSED(inhibitors);
481 switch (static_cast<Action>(type))
482 {
483 case Action::LOGOUT:
484 performAsyncUnityCall(QStringLiteral("RequestLogout"));
485 break;
487 case Action::REBOOT:
488 performAsyncUnityCall(QStringLiteral("RequestReboot"));
489 break;
491 case Action::SHUTDOWN:
492 performAsyncUnityCall(QStringLiteral("RequestShutdown"));
493 break;
495 default:
496 break;
497 }
501DBusGnomeScreensaverWrapper::DBusGnomeScreensaverWrapper()
502 : UnityDBusObject(QStringLiteral("/org/gnome/ScreenSaver"), QStringLiteral("org.gnome.ScreenSaver"))
504 connect(d, &DBusUnitySessionServicePrivate::screensaverActiveChanged, this, &DBusGnomeScreensaverWrapper::ActiveChanged);
507bool DBusGnomeScreensaverWrapper::GetActive() const
509 return !d->isSessionActive; // return whether the session is not active
512void DBusGnomeScreensaverWrapper::SetActive(bool lock)
514 if (lock) {
515 Lock();
516 }
519void DBusGnomeScreensaverWrapper::Lock()
521 performAsyncUnityCall(QStringLiteral("PromptLock"));
524quint32 DBusGnomeScreensaverWrapper::GetActiveTime() const
526 return d->screensaverActiveTime();
529void DBusGnomeScreensaverWrapper::SimulateUserActivity()
531 d->setIdleHint(false);
535DBusScreensaverWrapper::DBusScreensaverWrapper()
536 : UnityDBusObject(QStringLiteral("/org/freedesktop/ScreenSaver"), QStringLiteral("org.freedesktop.ScreenSaver"))
538 QDBusConnection::sessionBus().registerObject(QStringLiteral("/ScreenSaver"), this, QDBusConnection::ExportScriptableContents); // compat path, also register here
539 connect(d, &DBusUnitySessionServicePrivate::screensaverActiveChanged, this, &DBusScreensaverWrapper::ActiveChanged);
542bool DBusScreensaverWrapper::GetActive() const
544 return !d->isSessionActive; // return whether the session is not active
547bool DBusScreensaverWrapper::SetActive(bool lock)
549 if (lock) {
550 Lock();
551 return true;
552 }
553 return false;
556void DBusScreensaverWrapper::Lock()
558 performAsyncUnityCall(QStringLiteral("PromptLock"));
561quint32 DBusScreensaverWrapper::GetActiveTime() const
563 return d->screensaverActiveTime();
566quint32 DBusScreensaverWrapper::GetSessionIdleTime() const
568 return QDateTime::fromMSecsSinceEpoch(d->idleSinceUSecTimestamp()/1000).secsTo(QDateTime::currentDateTime());
571void DBusScreensaverWrapper::SimulateUserActivity()
573 d->setIdleHint(false);
576#include "dbusunitysessionservice.moc"