/*
* Copyright (C) 2014, 2015 Canonical, Ltd.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License version 3, as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
* SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
// local
#include "dbusunitysessionservice.h"
// system
#include
#include
#include
// Qt
#include
#include
#include
#include
#include
#define LOGIN1_SERVICE QStringLiteral("org.freedesktop.login1")
#define LOGIN1_PATH QStringLiteral("/org/freedesktop/login1")
#define LOGIN1_IFACE QStringLiteral("org.freedesktop.login1.Manager")
#define LOGIN1_SESSION_IFACE QStringLiteral("org.freedesktop.login1.Session")
#define ACTIVE_KEY QStringLiteral("Active")
#define IDLE_SINCE_KEY QStringLiteral("IdleSinceHint")
class DBusUnitySessionServicePrivate: public QObject
{
Q_OBJECT
public:
QString logindSessionPath;
bool isSessionActive = true;
QElapsedTimer screensaverActiveTimer;
DBusUnitySessionServicePrivate(): QObject() {
init();
checkActive();
}
void init()
{
// get our logind session path
QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
LOGIN1_PATH,
LOGIN1_IFACE,
"GetSessionByPID");
msg << (quint32) getpid();
QDBusReply reply = QDBusConnection::systemBus().asyncCall(msg);
if (reply.isValid()) {
logindSessionPath = reply.value().path();
// start watching the Active property
QDBusConnection::systemBus().connect(LOGIN1_SERVICE, logindSessionPath, "org.freedesktop.DBus.Properties", "PropertiesChanged",
this, SLOT(onPropertiesChanged(QString,QVariantMap,QStringList)));
} else {
qWarning() << "Failed to get logind session path" << reply.error().message();
}
}
bool checkLogin1Call(const QString &method) const
{
QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, method);
QDBusReply reply = QDBusConnection::systemBus().asyncCall(msg);
return reply.isValid() && (reply == QStringLiteral("yes") || reply == QStringLiteral("challenge"));
}
void makeLogin1Call(const QString &method, const QVariantList &args)
{
QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
LOGIN1_PATH,
LOGIN1_IFACE,
method);
msg.setArguments(args);
QDBusConnection::systemBus().asyncCall(msg);
}
void checkActive()
{
if (logindSessionPath.isEmpty()) {
qWarning() << "Invalid session path";
return;
}
QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
logindSessionPath,
"org.freedesktop.DBus.Properties",
"Get");
msg << LOGIN1_SESSION_IFACE;
msg << ACTIVE_KEY;
QDBusReply reply = QDBusConnection::systemBus().asyncCall(msg);
if (reply.isValid()) {
isSessionActive = reply.value().toBool();
qDebug() << "Session" << logindSessionPath << "is active:" << isSessionActive;
} else {
qWarning() << "Failed to get Active property" << reply.error().message();
}
}
quint32 screensaverActiveTime() const
{
if (!isSessionActive && screensaverActiveTimer.isValid()) {
return screensaverActiveTimer.elapsed() / 1000;
}
return 0;
}
quint64 idleSinceUSecTimestamp() const
{
QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
logindSessionPath,
"org.freedesktop.DBus.Properties",
"Get");
msg << LOGIN1_SESSION_IFACE;
msg << IDLE_SINCE_KEY;
QDBusReply reply = QDBusConnection::systemBus().asyncCall(msg);
if (reply.isValid()) {
return reply.value().value();
} else {
qWarning() << "Failed to get IdleSinceHint property" << reply.error().message();
}
return 0;
}
void setIdleHint(bool idle)
{
QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
logindSessionPath,
LOGIN1_SESSION_IFACE,
"SetIdleHint");
msg << idle;
QDBusConnection::systemBus().asyncCall(msg);
}
private Q_SLOTS:
void onPropertiesChanged(const QString &iface, const QVariantMap &changedProps, const QStringList &invalidatedProps)
{
Q_UNUSED(iface)
if (changedProps.contains(ACTIVE_KEY) || invalidatedProps.contains(ACTIVE_KEY)) {
if (changedProps.value(ACTIVE_KEY).isValid()) {
isSessionActive = changedProps.value(ACTIVE_KEY).toBool();
} else {
checkActive();
}
Q_EMIT screensaverActiveChanged(!isSessionActive);
if (isSessionActive) {
screensaverActiveTimer.invalidate();
setIdleHint(false);
} else {
screensaverActiveTimer.start();
setIdleHint(true);
}
}
}
Q_SIGNALS:
void screensaverActiveChanged(bool active);
};
Q_GLOBAL_STATIC(DBusUnitySessionServicePrivate, d)
DBusUnitySessionService::DBusUnitySessionService()
: UnityDBusObject("/com/canonical/Unity/Session", "com.canonical.Unity")
{
if (!d->logindSessionPath.isEmpty()) {
// connect our Lock() slot to the logind's session Lock() signal
QDBusConnection::systemBus().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, "Lock", this, SLOT(Lock()));
// ... and our Unlocked() signal to the logind's session Unlock() signal
// (lightdm handles the unlocking by calling logind's Unlock method which in turn emits this signal we connect to)
QDBusConnection::systemBus().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, "Unlock", this, SIGNAL(Unlocked()));
} else {
qWarning() << "Failed to connect to logind's session Lock/Unlock signals";
}
}
void DBusUnitySessionService::Logout()
{
// TODO ask the apps to quit and then emit the signal
Q_EMIT LogoutReady();
Q_EMIT logoutReady();
}
void DBusUnitySessionService::EndSession()
{
const QDBusMessage msg = QDBusMessage::createMethodCall("com.ubuntu.Upstart",
"/com/ubuntu/Upstart",
"com.ubuntu.Upstart0_6",
"EndSession");
QDBusConnection::sessionBus().asyncCall(msg);
}
bool DBusUnitySessionService::CanHibernate() const
{
return d->checkLogin1Call("CanHibernate");
}
bool DBusUnitySessionService::CanSuspend() const
{
return d->checkLogin1Call("CanSuspend");
}
bool DBusUnitySessionService::CanHybridSleep() const
{
return d->checkLogin1Call("CanHybridSleep");
}
bool DBusUnitySessionService::CanReboot() const
{
return d->checkLogin1Call("CanReboot");
}
bool DBusUnitySessionService::CanShutdown() const
{
return d->checkLogin1Call("CanPowerOff");
}
bool DBusUnitySessionService::CanLock() const
{
return true; // FIXME
}
QString DBusUnitySessionService::UserName() const
{
struct passwd *p = getpwuid(geteuid());
if (p) {
return QString::fromUtf8(p->pw_name);
}
return QString();
}
QString DBusUnitySessionService::RealName() const
{
struct passwd *p = getpwuid(geteuid());
if (p) {
const QString gecos = QString::fromLocal8Bit(p->pw_gecos);
if (!gecos.isEmpty()) {
return gecos.split(QLatin1Char(',')).first();
}
}
return QString();
}
QString DBusUnitySessionService::HostName() const
{
char hostName[512];
if (gethostname(hostName, sizeof(hostName)) == -1) {
qWarning() << "Could not determine local hostname";
return QString();
}
hostName[sizeof(hostName) - 1] = '\0';
return QString::fromLocal8Bit(hostName);
}
void DBusUnitySessionService::PromptLock()
{
Q_EMIT LockRequested();
Q_EMIT lockRequested();
}
void DBusUnitySessionService::Lock()
{
// lock the session using the org.freedesktop.DisplayManager system DBUS service
const QString sessionPath = QString::fromLocal8Bit(qgetenv("XDG_SESSION_PATH"));
QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.DisplayManager",
sessionPath,
"org.freedesktop.DisplayManager.Session",
"Lock");
qDebug() << "Locking session" << msg.path();
QDBusReply reply = QDBusConnection::systemBus().asyncCall(msg);
if (!reply.isValid()) {
qWarning() << "Lock call failed" << reply.error().message();
} else {
// emit Locked when the call succeeds
Q_EMIT Locked();
}
}
bool DBusUnitySessionService::IsLocked() const
{
return !d->isSessionActive;
}
void DBusUnitySessionService::RequestLogout()
{
Q_EMIT LogoutRequested(false);
Q_EMIT logoutRequested(false);
}
void DBusUnitySessionService::Reboot()
{
d->makeLogin1Call("Reboot", {false});
}
void DBusUnitySessionService::RequestReboot()
{
Q_EMIT RebootRequested(false);
Q_EMIT rebootRequested(false);
}
void DBusUnitySessionService::Shutdown()
{
d->makeLogin1Call("PowerOff", {false});
}
void DBusUnitySessionService::Suspend()
{
d->makeLogin1Call("Suspend", {false});
}
void DBusUnitySessionService::Hibernate()
{
d->makeLogin1Call("Hibernate", {false});
}
void DBusUnitySessionService::HybridSleep()
{
d->makeLogin1Call("HybridSleep", {false});
}
void DBusUnitySessionService::RequestShutdown()
{
Q_EMIT ShutdownRequested(false);
Q_EMIT shutdownRequested(false);
}
enum class Action : unsigned
{
LOGOUT = 0,
SHUTDOWN,
REBOOT,
NONE
};
void performAsyncUnityCall(const QString &method)
{
const QDBusMessage msg = QDBusMessage::createMethodCall("com.canonical.Unity",
"/com/canonical/Unity/Session",
"com.canonical.Unity.Session",
method);
QDBusConnection::sessionBus().asyncCall(msg);
}
DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
: UnityDBusObject("/org/gnome/SessionManager/EndSessionDialog", "com.canonical.Unity")
{
}
void DBusGnomeSessionManagerWrapper::Open(const unsigned type, const unsigned arg_1, const unsigned max_wait, const QList &inhibitors)
{
Q_UNUSED(arg_1);
Q_UNUSED(max_wait);
Q_UNUSED(inhibitors);
switch (static_cast(type))
{
case Action::LOGOUT:
performAsyncUnityCall("RequestLogout");
break;
case Action::REBOOT:
performAsyncUnityCall("RequestReboot");
break;
case Action::SHUTDOWN:
performAsyncUnityCall("RequestShutdown");
break;
default:
break;
}
}
DBusGnomeScreensaverWrapper::DBusGnomeScreensaverWrapper()
: UnityDBusObject("/org/gnome/ScreenSaver", "org.gnome.ScreenSaver")
{
connect(d, &DBusUnitySessionServicePrivate::screensaverActiveChanged, this, &DBusGnomeScreensaverWrapper::ActiveChanged);
}
bool DBusGnomeScreensaverWrapper::GetActive() const
{
return !d->isSessionActive; // return whether the session is not active
}
void DBusGnomeScreensaverWrapper::SetActive(bool lock)
{
if (lock) {
Lock();
}
}
void DBusGnomeScreensaverWrapper::Lock()
{
performAsyncUnityCall("Lock");
}
quint32 DBusGnomeScreensaverWrapper::GetActiveTime() const
{
return d->screensaverActiveTime();
}
void DBusGnomeScreensaverWrapper::SimulateUserActivity()
{
d->setIdleHint(false);
}
DBusScreensaverWrapper::DBusScreensaverWrapper()
: UnityDBusObject("/org/freedesktop/ScreenSaver", "org.freedesktop.ScreenSaver")
{
QDBusConnection::sessionBus().registerObject("/ScreenSaver", this, QDBusConnection::ExportScriptableContents); // compat path, also register here
connect(d, &DBusUnitySessionServicePrivate::screensaverActiveChanged, this, &DBusScreensaverWrapper::ActiveChanged);
}
bool DBusScreensaverWrapper::GetActive() const
{
return !d->isSessionActive; // return whether the session is not active
}
bool DBusScreensaverWrapper::SetActive(bool lock)
{
if (lock) {
Lock();
return true;
}
return false;
}
void DBusScreensaverWrapper::Lock()
{
performAsyncUnityCall("Lock");
}
quint32 DBusScreensaverWrapper::GetActiveTime() const
{
return d->screensaverActiveTime();
}
quint32 DBusScreensaverWrapper::GetSessionIdleTime() const
{
return QDateTime::fromMSecsSinceEpoch(d->idleSinceUSecTimestamp()/1000).secsTo(QDateTime::currentDateTime());
}
void DBusScreensaverWrapper::SimulateUserActivity()
{
d->setIdleHint(false);
}
#include "dbusunitysessionservice.moc"