summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorTimothy Flynn <trflynn89@pm.me>2022-12-13 14:49:32 -0500
committerTim Flynn <trflynn89@pm.me>2022-12-14 07:25:36 -0500
commit1ee808fae68ce1ebbe9eebd5924b87787317e617 (patch)
tree072ac4f57c17e919e57656cd2ff4bf7b0f0e6f8e /Userland
parentb367da592c568277ab76850dcde5422f4ae118de (diff)
downloadserenity-1ee808fae68ce1ebbe9eebd5924b87787317e617.zip
LibCore: Define and use a fallible, OS-independent getpwent(_r) wrapper
Rather than maintaining a list of #ifdef guards to check systems that do not provide the reentrant version of getpwent, we can use C++ concepts to let the compiler perform this check for us. While we're at it, we can also provide this wrapper as fallible to let the caller TRY calling it.
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibCore/Account.cpp45
-rw-r--r--Userland/Libraries/LibCore/System.cpp39
-rw-r--r--Userland/Libraries/LibCore/System.h1
3 files changed, 58 insertions, 27 deletions
diff --git a/Userland/Libraries/LibCore/Account.cpp b/Userland/Libraries/LibCore/Account.cpp
index e48a67541a..03808e089c 100644
--- a/Userland/Libraries/LibCore/Account.cpp
+++ b/Userland/Libraries/LibCore/Account.cpp
@@ -128,40 +128,31 @@ ErrorOr<Account> Account::from_uid(uid_t uid, [[maybe_unused]] Read options)
ErrorOr<Vector<Account>> Account::all([[maybe_unused]] Read options)
{
Vector<Account> accounts;
-
-#ifndef AK_OS_MACOS
- struct passwd pwd;
- struct passwd* ptr = nullptr;
char buffer[1024] = { 0 };
-#endif
ScopeGuard pwent_guard([] { endpwent(); });
setpwent();
- errno = 0;
-#ifndef AK_OS_MACOS
- while (getpwent_r(&pwd, buffer, sizeof(buffer), &ptr) == 0 && ptr) {
-#else
- while (auto const* ptr = getpwent()) {
-#endif
+ while (true) {
+ auto pwd = TRY(Core::System::getpwent({ buffer, sizeof(buffer) }));
+ if (!pwd.has_value())
+ break;
+
spwd spwd = {};
#ifndef AK_OS_BSD_GENERIC
ScopeGuard spent_guard([] { endspent(); });
if (options != Read::PasswdOnly) {
- auto maybe_spwd = TRY(Core::System::getspnam({ ptr->pw_name, strlen(ptr->pw_name) }));
+ auto maybe_spwd = TRY(Core::System::getspnam({ pwd->pw_name, strlen(pwd->pw_name) }));
if (!maybe_spwd.has_value())
return Error::from_string_literal("No shadow entry for user");
spwd = maybe_spwd.release_value();
}
#endif
- accounts.append({ *ptr, spwd, get_extra_gids(*ptr) });
+ accounts.append({ *pwd, spwd, get_extra_gids(*pwd) });
}
- if (errno)
- return Error::from_errno(errno);
-
return accounts;
}
@@ -226,13 +217,17 @@ Account::Account(passwd const& pwd, spwd const& spwd, Vector<gid_t> extra_gids)
ErrorOr<DeprecatedString> Account::generate_passwd_file() const
{
StringBuilder builder;
+ char buffer[1024] = { 0 };
+ ScopeGuard pwent_guard([] { endpwent(); });
setpwent();
- struct passwd* p;
- errno = 0;
- while ((p = getpwent())) {
- if (p->pw_name == m_username) {
+ while (true) {
+ auto pwd = TRY(Core::System::getpwent({ buffer, sizeof(buffer) }));
+ if (!pwd.has_value())
+ break;
+
+ if (pwd->pw_name == m_username) {
builder.appendff("{}:!:{}:{}:{}:{}:{}\n",
m_username,
m_uid, m_gid,
@@ -242,15 +237,11 @@ ErrorOr<DeprecatedString> Account::generate_passwd_file() const
} else {
builder.appendff("{}:!:{}:{}:{}:{}:{}\n",
- p->pw_name, p->pw_uid,
- p->pw_gid, p->pw_gecos, p->pw_dir,
- p->pw_shell);
+ pwd->pw_name, pwd->pw_uid,
+ pwd->pw_gid, pwd->pw_gecos, pwd->pw_dir,
+ pwd->pw_shell);
}
}
- endpwent();
-
- if (errno)
- return Error::from_errno(errno);
return builder.to_deprecated_string();
}
diff --git a/Userland/Libraries/LibCore/System.cpp b/Userland/Libraries/LibCore/System.cpp
index 46b92231dd..faba7a654c 100644
--- a/Userland/Libraries/LibCore/System.cpp
+++ b/Userland/Libraries/LibCore/System.cpp
@@ -52,6 +52,40 @@ static int memfd_create(char const* name, unsigned int flags)
} \
return success_value;
+// clang-format off
+template<typename T>
+concept SupportsReentrantGetpwent = requires(T passwd, T* ptr)
+{
+ getpwent_r(&passwd, nullptr, 0, &ptr);
+};
+// clang-format on
+
+// Note: This has to be in the global namespace for the extern declaration to trick the compiler
+// into finding a declaration of getpwent_r when it doesn't actually exist.
+static ErrorOr<Optional<struct passwd>> getpwent_impl(Span<char> buffer)
+{
+ if constexpr (SupportsReentrantGetpwent<struct passwd>) {
+ struct passwd passwd;
+ struct passwd* ptr = nullptr;
+
+ extern int getpwent_r(struct passwd*, char*, size_t, struct passwd**);
+ auto result = getpwent_r(&passwd, buffer.data(), buffer.size(), &ptr);
+
+ if (result == 0 && ptr)
+ return passwd;
+ if (result != 0 && result != ENOENT)
+ return Error::from_errno(result);
+ } else {
+ errno = 0;
+ if (auto const* passwd = ::getpwent())
+ return *passwd;
+ if (errno)
+ return Error::from_errno(errno);
+ }
+
+ return Optional<struct passwd> {};
+}
+
namespace Core::System {
#ifndef HOST_NAME_MAX
@@ -627,6 +661,11 @@ ErrorOr<void> chown(StringView pathname, uid_t uid, gid_t gid)
#endif
}
+ErrorOr<Optional<struct passwd>> getpwent(Span<char> buffer)
+{
+ return getpwent_impl(buffer);
+}
+
ErrorOr<Optional<struct passwd>> getpwuid(uid_t uid)
{
errno = 0;
diff --git a/Userland/Libraries/LibCore/System.h b/Userland/Libraries/LibCore/System.h
index e7c673a344..c9287cab71 100644
--- a/Userland/Libraries/LibCore/System.h
+++ b/Userland/Libraries/LibCore/System.h
@@ -122,6 +122,7 @@ ErrorOr<int> tcsetpgrp(int fd, pid_t pgrp);
ErrorOr<void> chmod(StringView pathname, mode_t mode);
ErrorOr<void> lchown(StringView pathname, uid_t uid, gid_t gid);
ErrorOr<void> chown(StringView pathname, uid_t uid, gid_t gid);
+ErrorOr<Optional<struct passwd>> getpwent(Span<char> buffer);
ErrorOr<Optional<struct passwd>> getpwnam(StringView name);
ErrorOr<Optional<struct group>> getgrnam(StringView name);
ErrorOr<Optional<struct passwd>> getpwuid(uid_t);