diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt index 3f6f9c50..85d4103d 100644 --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -8,6 +8,7 @@ set(LIBC_SOURCES dirent.cpp fcntl.cpp printf_impl.cpp + pwd.cpp stdio.cpp stdlib.cpp string.cpp diff --git a/libc/pwd.cpp b/libc/pwd.cpp new file mode 100644 index 00000000..79b9f183 --- /dev/null +++ b/libc/pwd.cpp @@ -0,0 +1,113 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static FILE* s_pwent_fp = nullptr; +static passwd s_pwent_struct; + +#define TRY_LIBC(expr, ret) ({ auto&& e = expr; if (e.is_error()) { errno = e.error().get_error_code(); return ret; } e.release_value(); }) + +static bool open_pwent() +{ + if (s_pwent_fp) + return true; + s_pwent_fp = fopen("/etc/passwd", "r"); + return s_pwent_fp; +} + +static void clear_pwent(passwd& passwd) +{ + if (passwd.pw_name) + free(passwd.pw_name); + passwd.pw_name = nullptr; + if (passwd.pw_dir) + free(passwd.pw_dir); + passwd.pw_dir = nullptr; + if (passwd.pw_shell) + free(passwd.pw_shell); + passwd.pw_shell = nullptr; +} + +void endpwent(void) +{ + if (!s_pwent_fp) + return; + fclose(s_pwent_fp); + s_pwent_fp = nullptr; + clear_pwent(s_pwent_struct); +} + +struct passwd* getpwent(void) +{ + if (!s_pwent_fp) + if (!open_pwent()) + return nullptr; + clear_pwent(s_pwent_struct); + + BAN::String line; + while (true) + { + char buffer[128]; + if (!fgets(buffer, sizeof(buffer), s_pwent_fp)) + return nullptr; + TRY_LIBC(line.append(buffer), nullptr); + + if (line.back() == '\n') + { + line.pop_back(); + break; + } + } + + auto parts = TRY_LIBC(line.sv().split(':', true), nullptr); + ASSERT(parts.size() == 7); + + ASSERT(1 <= parts[2].size() && parts[2].size() <= 9); + for (char c : parts[2]) + ASSERT(isdigit(c)); + ASSERT(1 <= parts[3].size() && parts[3].size() <= 9); + for (char c : parts[3]) + ASSERT(isdigit(c)); + + s_pwent_struct.pw_uid = atoi(parts[2].data()); + s_pwent_struct.pw_gid = atoi(parts[3].data()); + + s_pwent_struct.pw_name = (char*)malloc(parts[0].size() + 1); + if (!s_pwent_struct.pw_name) + return nullptr; + memcpy(s_pwent_struct.pw_name, parts[0].data(), parts[0].size()); + s_pwent_struct.pw_name[parts[0].size()] = '\0'; + + s_pwent_struct.pw_dir = (char*)malloc(parts[5].size() + 1); + if (!s_pwent_struct.pw_dir) + return nullptr; + memcpy(s_pwent_struct.pw_dir, parts[5].data(), parts[5].size()); + s_pwent_struct.pw_dir[parts[5].size()] = '\0'; + + s_pwent_struct.pw_shell = (char*)malloc(parts[6].size() + 1); + if (!s_pwent_struct.pw_shell) + return nullptr; + memcpy(s_pwent_struct.pw_shell, parts[6].data(), parts[6].size()); + s_pwent_struct.pw_shell[parts[6].size()] = '\0'; + + return &s_pwent_struct; +} + +struct passwd* getpwnam(const char* name); +int getpwnam_r(const char* name, struct passwd* pwd, char* buffer, size_t bufsize, struct passwd** result); +struct passwd* getpwuid(uid_t uid); +int getpwuid_r(uid_t uid, struct passwd* pwd, char* buffer, size_t bufsize, struct passwd** result); + +void setpwent(void) +{ + if (!s_pwent_fp) + return; + fseek(s_pwent_fp, 0, SEEK_SET); +} \ No newline at end of file