18#include "dbuslomirisessionservice.h"
28#include <QDBusPendingCall>
30#include <QElapsedTimer>
32#include <QDBusUnixFileDescriptor>
37#define LOGIN1_SERVICE QStringLiteral("org.freedesktop.login1")
38#define LOGIN1_PATH QStringLiteral("/org/freedesktop/login1")
39#define LOGIN1_IFACE QStringLiteral("org.freedesktop.login1.Manager")
40#define LOGIN1_SESSION_IFACE QStringLiteral("org.freedesktop.login1.Session")
42#define ACTIVE_KEY QStringLiteral("Active")
43#define IDLE_SINCE_KEY QStringLiteral("IdleSinceHint")
46static const QString LOGIN1_USER_SELF_PATH =
47 QStringLiteral(
"/org/freedesktop/login1/user/self");
48static const QString LOGIN1_USER_IFACE =
49 QStringLiteral(
"org.freedesktop.login1.User");
50static const QString DBUS_PROPERTIES_IFACE =
51 QStringLiteral(
"org.freedesktop.DBus.Properties");
53static const QString DISPLAY_KEY = QStringLiteral(
"Display");
55class DBusLomiriSessionServicePrivate:
public QObject
59 QString logindSessionPath;
60 bool isSessionActive =
true;
61 QElapsedTimer screensaverActiveTimer;
62 QDBusUnixFileDescriptor m_systemdInhibitFd;
64 DBusLomiriSessionServicePrivate(): QObject() {
69 QString determineLogindSessionPath() {
71 auto sessionEnv = qgetenv(
"XDG_SESSION_ID");
72 if (!sessionEnv.isEmpty()) {
73 QDBusMessage msg = QDBusMessage::createMethodCall(
77 QStringLiteral(
"GetSession"));
79 msg << QString::fromLocal8Bit(sessionEnv);
80 QDBusReply<QDBusObjectPath> reply = QDBusConnection::SM_BUSNAME().call(msg);
82 if (reply.isValid()) {
83 return reply.value().path();
85 qWarning() <<
"Failed to get logind session path for"
87 << reply.error().message();
96 if (qEnvironmentVariableIsSet(
"LOMIRI_AS_SYSTEMD_UNIT")) {
97 QDBusMessage msg = QDBusMessage::createMethodCall(
99 LOGIN1_USER_SELF_PATH,
100 DBUS_PROPERTIES_IFACE,
101 QStringLiteral(
"Get"));
102 msg << LOGIN1_USER_IFACE;
106 QDBusReply<QDBusVariant> reply = QDBusConnection::SM_BUSNAME().call(msg);
108 if (reply.isValid()) {
112 QDBusObjectPath sessionPath;
114 QVariant variant = reply.value().variant();
115 const QDBusArgument arg = variant.value<QDBusArgument>();
117 arg.beginStructure();
118 arg >> sessionName >> sessionPath;
121 return sessionPath.path();
123 qWarning() <<
"Failed to get user's Display session:"
124 << reply.error().message();
135 logindSessionPath = determineLogindSessionPath();
137 if (!logindSessionPath.isEmpty()) {
139 QDBusConnection::SM_BUSNAME().connect(LOGIN1_SERVICE, logindSessionPath, QStringLiteral(
"org.freedesktop.DBus.Properties"), QStringLiteral(
"PropertiesChanged"),
140 this, SLOT(onPropertiesChanged(QString,QVariantMap,QStringList)));
142 setupSystemdInhibition();
145 QDBusConnection::SM_BUSNAME().connect(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, QStringLiteral(
"PrepareForSleep"),
146 this, SLOT(onResuming(
bool)));
150 void setupSystemdInhibition()
152 if (m_systemdInhibitFd.isValid())
158 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, QStringLiteral(
"Inhibit"));
159 msg <<
"handle-power-key:handle-suspend-key:handle-hibernate-key";
161 msg <<
"Lomiri handles power events";
164 QDBusPendingCall pendingCall = QDBusConnection::SM_BUSNAME().asyncCall(msg);
165 QDBusPendingCallWatcher *watcher =
new QDBusPendingCallWatcher(pendingCall,
this);
166 connect(watcher, &QDBusPendingCallWatcher::finished,
167 this, [
this](QDBusPendingCallWatcher* watcher) {
168 QDBusPendingReply<QDBusUnixFileDescriptor> reply = *watcher;
169 watcher->deleteLater();
170 if (reply.isError()) {
171 qWarning() <<
"Failed to inhibit systemd powersave handling" << reply.error().message();
175 m_systemdInhibitFd = reply.value();
179 bool checkLogin1Call(
const QString &method)
const
181 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, method);
182 QDBusReply<QString> reply = QDBusConnection::SM_BUSNAME().call(msg);
183 return reply.isValid() && (reply == QStringLiteral(
"yes") || reply == QStringLiteral(
"challenge"));
186 void makeLogin1Call(
const QString &method,
const QVariantList &args)
188 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
192 msg.setArguments(args);
193 QDBusConnection::SM_BUSNAME().asyncCall(msg);
196 void setActive(
bool active)
198 isSessionActive = active;
200 Q_EMIT screensaverActiveChanged(!isSessionActive);
202 if (isSessionActive) {
203 screensaverActiveTimer.invalidate();
206 screensaverActiveTimer.start();
213 if (logindSessionPath.isEmpty()) {
214 qWarning() <<
"Invalid session path";
218 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
220 QStringLiteral(
"org.freedesktop.DBus.Properties"),
221 QStringLiteral(
"Get"));
222 msg << LOGIN1_SESSION_IFACE;
225 QDBusPendingCall pendingCall = QDBusConnection::SM_BUSNAME().asyncCall(msg);
226 QDBusPendingCallWatcher *watcher =
new QDBusPendingCallWatcher(pendingCall,
this);
227 connect(watcher, &QDBusPendingCallWatcher::finished,
228 this, [
this](QDBusPendingCallWatcher* watcher) {
230 QDBusPendingReply<QVariant> reply = *watcher;
231 watcher->deleteLater();
232 if (reply.isError()) {
233 qWarning() <<
"Failed to get Active property" << reply.error().message();
237 setActive(reply.value().toBool());
241 quint32 screensaverActiveTime()
const
243 if (!isSessionActive && screensaverActiveTimer.isValid()) {
244 return screensaverActiveTimer.elapsed() / 1000;
250 quint64 idleSinceUSecTimestamp()
const
252 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
254 QStringLiteral(
"org.freedesktop.DBus.Properties"),
255 QStringLiteral(
"Get"));
256 msg << LOGIN1_SESSION_IFACE;
257 msg << IDLE_SINCE_KEY;
259 QDBusReply<QVariant> reply = QDBusConnection::SM_BUSNAME().call(msg);
260 if (reply.isValid()) {
261 return reply.value().value<quint64>();
263 qWarning() <<
"Failed to get IdleSinceHint property" << reply.error().message();
269 void setIdleHint(
bool idle)
271 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
273 LOGIN1_SESSION_IFACE,
274 QStringLiteral(
"SetIdleHint"));
276 QDBusConnection::SM_BUSNAME().asyncCall(msg);
279 bool isUserInGroup(
const QString &user,
const QString &groupName)
const
281 auto group = getgrnam(groupName.toUtf8().data());
283 if (group && group->gr_mem)
285 for (
int i = 0; group->gr_mem[i]; ++i)
287 if (g_strcmp0(group->gr_mem[i], user.toUtf8().data()) == 0) {
297 void onPropertiesChanged(
const QString &iface,
const QVariantMap &changedProps,
const QStringList &invalidatedProps)
301 if (changedProps.contains(ACTIVE_KEY)) {
302 setActive(changedProps.value(ACTIVE_KEY).toBool());
303 }
else if (invalidatedProps.contains(ACTIVE_KEY)) {
308 void onResuming(
bool active)
311 setupSystemdInhibition();
313 Q_EMIT prepareForSleep();
318 void screensaverActiveChanged(
bool active);
319 void prepareForSleep();
322Q_GLOBAL_STATIC(DBusLomiriSessionServicePrivate, d)
324DBusLomiriSessionService::DBusLomiriSessionService()
325 : LomiriDBusObject(QStringLiteral(
"/com/lomiri/Shell/Session"), QStringLiteral(
"com.lomiri.Shell"))
327 if (!d->logindSessionPath.isEmpty()) {
329 QDBusConnection::SM_BUSNAME().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, QStringLiteral(
"Lock"),
this, SLOT(PromptLock()));
332 QDBusConnection::SM_BUSNAME().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, QStringLiteral(
"Unlock"),
this, SLOT(doUnlock()));
335 qWarning() <<
"Failed to connect to logind's session Lock/Unlock signals";
343 Q_EMIT logoutReady();
348 const QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral(
"com.lomiri.Upstart"),
349 QStringLiteral(
"/com/lomiri/Upstart"),
350 QStringLiteral(
"com.lomiri.Upstart0_6"),
351 QStringLiteral(
"EndSession"));
352 QDBusConnection::sessionBus().asyncCall(msg);
357 return d->checkLogin1Call(QStringLiteral(
"CanHibernate"));
362 return d->checkLogin1Call(QStringLiteral(
"CanSuspend"));
367 return d->checkLogin1Call(QStringLiteral(
"CanHybridSleep"));
372 return d->checkLogin1Call(QStringLiteral(
"CanReboot"));
377 return d->checkLogin1Call(QStringLiteral(
"CanPowerOff"));
383 if (user.startsWith(QStringLiteral(
"guest-")) ||
384 d->isUserInGroup(user, QStringLiteral(
"nopasswdlogin"))) {
393 return QString::fromUtf8(g_get_user_name());
398 struct passwd *p = getpwuid(geteuid());
400 const QString gecos = QString::fromLocal8Bit(p->pw_gecos);
401 if (!gecos.isEmpty()) {
402 const QStringList splitGecos = gecos.split(QLatin1Char(
','));
403 return splitGecos.first();
413 if (gethostname(hostName,
sizeof(hostName)) == -1) {
414 qWarning() <<
"Could not determine local hostname";
417 hostName[
sizeof(hostName) - 1] =
'\0';
418 return QString::fromLocal8Bit(hostName);
428 Q_EMIT lockRequested();
458void DBusLomiriSessionService::switchToGreeter()
461 const QString sessionPath = QString::fromLocal8Bit(qgetenv(
"XDG_SESSION_PATH"));
462 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral(
"org.freedesktop.DisplayManager"),
464 QStringLiteral(
"org.freedesktop.DisplayManager.Session"),
465 QStringLiteral(
"Lock"));
467 QDBusPendingCall pendingCall = QDBusConnection::SM_BUSNAME().asyncCall(msg);
468 QDBusPendingCallWatcher *watcher =
new QDBusPendingCallWatcher(pendingCall,
this);
469 connect(watcher, &QDBusPendingCallWatcher::finished,
470 this, [
this](QDBusPendingCallWatcher* watcher) {
472 QDBusPendingReply<void> reply = *watcher;
473 watcher->deleteLater();
474 if (reply.isError()) {
475 qWarning() <<
"Lock call failed" << reply.error().message();
484void DBusLomiriSessionService::doUnlock()
492 return !d->isSessionActive;
498 Q_EMIT logoutRequested(
false);
503 d->makeLogin1Call(QStringLiteral(
"Reboot"), {
false});
509 Q_EMIT rebootRequested(
false);
514 d->makeLogin1Call(QStringLiteral(
"PowerOff"), {
false});
520 d->makeLogin1Call(QStringLiteral(
"Suspend"), {
false});
526 d->makeLogin1Call(QStringLiteral(
"Hibernate"), {
false});
532 d->makeLogin1Call(QStringLiteral(
"HybridSleep"), {
false});
538 Q_EMIT shutdownRequested(
false);
541enum class Action :
unsigned
550void performAsyncLomiriCall(
const QString &method)
552 const QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral(
"com.lomiri.Shell"),
553 QStringLiteral(
"/com/lomiri/Shell/Session"),
554 QStringLiteral(
"com.lomiri.Shell.Session"),
556 QDBusConnection::sessionBus().asyncCall(msg);
560DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
561 : LomiriDBusObject(QStringLiteral(
"/org/gnome/SessionManager"), QStringLiteral(
"org.gnome.SessionManager"))
565void DBusGnomeSessionManagerWrapper::Logout(quint32 mode)
567 auto call = QStringLiteral(
"RequestLogout");
573 call = QStringLiteral(
"Logout");
575 call = QStringLiteral(
"Logout");
577 performAsyncLomiriCall(call);
580void DBusGnomeSessionManagerWrapper::Reboot()
583 performAsyncLomiriCall(QStringLiteral(
"RequestReboot"));
586void DBusGnomeSessionManagerWrapper::RequestReboot()
589 performAsyncLomiriCall(QStringLiteral(
"Reboot"));
592void DBusGnomeSessionManagerWrapper::RequestShutdown()
595 performAsyncLomiriCall(QStringLiteral(
"Shutdown"));
598void DBusGnomeSessionManagerWrapper::Shutdown()
601 performAsyncLomiriCall(QStringLiteral(
"RequestShutdown"));
605DBusGnomeSessionManagerDialogWrapper::DBusGnomeSessionManagerDialogWrapper()
606 : LomiriDBusObject(QStringLiteral(
"/org/gnome/SessionManager/EndSessionDialog"), QStringLiteral(
"com.lomiri.Shell"))
610void DBusGnomeSessionManagerDialogWrapper::Open(
const unsigned type,
const unsigned arg_1,
const unsigned max_wait,
const QList<QDBusObjectPath> &inhibitors)
614 Q_UNUSED(inhibitors);
616 switch (
static_cast<Action
>(type))
619 performAsyncLomiriCall(QStringLiteral(
"RequestLogout"));
623 performAsyncLomiriCall(QStringLiteral(
"RequestReboot"));
626 case Action::SHUTDOWN:
627 performAsyncLomiriCall(QStringLiteral(
"RequestShutdown"));
636DBusGnomeScreensaverWrapper::DBusGnomeScreensaverWrapper()
637 : LomiriDBusObject(QStringLiteral(
"/org/gnome/ScreenSaver"), QStringLiteral(
"org.gnome.ScreenSaver"))
639 connect(d, &DBusLomiriSessionServicePrivate::screensaverActiveChanged,
this, &DBusGnomeScreensaverWrapper::ActiveChanged);
642bool DBusGnomeScreensaverWrapper::GetActive()
const
644 return !d->isSessionActive;
647void DBusGnomeScreensaverWrapper::SetActive(
bool lock)
654void DBusGnomeScreensaverWrapper::Lock()
656 performAsyncLomiriCall(QStringLiteral(
"PromptLock"));
659quint32 DBusGnomeScreensaverWrapper::GetActiveTime()
const
661 return d->screensaverActiveTime();
664void DBusGnomeScreensaverWrapper::SimulateUserActivity()
666 d->setIdleHint(
false);
670DBusScreensaverWrapper::DBusScreensaverWrapper()
671 : LomiriDBusObject(QStringLiteral(
"/org/freedesktop/ScreenSaver"), QStringLiteral(
"org.freedesktop.ScreenSaver"))
673 QDBusConnection::sessionBus().registerObject(QStringLiteral(
"/ScreenSaver"),
this, QDBusConnection::ExportScriptableContents);
674 connect(d, &DBusLomiriSessionServicePrivate::screensaverActiveChanged,
this, &DBusScreensaverWrapper::ActiveChanged);
677bool DBusScreensaverWrapper::GetActive()
const
679 return !d->isSessionActive;
682bool DBusScreensaverWrapper::SetActive(
bool lock)
691void DBusScreensaverWrapper::Lock()
693 performAsyncLomiriCall(QStringLiteral(
"PromptLock"));
696quint32 DBusScreensaverWrapper::GetActiveTime()
const
698 return d->screensaverActiveTime();
701quint32 DBusScreensaverWrapper::GetSessionIdleTime()
const
703 return QDateTime::fromMSecsSinceEpoch(d->idleSinceUSecTimestamp()/1000).secsTo(QDateTime::currentDateTime());
706void DBusScreensaverWrapper::SimulateUserActivity()
708 d->setIdleHint(
false);
711#include "dbuslomirisessionservice.moc"
Q_SCRIPTABLE void PromptLock()
Q_SCRIPTABLE void Logout()
Q_SCRIPTABLE void LogoutReady()
Q_SCRIPTABLE QString HostName() const
Q_SCRIPTABLE bool IsLocked() const
Q_SCRIPTABLE void Reboot()
Q_SCRIPTABLE void Unlocked()
Q_SCRIPTABLE void RequestShutdown()
Q_SCRIPTABLE void LockRequested()
Q_SCRIPTABLE void ShutdownRequested(bool have_inhibitors)
Q_SCRIPTABLE void RequestReboot()
Q_SCRIPTABLE void RebootRequested(bool have_inhibitors)
Q_SCRIPTABLE void Locked()
Q_SCRIPTABLE void Hibernate()
Q_SCRIPTABLE void HybridSleep()
Q_SCRIPTABLE void Suspend()
Q_SCRIPTABLE bool CanHybridSleep() const
Q_SCRIPTABLE bool CanHibernate() const
Q_SCRIPTABLE void LogoutRequested(bool have_inhibitors)
Q_SCRIPTABLE bool CanShutdown() const
Q_SCRIPTABLE bool CanLock() const
Q_SCRIPTABLE void EndSession()
Q_SCRIPTABLE QString RealName() const
Q_SCRIPTABLE bool CanReboot() const
Q_SCRIPTABLE void RequestLogout()
Q_SCRIPTABLE void Shutdown()
Q_SCRIPTABLE QString UserName() const
Q_SCRIPTABLE bool CanSuspend() const