Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[nemo-systemsettings] Add alone property to UserInfo. Contributes to …
…JB#49696

Add alone property that can be used to determine if user is the only
user on device. It uses tristated value on the background so that if it
isn't needed, it's not checked. It is also wired up to the file system
watcher so that it gets updated if groups change.

Signed-off-by: Tomi Leppänen <tomi.leppanen@jolla.com>
  • Loading branch information
Tomin1 committed Apr 27, 2020
1 parent 92572a1 commit f948fe2
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 11 deletions.
103 changes: 92 additions & 11 deletions src/userinfo.cpp
Expand Up @@ -36,6 +36,7 @@
#include <QFile>
#include <QFileSystemWatcher>
#include <QSocketNotifier>
#include <grp.h>
#include <poll.h>
#include <pwd.h>
#include <sys/types.h>
Expand All @@ -44,6 +45,7 @@
namespace {

const auto UserDatabaseFile = QStringLiteral("/etc/passwd");
const auto GroupDatabaseFile = QStringLiteral("/etc/group");

enum SpecialIds : uid_t {
DeviceOwnerId = 100000,
Expand All @@ -70,6 +72,7 @@ UserInfoPrivate::UserInfoPrivate()
: m_uid(InvalidId)
, m_loggedIn(false)
, m_watcher(nullptr)
, m_alone(Unknown)
{
}

Expand All @@ -82,6 +85,7 @@ UserInfoPrivate::UserInfoPrivate(struct passwd *pwd)
// counted as they don't have seats.
, m_loggedIn(sd_uid_is_on_seat(m_uid, 1, "seat0") > 0)
, m_watcher(nullptr)
, m_alone(Unknown)
{
}

Expand Down Expand Up @@ -120,6 +124,56 @@ void UserInfoPrivate::set(struct passwd *pwd)
}
}

bool UserInfoPrivate::alone()
{
if (m_alone == Unknown)
updateAlone(true);
return m_alone == Yes;
}

void UserInfoPrivate::updateAlone(bool force)
{
if (!force && m_alone == Unknown) {
// Skip if the value is not needed and the check is not forced
return;
}

Tristated alone = Yes;

if (m_uid != InvalidId && m_uid != UnknownCurrentUserId && m_uid != DeviceOwnerId) {
// There must be at least one other user besides device owner
// if the uid is valid and known and it's not device owner
alone = No;
} else {
// Can not determine from uid, check users group
errno = 0;
struct group *grp = getgrnam("users");
if (!grp) {
qCWarning(lcUsersLog) << "Could not read users group:" << strerror(errno);
// Guessing that user is probably alone
} else {
for (int i = 0; grp->gr_mem[i] != nullptr; ++i) {
struct passwd *pwd = getpwnam(grp->gr_mem[i]);
if (pwd && pwd->pw_uid != DeviceOwnerId) {
// Found someone that's not device owner
alone = No;
break;
}
// pwd must not be free'd
}
// grp must not be free'd
}
}

if (m_alone != alone) {
m_alone = alone;
if (!force) {
// Emit only if something needed the value already, i.e. it was known
emit aloneChanged();
}
}
}

/**
* Construct UserInfo for the current user
*
Expand Down Expand Up @@ -326,11 +380,24 @@ bool UserInfo::updateCurrent()
return false;
}

/**
* Returns true if there is only one user on the device
*/
bool UserInfo::alone()
{
Q_D(UserInfo);
return d->alone();
}

/**
* Resets object reloading all information
*/
void UserInfo::reset()
{
Q_D(UserInfo);
d->set((isValid()) ? getpwuid(d->m_uid) : nullptr);
updateCurrent();
d->updateAlone();
}

void UserInfo::replace(QSharedPointer<UserInfoPrivate> other)
Expand Down Expand Up @@ -360,6 +427,10 @@ void UserInfo::replace(QSharedPointer<UserInfoPrivate> other)
if (old->m_watcher)
watchForChanges();

// If alone value was known, ensure that new d_ptr also knows it
if (old->m_alone != UserInfoPrivate::Unknown && old->alone() != d_ptr->alone())
emit aloneChanged();

connectSignals();
}

Expand Down Expand Up @@ -394,29 +465,39 @@ void UserInfo::connectSignals()
connect(d_ptr.data(), &UserInfoPrivate::nameChanged, this, &UserInfo::nameChanged);
connect(d_ptr.data(), &UserInfoPrivate::uidChanged, this, &UserInfo::uidChanged);
connect(d_ptr.data(), &UserInfoPrivate::currentChanged, this, &UserInfo::currentChanged);
connect(d_ptr.data(), &UserInfoPrivate::aloneChanged, this, &UserInfo::aloneChanged);
}

void UserInfo::watchForChanges()
{
Q_D(UserInfo);
d->m_watcher = new QFileSystemWatcher(this);
if (!d->m_watcher->addPath(UserDatabaseFile)) {
qCWarning(lcUsersLog) << "Could not watch for changes in user database";
QStringList missing = d->m_watcher->addPaths(QStringList() << UserDatabaseFile << GroupDatabaseFile);
if (missing.count() == 2) {
qCWarning(lcUsersLog) << "Could not watch for changes in user or group database";
delete d->m_watcher;
d->m_watcher = nullptr;
} else if (missing.count() > 0) {
qCWarning(lcUsersLog) << "Could not watch for changes in" << missing;
} else {
connect(d->m_watcher, &QFileSystemWatcher::fileChanged, this, [this] {
connect(d->m_watcher, &QFileSystemWatcher::fileChanged, this, [this] (const QString &path) {
Q_D(UserInfo);
if (QFile::exists(UserDatabaseFile)) {
// Database updated, reset
qCDebug(lcUsersLog) << "Reseting model because user database changed";
reset();
if (QFile::exists(path)) {
if (path == UserDatabaseFile) {
// User database updated, reset model
qCDebug(lcUsersLog) << UserDatabaseFile << "changed, reseting model";
reset();
} else if (d->m_alone != UserInfoPrivate::Unknown) { // && path == GroupDatabaseFile
// Group database updated, update alone status
qCDebug(lcUsersLog) << GroupDatabaseFile << "changed, checking alone status again";
d->updateAlone();
}
}
if (!d->m_watcher->files().contains(UserDatabaseFile)) {
if (QFile::exists(UserDatabaseFile) && d->m_watcher->addPath(UserDatabaseFile)) {
qCDebug(lcUsersLog) << "Re-watching user database for changes";
if (!d->m_watcher->files().contains(path)) {
if (QFile::exists(path) && d->m_watcher->addPath(path)) {
qCDebug(lcUsersLog) << "Re-watching" << path << "for changes";
} else {
qCWarning(lcUsersLog) << "Stopped watching user database for changes";
qCWarning(lcUsersLog) << "Stopped watching" << path << "for changes";
}
}
});
Expand Down
3 changes: 3 additions & 0 deletions src/userinfo.h
Expand Up @@ -52,6 +52,7 @@ class SYSTEMSETTINGS_EXPORT UserInfo: public QObject
Q_PROPERTY(UserType type READ type CONSTANT)
Q_PROPERTY(int uid READ uid WRITE setUid NOTIFY uidChanged)
Q_PROPERTY(bool current READ current NOTIFY currentChanged)
Q_PROPERTY(bool alone READ alone NOTIFY aloneChanged)

friend class UserModel;

Expand All @@ -77,6 +78,7 @@ class SYSTEMSETTINGS_EXPORT UserInfo: public QObject
int uid() const;
void setUid(int uid);
bool current() const;
bool alone();

Q_INVOKABLE void reset();

Expand All @@ -90,6 +92,7 @@ class SYSTEMSETTINGS_EXPORT UserInfo: public QObject
void nameChanged();
void uidChanged();
void currentChanged();
void aloneChanged();

private:
explicit UserInfo(int uid);
Expand Down
10 changes: 10 additions & 0 deletions src/userinfo_p.h
Expand Up @@ -49,21 +49,31 @@ class UserInfoPrivate : public QObject
UserInfoPrivate(struct passwd *pwd);
~UserInfoPrivate();

enum Tristated {
Yes = 1,
No = 0,
Unknown = -1
};

uid_t m_uid;
QString m_username;
QString m_name;
bool m_loggedIn;
static QWeakPointer<UserInfoPrivate> s_current;
QFileSystemWatcher *m_watcher;
Tristated m_alone;

void set(struct passwd *pwd);
bool alone();
void updateAlone(bool force = false);

signals:
void displayNameChanged();
void usernameChanged();
void nameChanged();
void uidChanged();
void currentChanged();
void aloneChanged();
};

#endif /* USERINFOPRIVATE_H */

0 comments on commit f948fe2

Please sign in to comment.