about summary refs log tree commit diff
path: root/REORG.TODO/manual/users.texi
diff options
context:
space:
mode:
Diffstat (limited to 'REORG.TODO/manual/users.texi')
-rw-r--r--REORG.TODO/manual/users.texi2837
1 files changed, 2837 insertions, 0 deletions
diff --git a/REORG.TODO/manual/users.texi b/REORG.TODO/manual/users.texi
new file mode 100644
index 0000000000..47e28febdc
--- /dev/null
+++ b/REORG.TODO/manual/users.texi
@@ -0,0 +1,2837 @@
+@node Users and Groups, System Management, Name Service Switch, Top
+@c %MENU% How users are identified and classified
+@chapter Users and Groups
+
+Every user who can log in on the system is identified by a unique number
+called the @dfn{user ID}.  Each process has an effective user ID which
+says which user's access permissions it has.
+
+Users are classified into @dfn{groups} for access control purposes.  Each
+process has one or more @dfn{group ID values} which say which groups the
+process can use for access to files.
+
+The effective user and group IDs of a process collectively form its
+@dfn{persona}.  This determines which files the process can access.
+Normally, a process inherits its persona from the parent process, but
+under special circumstances a process can change its persona and thus
+change its access permissions.
+
+Each file in the system also has a user ID and a group ID.  Access
+control works by comparing the user and group IDs of the file with those
+of the running process.
+
+The system keeps a database of all the registered users, and another
+database of all the defined groups.  There are library functions you
+can use to examine these databases.
+
+@menu
+* User and Group IDs::          Each user has a unique numeric ID;
+				 likewise for groups.
+* Process Persona::             The user IDs and group IDs of a process.
+* Why Change Persona::          Why a program might need to change
+				 its user and/or group IDs.
+* How Change Persona::          Changing the user and group IDs.
+* Reading Persona::             How to examine the user and group IDs.
+
+* Setting User ID::             Functions for setting the user ID.
+* Setting Groups::              Functions for setting the group IDs.
+
+* Enable/Disable Setuid::       Turning setuid access on and off.
+* Setuid Program Example::      The pertinent parts of one sample program.
+* Tips for Setuid::             How to avoid granting unlimited access.
+
+* Who Logged In::               Getting the name of the user who logged in,
+				 or of the real user ID of the current process.
+
+* User Accounting Database::    Keeping information about users and various
+                                 actions in databases.
+
+* User Database::               Functions and data structures for
+                        	 accessing the user database.
+* Group Database::              Functions and data structures for
+                        	 accessing the group database.
+* Database Example::            Example program showing the use of database
+				 inquiry functions.
+* Netgroup Database::           Functions for accessing the netgroup database.
+@end menu
+
+@node User and Group IDs
+@section User and Group IDs
+
+@cindex login name
+@cindex user name
+@cindex user ID
+Each user account on a computer system is identified by a @dfn{user
+name} (or @dfn{login name}) and @dfn{user ID}.  Normally, each user name
+has a unique user ID, but it is possible for several login names to have
+the same user ID.  The user names and corresponding user IDs are stored
+in a data base which you can access as described in @ref{User Database}.
+
+@cindex group name
+@cindex group ID
+Users are classified in @dfn{groups}.  Each user name belongs to one
+@dfn{default group} and may also belong to any number of
+@dfn{supplementary groups}.  Users who are members of the same group can
+share resources (such as files) that are not accessible to users who are
+not a member of that group.  Each group has a @dfn{group name} and
+@dfn{group ID}.  @xref{Group Database}, for how to find information
+about a group ID or group name.
+
+@node Process Persona
+@section The Persona of a Process
+@cindex persona
+@cindex effective user ID
+@cindex effective group ID
+@cindex supplementary group IDs
+
+@c When Hurd is more widely used, explain multiple effective user IDs
+@c here. -zw
+At any time, each process has an @dfn{effective user ID}, a @dfn{effective
+group ID}, and a set of @dfn{supplementary group IDs}.  These IDs
+determine the privileges of the process.  They are collectively
+called the @dfn{persona} of the process, because they determine ``who it
+is'' for purposes of access control.
+
+Your login shell starts out with a persona which consists of your user
+ID, your default group ID, and your supplementary group IDs (if you are
+in more than one group).  In normal circumstances, all your other processes
+inherit these values.
+
+@cindex real user ID
+@cindex real group ID
+A process also has a @dfn{real user ID} which identifies the user who
+created the process, and a @dfn{real group ID} which identifies that
+user's default group.  These values do not play a role in access
+control, so we do not consider them part of the persona.  But they are
+also important.
+
+Both the real and effective user ID can be changed during the lifetime
+of a process.  @xref{Why Change Persona}.
+
+For details on how a process's effective user ID and group IDs affect
+its permission to access files, see @ref{Access Permission}.
+
+The effective user ID of a process also controls permissions for sending
+signals using the @code{kill} function.  @xref{Signaling Another
+Process}.
+
+Finally, there are many operations which can only be performed by a
+process whose effective user ID is zero.  A process with this user ID is
+a @dfn{privileged process}.  Commonly the user name @code{root} is
+associated with user ID 0, but there may be other user names with this
+ID.
+@c !!! should mention POSIX capabilities here.
+
+@node Why Change Persona
+@section Why Change the Persona of a Process?
+
+The most obvious situation where it is necessary for a process to change
+its user and/or group IDs is the @code{login} program.  When
+@code{login} starts running, its user ID is @code{root}.  Its job is to
+start a shell whose user and group IDs are those of the user who is
+logging in.  (To accomplish this fully, @code{login} must set the real
+user and group IDs as well as its persona.  But this is a special case.)
+
+The more common case of changing persona is when an ordinary user
+program needs access to a resource that wouldn't ordinarily be
+accessible to the user actually running it.
+
+For example, you may have a file that is controlled by your program but
+that shouldn't be read or modified directly by other users, either
+because it implements some kind of locking protocol, or because you want
+to preserve the integrity or privacy of the information it contains.
+This kind of restricted access can be implemented by having the program
+change its effective user or group ID to match that of the resource.
+
+Thus, imagine a game program that saves scores in a file.  The game
+program itself needs to be able to update this file no matter who is
+running it, but if users can write the file without going through the
+game, they can give themselves any scores they like.  Some people
+consider this undesirable, or even reprehensible.  It can be prevented
+by creating a new user ID and login name (say, @code{games}) to own the
+scores file, and make the file writable only by this user.  Then, when
+the game program wants to update this file, it can change its effective
+user ID to be that for @code{games}.  In effect, the program must
+adopt the persona of @code{games} so it can write to the scores file.
+
+@node How Change Persona
+@section How an Application Can Change Persona
+@cindex @code{setuid} programs
+@cindex saved set-user-ID
+@cindex saved set-group-ID
+@cindex @code{_POSIX_SAVED_IDS}
+
+The ability to change the persona of a process can be a source of
+unintentional privacy violations, or even intentional abuse.  Because of
+the potential for problems, changing persona is restricted to special
+circumstances.
+
+You can't arbitrarily set your user ID or group ID to anything you want;
+only privileged processes can do that.  Instead, the normal way for a
+program to change its persona is that it has been set up in advance to
+change to a particular user or group.  This is the function of the setuid
+and setgid bits of a file's access mode.  @xref{Permission Bits}.
+
+When the setuid bit of an executable file is on, executing that file
+gives the process a third user ID: the @dfn{file user ID}.  This ID is
+set to the owner ID of the file.  The system then changes the effective
+user ID to the file user ID.  The real user ID remains as it was.
+Likewise, if the setgid bit is on, the process is given a @dfn{file
+group ID} equal to the group ID of the file, and its effective group ID
+is changed to the file group ID.
+
+If a process has a file ID (user or group), then it can at any time
+change its effective ID to its real ID and back to its file ID.
+Programs use this feature to relinquish their special privileges except
+when they actually need them.  This makes it less likely that they can
+be tricked into doing something inappropriate with their privileges.
+
+@strong{Portability Note:} Older systems do not have file IDs.
+To determine if a system has this feature, you can test the compiler
+define @code{_POSIX_SAVED_IDS}.  (In the POSIX standard, file IDs are
+known as saved IDs.)
+
+@xref{File Attributes}, for a more general discussion of file modes and
+accessibility.
+
+@node Reading Persona
+@section Reading the Persona of a Process
+
+Here are detailed descriptions of the functions for reading the user and
+group IDs of a process, both real and effective.  To use these
+facilities, you must include the header files @file{sys/types.h} and
+@file{unistd.h}.
+@pindex unistd.h
+@pindex sys/types.h
+
+@comment sys/types.h
+@comment POSIX.1
+@deftp {Data Type} uid_t
+This is an integer data type used to represent user IDs.  In
+@theglibc{}, this is an alias for @code{unsigned int}.
+@end deftp
+
+@comment sys/types.h
+@comment POSIX.1
+@deftp {Data Type} gid_t
+This is an integer data type used to represent group IDs.  In
+@theglibc{}, this is an alias for @code{unsigned int}.
+@end deftp
+
+@comment unistd.h
+@comment POSIX.1
+@deftypefun uid_t getuid (void)
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+@c Atomic syscall, except on hurd, where it takes a lock within a hurd
+@c critical section.
+The @code{getuid} function returns the real user ID of the process.
+@end deftypefun
+
+@comment unistd.h
+@comment POSIX.1
+@deftypefun gid_t getgid (void)
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+The @code{getgid} function returns the real group ID of the process.
+@end deftypefun
+
+@comment unistd.h
+@comment POSIX.1
+@deftypefun uid_t geteuid (void)
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+The @code{geteuid} function returns the effective user ID of the process.
+@end deftypefun
+
+@comment unistd.h
+@comment POSIX.1
+@deftypefun gid_t getegid (void)
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+The @code{getegid} function returns the effective group ID of the process.
+@end deftypefun
+
+@comment unistd.h
+@comment POSIX.1
+@deftypefun int getgroups (int @var{count}, gid_t *@var{groups})
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+The @code{getgroups} function is used to inquire about the supplementary
+group IDs of the process.  Up to @var{count} of these group IDs are
+stored in the array @var{groups}; the return value from the function is
+the number of group IDs actually stored.  If @var{count} is smaller than
+the total number of supplementary group IDs, then @code{getgroups}
+returns a value of @code{-1} and @code{errno} is set to @code{EINVAL}.
+
+If @var{count} is zero, then @code{getgroups} just returns the total
+number of supplementary group IDs.  On systems that do not support
+supplementary groups, this will always be zero.
+
+Here's how to use @code{getgroups} to read all the supplementary group
+IDs:
+
+@smallexample
+@group
+gid_t *
+read_all_groups (void)
+@{
+  int ngroups = getgroups (0, NULL);
+  gid_t *groups
+    = (gid_t *) xmalloc (ngroups * sizeof (gid_t));
+  int val = getgroups (ngroups, groups);
+  if (val < 0)
+    @{
+      free (groups);
+      return NULL;
+    @}
+  return groups;
+@}
+@end group
+@end smallexample
+@end deftypefun
+
+@node Setting User ID
+@section Setting the User ID
+
+This section describes the functions for altering the user ID (real
+and/or effective) of a process.  To use these facilities, you must
+include the header files @file{sys/types.h} and @file{unistd.h}.
+@pindex unistd.h
+@pindex sys/types.h
+
+@comment unistd.h
+@comment POSIX.1
+@deftypefun int seteuid (uid_t @var{neweuid})
+@safety{@prelim{}@mtsafe{}@asunsafe{@asulock{}}@acunsafe{@aculock{}}}
+@c seteuid @asulock @aculock
+@c  INLINE_SETXID_SYSCALL @asulock @aculock
+@c    This may be just a unix syscall, or the ugliness below used by
+@c    nptl to propagate the syscall to all cloned processes used to
+@c    implement threads.
+@c   nptl_setxid @asulock @aculock
+@c     while holding the stack_alloc_lock, mark with SETXID_BITMASK all
+@c     threads that are not exiting, signal them until no thread remains
+@c     marked, clear the marks and run the syscall, then release the lock.
+@c    lll_lock @asulock @aculock
+@c    list_for_each ok
+@c    list_entry ok
+@c    setxid_mark_thread ok
+@c      if a thread is initializing, wait for it to be cloned.
+@c      mark it with SETXID_BITMASK if it's not exiting
+@c    setxid_signal_thread ok
+@c      if a thread is marked with SETXID_BITMASK,
+@c        send it the SIGSETXID signal
+@c    setxid_unmark_thread ok
+@c      clear SETXID_BITMASK and release the futex if SETXID_BITMASK is
+@c      set.
+@c    <syscall> ok
+@c    lll_unlock @aculock
+@c
+@c  sighandler_setxid ok
+@c    issue the syscall, clear SETXID_BITMASK, release the futex, and
+@c    wake up the signaller loop if the counter reached zero.
+This function sets the effective user ID of a process to @var{neweuid},
+provided that the process is allowed to change its effective user ID.  A
+privileged process (effective user ID zero) can change its effective
+user ID to any legal value.  An unprivileged process with a file user ID
+can change its effective user ID to its real user ID or to its file user
+ID.  Otherwise, a process may not change its effective user ID at all.
+
+The @code{seteuid} function returns a value of @code{0} to indicate
+successful completion, and a value of @code{-1} to indicate an error.
+The following @code{errno} error conditions are defined for this
+function:
+
+@table @code
+@item EINVAL
+The value of the @var{neweuid} argument is invalid.
+
+@item EPERM
+The process may not change to the specified ID.
+@end table
+
+Older systems (those without the @code{_POSIX_SAVED_IDS} feature) do not
+have this function.
+@end deftypefun
+
+@comment unistd.h
+@comment POSIX.1
+@deftypefun int setuid (uid_t @var{newuid})
+@safety{@prelim{}@mtsafe{}@asunsafe{@asulock{}}@acunsafe{@aculock{}}}
+@c setuid @asulock @aculock
+@c  INLINE_SETXID_SYSCALL dup @asulock @aculock
+If the calling process is privileged, this function sets both the real
+and effective user IDs of the process to @var{newuid}.  It also deletes
+the file user ID of the process, if any.  @var{newuid} may be any
+legal value.  (Once this has been done, there is no way to recover the
+old effective user ID.)
+
+If the process is not privileged, and the system supports the
+@code{_POSIX_SAVED_IDS} feature, then this function behaves like
+@code{seteuid}.
+
+The return values and error conditions are the same as for @code{seteuid}.
+@end deftypefun
+
+@comment unistd.h
+@comment BSD
+@deftypefun int setreuid (uid_t @var{ruid}, uid_t @var{euid})
+@safety{@prelim{}@mtsafe{}@asunsafe{@asulock{}}@acunsafe{@aculock{}}}
+@c setreuid @asulock @aculock
+@c  INLINE_SETXID_SYSCALL dup @asulock @aculock
+This function sets the real user ID of the process to @var{ruid} and the
+effective user ID to @var{euid}.  If @var{ruid} is @code{-1}, it means
+not to change the real user ID; likewise if @var{euid} is @code{-1}, it
+means not to change the effective user ID.
+
+The @code{setreuid} function exists for compatibility with 4.3 BSD Unix,
+which does not support file IDs.  You can use this function to swap the
+effective and real user IDs of the process.  (Privileged processes are
+not limited to this particular usage.)  If file IDs are supported, you
+should use that feature instead of this function.  @xref{Enable/Disable
+Setuid}.
+
+The return value is @code{0} on success and @code{-1} on failure.
+The following @code{errno} error conditions are defined for this
+function:
+
+@table @code
+@item EPERM
+The process does not have the appropriate privileges; you do not
+have permission to change to the specified ID.
+@end table
+@end deftypefun
+
+@node Setting Groups
+@section Setting the Group IDs
+
+This section describes the functions for altering the group IDs (real
+and effective) of a process.  To use these facilities, you must include
+the header files @file{sys/types.h} and @file{unistd.h}.
+@pindex unistd.h
+@pindex sys/types.h
+
+@comment unistd.h
+@comment POSIX.1
+@deftypefun int setegid (gid_t @var{newgid})
+@safety{@prelim{}@mtsafe{}@asunsafe{@asulock{}}@acunsafe{@aculock{}}}
+@c setegid @asulock @aculock
+@c  INLINE_SETXID_SYSCALL dup @asulock @aculock
+This function sets the effective group ID of the process to
+@var{newgid}, provided that the process is allowed to change its group
+ID.  Just as with @code{seteuid}, if the process is privileged it may
+change its effective group ID to any value; if it isn't, but it has a
+file group ID, then it may change to its real group ID or file group ID;
+otherwise it may not change its effective group ID.
+
+Note that a process is only privileged if its effective @emph{user} ID
+is zero.  The effective group ID only affects access permissions.
+
+The return values and error conditions for @code{setegid} are the same
+as those for @code{seteuid}.
+
+This function is only present if @code{_POSIX_SAVED_IDS} is defined.
+@end deftypefun
+
+@comment unistd.h
+@comment POSIX.1
+@deftypefun int setgid (gid_t @var{newgid})
+@safety{@prelim{}@mtsafe{}@asunsafe{@asulock{}}@acunsafe{@aculock{}}}
+@c setgid @asulock @aculock
+@c  INLINE_SETXID_SYSCALL dup @asulock @aculock
+This function sets both the real and effective group ID of the process
+to @var{newgid}, provided that the process is privileged.  It also
+deletes the file group ID, if any.
+
+If the process is not privileged, then @code{setgid} behaves like
+@code{setegid}.
+
+The return values and error conditions for @code{setgid} are the same
+as those for @code{seteuid}.
+@end deftypefun
+
+@comment unistd.h
+@comment BSD
+@deftypefun int setregid (gid_t @var{rgid}, gid_t @var{egid})
+@safety{@prelim{}@mtsafe{}@asunsafe{@asulock{}}@acunsafe{@aculock{}}}
+@c setregid @asulock @aculock
+@c  INLINE_SETXID_SYSCALL dup @asulock @aculock
+This function sets the real group ID of the process to @var{rgid} and
+the effective group ID to @var{egid}.  If @var{rgid} is @code{-1}, it
+means not to change the real group ID; likewise if @var{egid} is
+@code{-1}, it means not to change the effective group ID.
+
+The @code{setregid} function is provided for compatibility with 4.3 BSD
+Unix, which does not support file IDs.  You can use this function to
+swap the effective and real group IDs of the process.  (Privileged
+processes are not limited to this usage.)  If file IDs are supported,
+you should use that feature instead of using this function.
+@xref{Enable/Disable Setuid}.
+
+The return values and error conditions for @code{setregid} are the same
+as those for @code{setreuid}.
+@end deftypefun
+
+@code{setuid} and @code{setgid} behave differently depending on whether
+the effective user ID at the time is zero.  If it is not zero, they
+behave like @code{seteuid} and @code{setegid}.  If it is, they change
+both effective and real IDs and delete the file ID.  To avoid confusion,
+we recommend you always use @code{seteuid} and @code{setegid} except
+when you know the effective user ID is zero and your intent is to change
+the persona permanently.  This case is rare---most of the programs that
+need it, such as @code{login} and @code{su}, have already been written.
+
+Note that if your program is setuid to some user other than @code{root},
+there is no way to drop privileges permanently.
+
+The system also lets privileged processes change their supplementary
+group IDs.  To use @code{setgroups} or @code{initgroups}, your programs
+should include the header file @file{grp.h}.
+@pindex grp.h
+
+@comment grp.h
+@comment BSD
+@deftypefun int setgroups (size_t @var{count}, const gid_t *@var{groups})
+@safety{@prelim{}@mtsafe{}@asunsafe{@asulock{}}@acunsafe{@aculock{}}}
+@c setgroups @asulock @aculock
+@c  INLINE_SETXID_SYSCALL dup @asulock @aculock
+This function sets the process's supplementary group IDs.  It can only
+be called from privileged processes.  The @var{count} argument specifies
+the number of group IDs in the array @var{groups}.
+
+This function returns @code{0} if successful and @code{-1} on error.
+The following @code{errno} error conditions are defined for this
+function:
+
+@table @code
+@item EPERM
+The calling process is not privileged.
+@end table
+@end deftypefun
+
+@comment grp.h
+@comment BSD
+@deftypefun int initgroups (const char *@var{user}, gid_t @var{group})
+@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @acsmem{} @acsfd{} @aculock{}}}
+@c initgroups @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  sysconf(_SC_NGROUPS_MAX) dup @acsfd
+@c  MIN dup ok
+@c  malloc @ascuheap @acsmem
+@c  internal_getgrouplist @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   nscd_getgrouplist @ascuheap @acsfd @acsmem
+@c    nscd_get_map_ref dup @ascuheap @acsfd @acsmem
+@c    nscd_cache_search dup ok
+@c    nscd_open_socket dup @acsfd
+@c    realloc dup @ascuheap @acsmem
+@c    readall dup ok
+@c    memcpy dup ok
+@c    close_not_cancel_no_status dup @acsfd
+@c    nscd_drop_map_ref dup @ascuheap @acsmem
+@c    nscd_unmap dup @ascuheap @acsmem
+@c   nss_database_lookup dup @mtslocale @ascuheap @asulock @acucorrupt @acsmem @acsfd @aculock
+@c   nss_lookup_function dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   compat_call @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c    sysconf(_SC_GETGR_R_SIZE_MAX) ok
+@c    nss_lookup_function dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c    *getgrent_fct @ascuplugin
+@c    *setgrent_fct @ascuplugin
+@c    *endgrent_fct @ascuplugin
+@c    realloc dup @ascuheap @acsmem
+@c    free dup @ascuheap @acsmem
+@c   *initgroups_dyn_fct @ascuplugin
+@c   nss_next_action dup ok
+@c  setgroups dup @asulock @aculock
+@c  free dup @ascuheap @acsmem
+The @code{initgroups} function sets the process's supplementary group
+IDs to be the normal default for the user name @var{user}.  The group
+@var{group} is automatically included.
+
+This function works by scanning the group database for all the groups
+@var{user} belongs to.  It then calls @code{setgroups} with the list it
+has constructed.
+
+The return values and error conditions are the same as for
+@code{setgroups}.
+@end deftypefun
+
+If you are interested in the groups a particular user belongs to, but do
+not want to change the process's supplementary group IDs, you can use
+@code{getgrouplist}.  To use @code{getgrouplist}, your programs should
+include the header file @file{grp.h}.
+@pindex grp.h
+
+@comment grp.h
+@comment BSD
+@deftypefun int getgrouplist (const char *@var{user}, gid_t @var{group}, gid_t *@var{groups}, int *@var{ngroups})
+@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @acsmem{} @acsfd{} @aculock{}}}
+@c getgrouplist @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  MAX dup ok
+@c  malloc dup @ascuheap @acsmem
+@c  internal_getgrouplist dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  memcpy dup ok
+@c  free dup @ascuheap @acsmem
+The @code{getgrouplist} function scans the group database for all the
+groups @var{user} belongs to.  Up to *@var{ngroups} group IDs
+corresponding to these groups are stored in the array @var{groups}; the
+return value from the function is the number of group IDs actually
+stored.  If *@var{ngroups} is smaller than the total number of groups
+found, then @code{getgrouplist} returns a value of @code{-1} and stores
+the actual number of groups in *@var{ngroups}.  The group @var{group} is
+automatically included in the list of groups returned by
+@code{getgrouplist}.
+
+Here's how to use @code{getgrouplist} to read all supplementary groups
+for @var{user}:
+
+@smallexample
+@group
+gid_t *
+supplementary_groups (char *user)
+@{
+  int ngroups = 16;
+  gid_t *groups
+    = (gid_t *) xmalloc (ngroups * sizeof (gid_t));
+  struct passwd *pw = getpwnam (user);
+
+  if (pw == NULL)
+    return NULL;
+
+  if (getgrouplist (pw->pw_name, pw->pw_gid, groups, &ngroups) < 0)
+    @{
+      groups = xrealloc (ngroups * sizeof (gid_t));
+      getgrouplist (pw->pw_name, pw->pw_gid, groups, &ngroups);
+    @}
+  return groups;
+@}
+@end group
+@end smallexample
+@end deftypefun
+
+@node Enable/Disable Setuid
+@section Enabling and Disabling Setuid Access
+
+A typical setuid program does not need its special access all of the
+time.  It's a good idea to turn off this access when it isn't needed,
+so it can't possibly give unintended access.
+
+If the system supports the @code{_POSIX_SAVED_IDS} feature, you can
+accomplish this with @code{seteuid}.  When the game program starts, its
+real user ID is @code{jdoe}, its effective user ID is @code{games}, and
+its saved user ID is also @code{games}.  The program should record both
+user ID values once at the beginning, like this:
+
+@smallexample
+user_user_id = getuid ();
+game_user_id = geteuid ();
+@end smallexample
+
+Then it can turn off game file access with
+
+@smallexample
+seteuid (user_user_id);
+@end smallexample
+
+@noindent
+and turn it on with
+
+@smallexample
+seteuid (game_user_id);
+@end smallexample
+
+@noindent
+Throughout this process, the real user ID remains @code{jdoe} and the
+file user ID remains @code{games}, so the program can always set its
+effective user ID to either one.
+
+On other systems that don't support file user IDs, you can
+turn setuid access on and off by using @code{setreuid} to swap the real
+and effective user IDs of the process, as follows:
+
+@smallexample
+setreuid (geteuid (), getuid ());
+@end smallexample
+
+@noindent
+This special case is always allowed---it cannot fail.
+
+Why does this have the effect of toggling the setuid access?  Suppose a
+game program has just started, and its real user ID is @code{jdoe} while
+its effective user ID is @code{games}.  In this state, the game can
+write the scores file.  If it swaps the two uids, the real becomes
+@code{games} and the effective becomes @code{jdoe}; now the program has
+only @code{jdoe} access.  Another swap brings @code{games} back to
+the effective user ID and restores access to the scores file.
+
+In order to handle both kinds of systems, test for the saved user ID
+feature with a preprocessor conditional, like this:
+
+@smallexample
+#ifdef _POSIX_SAVED_IDS
+  seteuid (user_user_id);
+#else
+  setreuid (geteuid (), getuid ());
+#endif
+@end smallexample
+
+@node Setuid Program Example
+@section Setuid Program Example
+
+Here's an example showing how to set up a program that changes its
+effective user ID.
+
+This is part of a game program called @code{caber-toss} that manipulates
+a file @file{scores} that should be writable only by the game program
+itself.  The program assumes that its executable file will be installed
+with the setuid bit set and owned by the same user as the @file{scores}
+file.  Typically, a system administrator will set up an account like
+@code{games} for this purpose.
+
+The executable file is given mode @code{4755}, so that doing an
+@samp{ls -l} on it produces output like:
+
+@smallexample
+-rwsr-xr-x   1 games    184422 Jul 30 15:17 caber-toss
+@end smallexample
+
+@noindent
+The setuid bit shows up in the file modes as the @samp{s}.
+
+The scores file is given mode @code{644}, and doing an @samp{ls -l} on
+it shows:
+
+@smallexample
+-rw-r--r--  1 games           0 Jul 31 15:33 scores
+@end smallexample
+
+Here are the parts of the program that show how to set up the changed
+user ID.  This program is conditionalized so that it makes use of the
+file IDs feature if it is supported, and otherwise uses @code{setreuid}
+to swap the effective and real user IDs.
+
+@smallexample
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+
+/* @r{Remember the effective and real UIDs.} */
+
+static uid_t euid, ruid;
+
+
+/* @r{Restore the effective UID to its original value.} */
+
+void
+do_setuid (void)
+@{
+  int status;
+
+#ifdef _POSIX_SAVED_IDS
+  status = seteuid (euid);
+#else
+  status = setreuid (ruid, euid);
+#endif
+  if (status < 0) @{
+    fprintf (stderr, "Couldn't set uid.\n");
+    exit (status);
+    @}
+@}
+
+
+@group
+/* @r{Set the effective UID to the real UID.} */
+
+void
+undo_setuid (void)
+@{
+  int status;
+
+#ifdef _POSIX_SAVED_IDS
+  status = seteuid (ruid);
+#else
+  status = setreuid (euid, ruid);
+#endif
+  if (status < 0) @{
+    fprintf (stderr, "Couldn't set uid.\n");
+    exit (status);
+    @}
+@}
+@end group
+
+/* @r{Main program.} */
+
+int
+main (void)
+@{
+  /* @r{Remember the real and effective user IDs.}  */
+  ruid = getuid ();
+  euid = geteuid ();
+  undo_setuid ();
+
+  /* @r{Do the game and record the score.}  */
+  @dots{}
+@}
+@end smallexample
+
+Notice how the first thing the @code{main} function does is to set the
+effective user ID back to the real user ID.  This is so that any other
+file accesses that are performed while the user is playing the game use
+the real user ID for determining permissions.  Only when the program
+needs to open the scores file does it switch back to the file user ID,
+like this:
+
+@smallexample
+/* @r{Record the score.} */
+
+int
+record_score (int score)
+@{
+  FILE *stream;
+  char *myname;
+
+  /* @r{Open the scores file.} */
+  do_setuid ();
+  stream = fopen (SCORES_FILE, "a");
+  undo_setuid ();
+
+@group
+  /* @r{Write the score to the file.} */
+  if (stream)
+    @{
+      myname = cuserid (NULL);
+      if (score < 0)
+        fprintf (stream, "%10s: Couldn't lift the caber.\n", myname);
+      else
+        fprintf (stream, "%10s: %d feet.\n", myname, score);
+      fclose (stream);
+      return 0;
+    @}
+  else
+    return -1;
+@}
+@end group
+@end smallexample
+
+@node Tips for Setuid
+@section Tips for Writing Setuid Programs
+
+It is easy for setuid programs to give the user access that isn't
+intended---in fact, if you want to avoid this, you need to be careful.
+Here are some guidelines for preventing unintended access and
+minimizing its consequences when it does occur:
+
+@itemize @bullet
+@item
+Don't have @code{setuid} programs with privileged user IDs such as
+@code{root} unless it is absolutely necessary.  If the resource is
+specific to your particular program, it's better to define a new,
+nonprivileged user ID or group ID just to manage that resource.
+It's better if you can write your program to use a special group than a
+special user.
+
+@item
+Be cautious about using the @code{exec} functions in combination with
+changing the effective user ID.  Don't let users of your program execute
+arbitrary programs under a changed user ID.  Executing a shell is
+especially bad news.  Less obviously, the @code{execlp} and @code{execvp}
+functions are a potential risk (since the program they execute depends
+on the user's @code{PATH} environment variable).
+
+If you must @code{exec} another program under a changed ID, specify an
+absolute file name (@pxref{File Name Resolution}) for the executable,
+and make sure that the protections on that executable and @emph{all}
+containing directories are such that ordinary users cannot replace it
+with some other program.
+
+You should also check the arguments passed to the program to make sure
+they do not have unexpected effects.  Likewise, you should examine the
+environment variables.  Decide which arguments and variables are safe,
+and reject all others.
+
+You should never use @code{system} in a privileged program, because it
+invokes a shell.
+
+@item
+Only use the user ID controlling the resource in the part of the program
+that actually uses that resource.  When you're finished with it, restore
+the effective user ID back to the actual user's user ID.
+@xref{Enable/Disable Setuid}.
+
+@item
+If the @code{setuid} part of your program needs to access other files
+besides the controlled resource, it should verify that the real user
+would ordinarily have permission to access those files.  You can use the
+@code{access} function (@pxref{Access Permission}) to check this; it
+uses the real user and group IDs, rather than the effective IDs.
+@end itemize
+
+@node Who Logged In
+@section Identifying Who Logged In
+@cindex login name, determining
+@cindex user ID, determining
+
+You can use the functions listed in this section to determine the login
+name of the user who is running a process, and the name of the user who
+logged in the current session.  See also the function @code{getuid} and
+friends (@pxref{Reading Persona}).  How this information is collected by
+the system and how to control/add/remove information from the background
+storage is described in @ref{User Accounting Database}.
+
+The @code{getlogin} function is declared in @file{unistd.h}, while
+@code{cuserid} and @code{L_cuserid} are declared in @file{stdio.h}.
+@pindex stdio.h
+@pindex unistd.h
+
+@comment unistd.h
+@comment POSIX.1
+@deftypefun {char *} getlogin (void)
+@safety{@prelim{}@mtunsafe{@mtasurace{:getlogin} @mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c getlogin (linux) @mtasurace:getlogin @mtasurace:utent @mtascusig:ALRM @mtascutimer @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  getlogin_r_loginuid dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  getlogin_fd0 (unix) @mtasurace:getlogin @mtasurace:utent @mtascusig:ALRM @mtascutimer @ascuheap @asulock @aculock @acsfd @acsmem
+@c    uses static buffer name => @mtasurace:getlogin
+@c   ttyname_r dup @ascuheap @acsmem @acsfd
+@c   strncpy dup ok
+@c   setutent dup @mtasurace:utent @asulock @aculock @acsfd
+@c   getutline_r dup @mtasurace:utent @mtascusig:ALRM @mtascutimer @asulock @aculock @acsfd
+@c   endutent dup @mtasurace:utent @asulock @aculock
+@c   libc_lock_unlock dup ok
+@c   strlen dup ok
+@c   memcpy dup ok
+@c
+@c getlogin_r (linux) @mtasurace:utent @mtascusig:ALRM @mtascutimer @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  getlogin_r_loginuid @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   open_not_cancel_2 dup @acsfd
+@c   read_not_cancel dup ok
+@c   close_not_cancel_no_status dup @acsfd
+@c   strtoul @mtslocale
+@c   getpwuid_r dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   realloc dup @asulock @aculock @acsfd @acsmem
+@c   strlen dup ok
+@c   memcpy dup ok
+@c   free dup @asulock @aculock @acsfd @acsmem
+@c  getlogin_r_fd0 (unix) @mtasurace:utent @mtascusig:ALRM @mtascutimer @ascuheap @asulock @aculock @acsmem @acsfd
+@c   ttyname_r dup @ascuheap @acsmem @acsfd
+@c   strncpy dup ok
+@c   libc_lock_lock dup @asulock @aculock
+@c   *libc_utmp_jump_table->setutent dup @mtasurace:utent @acsfd
+@c   *libc_utmp_jump_table->getutline_r dup @mtasurace:utent @mtascusig:ALRM @mtascutimer
+@c   *libc_utmp_jump_table->endutent dup @mtasurace:utent @asulock @aculock
+@c   libc_lock_unlock dup ok
+@c   strlen dup ok
+@c   memcpy dup ok
+The @code{getlogin} function returns a pointer to a string containing the
+name of the user logged in on the controlling terminal of the process,
+or a null pointer if this information cannot be determined.  The string
+is statically allocated and might be overwritten on subsequent calls to
+this function or to @code{cuserid}.
+@end deftypefun
+
+@comment stdio.h
+@comment POSIX.1
+@deftypefun {char *} cuserid (char *@var{string})
+@safety{@prelim{}@mtunsafe{@mtasurace{:cuserid/!string} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c cuserid @mtasurace:cuserid/!string @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   if string is NULL, cuserid will overwrite and return a static buffer
+@c  geteuid dup ok
+@c  getpwuid_r dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  strncpy dup ok
+The @code{cuserid} function returns a pointer to a string containing a
+user name associated with the effective ID of the process.  If
+@var{string} is not a null pointer, it should be an array that can hold
+at least @code{L_cuserid} characters; the string is returned in this
+array.  Otherwise, a pointer to a string in a static area is returned.
+This string is statically allocated and might be overwritten on
+subsequent calls to this function or to @code{getlogin}.
+
+The use of this function is deprecated since it is marked to be
+withdrawn in XPG4.2 and has already been removed from newer revisions of
+POSIX.1.
+@end deftypefun
+
+@comment stdio.h
+@comment POSIX.1
+@deftypevr Macro int L_cuserid
+An integer constant that indicates how long an array you might need to
+store a user name.
+@end deftypevr
+
+These functions let your program identify positively the user who is
+running or the user who logged in this session.  (These can differ when
+setuid programs are involved; see @ref{Process Persona}.)  The user cannot
+do anything to fool these functions.
+
+For most purposes, it is more useful to use the environment variable
+@code{LOGNAME} to find out who the user is.  This is more flexible
+precisely because the user can set @code{LOGNAME} arbitrarily.
+@xref{Standard Environment}.
+
+
+@node User Accounting Database
+@section The User Accounting Database
+@cindex user accounting database
+
+Most Unix-like operating systems keep track of logged in users by
+maintaining a user accounting database.  This user accounting database
+stores for each terminal, who has logged on, at what time, the process
+ID of the user's login shell, etc., etc., but also stores information
+about the run level of the system, the time of the last system reboot,
+and possibly more.
+
+The user accounting database typically lives in @file{/etc/utmp},
+@file{/var/adm/utmp} or @file{/var/run/utmp}.  However, these files
+should @strong{never} be accessed directly.  For reading information
+from and writing information to the user accounting database, the
+functions described in this section should be used.
+
+
+@menu
+* Manipulating the Database::   Scanning and modifying the user
+                                 accounting database.
+* XPG Functions::               A standardized way for doing the same thing.
+* Logging In and Out::          Functions from BSD that modify the user
+                                 accounting database.
+@end menu
+
+@node Manipulating the Database
+@subsection Manipulating the User Accounting Database
+
+These functions and the corresponding data structures are declared in
+the header file @file{utmp.h}.
+@pindex utmp.h
+
+@comment utmp.h
+@comment SVID
+@deftp {Data Type} {struct exit_status}
+The @code{exit_status} data structure is used to hold information about
+the exit status of processes marked as @code{DEAD_PROCESS} in the user
+accounting database.
+
+@table @code
+@item short int e_termination
+The exit status of the process.
+
+@item short int e_exit
+The exit status of the process.
+@end table
+@end deftp
+
+@deftp {Data Type} {struct utmp}
+The @code{utmp} data structure is used to hold information about entries
+in the user accounting database.  On @gnusystems{} it has the following
+members:
+
+@table @code
+@item short int ut_type
+Specifies the type of login; one of @code{EMPTY}, @code{RUN_LVL},
+@code{BOOT_TIME}, @code{OLD_TIME}, @code{NEW_TIME}, @code{INIT_PROCESS},
+@code{LOGIN_PROCESS}, @code{USER_PROCESS}, @code{DEAD_PROCESS} or
+@code{ACCOUNTING}.
+
+@item pid_t ut_pid
+The process ID number of the login process.
+
+@item char ut_line[]
+The device name of the tty (without @file{/dev/}).
+
+@item char ut_id[]
+The inittab ID of the process.
+
+@item char ut_user[]
+The user's login name.
+
+@item char ut_host[]
+The name of the host from which the user logged in.
+
+@item struct exit_status ut_exit
+The exit status of a process marked as @code{DEAD_PROCESS}.
+
+@item long ut_session
+The Session ID, used for windowing.
+
+@item struct timeval ut_tv
+Time the entry was made.  For entries of type @code{OLD_TIME} this is
+the time when the system clock changed, and for entries of type
+@code{NEW_TIME} this is the time the system clock was set to.
+
+@item int32_t ut_addr_v6[4]
+The Internet address of a remote host.
+@end table
+@end deftp
+
+The @code{ut_type}, @code{ut_pid}, @code{ut_id}, @code{ut_tv}, and
+@code{ut_host} fields are not available on all systems.  Portable
+applications therefore should be prepared for these situations.  To help
+do this the @file{utmp.h} header provides macros
+@code{_HAVE_UT_TYPE}, @code{_HAVE_UT_PID}, @code{_HAVE_UT_ID},
+@code{_HAVE_UT_TV}, and @code{_HAVE_UT_HOST} if the respective field is
+available.  The programmer can handle the situations by using
+@code{#ifdef} in the program code.
+
+The following macros are defined for use as values for the
+@code{ut_type} member of the @code{utmp} structure.  The values are
+integer constants.
+
+@vtable @code
+@comment utmp.h
+@comment SVID
+@item EMPTY
+This macro is used to indicate that the entry contains no valid user
+accounting information.
+
+@comment utmp.h
+@comment SVID
+@item RUN_LVL
+This macro is used to identify the system's runlevel.
+
+@comment utmp.h
+@comment SVID
+@item BOOT_TIME
+This macro is used to identify the time of system boot.
+
+@comment utmp.h
+@comment SVID
+@item OLD_TIME
+This macro is used to identify the time when the system clock changed.
+
+@comment utmp.h
+@comment SVID
+@item NEW_TIME
+This macro is used to identify the time after the system clock changed.
+
+@comment utmp.h
+@comment SVID
+@item INIT_PROCESS
+This macro is used to identify a process spawned by the init process.
+
+@comment utmp.h
+@comment SVID
+@item LOGIN_PROCESS
+This macro is used to identify the session leader of a logged in user.
+
+@comment utmp.h
+@comment SVID
+@item USER_PROCESS
+This macro is used to identify a user process.
+
+@comment utmp.h
+@comment SVID
+@item DEAD_PROCESS
+This macro is used to identify a terminated process.
+
+@comment utmp.h
+@comment SVID
+@item ACCOUNTING
+???
+@end vtable
+
+The size of the @code{ut_line}, @code{ut_id}, @code{ut_user} and
+@code{ut_host} arrays can be found using the @code{sizeof} operator.
+
+Many older systems have, instead of an @code{ut_tv} member, an
+@code{ut_time} member, usually of type @code{time_t}, for representing
+the time associated with the entry.  Therefore, for backwards
+compatibility only, @file{utmp.h} defines @code{ut_time} as an alias for
+@code{ut_tv.tv_sec}.
+
+@comment utmp.h
+@comment SVID
+@deftypefun void setutent (void)
+@safety{@prelim{}@mtunsafe{@mtasurace{:utent}}@asunsafe{@asulock{}}@acunsafe{@aculock{} @acsfd{}}}
+@c Besides the static variables in utmp_file.c, there's the jump_table.
+@c They're both modified while holding a lock, but other threads may
+@c cause the variables to be modified between calling this function and
+@c others that rely on the internal state it sets up.
+
+@c setutent @mtasurace:utent @asulock @aculock @acsfd
+@c  libc_lock_lock dup @asulock @aculock
+@c  *libc_utmp_jump_table->setutent @mtasurace:utent @acsfd
+@c   setutent_unknown @mtasurace:utent @acsfd
+@c    *libc_utmp_file_functions.setutent = setutent_file @mtasurace:utent @acsfd
+@c      open_not_cancel_2 dup @acsfd
+@c      fcntl_not_cancel dup ok
+@c      close_not_cancel_no_status dup @acsfd
+@c      lseek64 dup ok
+@c  libc_lock_unlock dup ok
+This function opens the user accounting database to begin scanning it.
+You can then call @code{getutent}, @code{getutid} or @code{getutline} to
+read entries and @code{pututline} to write entries.
+
+If the database is already open, it resets the input to the beginning of
+the database.
+@end deftypefun
+
+@comment utmp.h
+@comment SVID
+@deftypefun {struct utmp *} getutent (void)
+@safety{@prelim{}@mtunsafe{@mtuinit{} @mtasurace{:utent} @mtasurace{:utentbuf} @mtascusig{:ALRM} @mtascutimer{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsfd{} @acsmem{}}}
+@c The static buffer that holds results is allocated with malloc at
+@c the first call; the test is not thread-safe, so multiple concurrent
+@c calls could malloc multiple buffers.
+
+@c getutent @mtuinit @mtasurace:utent @mtasurace:utentbuf @mtascusig:ALRM @mtascutimer @ascuheap @asulock @aculock @acsfd @acsmem
+@c  malloc @asulock @aculock @acsfd @acsmem
+@c  getutent_r dup @mtasurace:utent @mtascusig:ALRM @mtascutimer @asulock @aculock @acsfd
+The @code{getutent} function reads the next entry from the user
+accounting database.  It returns a pointer to the entry, which is
+statically allocated and may be overwritten by subsequent calls to
+@code{getutent}.  You must copy the contents of the structure if you
+wish to save the information or you can use the @code{getutent_r}
+function which stores the data in a user-provided buffer.
+
+A null pointer is returned in case no further entry is available.
+@end deftypefun
+
+@comment utmp.h
+@comment SVID
+@deftypefun void endutent (void)
+@safety{@prelim{}@mtunsafe{@mtasurace{:utent}}@asunsafe{@asulock{}}@acunsafe{@aculock{} @acsfd{}}}
+@c endutent @mtasurace:utent @asulock @aculock @acsfd
+@c  libc_lock_lock dup @asulock @aculock
+@c  *libc_utmp_jump_table->endutent @mtasurace:utent @acsfd
+@c   endutent_unknown ok
+@c   endutent_file @mtasurace:utent @acsfd
+@c    close_not_cancel_no_status dup @acsfd
+@c  libc_lock_unlock dup ok
+This function closes the user accounting database.
+@end deftypefun
+
+@comment utmp.h
+@comment SVID
+@deftypefun {struct utmp *} getutid (const struct utmp *@var{id})
+@safety{@prelim{}@mtunsafe{@mtuinit{} @mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{}}@asunsafe{@asulock{} @ascuheap{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}}
+@c Same caveats as getutline.
+@c
+@c getutid @mtuinit @mtasurace:utent @mtascusig:ALRM @mtascutimer @ascuheap @asulock @aculock @acsmem @acsfd
+@c   uses a static buffer malloced on the first call
+@c  malloc dup @ascuheap @acsmem
+@c  getutid_r dup @mtasurace:utent @mtascusig:ALRM @mtascutimer @asulock @aculock @acsfd
+This function searches forward from the current point in the database
+for an entry that matches @var{id}.  If the @code{ut_type} member of the
+@var{id} structure is one of @code{RUN_LVL}, @code{BOOT_TIME},
+@code{OLD_TIME} or @code{NEW_TIME} the entries match if the
+@code{ut_type} members are identical.  If the @code{ut_type} member of
+the @var{id} structure is @code{INIT_PROCESS}, @code{LOGIN_PROCESS},
+@code{USER_PROCESS} or @code{DEAD_PROCESS}, the entries match if the
+@code{ut_type} member of the entry read from the database is one of
+these four, and the @code{ut_id} members match.  However if the
+@code{ut_id} member of either the @var{id} structure or the entry read
+from the database is empty it checks if the @code{ut_line} members match
+instead.  If a matching entry is found, @code{getutid} returns a pointer
+to the entry, which is statically allocated, and may be overwritten by a
+subsequent call to @code{getutent}, @code{getutid} or @code{getutline}.
+You must copy the contents of the structure if you wish to save the
+information.
+
+A null pointer is returned in case the end of the database is reached
+without a match.
+
+The @code{getutid} function may cache the last read entry.  Therefore,
+if you are using @code{getutid} to search for multiple occurrences, it
+is necessary to zero out the static data after each call.  Otherwise
+@code{getutid} could just return a pointer to the same entry over and
+over again.
+@end deftypefun
+
+@comment utmp.h
+@comment SVID
+@deftypefun {struct utmp *} getutline (const struct utmp *@var{line})
+@safety{@prelim{}@mtunsafe{@mtuinit{} @mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsfd{} @acsmem{}}}
+@c The static buffer that holds results is allocated with malloc at
+@c the first call; the test is not thread-safe, so multiple concurrent
+@c calls could malloc multiple buffers.
+
+@c getutline @mtuinit @mtasurace:utent @mtascusig:ALRM @mtascutimer @ascuheap @asulock @aculock @acsfd @acsmem
+@c  malloc @asulock @aculock @acsfd @acsmem
+@c  getutline_r dup @mtasurace:utent @mtascusig:ALRM @mtascutimer @asulock @aculock @acsfd
+This function searches forward from the current point in the database
+until it finds an entry whose @code{ut_type} value is
+@code{LOGIN_PROCESS} or @code{USER_PROCESS}, and whose @code{ut_line}
+member matches the @code{ut_line} member of the @var{line} structure.
+If it finds such an entry, it returns a pointer to the entry which is
+statically allocated, and may be overwritten by a subsequent call to
+@code{getutent}, @code{getutid} or @code{getutline}.  You must copy the
+contents of the structure if you wish to save the information.
+
+A null pointer is returned in case the end of the database is reached
+without a match.
+
+The @code{getutline} function may cache the last read entry.  Therefore
+if you are using @code{getutline} to search for multiple occurrences, it
+is necessary to zero out the static data after each call.  Otherwise
+@code{getutline} could just return a pointer to the same entry over and
+over again.
+@end deftypefun
+
+@comment utmp.h
+@comment SVID
+@deftypefun {struct utmp *} pututline (const struct utmp *@var{utmp})
+@safety{@prelim{}@mtunsafe{@mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{}}@asunsafe{@asulock{}}@acunsafe{@aculock{} @acsfd{}}}
+@c pututline @mtasurace:utent @mtascusig:ALRM @mtascutimer @asulock @aculock @acsfd
+@c  libc_lock_lock dup @asulock @aculock
+@c  *libc_utmp_jump_table->pututline @mtasurace:utent @mtascusig:ALRM @mtascutimer @acsfd
+@c   pututline_unknown @mtasurace:utent @acsfd
+@c    setutent_unknown dup @mtasurace:utent @acsfd
+@c   pututline_file @mtascusig:ALRM @mtascutimer @acsfd
+@c    TRANSFORM_UTMP_FILE_NAME ok
+@c     strcmp dup ok
+@c     acesss dup ok
+@c    open_not_cancel_2 dup @acsfd
+@c    fcntl_not_cancel dup ok
+@c    close_not_cancel_no_status dup @acsfd
+@c    llseek dup ok
+@c    dup2 dup ok
+@c    utmp_equal dup ok
+@c    internal_getut_r dup @mtascusig:ALRM @mtascutimer
+@c    LOCK_FILE dup @mtascusig:ALRM @mtasctimer
+@c    LOCKING_FAILED dup ok
+@c    ftruncate64 dup ok
+@c    write_not_cancel dup ok
+@c    UNLOCK_FILE dup @mtasctimer
+@c  libc_lock_unlock dup @aculock
+The @code{pututline} function inserts the entry @code{*@var{utmp}} at
+the appropriate place in the user accounting database.  If it finds that
+it is not already at the correct place in the database, it uses
+@code{getutid} to search for the position to insert the entry, however
+this will not modify the static structure returned by @code{getutent},
+@code{getutid} and @code{getutline}.  If this search fails, the entry
+is appended to the database.
+
+The @code{pututline} function returns a pointer to a copy of the entry
+inserted in the user accounting database, or a null pointer if the entry
+could not be added.  The following @code{errno} error conditions are
+defined for this function:
+
+@table @code
+@item EPERM
+The process does not have the appropriate privileges; you cannot modify
+the user accounting database.
+@end table
+@end deftypefun
+
+All the @code{get*} functions mentioned before store the information
+they return in a static buffer.  This can be a problem in multi-threaded
+programs since the data returned for the request is overwritten by the
+return value data in another thread.  Therefore @theglibc{}
+provides as extensions three more functions which return the data in a
+user-provided buffer.
+
+@comment utmp.h
+@comment GNU
+@deftypefun int getutent_r (struct utmp *@var{buffer}, struct utmp **@var{result})
+@safety{@prelim{}@mtunsafe{@mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{}}@asunsafe{@asulock{}}@acunsafe{@aculock{} @acsfd{}}}
+@c getutent_r @mtasurace:utent @mtascusig:ALRM @mtascutimer @asulock @aculock @acsfd
+@c  libc_lock_lock dup @asulock @aculock
+@c  *libc_utmp_jump_table->getutent_r @mtasurace:utent @mtascusig:ALRM @mtascutimer @acsfd
+@c   getutent_r_unknown @mtasurace:utent @acsfd
+@c    setutent_unknown dup @mtasurace:utent @acsfd
+@c   getutent_r_file @mtasurace:utent @mtascusig:ALRM @mtascutimer
+@c    LOCK_FILE @mtascusig:ALRM @mtascutimer
+@c     alarm dup @mtascutimer
+@c     sigemptyset dup ok
+@c     sigaction dup ok
+@c     memset dup ok
+@c     fcntl_not_cancel dup ok
+@c    LOCKING_FAILED ok
+@c    read_not_cancel dup ok
+@c    UNLOCK_FILE @mtascutimer
+@c     fcntl_not_cancel dup ok
+@c     alarm dup @mtascutimer
+@c     sigaction dup ok
+@c    memcpy dup ok
+@c  libc_lock_unlock dup ok
+The @code{getutent_r} is equivalent to the @code{getutent} function.  It
+returns the next entry from the database.  But instead of storing the
+information in a static buffer it stores it in the buffer pointed to by
+the parameter @var{buffer}.
+
+If the call was successful, the function returns @code{0} and the
+pointer variable pointed to by the parameter @var{result} contains a
+pointer to the buffer which contains the result (this is most probably
+the same value as @var{buffer}).  If something went wrong during the
+execution of @code{getutent_r} the function returns @code{-1}.
+
+This function is a GNU extension.
+@end deftypefun
+
+@comment utmp.h
+@comment GNU
+@deftypefun int getutid_r (const struct utmp *@var{id}, struct utmp *@var{buffer}, struct utmp **@var{result})
+@safety{@prelim{}@mtunsafe{@mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{}}@asunsafe{@asulock{}}@acunsafe{@aculock{} @acsfd{}}}
+@c getutid_r @mtasurace:utent @mtascusig:ALRM @mtascutimer @asulock @aculock @acsfd
+@c  libc_lock_lock dup @asulock @aculock
+@c  *libc_utmp_jump_table->getutid_r @mtasurace:utent @mtascusig:ALRM @mtascutimer @acsfd
+@c   getutid_r_unknown @mtasurace:utent @acsfd
+@c    setutent_unknown dup @mtasurace:utent @acsfd
+@c   getutid_r_file @mtascusig:ALRM @mtascutimer
+@c    internal_getut_r @mtascusig:ALRM @mtascutimer
+@c     LOCK_FILE dup @mtascusig:ALRM @mtascutimer
+@c     LOCKING_FAILED dup ok
+@c     read_not_cancel dup ok
+@c     utmp_equal ok
+@c      strncmp dup ok
+@c     UNLOCK_FILE dup @mtascutimer
+@c    memcpy dup ok
+@c  libc_lock_unlock dup @aculock
+This function retrieves just like @code{getutid} the next entry matching
+the information stored in @var{id}.  But the result is stored in the
+buffer pointed to by the parameter @var{buffer}.
+
+If successful the function returns @code{0} and the pointer variable
+pointed to by the parameter @var{result} contains a pointer to the
+buffer with the result (probably the same as @var{result}.  If not
+successful the function return @code{-1}.
+
+This function is a GNU extension.
+@end deftypefun
+
+@comment utmp.h
+@comment GNU
+@deftypefun int getutline_r (const struct utmp *@var{line}, struct utmp *@var{buffer}, struct utmp **@var{result})
+@safety{@prelim{}@mtunsafe{@mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{}}@asunsafe{@asulock{}}@acunsafe{@aculock{} @acsfd{}}}
+@c getutline_r @mtasurace:utent @mtascusig:ALRM @mtascutimer @asulock @aculock @acsfd
+@c  libc_lock_lock dup @asulock @aculock
+@c  *libc_utmp_jump_table->getutline_r @mtasurace:utent @mtascusig:ALRM @mtascutimer @acsfd
+@c   getutline_r_unknown @mtasurace:utent @acsfd
+@c    setutent_unknown dup @mtasurace:utent @acsfd
+@c   getutline_r_file @mtasurace:utent @mtascusig:ALRM @mtascutimer
+@c    LOCK_FILE @mtascusig:ALRM @mtascutimer
+@c     alarm dup @mtascutimer
+@c     sigemptyset dup ok
+@c     sigaction dup ok
+@c     memset dup ok
+@c     fcntl_not_cancel dup ok
+@c    LOCKING_FAILED ok
+@c    read_not_cancel dup ok
+@c    strncmp dup ok
+@c    UNLOCK_FILE @mtascutimer
+@c     fcntl_not_cancel dup ok
+@c     alarm dup @mtascutimer
+@c     sigaction dup ok
+@c    memcpy dup ok
+@c  libc_lock_unlock dup ok
+This function retrieves just like @code{getutline} the next entry
+matching the information stored in @var{line}.  But the result is stored
+in the buffer pointed to by the parameter @var{buffer}.
+
+If successful the function returns @code{0} and the pointer variable
+pointed to by the parameter @var{result} contains a pointer to the
+buffer with the result (probably the same as @var{result}.  If not
+successful the function return @code{-1}.
+
+This function is a GNU extension.
+@end deftypefun
+
+
+In addition to the user accounting database, most systems keep a number
+of similar databases.  For example most systems keep a log file with all
+previous logins (usually in @file{/etc/wtmp} or @file{/var/log/wtmp}).
+
+For specifying which database to examine, the following function should
+be used.
+
+@comment utmp.h
+@comment SVID
+@deftypefun int utmpname (const char *@var{file})
+@safety{@prelim{}@mtunsafe{@mtasurace{:utent}}@asunsafe{@asulock{} @ascuheap{}}@acunsafe{@aculock{} @acsmem{}}}
+@c utmpname @mtasurace:utent @asulock @ascuheap @aculock @acsmem
+@c  libc_lock_lock dup @asulock @aculock
+@c  *libc_utmp_jump_table->endutent dup @mtasurace:utent
+@c  strcmp dup ok
+@c  free dup @ascuheap @acsmem
+@c  strdup dup @ascuheap @acsmem
+@c  libc_lock_unlock dup @aculock
+The @code{utmpname} function changes the name of the database to be
+examined to @var{file}, and closes any previously opened database.  By
+default @code{getutent}, @code{getutid}, @code{getutline} and
+@code{pututline} read from and write to the user accounting database.
+
+The following macros are defined for use as the @var{file} argument:
+
+@deftypevr Macro {char *} _PATH_UTMP
+This macro is used to specify the user accounting database.
+@end deftypevr
+
+@deftypevr Macro {char *} _PATH_WTMP
+This macro is used to specify the user accounting log file.
+@end deftypevr
+
+The @code{utmpname} function returns a value of @code{0} if the new name
+was successfully stored, and a value of @code{-1} to indicate an error.
+Note that @code{utmpname} does not try to open the database, and that
+therefore the return value does not say anything about whether the
+database can be successfully opened.
+@end deftypefun
+
+Specially for maintaining log-like databases @theglibc{} provides
+the following function:
+
+@comment utmp.h
+@comment SVID
+@deftypefun void updwtmp (const char *@var{wtmp_file}, const struct utmp *@var{utmp})
+@safety{@prelim{}@mtunsafe{@mtascusig{:ALRM} @mtascutimer{}}@asunsafe{}@acunsafe{@acsfd{}}}
+@c updwtmp @mtascusig:ALRM @mtascutimer @acsfd
+@c  TRANSFORM_UTMP_FILE_NAME dup ok
+@c  *libc_utmp_file_functions->updwtmp = updwtmp_file @mtascusig:ALRM @mtascutimer @acsfd
+@c   open_not_cancel_2 dup @acsfd
+@c   LOCK_FILE dup @mtascusig:ALRM @mtascutimer
+@c   LOCKING_FAILED dup ok
+@c   lseek64 dup ok
+@c   ftruncate64 dup ok
+@c   write_not_cancel dup ok
+@c   UNLOCK_FILE dup @mtascutimer
+@c   close_not_cancel_no_status dup @acsfd
+The @code{updwtmp} function appends the entry *@var{utmp} to the
+database specified by @var{wtmp_file}.  For possible values for the
+@var{wtmp_file} argument see the @code{utmpname} function.
+@end deftypefun
+
+@strong{Portability Note:} Although many operating systems provide a
+subset of these functions, they are not standardized.  There are often
+subtle differences in the return types, and there are considerable
+differences between the various definitions of @code{struct utmp}.  When
+programming for @theglibc{}, it is probably best to stick
+with the functions described in this section.  If however, you want your
+program to be portable, consider using the XPG functions described in
+@ref{XPG Functions}, or take a look at the BSD compatible functions in
+@ref{Logging In and Out}.
+
+
+@node XPG Functions
+@subsection XPG User Accounting Database Functions
+
+These functions, described in the X/Open Portability Guide, are declared
+in the header file @file{utmpx.h}.
+@pindex utmpx.h
+
+@deftp {Data Type} {struct utmpx}
+The @code{utmpx} data structure contains at least the following members:
+
+@table @code
+@item short int ut_type
+Specifies the type of login; one of @code{EMPTY}, @code{RUN_LVL},
+@code{BOOT_TIME}, @code{OLD_TIME}, @code{NEW_TIME}, @code{INIT_PROCESS},
+@code{LOGIN_PROCESS}, @code{USER_PROCESS} or @code{DEAD_PROCESS}.
+
+@item pid_t ut_pid
+The process ID number of the login process.
+
+@item char ut_line[]
+The device name of the tty (without @file{/dev/}).
+
+@item char ut_id[]
+The inittab ID of the process.
+
+@item char ut_user[]
+The user's login name.
+
+@item struct timeval ut_tv
+Time the entry was made.  For entries of type @code{OLD_TIME} this is
+the time when the system clock changed, and for entries of type
+@code{NEW_TIME} this is the time the system clock was set to.
+@end table
+In @theglibc{}, @code{struct utmpx} is identical to @code{struct
+utmp} except for the fact that including @file{utmpx.h} does not make
+visible the declaration of @code{struct exit_status}.
+@end deftp
+
+The following macros are defined for use as values for the
+@code{ut_type} member of the @code{utmpx} structure.  The values are
+integer constants and are, in @theglibc{}, identical to the
+definitions in @file{utmp.h}.
+
+@vtable @code
+@comment utmpx.h
+@comment XPG4.2
+@item EMPTY
+This macro is used to indicate that the entry contains no valid user
+accounting information.
+
+@comment utmpx.h
+@comment XPG4.2
+@item RUN_LVL
+This macro is used to identify the system's runlevel.
+
+@comment utmpx.h
+@comment XPG4.2
+@item BOOT_TIME
+This macro is used to identify the time of system boot.
+
+@comment utmpx.h
+@comment XPG4.2
+@item OLD_TIME
+This macro is used to identify the time when the system clock changed.
+
+@comment utmpx.h
+@comment XPG4.2
+@item NEW_TIME
+This macro is used to identify the time after the system clock changed.
+
+@comment utmpx.h
+@comment XPG4.2
+@item INIT_PROCESS
+This macro is used to identify a process spawned by the init process.
+
+@comment utmpx.h
+@comment XPG4.2
+@item LOGIN_PROCESS
+This macro is used to identify the session leader of a logged in user.
+
+@comment utmpx.h
+@comment XPG4.2
+@item USER_PROCESS
+This macro is used to identify a user process.
+
+@comment utmpx.h
+@comment XPG4.2
+@item DEAD_PROCESS
+This macro is used to identify a terminated process.
+@end vtable
+
+The size of the @code{ut_line}, @code{ut_id} and @code{ut_user} arrays
+can be found using the @code{sizeof} operator.
+
+@comment utmpx.h
+@comment XPG4.2
+@deftypefun void setutxent (void)
+@safety{@prelim{}@mtunsafe{@mtasurace{:utent}}@asunsafe{@asulock{}}@acunsafe{@aculock{} @acsfd{}}}
+This function is similar to @code{setutent}.  In @theglibc{} it is
+simply an alias for @code{setutent}.
+@end deftypefun
+
+@comment utmpx.h
+@comment XPG4.2
+@deftypefun {struct utmpx *} getutxent (void)
+@safety{@prelim{}@mtunsafe{@mtuinit{} @mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsfd{} @acsmem{}}}
+The @code{getutxent} function is similar to @code{getutent}, but returns
+a pointer to a @code{struct utmpx} instead of @code{struct utmp}.  In
+@theglibc{} it simply is an alias for @code{getutent}.
+@end deftypefun
+
+@comment utmpx.h
+@comment XPG4.2
+@deftypefun void endutxent (void)
+@safety{@prelim{}@mtunsafe{@mtasurace{:utent}}@asunsafe{@asulock{}}@acunsafe{@aculock{}}}
+This function is similar to @code{endutent}.  In @theglibc{} it is
+simply an alias for @code{endutent}.
+@end deftypefun
+
+@comment utmpx.h
+@comment XPG4.2
+@deftypefun {struct utmpx *} getutxid (const struct utmpx *@var{id})
+@safety{@prelim{}@mtunsafe{@mtuinit{} @mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{}}@asunsafe{@asulock{} @ascuheap{}}@acunsafe{@aculock{} @acsmem{} @acsfd{}}}
+This function is similar to @code{getutid}, but uses @code{struct utmpx}
+instead of @code{struct utmp}.  In @theglibc{} it is simply an alias
+for @code{getutid}.
+@end deftypefun
+
+@comment utmpx.h
+@comment XPG4.2
+@deftypefun {struct utmpx *} getutxline (const struct utmpx *@var{line})
+@safety{@prelim{}@mtunsafe{@mtuinit{} @mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsfd{} @acsmem{}}}
+This function is similar to @code{getutid}, but uses @code{struct utmpx}
+instead of @code{struct utmp}.  In @theglibc{} it is simply an alias
+for @code{getutline}.
+@end deftypefun
+
+@comment utmpx.h
+@comment XPG4.2
+@deftypefun {struct utmpx *} pututxline (const struct utmpx *@var{utmp})
+@safety{@prelim{}@mtunsafe{@mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{}}@asunsafe{@asulock{}}@acunsafe{@aculock{} @acsfd{}}}
+The @code{pututxline} function is functionally identical to
+@code{pututline}, but uses @code{struct utmpx} instead of @code{struct
+utmp}.  In @theglibc{}, @code{pututxline} is simply an alias for
+@code{pututline}.
+@end deftypefun
+
+@comment utmpx.h
+@comment XPG4.2
+@deftypefun int utmpxname (const char *@var{file})
+@safety{@prelim{}@mtunsafe{@mtasurace{:utent}}@asunsafe{@asulock{} @ascuheap{}}@acunsafe{@aculock{} @acsmem{}}}
+The @code{utmpxname} function is functionally identical to
+@code{utmpname}.  In @theglibc{}, @code{utmpxname} is simply an
+alias for @code{utmpname}.
+@end deftypefun
+
+You can translate between a traditional @code{struct utmp} and an XPG
+@code{struct utmpx} with the following functions.  In @theglibc{},
+these functions are merely copies, since the two structures are
+identical.
+
+@comment utmp.h utmpx.h
+@comment GNU
+@deftypefun int getutmp (const struct utmpx *@var{utmpx}, struct utmp *@var{utmp})
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+@code{getutmp} copies the information, insofar as the structures are
+compatible, from @var{utmpx} to @var{utmp}.
+@end deftypefun
+
+@comment utmp.h utmpx.h
+@comment GNU
+@deftypefun int getutmpx (const struct utmp *@var{utmp}, struct utmpx *@var{utmpx})
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+@code{getutmpx} copies the information, insofar as the structures are
+compatible, from @var{utmp} to @var{utmpx}.
+@end deftypefun
+
+
+@node Logging In and Out
+@subsection Logging In and Out
+
+These functions, derived from BSD, are available in the separate
+@file{libutil} library, and declared in @file{utmp.h}.
+@pindex utmp.h
+
+Note that the @code{ut_user} member of @code{struct utmp} is called
+@code{ut_name} in BSD.  Therefore, @code{ut_name} is defined as an alias
+for @code{ut_user} in @file{utmp.h}.
+
+@comment utmp.h
+@comment BSD
+@deftypefun int login_tty (int @var{filedes})
+@safety{@prelim{}@mtunsafe{@mtasurace{:ttyname}}@asunsafe{@ascuheap{} @asulock{}}@acunsafe{@aculock{} @acsfd{} @acsmem{}}}
+@c If this function is canceled, it may have succeeded in redirecting
+@c only some of the standard streams to the newly opened terminal.
+@c Should there be a safety annotation for this?
+@c login_tty @mtasurace:ttyname @ascuheap @asulock @aculock @acsmem @acsfd
+@c  setsid dup ok
+@c  ioctl dup ok
+@c  ttyname dup @mtasurace:ttyname @ascuheap @asulock @aculock @acsmem @acsfd
+@c  close dup @acsfd
+@c  open dup @acsfd
+@c  dup2 dup ok
+This function makes @var{filedes} the controlling terminal of the
+current process, redirects standard input, standard output and
+standard error output to this terminal, and closes @var{filedes}.
+
+This function returns @code{0} on successful completion, and @code{-1}
+on error.
+@end deftypefun
+
+@comment utmp.h
+@comment BSD
+@deftypefun void login (const struct utmp *@var{entry})
+@safety{@prelim{}@mtunsafe{@mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{}}@asunsafe{@asulock{} @ascuheap{}}@acunsafe{@aculock{} @acucorrupt{} @acsfd{} @acsmem{}}}
+@c login @mtasurace:utent @mtascusig:ALRM @mtascutimer @asulock @ascuheap @aculock @acucorrupt @acsfd @acsmem
+@c  getpid dup ok
+@c  tty_name @ascuheap @acucorrupt @acsmem @acsfd
+@c   ttyname_r dup @ascuheap @acsmem @acsfd
+@c   memchr dup ok
+@c   realloc dup @ascuheap @acsmem
+@c   malloc dup @ascuheap @acsmem
+@c   free dup @ascuheap @acsmem
+@c  strncmp dup ok
+@c  basename dup ok
+@c  strncpy dup ok
+@c  utmpname dup @mtasurace:utent @asulock @ascuheap @aculock @acsmem
+@c  setutent dup @mtasurace:utent @asulock @aculock @acsfd
+@c  pututline dup @mtasurace:utent @mtascusig:ALRM @mtascutimer @asulock @aculock @acsfd
+@c  endutent dup @mtasurace:utent @asulock @aculock
+@c  free dup @ascuheap @acsmem
+@c  updwtmp dup @mtascusig:ALRM @mtascutimer @acsfd
+The @code{login} functions inserts an entry into the user accounting
+database.  The @code{ut_line} member is set to the name of the terminal
+on standard input.  If standard input is not a terminal @code{login}
+uses standard output or standard error output to determine the name of
+the terminal.  If @code{struct utmp} has a @code{ut_type} member,
+@code{login} sets it to @code{USER_PROCESS}, and if there is an
+@code{ut_pid} member, it will be set to the process ID of the current
+process.  The remaining entries are copied from @var{entry}.
+
+A copy of the entry is written to the user accounting log file.
+@end deftypefun
+
+@comment utmp.h
+@comment BSD
+@deftypefun int logout (const char *@var{ut_line})
+@safety{@prelim{}@mtunsafe{@mtasurace{:utent} @mtascusig{:ALRM} @mtascutimer{}}@asunsafe{@asulock{} @ascuheap{}}@acunsafe{@aculock{} @acsfd{} @acsmem{}}}
+@c logout @mtasurace:utent @mtascusig:ALRM @mtascutimer @asulock @ascuheap @aculock @acsfd @acsmem
+@c  utmpname dup @mtasurace:utent @asulock @ascuheap @aculock @acsmem
+@c  setutent dup @mtasurace:utent @asulock @aculock @acsfd
+@c  strncpy dup ok
+@c  getutline_r dup @mtasurace:utent @mtascusig:ALRM @mtascutimer @asulock @aculock @acsfd
+@c  bzero dup ok
+@c  gettimeofday dup ok
+@c  time dup ok
+@c  pututline dup @mtasurace:utent @mtascusig:ALRM @mtascutimer @asulock @aculock @acsfd
+@c  endutent dup @mtasurace:utent @asulock @aculock
+This function modifies the user accounting database to indicate that the
+user on @var{ut_line} has logged out.
+
+The @code{logout} function returns @code{1} if the entry was successfully
+written to the database, or @code{0} on error.
+@end deftypefun
+
+@comment utmp.h
+@comment BSD
+@deftypefun void logwtmp (const char *@var{ut_line}, const char *@var{ut_name}, const char *@var{ut_host})
+@safety{@prelim{}@mtunsafe{@mtascusig{:ALRM} @mtascutimer{}}@asunsafe{}@acunsafe{@acsfd{}}}
+@c logwtmp @mtascusig:ALRM @mtascutimer @acsfd
+@c  memset dup ok
+@c  getpid dup ok
+@c  strncpy dup ok
+@c  gettimeofday dup ok
+@c  time dup ok
+@c  updwtmp dup @mtascusig:ALRM @mtascutimer @acsfd
+The @code{logwtmp} function appends an entry to the user accounting log
+file, for the current time and the information provided in the
+@var{ut_line}, @var{ut_name} and @var{ut_host} arguments.
+@end deftypefun
+
+@strong{Portability Note:} The BSD @code{struct utmp} only has the
+@code{ut_line}, @code{ut_name}, @code{ut_host} and @code{ut_time}
+members.  Older systems do not even have the @code{ut_host} member.
+
+
+@node User Database
+@section User Database
+@cindex user database
+@cindex password database
+@pindex /etc/passwd
+
+This section describes how to search and scan the database of registered
+users.  The database itself is kept in the file @file{/etc/passwd} on
+most systems, but on some systems a special network server gives access
+to it.
+
+@menu
+* User Data Structure::         What each user record contains.
+* Lookup User::                 How to look for a particular user.
+* Scanning All Users::          Scanning the list of all users, one by one.
+* Writing a User Entry::        How a program can rewrite a user's record.
+@end menu
+
+@node User Data Structure
+@subsection The Data Structure that Describes a User
+
+The functions and data structures for accessing the system user database
+are declared in the header file @file{pwd.h}.
+@pindex pwd.h
+
+@comment pwd.h
+@comment POSIX.1
+@deftp {Data Type} {struct passwd}
+The @code{passwd} data structure is used to hold information about
+entries in the system user data base.  It has at least the following members:
+
+@table @code
+@item char *pw_name
+The user's login name.
+
+@item char *pw_passwd.
+The encrypted password string.
+
+@item uid_t pw_uid
+The user ID number.
+
+@item gid_t pw_gid
+The user's default group ID number.
+
+@item char *pw_gecos
+A string typically containing the user's real name, and possibly other
+information such as a phone number.
+
+@item char *pw_dir
+The user's home directory, or initial working directory.  This might be
+a null pointer, in which case the interpretation is system-dependent.
+
+@item char *pw_shell
+The user's default shell, or the initial program run when the user logs in.
+This might be a null pointer, indicating that the system default should
+be used.
+@end table
+@end deftp
+
+@node Lookup User
+@subsection Looking Up One User
+@cindex converting user ID to user name
+@cindex converting user name to user ID
+
+You can search the system user database for information about a
+specific user using @code{getpwuid} or @code{getpwnam}.  These
+functions are declared in @file{pwd.h}.
+
+@comment pwd.h
+@comment POSIX.1
+@deftypefun {struct passwd *} getpwuid (uid_t @var{uid})
+@safety{@prelim{}@mtunsafe{@mtasurace{:pwuid} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c getpwuid @mtasurace:pwuid @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  libc_lock_lock dup @asulock @aculock
+@c  malloc dup @ascuheap @acsmem
+@c  getpwuid_r dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  realloc dup @ascuheap @acsmem
+@c  free dup @ascuheap @acsmem
+@c  libc_lock_unlock dup @aculock
+This function returns a pointer to a statically-allocated structure
+containing information about the user whose user ID is @var{uid}.  This
+structure may be overwritten on subsequent calls to @code{getpwuid}.
+
+A null pointer value indicates there is no user in the data base with
+user ID @var{uid}.
+@end deftypefun
+
+@comment pwd.h
+@comment POSIX.1c
+@deftypefun int getpwuid_r (uid_t @var{uid}, struct passwd *@var{result_buf}, char *@var{buffer}, size_t @var{buflen}, struct passwd **@var{result})
+@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c getpwuid_r @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  nscd_getpwuid_r @ascuheap @acsfd @acsmem
+@c   itoa_word dup ok
+@c   nscd_getpw_r @ascuheap @acsfd @acsmem
+@c    nscd_get_map_ref @ascuheap @acsfd @acsmem
+@c     nscd_acquire_maplock ok
+@c     nscd_get_mapping @ascuheap @acsfd @acsmem
+@c      open_socket dup @acsfd
+@c      memset dup ok
+@c      wait_on_socket dup ok
+@c      recvmsg dup ok
+@c      strcmp dup ok
+@c      fstat64 dup ok
+@c      mmap dup @acsmem
+@c      munmap dup @acsmem
+@c      malloc dup @ascuheap @acsmem
+@c      close dup ok
+@c      nscd_unmap dup @ascuheap @acsmem
+@c    nscd_cache_search ok
+@c     nis_hash ok
+@c     memcmp dup ok
+@c    nscd_open_socket @acsfd
+@c     open_socket @acsfd
+@c      socket dup @acsfd
+@c      fcntl dup ok
+@c      strcpy dup ok
+@c      connect dup ok
+@c      send dup ok
+@c      gettimeofday dup ok
+@c      poll dup ok
+@c      close_not_cancel_no_status dup @acsfd
+@c     wait_on_socket dup ok
+@c     read dup ok
+@c     close_not_cancel_no_status dup @acsfd
+@c    readall ok
+@c     read dup ok
+@c     wait_on_socket ok
+@c      poll dup ok
+@c      gettimeofday dup ok
+@c    memcpy dup ok
+@c    close_not_cancel_no_status dup @acsfd
+@c    nscd_drop_map_ref @ascuheap @acsmem
+@c     nscd_unmap dup @ascuheap @acsmem
+@c    nscd_unmap @ascuheap @acsmem
+@c     munmap dup ok
+@c     free dup @ascuheap @acsmem
+@c  nss_passwd_lookup2 @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   nss_database_lookup @mtslocale @ascuheap @asulock @acucorrupt @acsmem @acsfd @aculock
+@c    libc_lock_lock @asulock @aculock
+@c    libc_lock_unlock @aculock
+@c    nss_parse_file @mtslocale @ascuheap @asulock @acucorrupt @acsmem @acsfd @aculock
+@c     fopen dup @ascuheap @asulock @acsmem @acsfd @aculock
+@c     fsetlocking dup ok [no concurrent uses]
+@c     malloc dup @asulock @aculock @acsfd @acsmem
+@c     fclose dup @ascuheap @asulock @acsmem @acsfd @aculock
+@c     getline dup @ascuheap @aculock @acucorrupt @acsmem
+@c     strchrnul dup ok
+@c     nss_getline @mtslocale @ascuheap @acsmem
+@c      isspace @mtslocale^^
+@c      strlen dup ok
+@c      malloc dup @asulock @aculock @acsfd @acsmem
+@c      memcpy dup ok
+@c      nss_parse_service_list dup @mtslocale^, @ascuheap @acsmem
+@c     feof_unlocked dup ok
+@c     free dup @asulock @aculock @acsfd @acsmem
+@c    strcmp dup ok
+@c    nss_parse_service_list @mtslocale^, @ascuheap @acsmem
+@c     isspace @mtslocale^^
+@c     malloc dup @asulock @aculock @acsfd @acsmem
+@c     mempcpy dup ok
+@c     strncasecmp dup ok
+@c     free dup @asulock @aculock @acsfd @acsmem
+@c    malloc dup @asulock @aculock @acsfd @acsmem
+@c   nss_lookup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c    nss_lookup_function @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c     libc_lock_lock @asulock @aculock
+@c     tsearch @ascuheap @acucorrupt @acsmem [no @mtsrace or @asucorrupt due to locking]
+@c      known_compare ok
+@c       strcmp dup ok
+@c     malloc dup @ascuheap @acsmem
+@c     tdelete @ascuheap @acucorrupt @acsmem [no @mtsrace or @asucorrupt due to locking]
+@c     free dup @ascuheap @acsmem
+@c     nss_load_library @ascudlopen @ascuplugin @ascuheap @asulock @aculock @acsfd @acsmem
+@c      nss_new_service @ascuheap @acsmem
+@c       strcmp dup ok
+@c       malloc dup @ascuheap @acsmem
+@c      strlen dup ok
+@c      stpcpy dup ok
+@c      libc_dlopen @ascudlopen @ascuheap @asulock @aculock @acsfd @acsmem
+@c      libc_dlsym dup @asulock @aculock @acsfd @acsmem
+@c      *ifct(*nscd_init_cb) @ascuplugin
+@c     stpcpy dup ok
+@c     libc_dlsym dup @asulock @aculock @acsfd @acsmem
+@c     libc_lock_unlock dup ok
+@c    nss_next_action ok
+@c  *fct.l -> _nss_*_getpwuid_r @ascuplugin
+@c  nss_next2 @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   nss_next_action dup ok
+@c   nss_lookup_function dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+
+@c _nss_files_getpwuid_r @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd
+@c  libc_lock_lock dup @asulock @aculock
+@c  internal_setent @ascuheap @asulock @aculock @acsmem @acsfd
+@c   fopen dup @ascuheap @asulock @acsmem @acsfd @aculock
+@c   fileno dup ok
+@c   fcntl dup ok
+@c   fclose dup @ascuheap @asulock @aculock @acsmem @acsfd
+@c   rewind dup @aculock [stream guarded by non-recursive pwent lock]
+@c  internal_getent @mtslocale^
+@c   fgets_unlocked dup ok [stream guarded by non-recursive pwent lock]
+@c   isspace dup @mtslocale^^
+@c   _nss_files_parse_pwent = parse_line ok
+@c    strpbrk dup ok
+@c  internal_endent @ascuheap @asulock @aculock @acsmem @acsfd
+@c   fclose dup @ascuheap @asulock @aculock @acsmem @acsfd
+@c  libc_lock_unlock dup @aculock
+
+@c _nss_nis_getpwuid_r ... not fully reviewed (assumed) @asuinit @asulock @acucorrupt @aculock
+@c  yp_get_default_domain @asulock @aculock
+@c   libc_lock_lock dup @asulock @aculock
+@c   getdomainname dup ok
+@c   strcmp dup ok
+@c   libc_lock_unlock dup @aculock
+@c  snprintf dup @ascuheap @acsmem
+@c  yp_match
+@c   do_ypcall_tr(xdr_ypreq_key,xdr_ypresp_val)
+@c    do_ypcall(xdr_ypreq_key,xdr_ypresp_val)
+@c     libc_lock_lock @asulock @aculock
+@c     strcmp
+@c     yp_bind
+@c     ypclnt_call
+@c      clnt_call
+@c      clnt_perror
+@c     libc_lock_unlock @aculock
+@c     yp_unbind_locked
+@c     yp_unbind
+@c      strcmp dup ok
+@c      calloc dup @asulock @aculock @acsfd @acsmem
+@c      yp_bind_file
+@c       strlen dup ok
+@c       snprintf dup @ascuheap @acsmem
+@c       open dup @acsfd [cancelpt]
+@c       pread dup [cancelpt]
+@c       yp_bind_client_create
+@c       close dup @acsfd [cancelpt]
+@c      yp_bind_ypbindprog
+@c       clnttcp_create
+@c       clnt_destroy
+@c       clnt_call(xdr_domainname,xdr_ypbind_resp)
+@c       memset dup ok
+@c       yp_bind_client_create
+@c      free dup @asulock @aculock @acsfd @acsmem
+@c     calloc dup @asulock @aculock @acsfd @acsmem
+@c     free dup @asulock @aculock @acsfd @acsmem
+@c    ypprot_err
+@c   memcpy dup ok
+@c   xdr_free(xdr_ypresp_val)
+@c    xdr_ypresp_val
+@c     xdr_ypstat
+@c      xdr_enum
+@c       XDR_PUTLONG
+@c        *x_putlong
+@c       XDR_GETLONG
+@c        *x_getlong
+@c       xdr_long
+@c        XDR_PUTLONG dup
+@c        XDR_GETLONG dup
+@c       xdr_short
+@c        XDR_PUTLONG dup
+@c        XDR_GETLONG dup
+@c     xdr_valdat
+@c      xdr_bytes
+@c       xdr_u_int
+@c        XDR_PUTLONG dup
+@c        XDR_GETLONG dup
+@c       mem_alloc @ascuheap @acsmem
+@c        malloc dup @ascuheap @acsmem
+@c       xdr_opaque
+@c        XDR_GETBYTES
+@c         *x_getbytes
+@c        XDR_PUTBYTES
+@c         *x_putbytes
+@c       mem_free @ascuheap @acsmem
+@c        free dup @ascuheap @acsmem
+@c  yperr2nss ok
+@c  strchr dup ok
+@c  _nls_default_nss @asuinit @ascuheap @asulock @acucorrupt @acsmem @acsfd @aculock
+@c   init @asuinit^, @ascuheap @asulock @acucorrupt @acsmem @acsfd @aculock
+@c    fopen dup @ascuheap @asulock @acsmem @acsfd @aculock
+@c    fsetlocking ok [no concurrent uses]
+@c    feof_unlocked dup ok
+@c    getline dup @ascuheap @aculock @acucorrupt @acsmem
+@c    isspace dup @mtslocale^^
+@c    strncmp dup ok
+@c    free dup @asulock @acsmem @acsfd @aculock
+@c    fclose dup @ascuheap @asulock @aculock @acsmem @acsfd
+@c  free dup @asulock @acsmem @acsfd @aculock
+@c  mempcpy dup ok
+@c  strncpy dup ok
+@c  isspace dup @mtslocale^^
+@c  _nss_files_parse_pwent ok
+This function is similar to @code{getpwuid} in that it returns
+information about the user whose user ID is @var{uid}.  However, it
+fills the user supplied structure pointed to by @var{result_buf} with
+the information instead of using a static buffer.  The first
+@var{buflen} bytes of the additional buffer pointed to by @var{buffer}
+are used to contain additional information, normally strings which are
+pointed to by the elements of the result structure.
+
+If a user with ID @var{uid} is found, the pointer returned in
+@var{result} points to the record which contains the wanted data (i.e.,
+@var{result} contains the value @var{result_buf}).  If no user is found
+or if an error occurred, the pointer returned in @var{result} is a null
+pointer.  The function returns zero or an error code.  If the buffer
+@var{buffer} is too small to contain all the needed information, the
+error code @code{ERANGE} is returned and @var{errno} is set to
+@code{ERANGE}.
+@end deftypefun
+
+
+@comment pwd.h
+@comment POSIX.1
+@deftypefun {struct passwd *} getpwnam (const char *@var{name})
+@safety{@prelim{}@mtunsafe{@mtasurace{:pwnam} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c getpwnam @mtasurace:pwnam @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  libc_lock_lock dup @asulock @aculock
+@c  malloc dup @ascuheap @acsmem
+@c  getpwnam_r dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  realloc dup @ascuheap @acsmem
+@c  free dup @ascuheap @acsmem
+@c  libc_lock_unlock dup @aculock
+This function returns a pointer to a statically-allocated structure
+containing information about the user whose user name is @var{name}.
+This structure may be overwritten on subsequent calls to
+@code{getpwnam}.
+
+A null pointer return indicates there is no user named @var{name}.
+@end deftypefun
+
+@comment pwd.h
+@comment POSIX.1c
+@deftypefun int getpwnam_r (const char *@var{name}, struct passwd *@var{result_buf}, char *@var{buffer}, size_t @var{buflen}, struct passwd **@var{result})
+@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c getpwnam_r @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  nscd_getpwnam_r @ascuheap @asulock @aculock @acsfd @acsmem
+@c   strlen dup ok
+@c   nscd_getpw_r dup @ascuheap @asulock @aculock @acsfd @acsmem
+@c  nss_passwd_lookup2 dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  *fct.l @ascuplugin
+@c  nss_next2 dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c
+@c _nss_files_getpwnam_r @mtslocale @ascuheap @asulock @aculock @acsmem @acsfd
+@c  libc_lock_lock dup @asulock @aculock
+@c  internal_setent dup @ascuheap @asulock @aculock @acsmem @acsfd
+@c  internal_getent dup @mtslocale^
+@c  strcmp dup ok
+@c  internal_endent dup @ascuheap @asulock @aculock @acsmem @acsfd
+@c  libc_lock_unlock dup @aculock
+@c
+@c _nss_*_getpwnam_r (assumed) @asuinit @asulock @acucorrupt @aculock
+
+This function is similar to @code{getpwnam} in that it returns
+information about the user whose user name is @var{name}.  However, like
+@code{getpwuid_r}, it fills the user supplied buffers in
+@var{result_buf} and @var{buffer} with the information instead of using
+a static buffer.
+
+The return values are the same as for @code{getpwuid_r}.
+@end deftypefun
+
+
+@node Scanning All Users
+@subsection Scanning the List of All Users
+@cindex scanning the user list
+
+This section explains how a program can read the list of all users in
+the system, one user at a time.  The functions described here are
+declared in @file{pwd.h}.
+
+You can use the @code{fgetpwent} function to read user entries from a
+particular file.
+
+@comment pwd.h
+@comment SVID
+@deftypefun {struct passwd *} fgetpwent (FILE *@var{stream})
+@safety{@prelim{}@mtunsafe{@mtasurace{:fpwent}}@asunsafe{@asucorrupt{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{}}}
+@c fgetpwent @mtasurace:fpwent @asucorrupt @asulock @acucorrupt @aculock
+@c  fgetpos dup @asucorrupt @aculock @acucorrupt
+@c  libc_lock_lock dup @asulock @aculock
+@c  malloc dup @ascuheap @acsmem
+@c  fgetpwent_r dup @asucorrupt @acucorrupt @aculock
+@c  realloc dup @ascuheap @acsmem
+@c  free dup @ascuheap @acsmem
+@c  fsetpos dup @asucorrupt @aculock @acucorrupt
+@c  libc_lock_unlock dup @aculock
+This function reads the next user entry from @var{stream} and returns a
+pointer to the entry.  The structure is statically allocated and is
+rewritten on subsequent calls to @code{fgetpwent}.  You must copy the
+contents of the structure if you wish to save the information.
+
+The stream must correspond to a file in the same format as the standard
+password database file.
+@end deftypefun
+
+@comment pwd.h
+@comment GNU
+@deftypefun int fgetpwent_r (FILE *@var{stream}, struct passwd *@var{result_buf}, char *@var{buffer}, size_t @var{buflen}, struct passwd **@var{result})
+@safety{@prelim{}@mtsafe{}@asunsafe{@asucorrupt{}}@acunsafe{@acucorrupt{} @aculock{}}}
+@c fgetpwent_r @asucorrupt @acucorrupt @aculock
+@c  flockfile dup @aculock
+@c  fgets_unlocked @asucorrupt @acucorrupt [no @mtsrace due to explicit locking]
+@c  feof_unlocked dup ok
+@c  funlockfile dup @aculock
+@c  isspace dup @mtslocale^^
+@c  parse_line dup ok
+This function is similar to @code{fgetpwent} in that it reads the next
+user entry from @var{stream}.  But the result is returned in the
+structure pointed to by @var{result_buf}.  The
+first @var{buflen} bytes of the additional buffer pointed to by
+@var{buffer} are used to contain additional information, normally
+strings which are pointed to by the elements of the result structure.
+
+The stream must correspond to a file in the same format as the standard
+password database file.
+
+If the function returns zero @var{result} points to the structure with
+the wanted data (normally this is in @var{result_buf}).  If errors
+occurred the return value is nonzero and @var{result} contains a null
+pointer.
+@end deftypefun
+
+The way to scan all the entries in the user database is with
+@code{setpwent}, @code{getpwent}, and @code{endpwent}.
+
+@comment pwd.h
+@comment SVID, BSD
+@deftypefun void setpwent (void)
+@safety{@prelim{}@mtunsafe{@mtasurace{:pwent} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c setpwent @mtasurace:pwent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  libc_lock_lock @asulock @aculock
+@c  nss_setent(nss_passwd_lookup2) @mtasurace:pwent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c    ** resolv's res_maybe_init not called here
+@c   setup(nss_passwd_lookup2) @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c    *lookup_fct = nss_passwd_lookup2 dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c    nss_lookup dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   *fct.f @mtasurace:pwent @ascuplugin
+@c   nss_next2 dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  libc_lock_unlock @aculock
+This function initializes a stream which @code{getpwent} and
+@code{getpwent_r} use to read the user database.
+@end deftypefun
+
+@comment pwd.h
+@comment POSIX.1
+@deftypefun {struct passwd *} getpwent (void)
+@safety{@prelim{}@mtunsafe{@mtasurace{:pwent} @mtasurace{:pwentbuf} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c getpwent @mtasurace:pwent @mtasurace:pwentbuf @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  libc_lock_lock dup @asulock @aculock
+@c  nss_getent(getpwent_r) @mtasurace:pwent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   malloc dup @ascuheap @acsmem
+@c   *func = getpwent_r dup @mtasurace:pwent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   realloc dup @ascuheap @acsmem
+@c   free dup @ascuheap @acsmem
+@c  libc_lock_unlock dup @aculock
+The @code{getpwent} function reads the next entry from the stream
+initialized by @code{setpwent}.  It returns a pointer to the entry.  The
+structure is statically allocated and is rewritten on subsequent calls
+to @code{getpwent}.  You must copy the contents of the structure if you
+wish to save the information.
+
+A null pointer is returned when no more entries are available.
+@end deftypefun
+
+@comment pwd.h
+@comment GNU
+@deftypefun int getpwent_r (struct passwd *@var{result_buf}, char *@var{buffer}, size_t @var{buflen}, struct passwd **@var{result})
+@safety{@prelim{}@mtunsafe{@mtasurace{:pwent} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c The static buffer here is not the result_buf, but rather the
+@c variables that keep track of what nss backend we've last used, and
+@c whatever internal state the nss backend uses to keep track of the
+@c last read entry.
+@c getpwent_r @mtasurace:pwent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  libc_lock_lock dup @asulock @aculock
+@c  nss_getent_r(nss_passwd_lookup2) @mtasurace:pwent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   setup(nss_passwd_lookup2) dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   *fct.f @mtasurace:pwent @ascuplugin
+@c   nss_next2 dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   nss_lookup dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   *sfct.f @mtasurace:pwent @ascuplugin
+@c  libc_lock_unlock dup @aculock
+This function is similar to @code{getpwent} in that it returns the next
+entry from the stream initialized by @code{setpwent}.  Like
+@code{fgetpwent_r}, it uses the user-supplied buffers in
+@var{result_buf} and @var{buffer} to return the information requested.
+
+The return values are the same as for @code{fgetpwent_r}.
+
+@end deftypefun
+
+@comment pwd.h
+@comment SVID, BSD
+@deftypefun void endpwent (void)
+@safety{@prelim{}@mtunsafe{@mtasurace{:pwent} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c endpwent @mtasurace:pwent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  libc_lock_lock @asulock @aculock
+@c  nss_endent(nss_passwd_lookup2) @mtasurace:pwent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c    ** resolv's res_maybe_init not called here
+@c   setup(nss_passwd_lookup2) dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   *fct.f @mtasurace:pwent @ascuplugin
+@c   nss_next2 dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  libc_lock_unlock @aculock
+This function closes the internal stream used by @code{getpwent} or
+@code{getpwent_r}.
+@end deftypefun
+
+@node Writing a User Entry
+@subsection Writing a User Entry
+
+@comment pwd.h
+@comment SVID
+@deftypefun int putpwent (const struct passwd *@var{p}, FILE *@var{stream})
+@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@asucorrupt{}}@acunsafe{@aculock{} @acucorrupt{}}}
+@c putpwent @mtslocale @asucorrupt @aculock @acucorrupt
+@c  fprintf dup @mtslocale @asucorrupt @aculock @acucorrupt [no @ascuheap @acsmem]
+This function writes the user entry @code{*@var{p}} to the stream
+@var{stream}, in the format used for the standard user database
+file.  The return value is zero on success and nonzero on failure.
+
+This function exists for compatibility with SVID.  We recommend that you
+avoid using it, because it makes sense only on the assumption that the
+@code{struct passwd} structure has no members except the standard ones;
+on a system which merges the traditional Unix data base with other
+extended information about users, adding an entry using this function
+would inevitably leave out much of the important information.
+@c Then how are programmers to modify the password file? -zw
+
+The group and user ID fields are left empty if the group or user name
+starts with a - or +.
+
+The function @code{putpwent} is declared in @file{pwd.h}.
+@end deftypefun
+
+@node Group Database
+@section Group Database
+@cindex group database
+@pindex /etc/group
+
+This section describes how to search and scan the database of
+registered groups.  The database itself is kept in the file
+@file{/etc/group} on most systems, but on some systems a special network
+service provides access to it.
+
+@menu
+* Group Data Structure::        What each group record contains.
+* Lookup Group::                How to look for a particular group.
+* Scanning All Groups::         Scanning the list of all groups.
+@end menu
+
+@node Group Data Structure
+@subsection The Data Structure for a Group
+
+The functions and data structures for accessing the system group
+database are declared in the header file @file{grp.h}.
+@pindex grp.h
+
+@comment grp.h
+@comment POSIX.1
+@deftp {Data Type} {struct group}
+The @code{group} structure is used to hold information about an entry in
+the system group database.  It has at least the following members:
+
+@table @code
+@item char *gr_name
+The name of the group.
+
+@item gid_t gr_gid
+The group ID of the group.
+
+@item char **gr_mem
+A vector of pointers to the names of users in the group.  Each user name
+is a null-terminated string, and the vector itself is terminated by a
+null pointer.
+@end table
+@end deftp
+
+@node Lookup Group
+@subsection Looking Up One Group
+@cindex converting group name to group ID
+@cindex converting group ID to group name
+
+You can search the group database for information about a specific
+group using @code{getgrgid} or @code{getgrnam}.  These functions are
+declared in @file{grp.h}.
+
+@comment grp.h
+@comment POSIX.1
+@deftypefun {struct group *} getgrgid (gid_t @var{gid})
+@safety{@prelim{}@mtunsafe{@mtasurace{:grgid} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c getgrgid =~ getpwuid dup @mtasurace:grgid @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  getgrgid_r dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+This function returns a pointer to a statically-allocated structure
+containing information about the group whose group ID is @var{gid}.
+This structure may be overwritten by subsequent calls to
+@code{getgrgid}.
+
+A null pointer indicates there is no group with ID @var{gid}.
+@end deftypefun
+
+@comment grp.h
+@comment POSIX.1c
+@deftypefun int getgrgid_r (gid_t @var{gid}, struct group *@var{result_buf}, char *@var{buffer}, size_t @var{buflen}, struct group **@var{result})
+@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c getgrgid_r =~ getpwuid_r dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  nscd_getgrgid_r @ascuheap @acsfd @acsmem
+@c   itoa_word dup ok
+@c   nscd_getgr_r @ascuheap @acsfd @acsmem
+@c    nscd_get_map_ref dup @ascuheap @acsfd @acsmem
+@c    nscd_cache_search dup ok
+@c    nscd_open_socket dup @acsfd
+@c    readvall ok
+@c     readv dup ok
+@c     memcpy dup ok
+@c      wait_on_socket dup ok
+@c    memcpy dup ok
+@c    readall dup ok
+@c    close_not_cancel_no_status dup @acsfd
+@c    nscd_drop_map_ref dup @ascuheap @acsmem
+@c    nscd_unmap dup @ascuheap @acsmem
+@c  nss_group_lookup2 =~ nss_passwd_lookup2 dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  *fct.l -> _nss_*_getgrgid_r @ascuplugin
+@c  nss_next2 dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+This function is similar to @code{getgrgid} in that it returns
+information about the group whose group ID is @var{gid}.  However, it
+fills the user supplied structure pointed to by @var{result_buf} with
+the information instead of using a static buffer.  The first
+@var{buflen} bytes of the additional buffer pointed to by @var{buffer}
+are used to contain additional information, normally strings which are
+pointed to by the elements of the result structure.
+
+If a group with ID @var{gid} is found, the pointer returned in
+@var{result} points to the record which contains the wanted data (i.e.,
+@var{result} contains the value @var{result_buf}).  If no group is found
+or if an error occurred, the pointer returned in @var{result} is a null
+pointer.  The function returns zero or an error code.  If the buffer
+@var{buffer} is too small to contain all the needed information, the
+error code @code{ERANGE} is returned and @var{errno} is set to
+@code{ERANGE}.
+@end deftypefun
+
+@comment grp.h
+@comment SVID, BSD
+@deftypefun {struct group *} getgrnam (const char *@var{name})
+@safety{@prelim{}@mtunsafe{@mtasurace{:grnam} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c getgrnam =~ getpwnam dup @mtasurace:grnam @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  getgrnam_r dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+This function returns a pointer to a statically-allocated structure
+containing information about the group whose group name is @var{name}.
+This structure may be overwritten by subsequent calls to
+@code{getgrnam}.
+
+A null pointer indicates there is no group named @var{name}.
+@end deftypefun
+
+@comment grp.h
+@comment POSIX.1c
+@deftypefun int getgrnam_r (const char *@var{name}, struct group *@var{result_buf}, char *@var{buffer}, size_t @var{buflen}, struct group **@var{result})
+@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c getgrnam_r =~ getpwnam_r dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  nscd_getgrnam_r @ascuheap @asulock @aculock @acsfd @acsmem
+@c   strlen dup ok
+@c   nscd_getgr_r dup @ascuheap @asulock @aculock @acsfd @acsmem
+@c  nss_group_lookup2 dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  *fct.l @ascuplugin
+@c  nss_next2 dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+This function is similar to @code{getgrnam} in that it returns
+information about the group whose group name is @var{name}.  Like
+@code{getgrgid_r}, it uses the user supplied buffers in
+@var{result_buf} and @var{buffer}, not a static buffer.
+
+The return values are the same as for @code{getgrgid_r}.
+@end deftypefun
+
+@node Scanning All Groups
+@subsection Scanning the List of All Groups
+@cindex scanning the group list
+
+This section explains how a program can read the list of all groups in
+the system, one group at a time.  The functions described here are
+declared in @file{grp.h}.
+
+You can use the @code{fgetgrent} function to read group entries from a
+particular file.
+
+@comment grp.h
+@comment SVID
+@deftypefun {struct group *} fgetgrent (FILE *@var{stream})
+@safety{@prelim{}@mtunsafe{@mtasurace{:fgrent}}@asunsafe{@asucorrupt{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{}}}
+@c fgetgrent @mtasurace:fgrent @asucorrupt @asulock @acucorrupt @aculock
+@c  fgetpos dup @asucorrupt @aculock @acucorrupt
+@c  libc_lock_lock dup @asulock @aculock
+@c  malloc dup @ascuheap @acsmem
+@c  fgetgrent_r dup @asucorrupt @acucorrupt @aculock
+@c  realloc dup @ascuheap @acsmem
+@c  free dup @ascuheap @acsmem
+@c  fsetpos dup @asucorrupt @aculock @acucorrupt
+@c  libc_lock_unlock dup @aculock
+The @code{fgetgrent} function reads the next entry from @var{stream}.
+It returns a pointer to the entry.  The structure is statically
+allocated and is overwritten on subsequent calls to @code{fgetgrent}.  You
+must copy the contents of the structure if you wish to save the
+information.
+
+The stream must correspond to a file in the same format as the standard
+group database file.
+@end deftypefun
+
+@comment grp.h
+@comment GNU
+@deftypefun int fgetgrent_r (FILE *@var{stream}, struct group *@var{result_buf}, char *@var{buffer}, size_t @var{buflen}, struct group **@var{result})
+@safety{@prelim{}@mtsafe{}@asunsafe{@asucorrupt{}}@acunsafe{@acucorrupt{} @aculock{}}}
+@c fgetgrent_r @asucorrupt @acucorrupt @aculock
+@c  flockfile dup @aculock
+@c  fgets_unlocked @asucorrupt @acucorrupt [no @mtsrace due to explicit locking]
+@c  feof_unlocked dup ok
+@c  funlockfile dup @aculock
+@c  isspace dup @mtslocale^^
+@c  parse_line dup ok
+This function is similar to @code{fgetgrent} in that it reads the next
+user entry from @var{stream}.  But the result is returned in the
+structure pointed to by @var{result_buf}.  The first @var{buflen} bytes
+of the additional buffer pointed to by @var{buffer} are used to contain
+additional information, normally strings which are pointed to by the
+elements of the result structure.
+
+This stream must correspond to a file in the same format as the standard
+group database file.
+
+If the function returns zero @var{result} points to the structure with
+the wanted data (normally this is in @var{result_buf}).  If errors
+occurred the return value is non-zero and @var{result} contains a null
+pointer.
+@end deftypefun
+
+The way to scan all the entries in the group database is with
+@code{setgrent}, @code{getgrent}, and @code{endgrent}.
+
+@comment grp.h
+@comment SVID, BSD
+@deftypefun void setgrent (void)
+@safety{@prelim{}@mtunsafe{@mtasurace{:grent} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c setgrent =~ setpwent dup @mtasurace:grent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c ...*lookup_fct = nss_group_lookup2 dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+This function initializes a stream for reading from the group data base.
+You use this stream by calling @code{getgrent} or @code{getgrent_r}.
+@end deftypefun
+
+@comment grp.h
+@comment SVID, BSD
+@deftypefun {struct group *} getgrent (void)
+@safety{@prelim{}@mtunsafe{@mtasurace{:grent} @mtasurace{:grentbuf} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c getgrent =~ getpwent dup @mtasurace:grent @mtasurace:grentbuf @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   *func = getgrent_r dup @mtasurace:grent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+The @code{getgrent} function reads the next entry from the stream
+initialized by @code{setgrent}.  It returns a pointer to the entry.  The
+structure is statically allocated and is overwritten on subsequent calls
+to @code{getgrent}.  You must copy the contents of the structure if you
+wish to save the information.
+@end deftypefun
+
+@comment grp.h
+@comment GNU
+@deftypefun int getgrent_r (struct group *@var{result_buf}, char *@var{buffer}, size_t @var{buflen}, struct group **@var{result})
+@safety{@prelim{}@mtunsafe{@mtasurace{:grent} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c getgrent_r =~ getpwent_r dup @mtasurace:grent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+This function is similar to @code{getgrent} in that it returns the next
+entry from the stream initialized by @code{setgrent}.  Like
+@code{fgetgrent_r}, it places the result in user-supplied buffers
+pointed to by @var{result_buf} and @var{buffer}.
+
+If the function returns zero @var{result} contains a pointer to the data
+(normally equal to @var{result_buf}).  If errors occurred the return
+value is non-zero and @var{result} contains a null pointer.
+@end deftypefun
+
+@comment grp.h
+@comment SVID, BSD
+@deftypefun void endgrent (void)
+@safety{@prelim{}@mtunsafe{@mtasurace{:grent} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c endgrent =~ endpwent dup @mtasurace:grent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+This function closes the internal stream used by @code{getgrent} or
+@code{getgrent_r}.
+@end deftypefun
+
+@node Database Example
+@section User and Group Database Example
+
+Here is an example program showing the use of the system database inquiry
+functions.  The program prints some information about the user running
+the program.
+
+@smallexample
+@include db.c.texi
+@end smallexample
+
+Here is some output from this program:
+
+@smallexample
+I am Throckmorton Snurd.
+My login name is snurd.
+My uid is 31093.
+My home directory is /home/fsg/snurd.
+My default shell is /bin/sh.
+My default group is guest (12).
+The members of this group are:
+  friedman
+  tami
+@end smallexample
+
+@node Netgroup Database
+@section Netgroup Database
+
+@menu
+* Netgroup Data::                  Data in the Netgroup database and where
+                                   it comes from.
+* Lookup Netgroup::                How to look for a particular netgroup.
+* Netgroup Membership::            How to test for netgroup membership.
+@end menu
+
+@node Netgroup Data
+@subsection Netgroup Data
+
+@cindex Netgroup
+Sometimes it is useful to group users according to other criteria
+(@pxref{Group Database}).  E.g., it is useful to associate a certain
+group of users with a certain machine.  On the other hand grouping of
+host names is not supported so far.
+
+In Sun Microsystems' SunOS appeared a new kind of database, the netgroup
+database.  It allows grouping hosts, users, and domains freely, giving
+them individual names.  To be more concrete, a netgroup is a list of triples
+consisting of a host name, a user name, and a domain name where any of
+the entries can be a wildcard entry matching all inputs.  A last
+possibility is that names of other netgroups can also be given in the
+list specifying a netgroup.  So one can construct arbitrary hierarchies
+without loops.
+
+Sun's implementation allows netgroups only for the @code{nis} or
+@code{nisplus} service, @pxref{Services in the NSS configuration}.  The
+implementation in @theglibc{} has no such restriction.  An entry
+in either of the input services must have the following form:
+
+@smallexample
+@var{groupname} ( @var{groupname} | @code{(}@var{hostname}@code{,}@var{username}@code{,}@code{domainname}@code{)} )+
+@end smallexample
+
+Any of the fields in the triple can be empty which means anything
+matches.  While describing the functions we will see that the opposite
+case is useful as well.  I.e., there may be entries which will not
+match any input.  For entries like this, a name consisting of the single
+character @code{-} shall be used.
+
+@node Lookup Netgroup
+@subsection Looking up one Netgroup
+
+The lookup functions for netgroups are a bit different than all other
+system database handling functions.  Since a single netgroup can contain
+many entries a two-step process is needed.  First a single netgroup is
+selected and then one can iterate over all entries in this netgroup.
+These functions are declared in @file{netdb.h}.
+
+@comment netdb.h
+@comment BSD
+@deftypefun int setnetgrent (const char *@var{netgroup})
+@safety{@prelim{}@mtunsafe{@mtasurace{:netgrent} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c setnetgrent @mtasurace:netgrent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  libc_lock_lock dup @asulock @aculock
+@c  nscd_setnetgrent @ascuheap @acsfd @acsmem
+@c   __nscd_setnetgrent @ascuheap @acsfd @acsmem
+@c    strlen dup ok
+@c    nscd_get_map_ref dup @ascuheap @acsfd @acsmem
+@c    nscd_cache_search dup ok
+@c    nscd_open_socket dup @acsfd
+@c    malloc dup @ascuheap @acsmem
+@c    readall dup ok
+@c    free dup @ascuheap @acsmem
+@c    close_not_cancel_no_status dup @acsfd
+@c    nscd_drop_map_ref dup @ascuheap @acsmem
+@c    nscd_unmap dup @ascuheap @acsmem
+@c  internal_setnetgrent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   free_memory dup @ascuheap @acsmem
+@c    free dup @ascuheap @acsmem
+@c   internal_setnetgrent_reuse @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c    endnetgrent_hook dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c     nss_lookup_function dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c     *endfct @ascuplugin
+@c    (netgroup::)setup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c     nss_netgroup_lookup dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c      nss_netgroup_lookup2 =~ nss_passwd_lookup2 dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c     nss_lookup dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c    *fct.f @ascuplugin
+@c    nss_next2 dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c    nss_lookup_function dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c    *endfct @ascuplugin
+@c    strlen dup ok
+@c    malloc dup @ascuheap @acsmem
+@c    memcpy dup ok
+@c  libc_lock_unlock dup @aculock
+A call to this function initializes the internal state of the library to
+allow following calls of @code{getnetgrent} to iterate over all entries
+in the netgroup with name @var{netgroup}.
+
+When the call is successful (i.e., when a netgroup with this name exists)
+the return value is @code{1}.  When the return value is @code{0} no
+netgroup of this name is known or some other error occurred.
+@end deftypefun
+
+It is important to remember that there is only one single state for
+iterating the netgroups.  Even if the programmer uses the
+@code{getnetgrent_r} function the result is not really reentrant since
+always only one single netgroup at a time can be processed.  If the
+program needs to process more than one netgroup simultaneously she
+must protect this by using external locking.  This problem was
+introduced in the original netgroups implementation in SunOS and since
+we must stay compatible it is not possible to change this.
+
+Some other functions also use the netgroups state.  Currently these are
+the @code{innetgr} function and parts of the implementation of the
+@code{compat} service part of the NSS implementation.
+
+@comment netdb.h
+@comment BSD
+@deftypefun int getnetgrent (char **@var{hostp}, char **@var{userp}, char **@var{domainp})
+@safety{@prelim{}@mtunsafe{@mtasurace{:netgrent} @mtasurace{:netgrentbuf} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c getnetgrent @mtasurace:netgrent @mtasurace:netgrentbuf @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   uses unsafely a static buffer allocated within a libc_once call
+@c  allocate (libc_once) @ascuheap @acsmem
+@c   malloc dup @ascuheap @acsmem
+@c  getnetgrent_r dup @mtasurace:netgrent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+This function returns the next unprocessed entry of the currently
+selected netgroup.  The string pointers, in which addresses are passed in
+the arguments @var{hostp}, @var{userp}, and @var{domainp}, will contain
+after a successful call pointers to appropriate strings.  If the string
+in the next entry is empty the pointer has the value @code{NULL}.
+The returned string pointers are only valid if none of the netgroup
+related functions are called.
+
+The return value is @code{1} if the next entry was successfully read.  A
+value of @code{0} means no further entries exist or internal errors occurred.
+@end deftypefun
+
+@comment netdb.h
+@comment GNU
+@deftypefun int getnetgrent_r (char **@var{hostp}, char **@var{userp}, char **@var{domainp}, char *@var{buffer}, size_t @var{buflen})
+@safety{@prelim{}@mtunsafe{@mtasurace{:netgrent} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c getnetgrent_r @mtasurace:netgrent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  libc_lock_lock dup @asulock @aculock
+@c  internal_getnetgrent_r @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   nss_lookup_function dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   *fct @ascuplugin
+@c   nscd_getnetgrent ok
+@c    rawmemchr dup ok
+@c   internal_setnetgrent_reuse dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   strcmp dup ok
+@c   malloc dup @ascuheap @acsmem
+@c   memcpy dup ok
+@c  libc_lock_unlock dup @aculock
+This function is similar to @code{getnetgrent} with only one exception:
+the strings the three string pointers @var{hostp}, @var{userp}, and
+@var{domainp} point to, are placed in the buffer of @var{buflen} bytes
+starting at @var{buffer}.  This means the returned values are valid
+even after other netgroup related functions are called.
+
+The return value is @code{1} if the next entry was successfully read and
+the buffer contains enough room to place the strings in it.  @code{0} is
+returned in case no more entries are found, the buffer is too small, or
+internal errors occurred.
+
+This function is a GNU extension.  The original implementation in the
+SunOS libc does not provide this function.
+@end deftypefun
+
+@comment netdb.h
+@comment BSD
+@deftypefun void endnetgrent (void)
+@safety{@prelim{}@mtunsafe{@mtasurace{:netgrent}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c endnetgrent @mtasurace:netgrent @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  libc_lock_lock dup @asulock @aculock
+@c  internal_endnetgrent @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   endnetgrent_hook dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c   free_memory dup @ascuheap @acsmem
+@c  libc_lock_unlock dup @aculock
+This function frees all buffers which were allocated to process the last
+selected netgroup.  As a result all string pointers returned by calls
+to @code{getnetgrent} are invalid afterwards.
+@end deftypefun
+
+@node Netgroup Membership
+@subsection Testing for Netgroup Membership
+
+It is often not necessary to scan the whole netgroup since often the
+only interesting question is whether a given entry is part of the
+selected netgroup.
+
+@comment netdb.h
+@comment BSD
+@deftypefun int innetgr (const char *@var{netgroup}, const char *@var{host}, const char *@var{user}, const char *@var{domain})
+@safety{@prelim{}@mtunsafe{@mtasurace{:netgrent} @mtslocale{}}@asunsafe{@ascudlopen{} @ascuplugin{} @ascuheap{} @asulock{}}@acunsafe{@acucorrupt{} @aculock{} @acsfd{} @acsmem{}}}
+@c This function does not use the static data structure that the
+@c *netgrent* ones do, but since each nss must maintains internal state
+@c to support iteration and concurrent iteration will interfere
+@c destructively, we regard this internal state as a static buffer.
+@c getnetgrent_r iteration in each nss backend.
+@c innetgr @mtasurace:netgrent @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  nscd_innetgr @ascuheap @acsfd @acsmem
+@c   strlen dup ok
+@c   malloc dup @ascuheap @acsmem
+@c   stpcpy dup ok
+@c   nscd_get_map_ref dup @ascuheap @acsfd @acsmem
+@c   nscd_cache_search dup ok
+@c   nscd_open_socket dup @acsfd
+@c   close_not_cancel_no_status dup @acsfd
+@c   nscd_drop_map_ref dup @ascuheap @acsmem
+@c   nscd_unmap dup @ascuheap @acsmem
+@c   free dup @ascuheap @acsmem
+@c  memset dup ok
+@c  (netgroup::)setup dup @mtslocale @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  *setfct.f @ascuplugin
+@c  nss_lookup_function dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  *getfct @ascuplugin
+@c  strcmp dup ok
+@c  strlen dup ok
+@c  malloc dup @ascuheap @acsmem
+@c  memcpy dup ok
+@c  strcasecmp dup
+@c  *endfct @ascuplugin
+@c  nss_next2 dup @ascudlopen @ascuplugin @ascuheap @asulock @acucorrupt @aculock @acsfd @acsmem
+@c  free_memory dup @ascuheap @acsmem
+This function tests whether the triple specified by the parameters
+@var{host}, @var{user}, and @var{domain} is part of the netgroup
+@var{netgroup}.  Using this function has the advantage that
+
+@enumerate
+@item
+no other netgroup function can use the global netgroup state since
+internal locking is used and
+@item
+the function is implemented more efficiently than successive calls
+to the other @code{set}/@code{get}/@code{endnetgrent} functions.
+@end enumerate
+
+Any of the pointers @var{host}, @var{user}, or @var{domain} can be
+@code{NULL} which means any value is accepted in this position.  This is
+also true for the name @code{-} which should not match any other string
+otherwise.
+
+The return value is @code{1} if an entry matching the given triple is
+found in the netgroup.  The return value is @code{0} if the netgroup
+itself is not found, the netgroup does not contain the triple or
+internal errors occurred.
+@end deftypefun
+
+@c FIXME these are undocumented:
+@c setresgid
+@c setresuid