1/* 2 * Top users/processes display for Unix 3 * Version 3 4 * 5 * This program may be freely redistributed, 6 * but this entire comment MUST remain intact. 7 * 8 * Copyright (c) 1984, 1989, William LeFebvre, Rice University 9 * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University 10 * 11 * $FreeBSD$ 12 */ 13 14/* 15 * Username translation code for top. 16 * 17 * These routines handle uid to username mapping. 18 * They use a hashing table scheme to reduce reading overhead. 19 * For the time being, these are very straightforward hashing routines. 20 * Maybe someday I'll put in something better. But with the advent of 21 * "random access" password files, it might not be worth the effort. 22 * 23 * Changes to these have been provided by John Gilmore (gnu@toad.com). 24 * 25 * The hash has been simplified in this release, to avoid the 26 * table overflow problems of previous releases. If the value 27 * at the initial hash location is not right, it is replaced 28 * by the right value. Collisions will cause us to call getpw* 29 * but hey, this is a cache, not the Library of Congress. 30 * This makes the table size independent of the passwd file size. 31 */ 32 33#include <sys/param.h> 34#include <sys/types.h> 35 36#include <pwd.h> 37#include <stdio.h> 38#include <stdlib.h> 39#include <string.h> 40 41#include "top.local.h" 42#include "utils.h" 43#include "username.h" 44 45struct hash_el { 46 int uid; 47 char name[MAXLOGNAME]; 48}; 49 50#define is_empty_hash(x) (hash_table[x].name[0] == 0) 51 52/* simple minded hashing function */ 53/* Uid "nobody" is -2 results in hashit(-2) = -2 which is out of bounds for 54 the hash_table. Applied abs() function to fix. 2/16/96 tpugh 55*/ 56#define hashit(i) (abs(i) % Table_size) 57 58/* K&R requires that statically declared tables be initialized to zero. */ 59/* We depend on that for hash_table and YOUR compiler had BETTER do it! */ 60struct hash_el hash_table[Table_size]; 61 62 63void 64init_hash() 65 66{ 67 /* 68 * There used to be some steps we had to take to initialize things. 69 * We don't need to do that anymore, but we will leave this stub in 70 * just in case future changes require initialization steps. 71 */ 72} 73 74char *username(uid) 75 76int uid; 77 78{ 79 register int hashindex; 80 81 hashindex = hashit(uid); 82 if (is_empty_hash(hashindex) || (hash_table[hashindex].uid != uid)) 83 { 84 /* not here or not right -- get it out of passwd */ 85 hashindex = get_user(uid); 86 } 87 return(hash_table[hashindex].name); 88} 89 90int userid(username) 91 92char *username; 93 94{ 95 struct passwd *pwd; 96 97 /* Eventually we want this to enter everything in the hash table, 98 but for now we just do it simply and remember just the result. 99 */ 100 101 if ((pwd = getpwnam(username)) == NULL) 102 { 103 return(-1); 104 } 105 106 /* enter the result in the hash table */ 107 enter_user(pwd->pw_uid, username, 1); 108 109 /* return our result */ 110 return(pwd->pw_uid); 111} 112 113int enter_user(uid, name, wecare) 114 115int uid; 116char *name; 117int wecare; /* 1 = enter it always, 0 = nice to have */ 118 119{ 120 register int hashindex; 121 122#ifdef DEBUG 123 fprintf(stderr, "enter_hash(%d, %s, %d)\n", uid, name, wecare); 124#endif 125 126 hashindex = hashit(uid); 127 128 if (!is_empty_hash(hashindex)) 129 { 130 if (!wecare) 131 return 0; /* Don't clobber a slot for trash */ 132 if (hash_table[hashindex].uid == uid) 133 return(hashindex); /* Fortuitous find */ 134 } 135 136 /* empty or wrong slot -- fill it with new value */ 137 hash_table[hashindex].uid = uid; 138 (void) strncpy(hash_table[hashindex].name, name, MAXLOGNAME - 1); 139 return(hashindex); 140} 141 142/* 143 * Get a userid->name mapping from the system. 144 * If the passwd database is hashed (#define RANDOM_PW), we 145 * just handle this uid. Otherwise we scan the passwd file 146 * and cache any entries we pass over while looking. 147 */ 148 149int get_user(uid) 150 151int uid; 152 153{ 154 struct passwd *pwd; 155 156#ifdef RANDOM_PW 157 /* no performance penalty for using getpwuid makes it easy */ 158 if ((pwd = getpwuid(uid)) != NULL) 159 { 160 return(enter_user(pwd->pw_uid, pwd->pw_name, 1)); 161 } 162#else 163 164 int from_start = 0; 165 166 /* 167 * If we just called getpwuid each time, things would be very slow 168 * since that just iterates through the passwd file each time. So, 169 * we walk through the file instead (using getpwent) and cache each 170 * entry as we go. Once the right record is found, we cache it and 171 * return immediately. The next time we come in, getpwent will get 172 * the next record. In theory, we never have to read the passwd file 173 * a second time (because we cache everything we read). But in 174 * practice, the cache may not be large enough, so if we don't find 175 * it the first time we have to scan the file a second time. This 176 * is not very efficient, but it will do for now. 177 */ 178 179 while (from_start++ < 2) 180 { 181 while ((pwd = getpwent()) != NULL) 182 { 183 if (pwd->pw_uid == uid) 184 { 185 return(enter_user(pwd->pw_uid, pwd->pw_name, 1)); 186 } 187 (void) enter_user(pwd->pw_uid, pwd->pw_name, 0); 188 } 189 /* try again */ 190 setpwent(); 191 } 192#endif 193 /* if we can't find the name at all, then use the uid as the name */ 194 return(enter_user(uid, itoa7(uid), 1)); 195} 196