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>
35300395Sngie
36300395Sngie#include <pwd.h>
3724139Sjoerg#include <stdio.h>
38300395Sngie#include <stdlib.h>
39300395Sngie#include <string.h>
4024139Sjoerg
4124139Sjoerg#include "top.local.h"
4224139Sjoerg#include "utils.h"
43300395Sngie#include "username.h"
4424139Sjoerg
4524139Sjoergstruct hash_el {
4624139Sjoerg    int  uid;
47200979Sed    char name[MAXLOGNAME];
4824139Sjoerg};
4924139Sjoerg
5024139Sjoerg#define    is_empty_hash(x)	(hash_table[x].name[0] == 0)
5124139Sjoerg
5224139Sjoerg/* simple minded hashing function */
5324139Sjoerg/* Uid "nobody" is -2 results in hashit(-2) = -2 which is out of bounds for
5424139Sjoerg   the hash_table.  Applied abs() function to fix. 2/16/96 tpugh
5524139Sjoerg*/
5624139Sjoerg#define    hashit(i)	(abs(i) % Table_size)
5724139Sjoerg
5824139Sjoerg/* K&R requires that statically declared tables be initialized to zero. */
5924139Sjoerg/* We depend on that for hash_table and YOUR compiler had BETTER do it! */
6024139Sjoergstruct hash_el hash_table[Table_size];
6124139Sjoerg
62300395Sngie
63300395Sngievoid
6424139Sjoerginit_hash()
6524139Sjoerg
6624139Sjoerg{
6724139Sjoerg    /*
6824139Sjoerg     *  There used to be some steps we had to take to initialize things.
6924139Sjoerg     *  We don't need to do that anymore, but we will leave this stub in
7024139Sjoerg     *  just in case future changes require initialization steps.
7124139Sjoerg     */
7224139Sjoerg}
7324139Sjoerg
7424139Sjoergchar *username(uid)
7524139Sjoerg
76300395Sngieint uid;
7724139Sjoerg
7824139Sjoerg{
7924139Sjoerg    register int hashindex;
8024139Sjoerg
8124139Sjoerg    hashindex = hashit(uid);
8224139Sjoerg    if (is_empty_hash(hashindex) || (hash_table[hashindex].uid != uid))
8324139Sjoerg    {
8424139Sjoerg	/* not here or not right -- get it out of passwd */
8524139Sjoerg	hashindex = get_user(uid);
8624139Sjoerg    }
8724139Sjoerg    return(hash_table[hashindex].name);
8824139Sjoerg}
8924139Sjoerg
9024139Sjoergint userid(username)
9124139Sjoerg
9224139Sjoergchar *username;
9324139Sjoerg
9424139Sjoerg{
9524139Sjoerg    struct passwd *pwd;
9624139Sjoerg
9724139Sjoerg    /* Eventually we want this to enter everything in the hash table,
9824139Sjoerg       but for now we just do it simply and remember just the result.
9924139Sjoerg     */
10024139Sjoerg
10124139Sjoerg    if ((pwd = getpwnam(username)) == NULL)
10224139Sjoerg    {
10324139Sjoerg	return(-1);
10424139Sjoerg    }
10524139Sjoerg
10624139Sjoerg    /* enter the result in the hash table */
10724139Sjoerg    enter_user(pwd->pw_uid, username, 1);
10824139Sjoerg
10924139Sjoerg    /* return our result */
11024139Sjoerg    return(pwd->pw_uid);
11124139Sjoerg}
11224139Sjoerg
11324139Sjoergint enter_user(uid, name, wecare)
11424139Sjoerg
115300395Sngieint  uid;
116300395Sngiechar *name;
11724139Sjoergint wecare;		/* 1 = enter it always, 0 = nice to have */
11824139Sjoerg
11924139Sjoerg{
12024139Sjoerg    register int hashindex;
12124139Sjoerg
12224139Sjoerg#ifdef DEBUG
12324139Sjoerg    fprintf(stderr, "enter_hash(%d, %s, %d)\n", uid, name, wecare);
12424139Sjoerg#endif
12524139Sjoerg
12624139Sjoerg    hashindex = hashit(uid);
12724139Sjoerg
12824139Sjoerg    if (!is_empty_hash(hashindex))
12924139Sjoerg    {
13024139Sjoerg	if (!wecare)
13124139Sjoerg	    return 0;		/* Don't clobber a slot for trash */
13224139Sjoerg	if (hash_table[hashindex].uid == uid)
13324139Sjoerg	    return(hashindex);	/* Fortuitous find */
13424139Sjoerg    }
13524139Sjoerg
13624139Sjoerg    /* empty or wrong slot -- fill it with new value */
13724139Sjoerg    hash_table[hashindex].uid = uid;
138200979Sed    (void) strncpy(hash_table[hashindex].name, name, MAXLOGNAME - 1);
13924139Sjoerg    return(hashindex);
14024139Sjoerg}
14124139Sjoerg
14224139Sjoerg/*
14324139Sjoerg * Get a userid->name mapping from the system.
14424139Sjoerg * If the passwd database is hashed (#define RANDOM_PW), we
14524139Sjoerg * just handle this uid.  Otherwise we scan the passwd file
14624139Sjoerg * and cache any entries we pass over while looking.
14724139Sjoerg */
14824139Sjoerg
14924139Sjoergint get_user(uid)
15024139Sjoerg
151300395Sngieint uid;
15224139Sjoerg
15324139Sjoerg{
15424139Sjoerg    struct passwd *pwd;
15524139Sjoerg
15624139Sjoerg#ifdef RANDOM_PW
15724139Sjoerg    /* no performance penalty for using getpwuid makes it easy */
15824139Sjoerg    if ((pwd = getpwuid(uid)) != NULL)
15924139Sjoerg    {
16024139Sjoerg	return(enter_user(pwd->pw_uid, pwd->pw_name, 1));
16124139Sjoerg    }
16224139Sjoerg#else
16324139Sjoerg
16424139Sjoerg    int from_start = 0;
16524139Sjoerg
16624139Sjoerg    /*
16724139Sjoerg     *  If we just called getpwuid each time, things would be very slow
16824139Sjoerg     *  since that just iterates through the passwd file each time.  So,
16924139Sjoerg     *  we walk through the file instead (using getpwent) and cache each
17024139Sjoerg     *  entry as we go.  Once the right record is found, we cache it and
17124139Sjoerg     *  return immediately.  The next time we come in, getpwent will get
17224139Sjoerg     *  the next record.  In theory, we never have to read the passwd file
17324139Sjoerg     *  a second time (because we cache everything we read).  But in
17424139Sjoerg     *  practice, the cache may not be large enough, so if we don't find
17524139Sjoerg     *  it the first time we have to scan the file a second time.  This
17624139Sjoerg     *  is not very efficient, but it will do for now.
17724139Sjoerg     */
17824139Sjoerg
17924139Sjoerg    while (from_start++ < 2)
18024139Sjoerg    {
18124139Sjoerg	while ((pwd = getpwent()) != NULL)
18224139Sjoerg	{
18324139Sjoerg	    if (pwd->pw_uid == uid)
18424139Sjoerg	    {
18524139Sjoerg		return(enter_user(pwd->pw_uid, pwd->pw_name, 1));
18624139Sjoerg	    }
18724139Sjoerg	    (void) enter_user(pwd->pw_uid, pwd->pw_name, 0);
18824139Sjoerg	}
18924139Sjoerg	/* try again */
19024139Sjoerg	setpwent();
19124139Sjoerg    }
19224139Sjoerg#endif
19324139Sjoerg    /* if we can't find the name at all, then use the uid as the name */
19424139Sjoerg    return(enter_user(uid, itoa7(uid), 1));
19524139Sjoerg}
196