getpwnam(3)
๐ getpwnam(3) โ the "who even IS this user" manual ๐
okay bestie, sit down, hydrate, because today we're reading the Library Functions Manual page for getpwnam and its little friend group. this is the lore on how you look somebody up in the password database and get their whole vibe back as a struct. it's giving โจidentity crisisโจ but make it POSIX.
๐ธ NAME ๐ธ
getpwnam, getpwnam_r, getpwuid, getpwuid_r โ get a password file entry. that's it. that's the whole job. fetch one (1) user's record and slay. ๐
๐ LIBRARY ๐
Standard C library (libc, link with -lc). yes you have to link it, no it's not automatic, don't @ me.
โจ SYNOPSIS โจ
include the headers first, bestie, or the compiler will simply perish:
#include <sys/types.h>
#include <pwd.h>
struct passwd *getpwnam(const char *name);
struct passwd *getpwuid(uid_t uid);
int getpwnam_r(size_t size;
const char *restrict name,
struct passwd *restrict pwd,
char buf[restrict size], size_t size,
struct passwd **restrict result);
int getpwuid_r(size_t size;
uid_t uid,
struct passwd *restrict pwd,
char buf[restrict size], size_t size,
struct passwd **restrict result);
look at the _r versions being extra with all their parameters. high maintenance? maybe. thread-safe? YES. worth it. ๐ซถ
๐งช Feature Test Macro Requirements for glibc ๐งช
(peep feature_test_macros(7) if you wanna know what these even are)
getpwnam_r(), getpwuid_r():
_POSIX_C_SOURCE
|| /* glibc <= 2.19: */ _BSD_SOURCE || _SVID_SOURCE
๐ DESCRIPTION ๐
getpwnam() hands you back a pointer to a struct stuffed with the broken-out fields of the record in the password database (think: the local password file /etc/passwd, NIS, LDAP โ the whole lineup) that matches the username name. so you give it a name, it gives you the whole dossier. ๐
getpwuid() does the exact same thing but it matches on the user ID uid instead. same energy, different key.
the passwd structure lives in <pwd.h> and it looks like this, no cap:
struct passwd {
char *pw_name; /* username */
char *pw_passwd; /* user password */
uid_t pw_uid; /* user ID */
gid_t pw_gid; /* group ID */
char *pw_gecos; /* user information */
char *pw_dir; /* home directory */
char *pw_shell; /* shell program */
};
want the tea on what each field actually means? run, don't walk, to passwd(5). ๐
so now, getpwnam_r() and getpwuid_r() โ the reentrant girlies โ get you the same info as getpwnam() and getpwuid(), but they store the retrieved passwd structure in the space that pwd points to. all those string fields hanging off the struct members get stashed inside the buffer buf, which is size bytes big. and then a pointer to the result (if it all worked out ๐) or NULL (if nothing was found or something went wrong ๐ญ) gets stored in *result.
and this little call right here:
sysconf(_SC_GETPW_R_SIZE_MAX)
gives you either -1, without touching errno, or an initial suggested size for buf. (-1 just means "idk girl, pick something".) and heads up: if that size turns out to be too small, the call fails with ERANGE, and then you just retry with a bigger buffer. glow up your malloc and try again. ๐
๐ RETURN VALUE ๐
getpwnam() and getpwuid() return a pointer to a passwd structure, or NULL if the matching entry isn't found or an error happens. if an error happens, errno gets set to tell you what went wrong. and if you're the type who wants to check errno after the call โ valid โ you gotta set it to zero before the call. otherwise you're just reading old gossip from a previous function. ๐
โ ๏ธ the return value may point to a static area, and it can get overwritten by later calls togetpwent(3),getpwnam(), orgetpwuid(). and do not pass that returned pointer tofree(3). it is NOT yours. it was never yours. don't touch it. ๐ซ
on success, getpwnam_r() and getpwuid_r() return zero, and set *result to pwd. if no matching password record was found, they return 0 and store NULL in *result (yes, really โ not an error, just a "she's not here"). and in case of error, an error number gets returned and NULL gets stored in *result.
๐ ERRORS ๐
0orENOENTorESRCHorEBADForEPERMor ... โ the givennameoruidwas simply not found. it's giving "who? never heard of her". ๐EINTRโ a signal was caught. seesignal(7).EIOโ I/O error. the disk said no. ๐ญEMFILEโ the per-process limit on the number of open file descriptors has been reached. too many files open, bestie, close some tabs (literally).ENFILEโ the system-wide limit on the total number of open files has been reached. this one's not even your fault. the whole machine is struggling.ENOMEMโ insufficient memory to allocate thepasswdstructure. ๐ญERANGEโ insufficient buffer space supplied. yourbufwas too small, grow up (the buffer, I mean).
๐ FILES ๐
/etc/passwdโ the local password database file. the OG. the blueprint. the file you should NOT be writing to by hand, girl. ๐
๐ ATTRIBUTES ๐
for what all these terms even mean, check attributes(7). here's the vibe check on thread safety:
getpwnam()โ Thread safety:MT-Unsafe race:pwnam localegetpwuid()โ Thread safety:MT-Unsafe race:pwuid localegetpwnam_r(),getpwuid_r()โ Thread safety:MT-Safe localeโจ
so yeah: the plain versions are racy (๐), the _r versions are safe (๐). choose wisely. this is the plot twist of the whole page.
๐ฆ VERSIONS ๐ฆ
the pw_gecos field is not specified in POSIX, but it is present on most implementations. she's not official, but she's everywhere. an icon of the people. ๐
โญ STANDARDS โญ
POSIX.1-2008. certified, standardized, notarized. โจ
๐ HISTORY ๐
POSIX.1-2001, SVr4, 4.3BSD. ancient lineage. these functions have been eating and leaving no crumbs since before your parents had email. ๐
๐ NOTES ๐
that formulation up there under "RETURN VALUE" comes straight from POSIX.1-2001. and here's the thing โ it does not call "not found" an error, so it also doesn't specify what errno might be in that situation. which, lowkey, makes it impossible to actually recognize errors. one might argue that per POSIX, errno should just be left unchanged if an entry isn't found. but the experiments?? the experiments on various UNIX-like systems show that lots of different values show up in this situation: 0, ENOENT, EBADF, ESRCH, EWOULDBLOCK, EPERM, and probably others. it's chaotic. it's ungovernable. it's Unix. ๐
now, the pw_dir field holds the name of the initial working directory of the user. login programs use this field's value to initialize the HOME environment variable for the login shell. but โ and this is the important part โ an application that wants to figure out its own user's home directory should inspect the value of HOME (rather than the value of getpwuid(getuid())->pw_dir), because that lets the user change their idea of "the home directory" mid-login-session. main character energy: let them define their own home. ๐
but if you need to determine the (initial) home directory of another user, then you have no choice โ you must use getpwnam("username")->pw_dir or something similar. no shortcuts, bestie.
๐ง EXAMPLES ๐ง
this program right here demonstrates getpwnam_r() in action: it finds the full username and user ID for whatever username you hand it as a command-line argument. copy it, compile it, run it, feel powerful. ๐
#include <errno.h>
#include <pwd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
struct passwd pwd;
struct passwd *result;
char *buf;
long bufsize;
int s;
if (argc != 2) {
fprintf(stderr, "Usage: %s username\n", argv[0]);
exit(EXIT_FAILURE);
}
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
if (bufsize == -1) /* Value was indeterminate */
bufsize = 16384; /* Should be more than enough */
buf = malloc(bufsize);
if (buf == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
s = getpwnam_r(argv[1], &pwd, buf, bufsize, &result);
if (result == NULL) {
if (s == 0)
printf("Not found\n");
else {
errno = s;
perror("getpwnam_r");
}
exit(EXIT_FAILURE);
}
printf("Name: %s; UID: %jd\n", pwd.pw_gecos,
(intmax_t) pwd.pw_uid);
exit(EXIT_SUCCESS);
}
๐ SEE ALSO ๐
endpwent(3), fgetpwent(3), getgrnam(3), getpw(3), getpwent(3), getspnam(3), putpwent(3), setpwent(3), passwd(5)
go read all of them. yes all of them. i'll wait. ๐ซถ
and that's the page!! ๐โจ shoutout to the Linux man-pages 6.20, dated 2026-02-08, for serving this absolute classic. go look up your users, stay thread-safe, and never free() a static pointer. mwah. ๐
๐๐ฆ
๐ฆ where this page comes from
- arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
- nixpkgs unstable: man-pages 6.20
- ๐ the page's license: Linux-man-pages-copyleft
- Copyright 1993, David Metcalfe <david@prism.demon.co.uk>
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms