on 86231ce help sign in
unity8/plugins/Unity/Session/dbusunitysessionservice.cpp
539 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/hibernate 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"; // 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) {
100 QDBusPendingReply<QDBusUnixFileDescriptor> reply = *watcher;
101 watcher->deleteLater();
102 if (reply.isError()) {
103 qWarning() << "Failed to inhibit systemd powersave handling" << reply.error().message();
104 return;
105 }
107 m_systemdInhibitFd = reply.value();
108 });
109 }
111 bool checkLogin1Call(const QString &method) const
112 {
113 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, method);
114 QDBusReply<QString> reply = QDBusConnection::systemBus().call(msg);
115 return reply.isValid() && (reply == QStringLiteral("yes") || reply == QStringLiteral("challenge"));
116 }
118 void makeLogin1Call(const QString &method, const QVariantList &args)
119 {
120 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
121 LOGIN1_PATH,
122 LOGIN1_IFACE,
123 method);
124 msg.setArguments(args);
125 QDBusConnection::systemBus().asyncCall(msg);
126 }
128 void setActive(bool active)
129 {
130 isSessionActive = active;
132 Q_EMIT screensaverActiveChanged(!isSessionActive);
134 if (isSessionActive) {
135 screensaverActiveTimer.invalidate();
136 setIdleHint(false);
137 } else {
138 screensaverActiveTimer.start();
139 setIdleHint(true);
140 }
141 }
143 void checkActive()
144 {
145 if (logindSessionPath.isEmpty()) {
146 qWarning() << "Invalid session path";
147 return;
148 }
150 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
151 logindSessionPath,
152 QStringLiteral("org.freedesktop.DBus.Properties"),
153 QStringLiteral("Get"));
154 msg << LOGIN1_SESSION_IFACE;
155 msg << ACTIVE_KEY;
157 QDBusPendingCall pendingCall = QDBusConnection::systemBus().asyncCall(msg);
158 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
159 connect(watcher, &QDBusPendingCallWatcher::finished,
160 this, [this](QDBusPendingCallWatcher* watcher) {
162 QDBusPendingReply<QVariant> reply = *watcher;
163 watcher->deleteLater();
164 if (reply.isError()) {
165 qWarning() << "Failed to get Active property" << reply.error().message();
166 return;
167 }
169 setActive(reply.value().toBool());
170 });
171 }
173 quint32 screensaverActiveTime() const
174 {
175 if (!isSessionActive && screensaverActiveTimer.isValid()) {
176 return screensaverActiveTimer.elapsed() / 1000;
177 }
179 return 0;
180 }
182 quint64 idleSinceUSecTimestamp() const
183 {
184 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
185 logindSessionPath,
186 QStringLiteral("org.freedesktop.DBus.Properties"),
187 QStringLiteral("Get"));
188 msg << LOGIN1_SESSION_IFACE;
189 msg << IDLE_SINCE_KEY;
191 QDBusReply<QVariant> reply = QDBusConnection::systemBus().call(msg);
192 if (reply.isValid()) {
193 return reply.value().value<quint64>();
194 } else {
195 qWarning() << "Failed to get IdleSinceHint property" << reply.error().message();
196 }
198 return 0;
199 }
201 void setIdleHint(bool idle)
202 {
203 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
204 logindSessionPath,
205 LOGIN1_SESSION_IFACE,
206 QStringLiteral("SetIdleHint"));
207 msg << idle;
208 QDBusConnection::systemBus().asyncCall(msg);
209 }
211private Q_SLOTS:
212 void onPropertiesChanged(const QString &iface, const QVariantMap &changedProps, const QStringList &invalidatedProps)
213 {
214 Q_UNUSED(iface)
216 if (changedProps.contains(ACTIVE_KEY)) {
217 setActive(changedProps.value(ACTIVE_KEY).toBool());
218 } else if (invalidatedProps.contains(ACTIVE_KEY)) {
219 checkActive();
220 }
221 }
223 void onResuming(bool active)
224 {
225 if (!active) {
226 setupSystemdInhibition();
227 }
228 }
230Q_SIGNALS:
231 void screensaverActiveChanged(bool active);
232};
234Q_GLOBAL_STATIC(DBusUnitySessionServicePrivate, d)
236DBusUnitySessionService::DBusUnitySessionService()
237 : UnityDBusObject(QStringLiteral("/com/canonical/Unity/Session"), QStringLiteral("com.canonical.Unity"))
239 if (!d->logindSessionPath.isEmpty()) {
240 // connect our Lock() slot to the logind's session Lock() signal
241 QDBusConnection::systemBus().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, QStringLiteral("Lock"), this, SLOT(Lock()));
242 // ... and our Unlocked() signal to the logind's session Unlock() signal
243 // (lightdm handles the unlocking by calling logind's Unlock method which in turn emits this signal we connect to)
244 QDBusConnection::systemBus().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, QStringLiteral("Unlock"), this, SIGNAL(Unlocked()));
245 } else {
246 qWarning() << "Failed to connect to logind's session Lock/Unlock signals";
247 }
250void DBusUnitySessionService::Logout()
252 // TODO ask the apps to quit and then emit the signal
253 Q_EMIT LogoutReady();
254 Q_EMIT logoutReady();
257void DBusUnitySessionService::EndSession()
259 const QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("com.ubuntu.Upstart"),
260 QStringLiteral("/com/ubuntu/Upstart"),
261 QStringLiteral("com.ubuntu.Upstart0_6"),
262 QStringLiteral("EndSession"));
263 QDBusConnection::sessionBus().asyncCall(msg);
266bool DBusUnitySessionService::CanHibernate() const
268 return d->checkLogin1Call(QStringLiteral("CanHibernate"));
271bool DBusUnitySessionService::CanSuspend() const
273 return d->checkLogin1Call(QStringLiteral("CanSuspend"));
276bool DBusUnitySessionService::CanHybridSleep() const
278 return d->checkLogin1Call(QStringLiteral("CanHybridSleep"));
281bool DBusUnitySessionService::CanReboot() const
283 return d->checkLogin1Call(QStringLiteral("CanReboot"));
286bool DBusUnitySessionService::CanShutdown() const
288 return d->checkLogin1Call(QStringLiteral("CanPowerOff"));
291bool DBusUnitySessionService::CanLock() const
293 return true; // FIXME
296QString DBusUnitySessionService::UserName() const
298 struct passwd *p = getpwuid(geteuid());
299 if (p) {
300 return QString::fromUtf8(p->pw_name);
301 }
303 return QString();
306QString DBusUnitySessionService::RealName() const
308 struct passwd *p = getpwuid(geteuid());
309 if (p) {
310 const QString gecos = QString::fromLocal8Bit(p->pw_gecos);
311 if (!gecos.isEmpty()) {
312 const QStringList splitGecos = gecos.split(QLatin1Char(','));
313 return splitGecos.first();
314 }
315 }
317 return QString();
320QString DBusUnitySessionService::HostName() const
322 char hostName[512];
323 if (gethostname(hostName, sizeof(hostName)) == -1) {
324 qWarning() << "Could not determine local hostname";
325 return QString();
326 }
327 hostName[sizeof(hostName) - 1] = '\0';
328 return QString::fromLocal8Bit(hostName);
331void DBusUnitySessionService::PromptLock()
333 Q_EMIT LockRequested();
334 Q_EMIT lockRequested();
337void DBusUnitySessionService::Lock()
339 // signal u8 to show the lockscreen/greeter
340 PromptLock();
342 // lock the session using the org.freedesktop.DisplayManager system DBUS service
343 const QString sessionPath = QString::fromLocal8Bit(qgetenv("XDG_SESSION_PATH"));
344 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DisplayManager"),
345 sessionPath,
346 QStringLiteral("org.freedesktop.DisplayManager.Session"),
347 QStringLiteral("Lock"));
349 QDBusPendingCall pendingCall = QDBusConnection::systemBus().asyncCall(msg);
350 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
351 connect(watcher, &QDBusPendingCallWatcher::finished,
352 this, [this](QDBusPendingCallWatcher* watcher) {
354 QDBusPendingReply<void> reply = *watcher;
355 watcher->deleteLater();
356 if (reply.isError()) {
357 qWarning() << "Lock call failed" << reply.error().message();
358 return;
359 }
361 // emit Locked when the call succeeds
362 Q_EMIT Locked();
363 });
366bool DBusUnitySessionService::IsLocked() const
368 return !d->isSessionActive;
371void DBusUnitySessionService::RequestLogout()
373 Q_EMIT LogoutRequested(false);
374 Q_EMIT logoutRequested(false);
377void DBusUnitySessionService::Reboot()
379 d->makeLogin1Call(QStringLiteral("Reboot"), {false});
382void DBusUnitySessionService::RequestReboot()
384 Q_EMIT RebootRequested(false);
385 Q_EMIT rebootRequested(false);
388void DBusUnitySessionService::Shutdown()
390 d->makeLogin1Call(QStringLiteral("PowerOff"), {false});
393void DBusUnitySessionService::Suspend()
395 d->makeLogin1Call(QStringLiteral("Suspend"), {false});
398void DBusUnitySessionService::Hibernate()
400 d->makeLogin1Call(QStringLiteral("Hibernate"), {false});
403void DBusUnitySessionService::HybridSleep()
405 d->makeLogin1Call(QStringLiteral("HybridSleep"), {false});
408void DBusUnitySessionService::RequestShutdown()
410 Q_EMIT ShutdownRequested(false);
411 Q_EMIT shutdownRequested(false);
414enum class Action : unsigned
416 LOGOUT = 0,
417 SHUTDOWN,
418 REBOOT,
419 NONE
420};
423void performAsyncUnityCall(const QString &method)
425 const QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("com.canonical.Unity"),
426 QStringLiteral("/com/canonical/Unity/Session"),
427 QStringLiteral("com.canonical.Unity.Session"),
428 method);
429 QDBusConnection::sessionBus().asyncCall(msg);
433DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
434 : UnityDBusObject(QStringLiteral("/org/gnome/SessionManager/EndSessionDialog"), QStringLiteral("com.canonical.Unity"))
438void DBusGnomeSessionManagerWrapper::Open(const unsigned type, const unsigned arg_1, const unsigned max_wait, const QList<QDBusObjectPath> &inhibitors)
440 Q_UNUSED(arg_1);
441 Q_UNUSED(max_wait);
442 Q_UNUSED(inhibitors);
444 switch (static_cast<Action>(type))
445 {
446 case Action::LOGOUT:
447 performAsyncUnityCall(QStringLiteral("RequestLogout"));
448 break;
450 case Action::REBOOT:
451 performAsyncUnityCall(QStringLiteral("RequestReboot"));
452 break;
454 case Action::SHUTDOWN:
455 performAsyncUnityCall(QStringLiteral("RequestShutdown"));
456 break;
458 default:
459 break;
460 }
464DBusGnomeScreensaverWrapper::DBusGnomeScreensaverWrapper()
465 : UnityDBusObject(QStringLiteral("/org/gnome/ScreenSaver"), QStringLiteral("org.gnome.ScreenSaver"))
467 connect(d, &DBusUnitySessionServicePrivate::screensaverActiveChanged, this, &DBusGnomeScreensaverWrapper::ActiveChanged);
470bool DBusGnomeScreensaverWrapper::GetActive() const
472 return !d->isSessionActive; // return whether the session is not active
475void DBusGnomeScreensaverWrapper::SetActive(bool lock)
477 if (lock) {
478 Lock();
479 }
482void DBusGnomeScreensaverWrapper::Lock()
484 performAsyncUnityCall(QStringLiteral("Lock"));
487quint32 DBusGnomeScreensaverWrapper::GetActiveTime() const
489 return d->screensaverActiveTime();
492void DBusGnomeScreensaverWrapper::SimulateUserActivity()
494 d->setIdleHint(false);
498DBusScreensaverWrapper::DBusScreensaverWrapper()
499 : UnityDBusObject(QStringLiteral("/org/freedesktop/ScreenSaver"), QStringLiteral("org.freedesktop.ScreenSaver"))
501 QDBusConnection::sessionBus().registerObject(QStringLiteral("/ScreenSaver"), this, QDBusConnection::ExportScriptableContents); // compat path, also register here
502 connect(d, &DBusUnitySessionServicePrivate::screensaverActiveChanged, this, &DBusScreensaverWrapper::ActiveChanged);
505bool DBusScreensaverWrapper::GetActive() const
507 return !d->isSessionActive; // return whether the session is not active
510bool DBusScreensaverWrapper::SetActive(bool lock)
512 if (lock) {
513 Lock();
514 return true;
515 }
516 return false;
519void DBusScreensaverWrapper::Lock()
521 performAsyncUnityCall(QStringLiteral("Lock"));
524quint32 DBusScreensaverWrapper::GetActiveTime() const
526 return d->screensaverActiveTime();
529quint32 DBusScreensaverWrapper::GetSessionIdleTime() const
531 return QDateTime::fromMSecsSinceEpoch(d->idleSinceUSecTimestamp()/1000).secsTo(QDateTime::currentDateTime());
534void DBusScreensaverWrapper::SimulateUserActivity()
536 d->setIdleHint(false);
539#include "dbusunitysessionservice.moc"