#include #include #include #include #include static struct passwd pwd; static FILE* f { nullptr }; static char buf[4096]; // Variant of strtok that returns empty tokens when there are several delimiters, instead of skipping all at once. // Example: // strtok("hello:world::!:", ":") -> "hello", "world", "!" // _strtok_once("hello:world::!:", ":") -> "hello", "world", "", "!" char* _strtok_once(char* str, const char* delim) { static char* s = nullptr; if (str) s = str; if (!s) return nullptr; if (*s) { if (*s == 0) return nullptr; size_t use = strcspn(s, delim); char* result = s; if (s[use] != 0) { s[use] = 0; s += (use + 1); } else { s = nullptr; } return result; } return nullptr; } extern "C" { struct passwd* getpwent() { if (!f) { f = fopen("/etc/passwd", "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* uid = _strtok_once(nullptr, ":\n"); if (!uid) continue; char* gid = _strtok_once(nullptr, ":\n"); if (!gid) continue; char* gecos = _strtok_once(nullptr, ":\n"); if (!gecos) continue; char* dir = _strtok_once(nullptr, ":\n"); if (!dir) continue; char* shell = _strtok_once(nullptr, ":\n"); if (!shell) continue; pwd.pw_name = name; pwd.pw_passwd = passwd; pwd.pw_uid = (uid_t)strtoul(uid, NULL, 10); pwd.pw_gid = (gid_t)strtoul(gid, NULL, 10); pwd.pw_gecos = gecos; pwd.pw_dir = dir; pwd.pw_shell = shell; return &pwd; } } struct passwd* getpwnam(const char* name) { setpwent(); struct passwd* entry; while ((entry = getpwent())) { if (!strcmp(entry->pw_name, name)) { return entry; } } return entry; } struct passwd* getpwuid(uid_t uid) { setpwent(); struct passwd* entry; while ((entry = getpwent())) { if (entry->pw_uid == uid) { return entry; } } return entry; } void setpwent() { if (f) rewind(f); } void endpwent() { if (f) { fclose(f); f = nullptr; } } }