Add Book to My BookshelfPurchase This Book Online

Chapter 8 - Users and Groups

UNIX Systems Programming for SVR4
David A. Curry
 Copyright © 1996 O'Reilly & Associates, Inc.

The Group File
The group file, /etc/group, contains one entry for each group on the system. Each entry is contained on a single line, and consists of several colon-separated fields. The last field is a comma-separated list of login names; these users are members of the group. The format of an entry is described for programs by the include file grp.h:
    struct group {
        char     *gr_name;
        char     *gr_passwd;
        gid_t     gr_gid;
        char    **gr_mem;
    };
The meanings of the fields are:
gr_name
The name of the group.
gr_passwd
This field is usually blank. If it is not blank, it contains a 13-character encrypted password (just like the password file). When the newgrp command is executed, if a password is present, the user must enter that password to gain access to the new group. With the advent of group membership lists, in which a user is in all of his groups at once, this field has become mostly obsolete.
gr_gid
The group-id number of the group.
gr_mem
An array of pointers to character strings; each string contains the login name of one of the members of the group. The list is terminated by a null pointer.
If you've been reading the previous sections, the functions for reading the group file should look very familiar:
    #include <grp.h>
    struct group *getgrnam(const char *name);
    struct group *getgrgid(gid_t gid);
    struct group *fgetgrent(FILE *fp);
    struct group *getgrent(void);
    void setgrent(void);
    void endgrent(void);
The getgrnam function searches the group file for an entry with the group name contained in name. The getgrgid function searches for an entry with the group ID number equal to gid. To read the group file one entry at a time, getgrent is used; fgetgrent allows an alternate file to be read. All of these functions return a pointer to a structure of type struct group, or the constant NULL if an entry cannot be found or end-of-file is encountered.
The setgrent function opens the group file and sets the read/write offset to the beginning of the file, while endgrent closes the file.
In order to initialize a user's group membership list, the initgroups function is provided:
    #include <sys/types.h>
    #include <grp.h>
    int initgroups(const char *name, gid_t basegid);
 NoteThe initgroups function prototype is declared in unistd.h on HP-UX 10.x systems.
The name parameter contains a login name, and basegid contains the login's primary group ID number from the password file. The initgroups function reads the group file, and for each group that lists name in its membership list, adds that group ID number to an array of group ID numbers. It then calls setgroups to initialize the group membership list. If the function is successful, 0 is returned. Otherwise, -1 is returned and the external integer errno is set to indicate the error.
Example 8-1 shows a modified version of the listfiles program from Chapter 5. This program, you'll recall, reads each directory named on its command line and displays a line for each file in the directory, much like the ls -l command. In the original program, we printed out the numeric user ID and group ID for each file; in Example 8-1, we have modified the program to print out the login name and group name.
Example 8-1:  newlistfiles
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <dirent.h>
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
char     typeOfFile(mode_t);
char    *permOfFile(mode_t);
void     outputStatInfo(char *, char *, struct stat *);
int
main(int argc, char **argv)
{
    DIR *dp;
    char *dirname;
    struct stat st;
    struct dirent *d;
    char filename[BUFSIZ+1];
    /*
     * For each directory on the command line...
     */
    while (--argc) {
        dirname = *++argv;
        /*
         * Open the directory.
         */
        if ((dp = opendir(dirname)) == NULL) {
            perror(dirname);
            continue;
        }
        printf("%s:\n", dirname);
        /*
         * For each file in the directory...
         */
        while ((d = readdir(dp)) != NULL) {
            /*
             * Create the full file name.
             */
            sprintf(filename, "%s/%s", dirname, d->d_name);
            /*
            * Find out about it.
            */
            if (lstat(filename, &st) < 0) {
                perror(filename);
                putchar('\n');
                continue;
            }
            /*
             * Print out the information.
             */
            outputStatInfo(filename, d->d_name, &st);
            putchar('\n');
        }
        putchar('\n');
        closedir(dp);
    }
    exit(0);
}
/*
* outputStatInfo - print out the contents of the stat structure.
*/
void
outputStatInfo(char *pathname, char *filename, struct stat *st)
{
    int n;
    struct group *gr;
    struct passwd *pw;
    char login[16], group[16], slink[BUFSIZ+1];
    /*
     * Print the number of file system blocks, permission bits,
     * and number of links.
     */
    printf("%5d ", st->st_blocks);
    printf("%c%s ", typeOfFile(st->st_mode), permOfFile(st->st_mode));
    printf("%3d ", st->st_nlink);
    /*
     * Look up the owner's login name.  Use the user-id if we
     * can't find it.
     */
    if ((pw = getpwuid(st->st_uid)) != NULL)
        strcpy(login, pw->pw_name);
    else
        sprintf(login, "%d", st->st_uid);
   
    /*
     * Look up the group's name.  Use the group-id if we
     * can't find it.
     */
    if ((gr = getgrgid(st->st_gid)) != NULL)
        strcpy(group, gr->gr_name);
    else
        sprintf(group, "%d", st->st_gid);
    /*
     * Print the owner and group.
     */
    printf("%-8s %-8s ", login, group);
    /*
     * If the file is not a device, print its size; otherwise
     * print its major and minor device numbers.
     */
    if (((st->st_mode & S_IFMT) != S_IFCHR) &&
        ((st->st_mode & S_IFMT) != S_IFBLK))
        printf("%9d ", st->st_size);
    else
        printf("%4d,%4d ", major(st->st_rdev), minor(st->st_rdev));
    /*
     * Print the access time.  The ctime() function is
     * described in Chapter 7, "Time and Timers".
     */
    printf("%.12s ", ctime(&st->st_mtime) + 4);
    /*
     * Print the file name.  If it's a symblic link, also print
     * what it points to.
     */
    printf("%s", filename);
    if ((st->st_mode & S_IFMT) == S_IFLNK) {
        if ((n = readlink(pathname, slink, sizeof(slink))) < 0)
            printf(" -> ???");
        else
            printf(" -> %.*s", n, slink);
    }
}
/*
* typeOfFile - return the english description of the file type.
*/
char
typeOfFile(mode_t mode)
{
    switch (mode & S_IFMT) {
    case S_IFREG:
        return('-');
    case S_IFDIR:
        return('d');
    case S_IFCHR:
        return('c');
    case S_IFBLK:
        return('b');
    case S_IFLNK:
        return('l');
    case S_IFIFO:
        return('p');
    case S_IFSOCK:
        return('s');
    }
    return('?');
}
/*
* permOfFile - return the file permissions in an "ls"-like string.
*/
char *
permOfFile(mode_t mode)
{
    int i;
    char *p;
    static char perms[10];
    p = perms;
    strcpy(perms, "---------");
    /*
     * The permission bits are three sets of three
     * bits: user read/write/exec, group read/write/exec,
     * other read/write/exec.  We deal with each set
     * of three bits in one pass through the loop.
     */
    for (i=0; i < 3; i++) {
        if (mode & (S_IREAD >> i*3))
            *p = 'r';
        p++;
        if (mode & (S_IWRITE >> i*3))
            *p = 'w';
        p++;
        if (mode & (S_IEXEC >> i*3))
            *p = 'x';
        p++;
    }
    /*
     * Put special codes in for set-user-id, set-group-id,
     * and the sticky bit.  (This part is incomplete; "ls"
     * uses some other letters as well for cases such as
     * set-user-id bit without execute bit, and so forth.)
     */
    if ((mode & S_ISUID) != 0)
        perms[2] = 's';
    if ((mode & S_ISGID) != 0)
        perms[5] = 's';
    if ((mode & S_ISVTX) != 0)
        perms[8] = 't';
    return(perms);
}
    % newlistfiles /home/msw/a
    /home/msw/a:
        2 drwxr-sr-x   7 root     other     512 Dec 21 22:20 .
        2 drwxr-xr-x   3 root     root      512 Dec 21 20:45 ..
       16 drwx------   2 root     root     8192 Apr 19 16:04 lost+found
        2 drwxr-sr-x  12 davy     other    1024 May 29 10:19 davy
        2 drwxr-sr-x   2 sean     other     512 Apr 19 17:57 sean
        2 drwxr-sr-x   3 trevor   other     512 Jan 12 19:59 trevor
        2 drwxr-sr-x   6 cathy    other     512 Mar 19 11:33 cathy
Note that the method used in the example is awfully inefficient. In a directory with 100 files in it, all owned by the same user, the getpwnam function is called 100 times. A similar problem exists with group names. A more efficient method would be to store the information returned from these functions each time they are called, and to search the stored information first, calling the functions only when a user ID or group ID is encountered for the first time.

Previous SectionNext Section
Books24x7.com, Inc © 2000 –  Feedback