#include #include #include #include #include #include #include static struct group grp; static FILE* f { nullptr }; static char buf[4096]; static Vector g_members; extern char* _strtok_once(char* str, const char* delim); extern "C" { static Result try_getgrent() { if (!f) { f = fopen("/etc/group", "r"); if (!f) return nullptr; fcntl(fileno(f), F_SETFD, FD_CLOEXEC); } while (true) { char* rc = fgets(buf, sizeof(buf), f); if (!rc) return nullptr; char* name = _strtok_once(rc, ":\n"); if (!name) continue; char* passwd = _strtok_once(nullptr, ":\n"); if (!passwd) continue; char* gid = _strtok_once(nullptr, ":\n"); if (!gid) continue; char* members = _strtok_once(nullptr, ":\n"); if (!members) continue; g_members.clear(); char* member = strtok(members, ","); TRY(g_members.try_append(member)); if (member) { while (true) { member = strtok(nullptr, ","); TRY(g_members.try_append(member)); if (!member) break; } } grp.gr_name = name; grp.gr_passwd = passwd; grp.gr_gid = (gid_t)strtoul(gid, NULL, 10); grp.gr_mem = g_members.data(); return &grp; } } struct group* getgrent() { return TRY_OR_SET_ERRNO(try_getgrent(), group*, nullptr); } struct group* getgrnam(const char* name) { setgrent(); struct group* entry; while ((entry = getgrent())) { if (!strcmp(entry->gr_name, name)) { return entry; } } return entry; } struct group* getgrgid(gid_t gid) { setgrent(); struct group* entry; while ((entry = getgrent())) { if (entry->gr_gid == gid) { return entry; } } return entry; } void setgrent() { if (f) rewind(f); } void endgrent() { if (f) { fclose(f); f = nullptr; } } }