aboutsummaryrefslogtreecommitdiff
path: root/username.c
diff options
context:
space:
mode:
Diffstat (limited to 'username.c')
-rw-r--r--username.c245
1 files changed, 105 insertions, 140 deletions
diff --git a/username.c b/username.c
index 4a98a2bfc973..9866bffb320b 100644
--- a/username.c
+++ b/username.c
@@ -1,185 +1,150 @@
/*
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
* Top users/processes display for Unix
* Version 3
- *
- * This program may be freely redistributed,
- * but this entire comment MUST remain intact.
- *
- * Copyright (c) 1984, 1989, William LeFebvre, Rice University
- * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
*/
/*
* Username translation code for top.
*
- * These routines handle uid to username mapping.
- * They use a hashing table scheme to reduce reading overhead.
- * For the time being, these are very straightforward hashing routines.
- * Maybe someday I'll put in something better. But with the advent of
- * "random access" password files, it might not be worth the effort.
- *
- * Changes to these have been provided by John Gilmore (gnu@toad.com).
+ * These routines handle uid to username mapping. They use a hash table to
+ * reduce reading overhead. Entries are refreshed every EXPIRETIME seconds.
*
- * The hash has been simplified in this release, to avoid the
- * table overflow problems of previous releases. If the value
- * at the initial hash location is not right, it is replaced
- * by the right value. Collisions will cause us to call getpw*
- * but hey, this is a cache, not the Library of Congress.
- * This makes the table size independent of the passwd file size.
+ * The old ad-hoc hash functions have been replaced with something a little
+ * more formal and (hopefully) more robust (found in hash.c)
*/
-#include <stdio.h>
+#include "os.h"
+
#include <pwd.h>
-#include "top.local.h"
+#include "top.h"
#include "utils.h"
+#include "hash.h"
-struct hash_el {
- int uid;
- char name[9];
-};
+#define EXPIRETIME (60 * 5)
-#define is_empty_hash(x) (hash_table[x].name[0] == 0)
+/* we need some sort of idea how long usernames can be */
+#ifndef MAXLOGNAME
+#ifdef _POSIX_LOGIN_NAME_MAX
+#define MAXLOGNAME _POSIX_LOGIN_NAME_MAX
+#else
+#define MAXLOGNAME 9
+#endif
+#endif
-/* simple minded hashing function */
-/* Uid "nobody" is -2 results in hashit(-2) = -2 which is out of bounds for
- the hash_table. Applied abs() function to fix. 2/16/96 tpugh
-*/
-#define hashit(i) (abs(i) % Table_size)
+struct hash_data {
+ int uid;
+ char name[MAXLOGNAME]; /* big enough? */
+ time_t expire;
+};
-/* K&R requires that statically declared tables be initialized to zero. */
-/* We depend on that for hash_table and YOUR compiler had BETTER do it! */
-struct hash_el hash_table[Table_size];
+hash_table *userhash;
-init_hash()
-{
- /*
- * There used to be some steps we had to take to initialize things.
- * We don't need to do that anymore, but we will leave this stub in
- * just in case future changes require initialization steps.
- */
-}
-
-char *username(uid)
-
-register int uid;
+void
+init_username()
{
- register int hashindex;
-
- hashindex = hashit(uid);
- if (is_empty_hash(hashindex) || (hash_table[hashindex].uid != uid))
- {
- /* not here or not right -- get it out of passwd */
- hashindex = get_user(uid);
- }
- return(hash_table[hashindex].name);
+ userhash = hash_create(211);
}
-int userid(username)
-
-char *username;
+char *
+username(int uid)
{
- struct passwd *pwd;
-
- /* Eventually we want this to enter everything in the hash table,
- but for now we just do it simply and remember just the result.
- */
-
- if ((pwd = getpwnam(username)) == NULL)
- {
- return(-1);
- }
+ struct hash_data *data;
+ struct passwd *pw;
+ time_t now;
- /* enter the result in the hash table */
- enter_user(pwd->pw_uid, username, 1);
+ /* what time is it? */
+ now = time(NULL);
- /* return our result */
- return(pwd->pw_uid);
-}
-
-int enter_user(uid, name, wecare)
+ /* get whatever is in the cache */
+ data = hash_lookup_uint(userhash, (unsigned int)uid);
-register int uid;
-register char *name;
-int wecare; /* 1 = enter it always, 0 = nice to have */
-
-{
- register int hashindex;
+ /* if we had a cache miss, then create space for a new entry */
+ if (data == NULL)
+ {
+ /* make space */
+ data = (struct hash_data *)malloc(sizeof(struct hash_data));
-#ifdef DEBUG
- fprintf(stderr, "enter_hash(%d, %s, %d)\n", uid, name, wecare);
-#endif
+ /* fill in some data, including an already expired time */
+ data->uid = uid;
+ data->expire = (time_t)0;
- hashindex = hashit(uid);
+ /* add it to the hash: the rest gets filled in later */
+ hash_add_uint(userhash, uid, data);
+ }
- if (!is_empty_hash(hashindex))
+ /* Now data points to the correct hash entry for "uid". If this is
+ a new entry, then expire is 0 and the next test will be true. */
+ if (data->expire <= now)
{
- if (!wecare)
- return 0; /* Don't clobber a slot for trash */
- if (hash_table[hashindex].uid == uid)
- return(hashindex); /* Fortuitous find */
+ if ((pw = getpwuid(uid)) != NULL)
+ {
+ strncpy(data->name, pw->pw_name, MAXLOGNAME-1);
+ data->expire = now + EXPIRETIME;
+ dprintf("username: updating %d with %s, expires %d\n",
+ data->uid, data->name, data->expire);
+ }
+ else
+ {
+ /* username doesnt exist ... so invent one */
+ snprintf(data->name, sizeof(data->name), "%d", uid);
+ data->expire = now + EXPIRETIME;
+ dprintf("username: updating %d with %s, expires %d\n",
+ data->uid, data->name, data->expire);
+ }
}
- /* empty or wrong slot -- fill it with new value */
- hash_table[hashindex].uid = uid;
- (void) strncpy(hash_table[hashindex].name, name, 8);
- return(hashindex);
+ /* return what we have */
+ return data->name;
}
-/*
- * Get a userid->name mapping from the system.
- * If the passwd database is hashed (#define RANDOM_PW), we
- * just handle this uid. Otherwise we scan the passwd file
- * and cache any entries we pass over while looking.
- */
-
-int get_user(uid)
-
-register int uid;
+int
+userid(char *username)
{
struct passwd *pwd;
-#ifdef RANDOM_PW
- /* no performance penalty for using getpwuid makes it easy */
- if ((pwd = getpwuid(uid)) != NULL)
+ if ((pwd = getpwnam(username)) == NULL)
{
- return(enter_user(pwd->pw_uid, pwd->pw_name, 1));
+ return(-1);
}
-#else
- int from_start = 0;
-
- /*
- * If we just called getpwuid each time, things would be very slow
- * since that just iterates through the passwd file each time. So,
- * we walk through the file instead (using getpwent) and cache each
- * entry as we go. Once the right record is found, we cache it and
- * return immediately. The next time we come in, getpwent will get
- * the next record. In theory, we never have to read the passwd file
- * a second time (because we cache everything we read). But in
- * practice, the cache may not be large enough, so if we don't find
- * it the first time we have to scan the file a second time. This
- * is not very efficient, but it will do for now.
- */
-
- while (from_start++ < 2)
- {
- while ((pwd = getpwent()) != NULL)
- {
- if (pwd->pw_uid == uid)
- {
- return(enter_user(pwd->pw_uid, pwd->pw_name, 1));
- }
- (void) enter_user(pwd->pw_uid, pwd->pw_name, 0);
- }
- /* try again */
- setpwent();
- }
-#endif
- /* if we can't find the name at all, then use the uid as the name */
- return(enter_user(uid, itoa7(uid), 1));
+ /* return our result */
+ return(pwd->pw_uid);
}
+