glib-aux: add nm_getpwuid() helper

Calling getpwuid_r() is cumbersome, because it has a separate passwd and
string buffer, and you shall retry, when the buffer is too small.

Extract nm_getpwuid() for that. This one always allocates a suitable
buffer, that the caller can free.

This will allow callers to get the full passwd struct. It will also
allow callers to avoid the additional strdup() of nm_utils_uid_to_name(),
when we don't need a clone of the string.
This commit is contained in:
Thomas Haller 2023-10-23 12:53:31 +02:00
parent b2b2823c53
commit 5a7d1ec208
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
3 changed files with 71 additions and 20 deletions

View file

@ -5771,40 +5771,81 @@ nm_utils_is_specific_hostname(const char *name)
/*****************************************************************************/
/* taken from systemd's uid_to_name(). */
char *
nm_utils_uid_to_name(uid_t uid)
{
gs_free char *buf_heap = NULL;
char buf_stack[4096];
gsize bufsize;
char *buf;
typedef struct {
struct passwd pw;
_nm_alignas(max_align_t) char buf[];
} GetPwuidData;
bufsize = sizeof(buf_stack);
buf = buf_stack;
/**
* nm_getpwuid:
* uid: the user Id to look up
*
* Calls getpwuid_r() to lookup the passwd entry. See the manual.
* Allocates and returns a suitable buffer.
*
* The returned buffer is likely much large than required. You don't want to
* keep this buffer around for longer than necessary.
*
* Returns: (transfer full): the passwd entry, if found or NULL on error
* or if the entry was not found.
*/
struct passwd *
nm_getpwuid(uid_t uid)
{
gs_free GetPwuidData *data = NULL;
gsize bufsize;
const gsize OFFSET = G_STRUCT_OFFSET(GetPwuidData, buf);
long int size_max;
size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
if (size_max > 0 && ((unsigned long int) size_max < G_MAXSIZE - OFFSET))
bufsize = size_max;
else
bufsize = 4096;
for (;;) {
struct passwd pwbuf;
struct passwd *pw = NULL;
int r;
r = getpwuid_r(uid, &pwbuf, buf, bufsize, &pw);
if (r == 0 && pw)
return nm_strdup_not_empty(pw->pw_name);
if (bufsize >= G_MAXSIZE - OFFSET)
return NULL;
nm_clear_g_free(&data);
data = g_malloc(OFFSET + bufsize);
r = getpwuid_r(uid, &data->pw, data->buf, bufsize, &pw);
if (r == 0) {
if (!pw)
return NULL;
nm_assert(pw == (gpointer) data);
return (gpointer) g_steal_pointer(&data);
}
if (r != ERANGE)
return NULL;
if (bufsize > G_MAXSIZE / 2u)
return NULL;
bufsize *= 2u;
g_free(buf_heap);
buf_heap = g_malloc(bufsize);
buf = buf_heap;
}
}
const char *
nm_passwd_name(const struct passwd *pw)
{
/* Normalize "pw->pw_name" and return it. */
return pw ? nm_str_not_empty(pw->pw_name) : NULL;
}
char *
nm_utils_uid_to_name(uid_t uid)
{
gs_free struct passwd *pw = NULL;
pw = nm_getpwuid(uid);
return g_strdup(nm_passwd_name(pw));
}
/* taken from systemd's nss_user_record_by_name() */
gboolean
nm_utils_name_to_uid(const char *name, uid_t *out_uid)

View file

@ -3196,6 +3196,12 @@ gboolean nm_utils_is_localhost(const char *name);
gboolean nm_utils_is_specific_hostname(const char *name);
struct passwd;
struct passwd *nm_getpwuid(uid_t uid);
const char *nm_passwd_name(const struct passwd *pw);
char *nm_utils_uid_to_name(uid_t uid);
gboolean nm_utils_name_to_uid(const char *name, uid_t *out_uid);

View file

@ -2597,8 +2597,9 @@ test_uid_to_name(void)
int i;
for (i = 0; i < 20; i++) {
gs_free char *name = NULL;
uid_t uid;
gs_free char *name = NULL;
gs_free struct passwd *pw = NULL;
uid_t uid;
if (i < 5)
uid = i;
@ -2607,6 +2608,9 @@ test_uid_to_name(void)
name = nm_utils_uid_to_name(uid);
g_assert_cmpstr(name, ==, _getpwuid_name(uid));
pw = nm_getpwuid(uid);
g_assert_cmpstr(name, ==, nm_passwd_name(pw));
}
}