root/usr/src/cmd/lastcomm/lc_utils.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include "lastcomm.h"

/*
 * lc_utils contains utility functions used by both the basic and extended
 * accounting components of lastcomm.  getdev(), on its first call, builds
 * the set of tty device name to dev_t mappings.
 */

#define N_DEVS          43              /* hash value for device names */
#define NDEVS           500             /* max number of file names in /dev */

#define HASH(d) (((int)d) % N_DEVS)     /* hash function */

struct  devhash {
        dev_t   dev_dev;
        char    dev_name [PATHNAMLEN];
        struct  devhash *dev_nxt;
};

static struct devhash *dev_hash[N_DEVS];
static struct devhash *dev_chain;
static int ndevs = NDEVS;
static struct devhash *hashtab;

/*
 * Default search list, used if /etc/ttysrch unavailable or unparsable.
 */
static char *def_srch_dirs[] = {
        "/dev/term",
        "/dev/pts",
        "/dev/xt",
        NULL
};
static char *raw_sf;    /* buffer containing raw image of the search file */

#define SRCH_FILE_NAME  "/etc/ttysrch"
/*
 * /etc/ttysrch tokens.
 */
#define COMMENT_CHAR    '#'
#define EOLN            '\n'
/*
 * /etc/ttysrch parser states.
 */
#define START_STATE     1
#define COMMENT_STATE   2
#define DIRNAME_STATE   3

/*
 * The following 2 routines are modified version of get_pri_dirs
 * and srch_dir in ttyname.c.
 */
static char **
get_pri_dirs()
{
        int bcount = 0;
        int c;
        int sf_lines = 0;       /* number of lines in search file */
        int dirno = 0;
        int state;
        FILE *sf;
        char **pri_dirs;        /* priority search list */
        char *sfp;              /* pointer inside the raw image buffer */
        struct stat sfsb;       /* search file's stat structure buffer */


        if ((sf = fopen(SRCH_FILE_NAME, "r")) == NULL)
                return (def_srch_dirs);
        if (stat(SRCH_FILE_NAME, &sfsb) < 0) {
                (void) fclose(sf);
                return (def_srch_dirs);
        }
        raw_sf = malloc(sfsb.st_size + 1);
        sfp = raw_sf;
        while ((bcount++ < sfsb.st_size) && ((c = getc(sf)) != EOF)) {
                *sfp++ = (char)c;
                if (c == EOLN)
                        sf_lines++;
        }
        (void) fclose(sf);
        *sfp = EOLN;
        pri_dirs = malloc(++sf_lines * sizeof (char *));

        sfp = raw_sf;
        state = START_STATE;
        while (--bcount) {
                switch (state) {
                case START_STATE:
                        if (*sfp == COMMENT_CHAR) {
                                state = COMMENT_STATE;
                        } else if (!isspace(*sfp)) {
                                state = DIRNAME_STATE;
                                pri_dirs[dirno++] = sfp;
                        }
                        break;
                case COMMENT_STATE:
                        if (*sfp == EOLN)
                                state = START_STATE;
                        break;
                case DIRNAME_STATE:
                        if (*sfp == EOLN) {
                                *sfp = '\0';
                                state = START_STATE;
                        } else if (isspace(*sfp)) {
                                *sfp = '\0';
                                state = COMMENT_STATE;
                        }
                        break;

                } /* switch */
                sfp++;
        }

        *sfp = '\0';
        pri_dirs[dirno] = NULL;
        return (pri_dirs);
}

/*
 * Build a chain of character devices in dev_chain, starting with the given
 * path.
 */
static int
srch_dir(char *path)
{
        DIR *dirp;
        struct dirent *direntp;
        struct stat st;
        char file_name[PATHNAMLEN];

        if ((dirp = opendir(path)) == NULL)
                return (0);

        if ((readdir(dirp) == NULL) || (readdir(dirp) == NULL))
                return (0);

        while ((direntp = readdir(dirp)) != NULL) {
                (void) strcpy(file_name, path);
                (void) strcat(file_name, "/");
                (void) strcat(file_name, direntp->d_name);
                if (stat((const char *)file_name, &st) < 0)
                        continue;
                if ((st.st_mode & S_IFMT) == S_IFCHR) {
                        (void) strcpy(hashtab->dev_name,
                            file_name + strlen("/dev/"));
                        hashtab->dev_nxt = dev_chain;
                        dev_chain = hashtab;
                        hashtab++;
                        if (--ndevs < 0)
                                return (-1);
                }
        }
        (void) closedir(dirp);
        return (1);
}


static void
setupdevs()
{
        int dirno = 0;
        char **srch_dirs;

        hashtab = malloc(NDEVS * sizeof (struct devhash));
        if (hashtab == NULL) {
                (void) fprintf(stderr, gettext("No memory for device table\n"));
                return;
        }

        srch_dirs = get_pri_dirs();

        while (srch_dirs[dirno] != NULL) {
                if (srch_dir(srch_dirs[dirno]) < 0)
                        return;
                dirno++;
        }

        dirno = 0;
        while (srch_dirs[dirno] != NULL) {
                if (strcmp("/dev", srch_dirs[dirno]) == 0)
                        /*
                         * Don't search /dev twice.
                         */
                        return;
                dirno++;
        }
}

char *
getdev(dev_t dev)
{
        struct devhash *hp, *nhp;
        struct stat statb;
        char name[PATHNAMLEN];
        static dev_t lastdev = (dev_t)-1;
        static char *lastname;
        static int init = 0;

        if (dev == NODEV)
                return ("__");
        if (dev == lastdev)
                return (lastname);
        if (!init) {
                setupdevs();
                init++;
        }

        for (hp = dev_hash[HASH(dev)]; hp; hp = hp->dev_nxt)
                if (hp->dev_dev == dev) {
                        lastdev = dev;
                        return (lastname = hp->dev_name);
                }

        for (hp = dev_chain; hp; hp = nhp) {
                nhp = hp->dev_nxt;
                (void) strcpy(name, "/dev/");
                (void) strcat(name, hp->dev_name);
                if (stat(name, &statb) < 0)     /* name truncated usually */
                        continue;
                if ((statb.st_mode & S_IFMT) != S_IFCHR)
                        continue;
                hp->dev_dev = statb.st_rdev;
                hp->dev_nxt = dev_hash[HASH(hp->dev_dev)];
                dev_hash[HASH(hp->dev_dev)] = hp;
                if (hp->dev_dev == dev) {
                        dev_chain = nhp;
                        lastdev = dev;
                        return (lastname = hp->dev_name);
                }
        }
        dev_chain = NULL;
        return ("??");
}

char *
flagbits(int f)
{
        int i = 0;
        static char flags[20];

#define BIT(flag, ch)   flags[i++] = (f & flag) ? ch : ' '
        BIT(ASU, 'S');
        BIT(AFORK, 'F');
        flags[i] = '\0';
        return (flags);
#undef  BIT
}

char *
getname(uid_t uid)
{
        struct passwd *pw;
        static char uidname[NMAX];

        if ((pw = getpwuid(uid)) == NULL) {
                (void) sprintf(uidname, "%u", uid);
                return (uidname);
        }
        return (pw->pw_name);
}