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 utmp and wtmp Files
The files /var/adm/utmp (/etc/utmp on older systems) and /var/adm/wtmp (/usr/adm/wtmp or /etc/wtmp on older systems) record user and accounting information for commands such as who, finger, and login. The format of these files on System V-based systems is substantially different than on all other versions of UNIX; the System V format is described here, and the more “traditional” format is described in the porting notes.
The utmp file contains records that describe the current state of the system. This includes one record for each logged-in user, and some additional records that will be described later. The login command writes a record to the utmp file each time a user logs in; the record is removed when the user logs out. The wtmp file contains historical data in the same format. Each time a user logs in, a record is written to the file. Each time a user logs out, the same record is written to the file again, except that the login name field (ut_user or ut_name) is empty, and the ut_time field contains the logout time instead of the login time. Programs such as last can read this file, match up the entries with login names and those without, and produce a summary of when each user logged in and out.
In System V versions of UNIX, the utmp file also records information about the execution of certain system processes such as a change in the system's run level or the programs that allow users to log in. This information is not transferred to the wtmp file. Two additional files, /var/adm/utmpx and /var/adm/wtmpx, are used to record additional information. These files have a slightly larger record than their counterparts; the primary difference is that the “x” files also contain the name of the remote host for users who log in via the network. (It probably would have made more sense to just add this information to the utmp and wtmp files, but this would have broken older programs that read these files.)
The record format for the utmp and wtmp files is described in the include file utmp.h:
    struct utmp {
        char    ut_user[8];
        char    ut_id[4];
        char    ut_line[12];
        short   ut_pid;
        short   ut_type;
        struct exit_status    ut_exit;
        time_t  ut_time;
    };
    struct exit_status {
        short    e_termination;
        short    e_exit;
    };
The fields of the structure have the following meanings:
ut_user
The user's login name. Note that this field is not always null-terminated; an eight-character login name has no room in the string for a terminating null byte.
ut_id
The id field from /etc/inittab for a process spawned by the init program.
ut_line
The name of the device on which the user is logged in; this string can be concatenated with “/dev/ ” to obtain the pathname for the device.
ut_pid
The process ID of the described process.
ut_type
An indication of the type of data contained in this record. Legal values for this field are:
EMPTY
The record is empty.
RUN_LVL
This record indicates a change in the system run-level. The new level can be determined from the ut_id field.
BOOT_TIME
A system boot. The time is recorded in the ut_time field.
OLD_TIME
A change in the system time with the date command. This record stores the time prior to the change.
NEW_TIME
A change in the system time with the date command. The record stores the time after the change.
INIT_PROCESS
A process spawned by init. The process' name is stored in ut_name, its process ID number is stored in ut_pid.
LOGIN_PROCESS
A process waiting for a user to log in; there is usually one of these for each terminal connected to the system.
USER_PROCESS
A user login session.
DEAD_PROCESS
A process that has exited. The exit status and return code are stored in ut_exit.
ACCOUNTING
An accounting record (not implemented).
ut_exit
The termination and exit status of a process recorded in a DEAD_PROCESS record.
ut_time
The time at which this record was last modified.
The record format for the utmpx file is described in the utmpx.h include file:
    struct utmpx {
        char    ut_user[32];
        char    ut_id[4];
        char    ut_line[32];
        pid_t   ut_pid;
        short   ut_type;
        struct exit_status    ut_exit;
        struct timeval    ut_tv;
        long    ut_session;
        long    pad[5];
        short   ut_syslen;
        char    ut_host[257];
    };
All of the common fields have the same meaning as those in the struct utmp structure. The new fields are:
ut_tv
The time this record was last modified (this is the same as ut_time, except a different format).
ut_session
The session ID number (see Chapter 11, Processes).
pad
Reserved for future use.
ut_syslen
The length, including the terminating null byte, of the hostname in the ut_host field.
ut_host
The name of the remote host, if a user is logged in via the network (e.g., with rlogin or telnet).
There are two essentially identical sets of functions provided for manipulating the utmp and utmpx files:
    #include <utmp.h>
    struct utmp *getutent(void);
    struct utmp *getutid(const struct utmp *id);
    struct utmp *getutline(const struct utmp *line);
    struct utmp *pututline(const struct utmp *utmp);
    void setutent(void);
    void endutent(void);
    int utmpname(const char *filename);
    #include <utmpx.h>
    struct utmpx *getutxent(void);
    struct utmpx *getutxid(const struct utmpx *id);
    struct utmpx *getutxline(const struct utmpx *line);
    struct utmpx *pututxline(const struct utmpx *utmpx);
    void setutxent(void);
    void endutxent(void);
    int utmpxname(const char *filename);
The getutent and getutxent functions read the next entry from a utmp-like or utmpx-like file. The getutid and getutxid functions search forward from the current location in the file for an entry whose ut_type field matches id->ut_type if the type is RUN_LVL, BOOT_TIME, OLD_TIME, or NEW_TIME. If the type is one of INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS, or DEAD_PROCESS, then they search for an entry whose type is one of those four and whose ut_id field matches id->ut_id. The functions return the first entry found. The getutline and getutxline functions search forward from the current location in the file for an entry of type LOGIN_PROCESS or USER_PROCESS whose ut_line field matches line->ut_line and return the first entry found. All of these functions return the constant NULL if no entry is found or end-of-file is encountered.
The pututline and pututxline functions write out the supplied entry to the file. They first use getutid or getutxid to find the correct location in the file; if no slot for the entry exists, it is added to the end of the file.
The setutent and setutxent functions open the file and reset the read/write offset to the beginning of the file. The endutent and endutxent functions close the file. The utmpname and utmpxname functions allow the name of the file to be changed.
There are also functions provided for converting between the two record types:
    #include <utmpx.h>
    void getutmp(struct utmpx *utmpx, struct utmp *utmp);
    void getutmpx(struct utmp *utmp, struct utmpx *utmpx);
    void updwtmp(char *wfile, struct utmp *utmp);
    void updwtmpx(char *wfilex, struct utmpx *utmpx);
The getutmp function copies the fields of the utmpx structure to the corresponding utmp structure. The getutmpx function does the reverse. The updwtmp and updwtmpx functions check the existence of the named file and its parallel file (named by adding or removing an “x”) in the filename. If only one of them exists, the other file is created and the contents of the existing file are copied to it. Then the utmp or utmpx structure is written to the file, and the corresponding structure written to the parallel file.
Because the utmpx functions update the utmp file too, it is generally better to use them over their utmp counterparts.
Example 8-2 shows a program that reads the utmpx file and prints a list of currently logged-in users. For each user, the getpwnam function is used to obtain the user's real name. This program could just as easily use the utmp file, but then the remote host could not be printed.
Example 8-2:  whom
#include <sys/types.h>
#include <sys/time.h>
#include <utmpx.h>
#include <pwd.h>
int
main(void)
{
    char name[64];
    struct passwd *pwd;
    struct utmpx *utmpx;
    printf("Login    Name             Line     Time             Host\n");
    printf("--------------------------------------------------------\n");
    /*
     * Read each entry from the file.
     */
    while ((utmpx = getutxent()) != NULL) {
        /*
         * Skip records that aren't logins.
         */
        if (utmpx->ut_type != USER_PROCESS)
            continue;
        /*
         * Get the real name.
         */
        if ((pwd = getpwnam(utmpx->ut_user)) != NULL)
            strcpy(name, pwd->pw_gecos);
        else
            sprintf(name, "#%d", pwd->pw_uid);
   
        /*
         * Print stuff out.
         */
        printf("%-8s %-16.16s %-8.8s %.12s", utmpx->ut_user, name,
               utmpx->ut_line, ctime(&utmpx->ut_tv.tv_sec)+4);
       
        if (utmpx->ut_syslen > 0)
            printf(" %s", utmpx->ut_host);
       
        putchar('\n');
    }
    exit(0);
}
    % whom
    Login    Name             Line     Time             Host
    --------------------------------------------------------
    davy     David A. Curry   console  May 29 10:19
    davy     David A. Curry   pts/1    May 29 10:19
    davy     David A. Curry   pts/0    May 29 10:19
    cathy    Cathy L. Curry   pts/2    May 29 15:30     big.school.edu
This example shows only the use of USER_PROCESS records. To see what the other types of records contain, the easiest thing to do is execute the who -a command.
 NoteThe utmpx functions are not provided in HP-UX 10.x, nor are the utmpx and wtmpx files. Instead, HP-UX 10.x provides an unsigned long ut_addr field in the struct utmp structure; this field contains the IP address of the remote host that a user has logged in from.
Example 8.3 shows a modified version of the whom program from the previous example; this one has been rewritten for HP-UX 10.x to use the utmp file and functions.
Example 8-3: whom
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netdb.h>
#include <utmp.h>
#include <pwd.h>
int
main(void)
{
    char name[64];
    struct utmp *utmp;
    struct passwd *pwd;
    struct hostent *hp;
    printf("Login    Name             Line     Time             Host\n");
    printf("--------------------------------------------------------\n");
    /*
     * Read each entry from the file.
     */
    while ((utmp = getutent()) != NULL) {
        /*
         * Skip records that aren't logins.
         */
        if (utmp->ut_type != USER_PROCESS)
            continue;
        /*
         * Get the real name.
         */
        if ((pwd = getpwnam(utmp->ut_user)) != NULL)
            strcpy(name, pwd->pw_gecos);
        else
            sprintf(name, "#%d", pwd->pw_uid);
   
        /*
         * Print stuff out.
         */
        printf("%-8s %-16.16s %-8.8s %.12s", utmp->ut_user, name,
               utmp->ut_line,
               ctime(&utmp->ut_time)+4);
       
        /*
         * If there's a remote host, get its name and print it.  The
         * gethostbyaddr() function is described in Chapter 14,
         * Networking With Sockets.
         */
        if (utmp->ut_addr != 0) {
                hp = gethostbyaddr((char *) &utmp->ut_addr, sizeof(long),
                        AF_INET);
               
                if (hp != NULL)
                        printf(" %s", hp->h_name);
        }
        putchar('\n');
    }
    exit(0);
}
Porting Notes
As mentioned earlier, non-System V versions of UNIX do not use the rather elaborate utmp file described above. Instead, they use a simple record format, described in the include file utmp.h:
    struct utmp {
        char    ut_line[8];
        char    ut_name[8];
        char    ut_host[16];
        long    ut_time;
    };
The fields are:
ut_line
The name of the device the user is logged in on, with the  leading “/dev/ ” stripped off.
ut_name
The user's login name. If this field is empty, the port is not in use.
ut_host
The name of the remote host, if the user is logged in via the network. This field does not exist in all versions.
ut_time
The time the user logged in. file, the ttyslot function is used:
    #include <stdlib.h>
    int ttyslot(void);
This function returns the index of the current user's entry in the utmp file. The function scans the files in /dev for the device associated with the standard input, standard output, or standard error output, and returns the index of the struct utmp that contains that device's name in its ut_line field or -1 if an error occurs.

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