8.2 Getting User and Group Information on Unix
8.2.1 Problem
You need to
discover information about a user or group, and you have a username
or user ID or a group name or ID.
8.2.2 Solution
On Unix, user and group names correspond to numeric identifiers. Most
system calls require numeric identifiers upon which to operate, but
names are typically easier for people to remember. Therefore, most
user interactions involve the use of names rather than numbers. The
standard C runtime library provides several functions to map between
names and numeric identifiers for both groups and users.
8.2.3 Discussion
Declarations for the functions and data types needed to map between
names and numeric identifiers for users are in the header file
pwd.h. Strictly speaking, mapping functions do
not actually exist. Instead, one function provides the ability to
look up user information using the user's numeric
identifier, and another function provides the ability to look up user
information using the user's name.
The function used to look up user information by numeric identifier
has the following signature:
#include <sys/types.h>
#include <pwd.h>
struct passwd *getpwuid(uid_t uid);
The function used to look up user information by name has the
following signature:
#include <sys/types.h>
#include <pwd.h>
struct passwd *getpwnam(const char *name);
Both functions return a pointer to a structure allocated internally
by the runtime library. One side effect of this behavior is that
successive calls replace the information from the previous call.
Another is that the functions are not thread-safe. If either function
fails to find the requested user information, a
NULL pointer is returned.
The contents of the passwd structure differ across
platforms, but some fields remain the same everywhere. Of particular
interest to us in this recipe are the two fields
pw_name
and
pw_uid.
These two fields are what enable mapping between names and numeric
identifiers. For example, the following two functions will obtain
mappings:
#include <sys/types.h>
#include <pwd.h>
#include <string.h>
int spc_user_getname(uid_t uid, char **name) {
struct passwd *pw;
if (!(pw = getpwuid(uid)) ) {
endpwent( );
return -1;
}
*name = strdup(pw->pw_name);
endpwent( );
return 0;
}
int spc_user_getuid(char *name, uid_t *uid) {
struct passwd *pw;
if (!(pw = getpwnam(name))) {
endpwent( );
return -1;
}
*uid = pw->pw_uid;
endpwent( );
return 0;
}
Note that spc_user_getname( ) will dynamically
allocate a buffer to return the user's name, which
must be freed by the caller. Also notice the use of the function
endpwent( ). This function frees any resources allocated
by the lookup functions. Its use is important because failure to free
the resources can cause unexpected leaking of memory, file
descriptors, socket descriptors, and so on. Exactly what types of
resources may be leaked vary depending on the underlying
implementation, which may differ not only from platform to platform,
but also from installation to installation.
In our example code, we call endpwent( ) after
every lookup operation, but this isn't necessary if
you need to perform multiple lookups. In fact, if you know you will
be performing a large number of lookups, always calling
endpwent( ) after each one is wasteful. Any number
of lookup operations may be performed safely before eventually
calling endpwent( ).
Looking up group information is similar to looking up user
information. The header file
grp.h contains the declarations for the needed
functions and data types. Two functions similar to getpwnam(
) and getpwuid( ) also exist for
groups:
#include <sys/types.h>
#include <grp.h>
struct group *getgrgid(gid_t gid);
struct group *getgrnam(const char *name);
These two functions behave as their user counterparts do. Thus, we
can use them to perform name-to-numeric-identifier mappings, and vice
versa. Just as user information lookups require a call to
endpwent( ) to clean up any resources allocated
during the lookup, group information lookups require a call to
endgrent( ) to do the same.
#include <sys/types.h>
#include <grp.h>
#include <string.h>
int spc_group_getname(gid_t gid, char **name) {
struct group *gr;
if (!(gr = getgruid(gid)) ) {
endgrent( );
return -1;
}
*name = strdup(gr->gr_name);
endgrent( );
return 0;
}
int spc_group_getgid(char *name, gid_t *gid) {
struct group *gr;
if (!(gr = getgrnam(name))) {
endgrent( );
return -1;
}
*gid = gr->gr_gid;
endgrent( );
return 0;
}
Groups may contain more than a single user. Theoretically, groups may
contain any number of members, but be aware that some implementations
may impose artificial limits on the number of users that may belong
to a group.
The group structure that is returned by either
getgrnam( ) or getgrgid( )
contains a field called gr_mem that is an array of
strings containing the names of all the member users. The last
element in the array will always be a NULL
pointer. Determining whether a user is a member of a group is a
simple matter of iterating over the elements in the array, comparing
each one to the name of the user for which to
look:
#include <sys/types.h>
#include <grp.h>
#include <string.h>
int spc_group_ismember(char *group_name, char *user_name) {
int i;
struct group *gr;
if (!(gr = getgrnam(group_name))) {
endgrent( );
return 0;
}
for (i = 0; gr->gr_mem[i]; i++)
if (!strcmp(user_name, gr->gr_mem[i])) {
endgrent( );
return 1;
}
endgrent( );
return 0;
}
|