Changeset View
Changeset View
Standalone View
Standalone View
contrib/top/username.c
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
/* | /* | ||||
* Top users/processes display for Unix | * Copyright (c) 1984 through 2008, William LeFebvre | ||||
* Version 3 | * All rights reserved. | ||||
* | * | ||||
* This program may be freely redistributed, | * Redistribution and use in source and binary forms, with or without | ||||
* but this entire comment MUST remain intact. | * modification, are permitted provided that the following conditions are met: | ||||
* | * | ||||
* Copyright (c) 1984, 1989, William LeFebvre, Rice University | * * Redistributions of source code must retain the above copyright | ||||
* Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University | * notice, this list of conditions and the following disclaimer. | ||||
* | * | ||||
* $FreeBSD$ | * * 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 | |||||
*/ | |||||
/* | |||||
* Username translation code for top. | * Username translation code for top. | ||||
* | * | ||||
* These routines handle uid to username mapping. | * These routines handle uid to username mapping. They use a hash table to | ||||
* They use a hashing table scheme to reduce reading overhead. | * reduce reading overhead. Entries are refreshed every EXPIRETIME seconds. | ||||
* 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). | * The old ad-hoc hash functions have been replaced with something a little | ||||
* | * more formal and (hopefully) more robust (found in hash.c) | ||||
* 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. | |||||
*/ | */ | ||||
#include <sys/param.h> | #include "os.h" | ||||
#include <sys/types.h> | |||||
#include <pwd.h> | #include <pwd.h> | ||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include "top.local.h" | #include "top.h" | ||||
#include "utils.h" | #include "utils.h" | ||||
#include "username.h" | #include "hash.h" | ||||
struct hash_el { | #define EXPIRETIME (60 * 5) | ||||
/* 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 | |||||
struct hash_data { | |||||
int uid; | int uid; | ||||
char name[MAXLOGNAME]; | char name[MAXLOGNAME]; /* big enough? */ | ||||
time_t expire; | |||||
}; | }; | ||||
#define is_empty_hash(x) (hash_table[x].name[0] == 0) | hash_table *userhash; | ||||
/* 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) | |||||
/* 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]; | |||||
void | void | ||||
init_hash() | init_username() | ||||
{ | { | ||||
/* | userhash = hash_create(211); | ||||
* 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) | char * | ||||
username(int uid) | |||||
int uid; | |||||
{ | { | ||||
register int hashindex; | struct hash_data *data; | ||||
struct passwd *pw; | |||||
time_t now; | |||||
hashindex = hashit(uid); | /* what time is it? */ | ||||
if (is_empty_hash(hashindex) || (hash_table[hashindex].uid != uid)) | now = time(NULL); | ||||
{ | |||||
/* not here or not right -- get it out of passwd */ | |||||
hashindex = get_user(uid); | |||||
} | |||||
return(hash_table[hashindex].name); | |||||
} | |||||
int userid(username) | /* get whatever is in the cache */ | ||||
data = hash_lookup_uint(userhash, (unsigned int)uid); | |||||
char *username; | /* if we had a cache miss, then create space for a new entry */ | ||||
if (data == NULL) | |||||
{ | { | ||||
struct passwd *pwd; | /* make space */ | ||||
data = (struct hash_data *)malloc(sizeof(struct hash_data)); | |||||
/* Eventually we want this to enter everything in the hash table, | /* fill in some data, including an already expired time */ | ||||
but for now we just do it simply and remember just the result. | data->uid = uid; | ||||
*/ | data->expire = (time_t)0; | ||||
if ((pwd = getpwnam(username)) == NULL) | /* add it to the hash: the rest gets filled in later */ | ||||
{ | hash_add_uint(userhash, uid, data); | ||||
return(-1); | |||||
} | } | ||||
/* enter the result in the hash table */ | /* Now data points to the correct hash entry for "uid". If this is | ||||
enter_user(pwd->pw_uid, username, 1); | a new entry, then expire is 0 and the next test will be true. */ | ||||
if (data->expire <= now) | |||||
/* return our result */ | |||||
return(pwd->pw_uid); | |||||
} | |||||
int enter_user(uid, name, wecare) | |||||
int uid; | |||||
char *name; | |||||
int wecare; /* 1 = enter it always, 0 = nice to have */ | |||||
{ | { | ||||
register int hashindex; | if ((pw = getpwuid(uid)) != NULL) | ||||
#ifdef DEBUG | |||||
fprintf(stderr, "enter_hash(%d, %s, %d)\n", uid, name, wecare); | |||||
#endif | |||||
hashindex = hashit(uid); | |||||
if (!is_empty_hash(hashindex)) | |||||
{ | { | ||||
if (!wecare) | strncpy(data->name, pw->pw_name, MAXLOGNAME-1); | ||||
return 0; /* Don't clobber a slot for trash */ | data->expire = now + EXPIRETIME; | ||||
if (hash_table[hashindex].uid == uid) | dprintf("username: updating %d with %s, expires %d\n", | ||||
return(hashindex); /* Fortuitous find */ | 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 */ | /* return what we have */ | ||||
hash_table[hashindex].uid = uid; | return data->name; | ||||
(void) strncpy(hash_table[hashindex].name, name, MAXLOGNAME - 1); | |||||
return(hashindex); | |||||
} | } | ||||
/* | int | ||||
* Get a userid->name mapping from the system. | userid(char *username) | ||||
* 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) | |||||
int uid; | |||||
{ | { | ||||
struct passwd *pwd; | struct passwd *pwd; | ||||
#ifdef RANDOM_PW | if ((pwd = getpwnam(username)) == NULL) | ||||
/* no performance penalty for using getpwuid makes it easy */ | |||||
if ((pwd = getpwuid(uid)) != NULL) | |||||
{ | { | ||||
return(enter_user(pwd->pw_uid, pwd->pw_name, 1)); | return(-1); | ||||
} | } | ||||
#else | |||||
int from_start = 0; | /* return our result */ | ||||
return(pwd->pw_uid); | |||||
/* | |||||
* 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)); | |||||
} |