KCoreAddons

kuser_unix.cpp
1 /*
2  KUser - represent a user/account
3 
4  SPDX-FileCopyrightText: 2002 Tim Jansen <[email protected]>
5  SPDX-FileCopyrightText: 2014 Alex Richardson <[email protected]>
6 
7  SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9 
10 #include "kuser.h"
11 #include "config-getgrouplist.h"
12 #include "config-accountsservice.h"
13 #include "kcoreaddons_debug.h"
14 
15 #include <QFileInfo>
16 
17 #include <pwd.h>
18 #include <unistd.h>
19 #include <stdlib.h>
20 #include <grp.h>
21 #include <errno.h>
22 
23 #include <algorithm> // std::find
24 #include <functional> // std::function
25 
26 #if defined(__BIONIC__) && __ANDROID_API__ < 26
27 static inline struct passwd * getpwent() { return nullptr; }
28 inline void setpwent() { }
29 static inline void setgrent() { }
30 static inline struct group * getgrent() { return nullptr; }
31 inline void endpwent() { }
32 static inline void endgrent() { }
33 #endif
34 
35 class Q_DECL_HIDDEN KUser::Private : public QSharedData
36 {
37 public:
38  uid_t uid;
39  gid_t gid;
40  QString loginName;
41  QString homeDir, shell;
43 
44  Private() : uid(uid_t(-1)), gid(gid_t(-1)) {}
45  Private(const char *name) : uid(uid_t(-1)), gid(gid_t(-1))
46  {
47  fillPasswd(name ? ::getpwnam(name) : nullptr);
48  }
49  Private(const passwd *p) : uid(uid_t(-1)), gid(gid_t(-1))
50  {
51  fillPasswd(p);
52  }
53 
54  void fillPasswd(const passwd *p)
55  {
56  if (p) {
57 #ifndef __BIONIC__
58  QString gecos = QString::fromLocal8Bit(p->pw_gecos);
59 #else
60  QString gecos = QString();
61 #endif
62  QStringList gecosList = gecos.split(QLatin1Char(','));
63  // fill up the list, should be at least 4 entries
64  while (gecosList.size() < 4) {
65  gecosList << QString();
66  }
67 
68  uid = p->pw_uid;
69  gid = p->pw_gid;
70  loginName = QString::fromLocal8Bit(p->pw_name);
71  properties[KUser::FullName] = QVariant(gecosList[0]);
72  properties[KUser::RoomNumber] = QVariant(gecosList[1]);
73  properties[KUser::WorkPhone] = QVariant(gecosList[2]);
74  properties[KUser::HomePhone] = QVariant(gecosList[3]);
75  if (uid == ::getuid() && uid == ::geteuid()) {
76  homeDir = QFile::decodeName(qgetenv("HOME"));
77  }
78  if (homeDir.isEmpty()) {
79  homeDir = QFile::decodeName(p->pw_dir);
80  }
81  shell = QString::fromLocal8Bit(p->pw_shell);
82  }
83  }
84 };
85 
87 {
88  uid_t _uid = ::getuid(), _euid;
89  if (mode == UseEffectiveUID && (_euid = ::geteuid()) != _uid) {
90  d = new Private(::getpwuid(_euid));
91  } else {
92  d = new Private(qgetenv("LOGNAME").constData());
93  if (d->uid != _uid) {
94  d = new Private(qgetenv("USER").constData());
95  if (d->uid != _uid) {
96  d = new Private(::getpwuid(_uid));
97  }
98  }
99  }
100 }
101 
102 KUser::KUser(K_UID _uid)
103  : d(new Private(::getpwuid(_uid)))
104 {
105 }
106 
108  : d(new Private(::getpwuid(_uid.nativeId())))
109 {
110 }
111 
112 KUser::KUser(const QString &name)
113  : d(new Private(name.toLocal8Bit().data()))
114 {
115 }
116 
117 KUser::KUser(const char *name)
118  : d(new Private(name))
119 {
120 }
121 
122 KUser::KUser(const passwd *p)
123  : d(new Private(p))
124 {
125 }
126 
127 KUser::KUser(const KUser &user)
128  : d(user.d)
129 {
130 }
131 
133 {
134  d = user.d;
135  return *this;
136 }
137 
138 bool KUser::operator ==(const KUser &user) const
139 {
140  return isValid() && (d->uid == user.d->uid);
141 }
142 
143 bool KUser::isValid() const
144 {
145  return d->uid != uid_t(-1);
146 }
147 
149 {
150  return KUserId(d->uid);
151 }
152 
154 {
155  return KGroupId(d->gid);
156 }
157 
158 bool KUser::isSuperUser() const
159 {
160  return d->uid == 0;
161 }
162 
164 {
165  return d->loginName;
166 }
167 
169 {
170  return d->homeDir;
171 }
172 
174 {
175  QString pathToFaceIcon;
176  if (!d->loginName.isEmpty()) {
177  pathToFaceIcon = QStringLiteral(ACCOUNTS_SERVICE_ICON_DIR) + QLatin1Char('/') + d->loginName;
178  }
179 
180  if (QFile::exists(pathToFaceIcon)) {
181  return pathToFaceIcon;
182  }
183 
184  pathToFaceIcon = homeDir() + QLatin1Char('/') + QLatin1String(".face.icon");
185 
186  if (QFileInfo(pathToFaceIcon).isReadable()) {
187  return pathToFaceIcon;
188  }
189 
190  return QString();
191 }
192 
194 {
195  return d->shell;
196 }
197 
198 template<class Func>
199 static void listGroupsForUser(const char *name, gid_t gid, uint maxCount, Func handleNextGroup)
200 {
201  if (Q_UNLIKELY(maxCount == 0)) {
202  return;
203  }
204  uint found = 0;
205 #if HAVE_GETGROUPLIST
206  QVarLengthArray<gid_t, 100> gid_buffer;
207  gid_buffer.resize(100);
208  int numGroups = gid_buffer.size();
209  int result = getgrouplist(name, gid, gid_buffer.data(), &numGroups);
210  if (result < 0 && uint(numGroups) < maxCount) {
211  // getgrouplist returns -1 if the buffer was too small to store all entries, the required size is in numGroups
212  qCDebug(KCOREADDONS_DEBUG, "Buffer was too small: %d, need %d", gid_buffer.size(), numGroups);
213  gid_buffer.resize(numGroups);
214  numGroups = gid_buffer.size();
215  getgrouplist(name, gid, gid_buffer.data(), &numGroups);
216  }
217  for (int i = 0; i < numGroups && found < maxCount; ++i) {
218  struct group *g = getgrgid(gid_buffer[i]);
219  // should never be null, but better be safe than crash
220  if (g) {
221  found++;
222  handleNextGroup(g);
223  }
224  }
225 #else
226  // fall back to getgrent() and reading gr->gr_mem
227  // This is slower than getgrouplist, but works as well
228  // add the current gid, this is often not part of g->gr_mem (e.g. build.kde.org or my openSuSE 13.1 system)
229  struct group *g = getgrgid(gid);
230  if (g) {
231  handleNextGroup(g);
232  found++;
233  if (found >= maxCount) {
234  return;
235  }
236  }
237 
238  static const auto groupContainsUser = [](struct group * g, const char *name) -> bool {
239  for (char **user = g->gr_mem; *user; user++)
240  {
241  if (strcmp(name, *user) == 0) {
242  return true;
243  }
244  }
245  return false;
246  };
247 
248  setgrent();
249  while ((g = getgrent())) {
250  // don't add the current gid again
251  if (g->gr_gid != gid && groupContainsUser(g, name)) {
252  handleNextGroup(g);
253  found++;
254  if (found >= maxCount) {
255  break;
256  }
257  }
258  }
259  endgrent();
260 #endif
261 }
262 
263 QList<KUserGroup> KUser::groups(uint maxCount) const
264 {
265  QList<KUserGroup> result;
266  listGroupsForUser(
267  d->loginName.toLocal8Bit().constData(), d->gid, maxCount,
268  [&](const group * g) {
269  result.append(KUserGroup(g));
270  }
271  );
272  return result;
273 }
274 
275 QStringList KUser::groupNames(uint maxCount) const
276 {
277  QStringList result;
278  listGroupsForUser(
279  d->loginName.toLocal8Bit().constData(), d->gid, maxCount,
280  [&](const group * g) {
281  result.append(QString::fromLocal8Bit(g->gr_name));
282  }
283  );
284  return result;
285 }
286 
287 QVariant KUser::property(UserProperty which) const
288 {
289  return d->properties.value(which);
290 }
291 
293 {
294  QList<KUser> result;
295 
296  passwd *p;
297  setpwent();
298 
299  for (uint i = 0; i < maxCount && (p = getpwent()); ++i) {
300  result.append(KUser(p));
301  }
302 
303  endpwent();
304 
305  return result;
306 }
307 
309 {
310  QStringList result;
311 
312  passwd *p;
313  setpwent();
314 
315  for (uint i = 0; i < maxCount && (p = getpwent()); ++i) {
316  result.append(QString::fromLocal8Bit(p->pw_name));
317  }
318 
319  endpwent();
320  return result;
321 }
322 
324 {
325 }
326 
327 class Q_DECL_HIDDEN KUserGroup::Private : public QSharedData
328 {
329 public:
330  gid_t gid;
331  QString name;
332 
333  Private() : gid(gid_t(-1)) {}
334  Private(const char *_name) : gid(gid_t(-1))
335  {
336  fillGroup(_name ? ::getgrnam(_name) : nullptr);
337  }
338  Private(const ::group *p) : gid(gid_t(-1))
339  {
340  fillGroup(p);
341  }
342 
343  void fillGroup(const ::group *p)
344  {
345  if (p) {
346  gid = p->gr_gid;
347  name = QString::fromLocal8Bit(p->gr_name);
348  }
349  }
350 };
351 
353 {
354  d = new Private(getgrgid(KUser(mode).groupId().nativeId()));
355 }
356 
358  : d(new Private(getgrgid(_gid)))
359 {
360 }
361 
363  : d(new Private(getgrgid(_gid.nativeId())))
364 {
365 }
366 
368  : d(new Private(_name.toLocal8Bit().data()))
369 {
370 }
371 
372 KUserGroup::KUserGroup(const char *_name)
373  : d(new Private(_name))
374 {
375 }
376 
377 KUserGroup::KUserGroup(const ::group *g)
378  : d(new Private(g))
379 {
380 }
381 
382 KUserGroup::KUserGroup(const KUserGroup &group)
383  : d(group.d)
384 {
385 }
386 
387 KUserGroup &KUserGroup::operator =(const KUserGroup &group)
388 {
389  d = group.d;
390  return *this;
391 }
392 
393 bool KUserGroup::operator ==(const KUserGroup &group) const
394 {
395  return isValid() && (d->gid == group.d->gid);
396 }
397 
399 {
400  return d->gid != gid_t(-1);
401 }
402 
404 {
405  return KGroupId(d->gid);
406 }
407 
409 {
410  return d->name;
411 }
412 
413 static void listGroupMembers(gid_t gid, uint maxCount, std::function<void(passwd *)> handleNextGroupUser)
414 {
415  if (maxCount == 0) {
416  return;
417  }
418  struct group *g = getgrgid(gid);
419  if (!g) {
420  return;
421  }
422  uint found = 0;
423  QVarLengthArray<uid_t> addedUsers;
424  struct passwd *p = nullptr;
425  for (char **user = g->gr_mem; *user; user++) {
426  if ((p = getpwnam(*user))) {
427  addedUsers.append(p->pw_uid);
428  handleNextGroupUser(p);
429  found++;
430  if (found >= maxCount) {
431  break;
432  }
433  }
434  }
435 
436  //gr_mem doesn't contain users where the primary group == gid -> we have to iterate over all users
437  setpwent();
438  while ((p = getpwent()) && found < maxCount) {
439  if (p->pw_gid != gid) {
440  continue; // only need primary gid since otherwise gr_mem already contains this user
441  }
442  // make sure we don't list a user twice
443  if (std::find(addedUsers.cbegin(), addedUsers.cend(), p->pw_uid) == addedUsers.cend()) {
444  handleNextGroupUser(p);
445  found++;
446  }
447  }
448  endpwent();
449 }
450 
451 QList<KUser> KUserGroup::users(uint maxCount) const
452 {
453  QList<KUser> result;
454  listGroupMembers(d->gid, maxCount, [&](const passwd *p) {
455  result.append(KUser(p));
456  });
457  return result;
458 }
459 
460 QStringList KUserGroup::userNames(uint maxCount) const
461 {
462  QStringList result;
463  listGroupMembers(d->gid, maxCount, [&](const passwd *p) {
464  result.append(QString::fromLocal8Bit(p->pw_name));
465  });
466  return result;
467 }
468 
470 {
471  QList<KUserGroup> result;
472 
473  ::group *g;
474  setgrent();
475 
476  for (uint i = 0; i < maxCount && (g = getgrent()); ++i) {
477  result.append(KUserGroup(g));
478  }
479 
480  endgrent();
481 
482  return result;
483 }
484 
486 {
487  QStringList result;
488 
489  ::group *g;
490  setgrent();
491 
492  for (uint i = 0; i < maxCount && (g = getgrent()); ++i) {
493  result.append(QString::fromLocal8Bit(g->gr_name));
494  }
495 
496  endgrent();
497 
498  return result;
499 }
500 
502 {
503 }
504 
506 {
507  if (name.isEmpty()) {
508  return KUserId();
509  }
510  QByteArray name8Bit = name.toLocal8Bit();
511  struct passwd *p = ::getpwnam(name8Bit.constData());
512  if (!p) {
513  qCWarning(KCOREADDONS_DEBUG, "Failed to lookup user %s: %s", name8Bit.constData(), strerror(errno));
514  return KUserId();
515  }
516  return KUserId(p->pw_uid);
517 }
518 
520 {
521  if (name.isEmpty()) {
522  return KGroupId();
523  }
524  QByteArray name8Bit = name.toLocal8Bit();
525  struct group *g = ::getgrnam(name8Bit.constData());
526  if (!g) {
527  qCWarning(KCOREADDONS_DEBUG, "Failed to lookup group %s: %s", name8Bit.constData(), strerror(errno));
528  return KGroupId();
529  }
530  return KGroupId(g->gr_gid);
531 }
532 
534 {
535  return KUserId(getuid());
536 }
537 
539 {
540  return KUserId(geteuid());
541 }
542 
544 {
545  return KGroupId(getgid());
546 }
547 
549 {
550  return KGroupId(getegid());
551 }
KUserGroup(const QString &name)
Create an object from a group name.
Definition: kuser_unix.cpp:367
QVarLengthArray::const_iterator cend() const const
static QList< KUser > allUsers(uint maxCount=KCOREADDONS_UINT_MAX)
Definition: kuser_unix.cpp:292
bool isValid() const
Returns whether the group is valid.
Definition: kuser_unix.cpp:398
static KGroupId currentGroupId()
Definition: kuser_unix.cpp:543
QString name() const
The name of the group.
Definition: kuser_unix.cpp:408
void append(const T &t)
static KGroupId currentEffectiveGroupId()
Definition: kuser_unix.cpp:548
const T * constData() const const
QVariant property(UserProperty which) const
Returns an extended property.
Definition: kuser_unix.cpp:287
Represents a group on your system.
Definition: kuser.h:422
static KUserId currentUserId()
Definition: kuser_unix.cpp:533
bool exists() const const
static KGroupId fromName(const QString &name)
Definition: kuser_unix.cpp:519
int size() const const
static QStringList allUserNames(uint maxCount=KCOREADDONS_UINT_MAX)
Definition: kuser_unix.cpp:308
Represents a user on your system.
Definition: kuser.h:192
bool operator==(const KUser &user) const
Two KUser objects are equal if the userId() are identical.
Definition: kuser_unix.cpp:138
static KUserId fromName(const QString &name)
Definition: kuser_unix.cpp:505
QString fromLocal8Bit(const char *str, int size)
void append(const T &value)
KGuiItem properties()
QList< KUser > users(uint maxCount=KCOREADDONS_UINT_MAX) const
Definition: kuser_unix.cpp:451
QString loginName() const
The login name of the user.
Definition: kuser_unix.cpp:163
bool isEmpty() const const
const char * constData() const const
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool operator==(const KUserGroup &group) const
Two KUserGroup objects are equal if their gid()s are identical.
Definition: kuser_unix.cpp:393
KUserGroup & operator=(const KUserGroup &group)
Copies a group.
Definition: kuser_unix.cpp:387
QStringList userNames(uint maxCount=KCOREADDONS_UINT_MAX) const
Definition: kuser_unix.cpp:460
KGroupId groupId() const
Definition: kuser_unix.cpp:153
static KUserId currentEffectiveUserId()
Definition: kuser_unix.cpp:538
KUserId userId() const
Definition: kuser_unix.cpp:148
QByteArray toLocal8Bit() const const
UIDMode
Definition: kuser.h:197
QString homeDir() const
The path to the user&#39;s home directory.
Definition: kuser_unix.cpp:168
A platform independent group ID.
Definition: kuser.h:145
QString shell() const
The path to the user&#39;s login shell.
Definition: kuser_unix.cpp:193
bool isSuperUser() const
Checks whether the user is the super user (root).
Definition: kuser_unix.cpp:158
~KUserGroup()
Destructor.
Definition: kuser_unix.cpp:501
QStringList groupNames(uint maxCount=KCOREADDONS_UINT_MAX) const
Definition: kuser_unix.cpp:275
~KUser()
Destructor.
Definition: kuser_unix.cpp:323
QList< KUserGroup > groups(uint maxCount=KCOREADDONS_UINT_MAX) const
Definition: kuser_unix.cpp:263
static QList< KUserGroup > allGroups(uint maxCount=KCOREADDONS_UINT_MAX)
Definition: kuser_unix.cpp:469
KGroupId groupId() const
Definition: kuser_unix.cpp:403
K_GID gid() const
Returns the group id of the user.
Definition: kuser.h:302
KUser & operator=(const KUser &user)
Copies a user.
Definition: kuser_unix.cpp:132
bool isValid() const
Returns true if the user is valid.
Definition: kuser_unix.cpp:143
static QStringList allGroupNames(uint maxCount=KCOREADDONS_UINT_MAX)
Definition: kuser_unix.cpp:485
QVarLengthArray::const_iterator cbegin() const const
KUser(UIDMode mode=UseEffectiveUID)
Creates an object that contains information about the current user.
Definition: kuser_unix.cpp:86
int size() const const
void resize(int size)
QString faceIconPath() const
The path to the user&#39;s face file.
Definition: kuser_unix.cpp:173
QString decodeName(const QByteArray &localFileName)
A platform independent user ID.
Definition: kuser.h:120
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Wed Oct 28 2020 23:06:52 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.