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 to getpwent(3), getpwnam(), or getpwuid(). and do not pass that returned pointer to free(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 ๐Ÿ’€

  • 0 or ENOENT or ESRCH or EBADF or EPERM or ... โ€” the given name or uid was simply not found. it's giving "who? never heard of her". ๐Ÿ’€
  • EINTR โ€” a signal was caught. see signal(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 the passwd structure. ๐Ÿ˜ญ
  • ERANGE โ€” insufficient buffer space supplied. your buf was 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 locale
  • getpwuid() โ€” Thread safety: MT-Unsafe race:pwuid locale
  • getpwnam_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
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms