getgrent_r(3)

✨ getgrent_r(3) β€” the reentrant group-file getters, bestie edition ✨

okay so real talk: this is getgrent_r(3) from the Library Functions Manual, and if you've ever wanted to read group entries without the whole "everyone shares one static buffer and chaos ensues" thing, you are SO in the right place. πŸ’–


🌸 NAME 🌸

getgrent_r, fgetgrent_r β€” get group file entry, but reentrantly, bestie πŸ’…


πŸŽ€ LIBRARY πŸŽ€

Standard C library (libc, -lc). the classic. the icon. πŸ‘‘


✨ SYNOPSIS ✨

#include <grp.h>

int getgrent_r(size_t size;
               struct group *restrict gbuf,
               char buf[restrict size], size_t size,
               struct group **restrict gbufp);
int fgetgrent_r(size_t size;
               FILE *restrict stream, struct group *restrict gbuf,
               char buf[restrict size], size_t size,
               struct group **restrict gbufp);

🧚 Feature Test Macro Requirements for glibc 🧚

(peep feature_test_macros(7) for the lore, iykyk)

  • getgrent_r():
    • _GNU_SOURCE
  • fgetgrent_r():
    • Since glibc 2.19:
      • _DEFAULT_SOURCE
    • glibc 2.19 and earlier:
      • _SVID_SOURCE

πŸ’œ DESCRIPTION πŸ’œ

So. getgrent_r() and fgetgrent_r() are the reentrant versions of getgrent(3) and fgetgrent(3) β€” same energy, but you bring the storage. 🫢

  • getgrent_r() reads the next group entry from the stream that got initialized by setgrent(3).
  • fgetgrent_r() reads the next group entry from stream.

The group structure lives in <grp.h> and it looks like this, no cap:

struct group {
    char   *gr_name;        /* group name */
    char   *gr_passwd;      /* group password */
    gid_t   gr_gid;         /* group ID */
    char  **gr_mem;         /* NULL-terminated array of pointers
                               to names of group members */
};

Wanna know more about those fields? group(5). go, read, slay. πŸ“–

The nonreentrant functions hand you back a pointer to static storage β€” and that static storage is holding even more pointers to the group name, password, and members. 🫠 The reentrant girlies described here though? They put ALL of that in buffers you provide. πŸ’…

First up there's the buffer gbuf, which can hold a struct group. Then there's the buffer buf of size size that can hold the additional strings. The result β€” the struct group that got read from the stream β€” gets stored in your provided buffer *gbuf, and a pointer to that struct group is returned in *gbufp. ✨


πŸ”₯ RETURN VALUE πŸ”₯

  • On success: these functions return 0, and *gbufp is a pointer to the struct group. ate. πŸ’–
  • On error: these functions return an error value, and *gbufp is NULL. 😭

πŸ’€ ERRORS πŸ’€

  • ENOENT β€” No more entries. that's it, that's the whole stream. bye πŸ‘‹
  • ERANGE β€” Insufficient buffer space supplied. Try again with a larger buffer. lowkey the buffer said "not today" πŸ™ˆ

πŸ‘‘ ATTRIBUTES πŸ‘‘

For an explanation of the terms used in this section, see attributes(7).

  • getgrent_r() β€” Thread safety: MT-Unsafe race:grent locale
  • fgetgrent_r() β€” Thread safety: MT-Safe
In the above table, grent in race:grent signifies that if any of the functions setgrent(3), getgrent(3), endgrent(3), or getgrent_r() are used in parallel in different threads of a program, then data races could occur. πŸ’€ pls be careful bestie.

🌈 VERSIONS 🌈

Other systems use the prototype

struct group *getgrent_r(struct group *grp, char buf[.size],
                         int size);

or, better,

int getgrent_r(struct group *grp, char buf[.size], int size,
               FILE **gr_fp);

not us though. we're built different. ⭐


πŸ“ STANDARDS πŸ“

GNU. periodt. πŸ’…


πŸ¦‹ HISTORY πŸ¦‹

These functions are done in a style resembling the POSIX version of functions like getpwnam_r(3). so if you know her, you know them. main character lineage fr fr. ✨


🌸 NOTES 🌸

getgrent_r() is not really reentrant since it shares the reading position in the stream with all other threads. the name is giving reentrant but the stream position is communal property, iykyk 😭

πŸ’– EXAMPLES πŸ’–

here's the whole thing, copy it, run it, obsess:

#define _GNU_SOURCE
#include <grp.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#define BUFLEN 4096

int
main(void)
{
    struct group grp;
    struct group *grpp;
    char buf[BUFLEN];
    int i;

    setgrent();
    for (;;) {
        i = getgrent_r(&grp, buf, sizeof(buf), &grpp);
        if (i)
            break;
        printf("%s (%jd):", grpp->gr_name, (intmax_t) grpp->gr_gid);
        for (size_t j = 0; ; j++) {
            if (grpp->gr_mem[j] == NULL)
                break;
            printf(" %s", grpp->gr_mem[j]);
        }
        printf("\n");
    }
    endgrent();
    exit(EXIT_SUCCESS);
}

it walks every group, prints the group name and gid, then dumps all the members. iconic behavior. πŸŽ€


⭐ SEE ALSO ⭐

fgetgrent(3), getgrent(3), getgrgid(3), getgrnam(3), putgrent(3), group(5)


okay that's the whole page, bestie, you're officially fluent in reentrant group lookups πŸ’…βœ¨

this was Linux man-pages 6.20, dated 2026-02-14. valentine's day drop, we love a romantic release schedule. 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: GPL-2.0-or-later
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms