1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 10:27:34 +00:00

LibCore: Define and use a fallible, OS-independent getgrent(_r) wrapper

Rather than maintaining a list of #ifdef guards to check systems that do
not provide the reentrant version of getgrent, 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.
This commit is contained in:
Timothy Flynn 2022-12-13 15:08:58 -05:00 committed by Tim Flynn
parent 1ee808fae6
commit d09266237d
3 changed files with 61 additions and 31 deletions

View file

@ -19,32 +19,27 @@ namespace Core {
ErrorOr<DeprecatedString> Group::generate_group_file() const ErrorOr<DeprecatedString> Group::generate_group_file() const
{ {
StringBuilder builder; StringBuilder builder;
char buffer[1024] = { 0 };
ScopeGuard grent_guard([] { endgrent(); }); ScopeGuard grent_guard([] { endgrent(); });
setgrent(); setgrent();
errno = 0;
#ifndef AK_OS_MACOS while (true) {
struct group group; auto group = TRY(Core::System::getgrent({ buffer, sizeof(buffer) }));
struct group* gr = nullptr; if (!group.has_value())
char buffer[1024] = { 0 }; break;
while (getgrent_r(&group, buffer, sizeof(buffer), &gr) == 0 && gr) {
#else if (group->gr_name == m_name)
for (auto const* gr = getgrent(); gr; gr = getgrent()) {
#endif
if (gr->gr_name == m_name)
builder.appendff("{}:x:{}:{}\n", m_name, m_id, DeprecatedString::join(',', m_members)); builder.appendff("{}:x:{}:{}\n", m_name, m_id, DeprecatedString::join(',', m_members));
else { else {
Vector<DeprecatedString> members; Vector<DeprecatedString> members;
for (size_t i = 0; gr->gr_mem[i]; ++i) for (size_t i = 0; group->gr_mem[i]; ++i)
members.append(gr->gr_mem[i]); members.append(group->gr_mem[i]);
builder.appendff("{}:x:{}:{}\n", gr->gr_name, gr->gr_gid, DeprecatedString::join(',', members)); builder.appendff("{}:x:{}:{}\n", group->gr_name, group->gr_gid, DeprecatedString::join(',', members));
} }
} }
if (errno)
return Error::from_errno(errno);
return builder.to_deprecated_string(); return builder.to_deprecated_string();
} }
@ -123,28 +118,23 @@ ErrorOr<void> Group::add_group(Group& group)
ErrorOr<Vector<Group>> Group::all() ErrorOr<Vector<Group>> Group::all()
{ {
Vector<Group> groups; Vector<Group> groups;
char buffer[1024] = { 0 };
ScopeGuard grent_guard([] { endgrent(); }); ScopeGuard grent_guard([] { endgrent(); });
setgrent(); setgrent();
errno = 0;
#ifndef AK_OS_MACOS while (true) {
struct group group; auto group = TRY(Core::System::getgrent({ buffer, sizeof(buffer) }));
struct group* gr = nullptr; if (!group.has_value())
char buffer[1024] = { 0 }; break;
while (getgrent_r(&group, buffer, sizeof(buffer), &gr) == 0 && gr) {
#else
for (auto const* gr = getgrent(); gr; gr = getgrent()) {
#endif
Vector<DeprecatedString> members; Vector<DeprecatedString> members;
for (size_t i = 0; gr->gr_mem[i]; ++i) for (size_t i = 0; group->gr_mem[i]; ++i)
members.append(gr->gr_mem[i]); members.append(group->gr_mem[i]);
groups.append({ gr->gr_name, gr->gr_gid, members }); groups.append({ group->gr_name, group->gr_gid, members });
} }
if (errno)
return Error::from_errno(errno);
return groups; return groups;
} }

View file

@ -86,6 +86,40 @@ static ErrorOr<Optional<struct passwd>> getpwent_impl(Span<char> buffer)
return Optional<struct passwd> {}; return Optional<struct passwd> {};
} }
// clang-format off
template<typename T>
concept SupportsReentrantGetgrent = requires(T group, T* ptr)
{
getgrent_r(&group, 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 getgrent_r when it doesn't actually exist.
static ErrorOr<Optional<struct group>> getgrent_impl(Span<char> buffer)
{
if constexpr (SupportsReentrantGetgrent<struct group>) {
struct group group;
struct group* ptr = nullptr;
extern int getgrent_r(struct group*, char*, size_t, struct group**);
auto result = getgrent_r(&group, buffer.data(), buffer.size(), &ptr);
if (result == 0 && ptr)
return group;
if (result != 0 && result != ENOENT)
return Error::from_errno(result);
} else {
errno = 0;
if (auto const* group = ::getgrent())
return *group;
if (errno)
return Error::from_errno(errno);
}
return Optional<struct group> {};
}
namespace Core::System { namespace Core::System {
#ifndef HOST_NAME_MAX #ifndef HOST_NAME_MAX
@ -676,6 +710,11 @@ ErrorOr<Optional<struct passwd>> getpwuid(uid_t uid)
return Optional<struct passwd> {}; return Optional<struct passwd> {};
} }
ErrorOr<Optional<struct group>> getgrent(Span<char> buffer)
{
return getgrent_impl(buffer);
}
ErrorOr<Optional<struct group>> getgrgid(gid_t gid) ErrorOr<Optional<struct group>> getgrgid(gid_t gid)
{ {
errno = 0; errno = 0;

View file

@ -126,6 +126,7 @@ ErrorOr<Optional<struct passwd>> getpwent(Span<char> buffer);
ErrorOr<Optional<struct passwd>> getpwnam(StringView name); ErrorOr<Optional<struct passwd>> getpwnam(StringView name);
ErrorOr<Optional<struct group>> getgrnam(StringView name); ErrorOr<Optional<struct group>> getgrnam(StringView name);
ErrorOr<Optional<struct passwd>> getpwuid(uid_t); ErrorOr<Optional<struct passwd>> getpwuid(uid_t);
ErrorOr<Optional<struct group>> getgrent(Span<char> buffer);
ErrorOr<Optional<struct group>> getgrgid(gid_t); ErrorOr<Optional<struct group>> getgrgid(gid_t);
ErrorOr<void> clock_settime(clockid_t clock_id, struct timespec* ts); ErrorOr<void> clock_settime(clockid_t clock_id, struct timespec* ts);
ErrorOr<pid_t> posix_spawn(StringView path, posix_spawn_file_actions_t const* file_actions, posix_spawnattr_t const* attr, char* const arguments[], char* const envp[]); ErrorOr<pid_t> posix_spawn(StringView path, posix_spawn_file_actions_t const* file_actions, posix_spawnattr_t const* attr, char* const arguments[], char* const envp[]);