#include #include #include #include #include #include #include static FILE* pwd_file = nullptr; static FILE* pwnam_file = nullptr; static FILE* pwuid_file = nullptr; static int initialize_pwd(FILE** stream) { int pwfd = open("/etc/passwd", O_RDONLY | O_CLOEXEC); if (pwfd < 0) return 0; FILE* file = fdopen(pwfd, "r"); if (!file) return 0; *stream = file; return 1; } static int pwd_unbuffered_fgetc(FILE* stream) { char c; fread(&c, 1, 1, stream); if (ferror(stream) || feof(stream)) return EOF; return (int)c; } static char* pwd_unbuffered_fgets(char* buf, size_t size, FILE* stream) { char* s = buf; memset(buf, 0, size); while (size) { int c = pwd_unbuffered_fgetc(stream); if (c == EOF) { if (ferror(stream)) return NULL; if (feof(stream)) { if (s != buf) return s; else return NULL; }; } size--; *buf = (char)c; buf++; *buf = 0; if ((char)c == '\n') return s; } return s; } static char* pwd_strchrtok(char* s, char delim) { static char* saved = nullptr; if (s) saved = s; else s = saved; if (!saved) return nullptr; char* loc = strchr(saved, delim); if (loc) { saved = loc + 1; if (*saved == 0) saved = nullptr; *loc = 0; } else { saved = nullptr; } return s; } static void pwd_strip_newline(char* buf) { size_t len = strlen(buf); if (buf[len - 1] == '\n') buf[len - 1] = 0; } static struct passwd* internal_getpwent(FILE* stream) { #define PWENT_INVALID \ do { \ errno = EINVAL; \ return NULL; \ } while (0) static struct passwd result; static char buf[BUFSIZ]; if (!pwd_unbuffered_fgets(buf, BUFSIZ, stream)) return NULL; pwd_strip_newline(buf); result.pw_name = pwd_strchrtok(buf, ':'); if (!result.pw_name) PWENT_INVALID; result.pw_passwd = pwd_strchrtok(NULL, ':'); if (!result.pw_passwd) PWENT_INVALID; char* uid_string = pwd_strchrtok(NULL, ':'); if (!uid_string) PWENT_INVALID; result.pw_uid = atoi(uid_string); char* gid_string = pwd_strchrtok(NULL, ':'); if (!gid_string) PWENT_INVALID; result.pw_gid = atoi(gid_string); result.pw_gecos = pwd_strchrtok(NULL, ':'); if (!result.pw_gecos) PWENT_INVALID; result.pw_dir = pwd_strchrtok(NULL, ':'); if (!result.pw_dir) PWENT_INVALID; result.pw_shell = pwd_strchrtok(NULL, ':'); if (!result.pw_shell) PWENT_INVALID; return &result; } extern "C" { struct passwd* getpwent() { if (!pwd_file) { if (!initialize_pwd(&pwd_file)) return NULL; } if (feof(pwd_file)) { endpwent(); return NULL; } return internal_getpwent(pwd_file); } struct passwd* getpwnam(const char* name) { if (!pwnam_file) { if (!initialize_pwd(&pwnam_file)) return NULL; } else rewind(pwnam_file); struct passwd* pwd = internal_getpwent(pwnam_file); while (pwd) { if (strcmp(pwd->pw_name, name) == 0) break; if (feof(pwnam_file)) { pwd = nullptr; break; } pwd = internal_getpwent(pwnam_file); } return pwd; // if we matched one, pwd contains a pointer to it. else it is NULL } struct passwd* getpwuid(uid_t uid) { if (!pwuid_file) { if (!initialize_pwd(&pwuid_file)) return NULL; } else rewind(pwuid_file); struct passwd* pwd = internal_getpwent(pwuid_file); while (pwd) { if (pwd->pw_uid == uid) break; if (feof(pwuid_file)) { pwd = nullptr; break; } pwd = internal_getpwent(pwuid_file); } return pwd; // if we matched one, pwd contains a pointer to it. else it is NULL } void setpwent() { if (pwd_file) rewind(pwd_file); } void endpwent() { if (pwd_file) { fclose(pwd_file); pwd_file = nullptr; } if (pwuid_file) { fclose(pwuid_file); pwuid_file = nullptr; } if (pwnam_file) { fclose(pwnam_file); pwnam_file = nullptr; } } }