LibC: Implement getpw{nam,uid}_r
This commit is contained in:
parent
654e8bb7f6
commit
fda0ced72e
|
@ -1,118 +1,142 @@
|
||||||
#include <BAN/Assert.h>
|
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
static FILE* s_pwent_fp = nullptr;
|
static FILE* s_pwent_fp = nullptr;
|
||||||
static passwd s_pwent_struct;
|
static passwd s_pwent_struct;
|
||||||
|
|
||||||
static bool open_pwent()
|
static char* s_pwent_buffer = nullptr;
|
||||||
{
|
static size_t s_pwent_buffer_size = 0;
|
||||||
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)
|
void endpwent(void)
|
||||||
{
|
{
|
||||||
if (!s_pwent_fp)
|
if (s_pwent_fp)
|
||||||
return;
|
|
||||||
fclose(s_pwent_fp);
|
fclose(s_pwent_fp);
|
||||||
s_pwent_fp = nullptr;
|
s_pwent_fp = nullptr;
|
||||||
clear_pwent(s_pwent_struct);
|
|
||||||
|
if (s_pwent_buffer)
|
||||||
|
free(s_pwent_buffer);
|
||||||
|
s_pwent_buffer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setpwent(void)
|
||||||
|
{
|
||||||
|
if (!s_pwent_fp)
|
||||||
|
return;
|
||||||
|
fseek(s_pwent_fp, 0, SEEK_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getpwent_impl(FILE* fp, struct passwd* pwd, char* buffer, size_t bufsize, struct passwd** result)
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (fgets(buffer, bufsize, fp) == nullptr)
|
||||||
|
{
|
||||||
|
if (ferror(fp))
|
||||||
|
return errno;
|
||||||
|
*result = nullptr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t line_len = strlen(buffer);
|
||||||
|
if (line_len == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (buffer[line_len - 1] == '\n')
|
||||||
|
buffer[line_len - 1] = '\0';
|
||||||
|
else if (!feof(fp))
|
||||||
|
return (errno = ERANGE);
|
||||||
|
|
||||||
|
#define GET_STRING() ({ \
|
||||||
|
ptr = strchr(ptr, ':'); \
|
||||||
|
if (ptr == nullptr) \
|
||||||
|
continue; \
|
||||||
|
*ptr++ = '\0'; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define GET_INT() ({ \
|
||||||
|
if (!isdigit(*ptr)) \
|
||||||
|
continue; \
|
||||||
|
long val = 0; \
|
||||||
|
while (isdigit(*ptr)) \
|
||||||
|
val = (val * 10) + (*ptr++ - '0'); \
|
||||||
|
if (*ptr != ':') \
|
||||||
|
continue; \
|
||||||
|
*ptr++ = '\0'; \
|
||||||
|
val; \
|
||||||
|
})
|
||||||
|
|
||||||
|
char* ptr = buffer;
|
||||||
|
|
||||||
|
pwd->pw_name = ptr;
|
||||||
|
GET_STRING();
|
||||||
|
|
||||||
|
pwd->pw_passwd = ptr;
|
||||||
|
GET_STRING();
|
||||||
|
|
||||||
|
pwd->pw_uid = GET_INT();
|
||||||
|
|
||||||
|
pwd->pw_gid = GET_INT();
|
||||||
|
|
||||||
|
pwd->pw_gecos = ptr;
|
||||||
|
GET_STRING();
|
||||||
|
|
||||||
|
pwd->pw_dir = ptr;
|
||||||
|
GET_STRING();
|
||||||
|
|
||||||
|
pwd->pw_shell = ptr;
|
||||||
|
|
||||||
|
*result = pwd;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct passwd* getpwent(void)
|
struct passwd* getpwent(void)
|
||||||
{
|
{
|
||||||
if (!s_pwent_fp)
|
if (s_pwent_fp == nullptr)
|
||||||
if (!open_pwent())
|
|
||||||
return nullptr;
|
|
||||||
clear_pwent(s_pwent_struct);
|
|
||||||
|
|
||||||
static char buffer[4096];
|
|
||||||
if (!fgets(buffer, sizeof(buffer), s_pwent_fp))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
size_t buffer_len = strlen(buffer);
|
|
||||||
|
|
||||||
ASSERT(buffer[buffer_len - 1] == '\n');
|
|
||||||
buffer[buffer_len - 1] = '\0';
|
|
||||||
buffer_len--;
|
|
||||||
|
|
||||||
const char* ptr = buffer;
|
|
||||||
for (int i = 0; i < 7; i++)
|
|
||||||
{
|
{
|
||||||
char* end = strchr(ptr, ':');
|
s_pwent_fp = fopen("/etc/passwd", "r");
|
||||||
ASSERT((i < 6) ? end != nullptr : end == nullptr);
|
if (s_pwent_fp == nullptr)
|
||||||
if (!end)
|
|
||||||
end = buffer + buffer_len;
|
|
||||||
*end = '\0';
|
|
||||||
|
|
||||||
const size_t field_len = end - ptr;
|
|
||||||
|
|
||||||
switch (i)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
s_pwent_struct.pw_name = strndup(ptr, field_len + 1);
|
|
||||||
if (!s_pwent_struct.pw_name)
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
s_pwent_struct.pw_passwd = strndup(ptr, field_len + 1);
|
|
||||||
if (!s_pwent_struct.pw_passwd)
|
|
||||||
return nullptr;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
ASSERT(1 <= field_len && field_len <= 9);
|
|
||||||
for (size_t j = 0; j < field_len; j++)
|
|
||||||
ASSERT(isdigit(ptr[j]));
|
|
||||||
s_pwent_struct.pw_uid = atoi(ptr);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
ASSERT(1 <= field_len && field_len <= 9);
|
|
||||||
for (size_t j = 0; j < field_len; j++)
|
|
||||||
ASSERT(isdigit(ptr[j]));
|
|
||||||
s_pwent_struct.pw_gid = atoi(ptr);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
s_pwent_struct.pw_gecos = strndup(ptr, field_len + 1);
|
|
||||||
if (!s_pwent_struct.pw_gecos)
|
|
||||||
return nullptr;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
s_pwent_struct.pw_dir = strndup(ptr, field_len + 1);
|
|
||||||
if (!s_pwent_struct.pw_dir)
|
|
||||||
return nullptr;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
s_pwent_struct.pw_shell = strndup(ptr, field_len + 1);
|
|
||||||
if (!s_pwent_struct.pw_shell)
|
|
||||||
return nullptr;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr = end + 1;
|
if (s_pwent_buffer == nullptr)
|
||||||
|
{
|
||||||
|
long size = sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||||
|
if (size == -1)
|
||||||
|
size = 512;
|
||||||
|
|
||||||
|
s_pwent_buffer = static_cast<char*>(malloc(size));
|
||||||
|
if (s_pwent_buffer == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
s_pwent_buffer_size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return &s_pwent_struct;
|
const off_t old_offset = ftello(s_pwent_fp);
|
||||||
|
|
||||||
|
passwd* result;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
const int error = getpwent_impl(s_pwent_fp, &s_pwent_struct, s_pwent_buffer, s_pwent_buffer_size, &result);
|
||||||
|
if (error == 0)
|
||||||
|
break;
|
||||||
|
fseeko(s_pwent_fp, old_offset, SEEK_SET);
|
||||||
|
if (error != ERANGE)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const size_t new_size = s_pwent_buffer_size * 2;
|
||||||
|
char* new_buffer = static_cast<char*>(realloc(s_pwent_buffer, new_size));
|
||||||
|
if (new_buffer == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
s_pwent_buffer = new_buffer;
|
||||||
|
s_pwent_buffer_size = new_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct passwd* getpwnam(const char* name)
|
struct passwd* getpwnam(const char* name)
|
||||||
|
@ -135,9 +159,44 @@ struct passwd* getpwuid(uid_t uid)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setpwent(void)
|
int getpwuid_r(uid_t uid, struct passwd* pwd, char* buffer, size_t bufsize, struct passwd** result)
|
||||||
{
|
{
|
||||||
if (!s_pwent_fp)
|
FILE* fp = fopen("/etc/passwd", "r");
|
||||||
return;
|
if (fp == nullptr)
|
||||||
fseek(s_pwent_fp, 0, SEEK_SET);
|
return errno;
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if ((ret = getpwent_impl(fp, pwd, buffer, bufsize, result)))
|
||||||
|
break;
|
||||||
|
if (*result == nullptr)
|
||||||
|
break;
|
||||||
|
if (pwd->pw_uid == uid)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getpwnam_r(const char* name, struct passwd* pwd, char* buffer, size_t bufsize, struct passwd** result)
|
||||||
|
{
|
||||||
|
FILE* fp = fopen("/etc/passwd", "r");
|
||||||
|
if (fp == nullptr)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if ((ret = getpwent_impl(fp, pwd, buffer, bufsize, result)))
|
||||||
|
break;
|
||||||
|
if (*result == nullptr)
|
||||||
|
break;
|
||||||
|
if (strcmp(pwd->pw_name, name) == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue