124139Sjoerg/*
224139Sjoerg *  Top users/processes display for Unix
324139Sjoerg *  Version 3
424139Sjoerg *
524139Sjoerg *  This program may be freely redistributed,
624139Sjoerg *  but this entire comment MUST remain intact.
724139Sjoerg *
824139Sjoerg *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
924139Sjoerg *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
1098069Smike *
1198069Smike * $FreeBSD$
1224139Sjoerg */
1324139Sjoerg
1424139Sjoerg/*
1524139Sjoerg *  Username translation code for top.
1624139Sjoerg *
1724139Sjoerg *  These routines handle uid to username mapping.
1824139Sjoerg *  They use a hashing table scheme to reduce reading overhead.
1924139Sjoerg *  For the time being, these are very straightforward hashing routines.
2024139Sjoerg *  Maybe someday I'll put in something better.  But with the advent of
2124139Sjoerg *  "random access" password files, it might not be worth the effort.
2224139Sjoerg *
2324139Sjoerg *  Changes to these have been provided by John Gilmore (gnu@toad.com).
2424139Sjoerg *
2524139Sjoerg *  The hash has been simplified in this release, to avoid the
2624139Sjoerg *  table overflow problems of previous releases.  If the value
2724139Sjoerg *  at the initial hash location is not right, it is replaced
2824139Sjoerg *  by the right value.  Collisions will cause us to call getpw*
2924139Sjoerg *  but hey, this is a cache, not the Library of Congress.
3024139Sjoerg *  This makes the table size independent of the passwd file size.
3124139Sjoerg */
3224139Sjoerg
33200979Sed#include <sys/param.h>
3498069Smike#include <sys/types.h>
3524139Sjoerg#include <stdio.h>
3624139Sjoerg#include <pwd.h>
3724139Sjoerg
3824139Sjoerg#include "top.local.h"
3924139Sjoerg#include "utils.h"
4024139Sjoerg
4124139Sjoergstruct hash_el {
4224139Sjoerg    int  uid;
43200979Sed    char name[MAXLOGNAME];
4424139Sjoerg};
4524139Sjoerg
4624139Sjoerg#define    is_empty_hash(x)	(hash_table[x].name[0] == 0)
4724139Sjoerg
4824139Sjoerg/* simple minded hashing function */
4924139Sjoerg/* Uid "nobody" is -2 results in hashit(-2) = -2 which is out of bounds for
5024139Sjoerg   the hash_table.  Applied abs() function to fix. 2/16/96 tpugh
5124139Sjoerg*/
5224139Sjoerg#define    hashit(i)	(abs(i) % Table_size)
5324139Sjoerg
5424139Sjoerg/* K&R requires that statically declared tables be initialized to zero. */
5524139Sjoerg/* We depend on that for hash_table and YOUR compiler had BETTER do it! */
5624139Sjoergstruct hash_el hash_table[Table_size];
5724139Sjoerg
5824139Sjoerginit_hash()
5924139Sjoerg
6024139Sjoerg{
6124139Sjoerg    /*
6224139Sjoerg     *  There used to be some steps we had to take to initialize things.
6324139Sjoerg     *  We don't need to do that anymore, but we will leave this stub in
6424139Sjoerg     *  just in case future changes require initialization steps.
6524139Sjoerg     */
6624139Sjoerg}
6724139Sjoerg
6824139Sjoergchar *username(uid)
6924139Sjoerg
7024139Sjoergregister int uid;
7124139Sjoerg
7224139Sjoerg{
7324139Sjoerg    register int hashindex;
7424139Sjoerg
7524139Sjoerg    hashindex = hashit(uid);
7624139Sjoerg    if (is_empty_hash(hashindex) || (hash_table[hashindex].uid != uid))
7724139Sjoerg    {
7824139Sjoerg	/* not here or not right -- get it out of passwd */
7924139Sjoerg	hashindex = get_user(uid);
8024139Sjoerg    }
8124139Sjoerg    return(hash_table[hashindex].name);
8224139Sjoerg}
8324139Sjoerg
8424139Sjoergint userid(username)
8524139Sjoerg
8624139Sjoergchar *username;
8724139Sjoerg
8824139Sjoerg{
8924139Sjoerg    struct passwd *pwd;
9024139Sjoerg
9124139Sjoerg    /* Eventually we want this to enter everything in the hash table,
9224139Sjoerg       but for now we just do it simply and remember just the result.
9324139Sjoerg     */
9424139Sjoerg
9524139Sjoerg    if ((pwd = getpwnam(username)) == NULL)
9624139Sjoerg    {
9724139Sjoerg	return(-1);
9824139Sjoerg    }
9924139Sjoerg
10024139Sjoerg    /* enter the result in the hash table */
10124139Sjoerg    enter_user(pwd->pw_uid, username, 1);
10224139Sjoerg
10324139Sjoerg    /* return our result */
10424139Sjoerg    return(pwd->pw_uid);
10524139Sjoerg}
10624139Sjoerg
10724139Sjoergint enter_user(uid, name, wecare)
10824139Sjoerg
10924139Sjoergregister int  uid;
11024139Sjoergregister char *name;
11124139Sjoergint wecare;		/* 1 = enter it always, 0 = nice to have */
11224139Sjoerg
11324139Sjoerg{
11424139Sjoerg    register int hashindex;
11524139Sjoerg
11624139Sjoerg#ifdef DEBUG
11724139Sjoerg    fprintf(stderr, "enter_hash(%d, %s, %d)\n", uid, name, wecare);
11824139Sjoerg#endif
11924139Sjoerg
12024139Sjoerg    hashindex = hashit(uid);
12124139Sjoerg
12224139Sjoerg    if (!is_empty_hash(hashindex))
12324139Sjoerg    {
12424139Sjoerg	if (!wecare)
12524139Sjoerg	    return 0;		/* Don't clobber a slot for trash */
12624139Sjoerg	if (hash_table[hashindex].uid == uid)
12724139Sjoerg	    return(hashindex);	/* Fortuitous find */
12824139Sjoerg    }
12924139Sjoerg
13024139Sjoerg    /* empty or wrong slot -- fill it with new value */
13124139Sjoerg    hash_table[hashindex].uid = uid;
132200979Sed    (void) strncpy(hash_table[hashindex].name, name, MAXLOGNAME - 1);
13324139Sjoerg    return(hashindex);
13424139Sjoerg}
13524139Sjoerg
13624139Sjoerg/*
13724139Sjoerg * Get a userid->name mapping from the system.
13824139Sjoerg * If the passwd database is hashed (#define RANDOM_PW), we
13924139Sjoerg * just handle this uid.  Otherwise we scan the passwd file
14024139Sjoerg * and cache any entries we pass over while looking.
14124139Sjoerg */
14224139Sjoerg
14324139Sjoergint get_user(uid)
14424139Sjoerg
14524139Sjoergregister int uid;
14624139Sjoerg
14724139Sjoerg{
14824139Sjoerg    struct passwd *pwd;
14924139Sjoerg
15024139Sjoerg#ifdef RANDOM_PW
15124139Sjoerg    /* no performance penalty for using getpwuid makes it easy */
15224139Sjoerg    if ((pwd = getpwuid(uid)) != NULL)
15324139Sjoerg    {
15424139Sjoerg	return(enter_user(pwd->pw_uid, pwd->pw_name, 1));
15524139Sjoerg    }
15624139Sjoerg#else
15724139Sjoerg
15824139Sjoerg    int from_start = 0;
15924139Sjoerg
16024139Sjoerg    /*
16124139Sjoerg     *  If we just called getpwuid each time, things would be very slow
16224139Sjoerg     *  since that just iterates through the passwd file each time.  So,
16324139Sjoerg     *  we walk through the file instead (using getpwent) and cache each
16424139Sjoerg     *  entry as we go.  Once the right record is found, we cache it and
16524139Sjoerg     *  return immediately.  The next time we come in, getpwent will get
16624139Sjoerg     *  the next record.  In theory, we never have to read the passwd file
16724139Sjoerg     *  a second time (because we cache everything we read).  But in
16824139Sjoerg     *  practice, the cache may not be large enough, so if we don't find
16924139Sjoerg     *  it the first time we have to scan the file a second time.  This
17024139Sjoerg     *  is not very efficient, but it will do for now.
17124139Sjoerg     */
17224139Sjoerg
17324139Sjoerg    while (from_start++ < 2)
17424139Sjoerg    {
17524139Sjoerg	while ((pwd = getpwent()) != NULL)
17624139Sjoerg	{
17724139Sjoerg	    if (pwd->pw_uid == uid)
17824139Sjoerg	    {
17924139Sjoerg		return(enter_user(pwd->pw_uid, pwd->pw_name, 1));
18024139Sjoerg	    }
18124139Sjoerg	    (void) enter_user(pwd->pw_uid, pwd->pw_name, 0);
18224139Sjoerg	}
18324139Sjoerg	/* try again */
18424139Sjoerg	setpwent();
18524139Sjoerg    }
18624139Sjoerg#endif
18724139Sjoerg    /* if we can't find the name at all, then use the uid as the name */
18824139Sjoerg    return(enter_user(uid, itoa7(uid), 1));
18924139Sjoerg}
190