username.c revision 200979
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: head/contrib/top/username.c 200979 2009-12-25 09:02:41Z ed $ 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#include <stdio.h> 36#include <pwd.h> 37 38#include "top.local.h" 39#include "utils.h" 40 41struct hash_el { 42 int uid; 43 char name[MAXLOGNAME]; 44}; 45 46#define is_empty_hash(x) (hash_table[x].name[0] == 0) 47 48/* simple minded hashing function */ 49/* Uid "nobody" is -2 results in hashit(-2) = -2 which is out of bounds for 50 the hash_table. Applied abs() function to fix. 2/16/96 tpugh 51*/ 52#define hashit(i) (abs(i) % Table_size) 53 54/* K&R requires that statically declared tables be initialized to zero. */ 55/* We depend on that for hash_table and YOUR compiler had BETTER do it! */ 56struct hash_el hash_table[Table_size]; 57 58init_hash() 59 60{ 61 /* 62 * There used to be some steps we had to take to initialize things. 63 * We don't need to do that anymore, but we will leave this stub in 64 * just in case future changes require initialization steps. 65 */ 66} 67 68char *username(uid) 69 70register int uid; 71 72{ 73 register int hashindex; 74 75 hashindex = hashit(uid); 76 if (is_empty_hash(hashindex) || (hash_table[hashindex].uid != uid)) 77 { 78 /* not here or not right -- get it out of passwd */ 79 hashindex = get_user(uid); 80 } 81 return(hash_table[hashindex].name); 82} 83 84int userid(username) 85 86char *username; 87 88{ 89 struct passwd *pwd; 90 91 /* Eventually we want this to enter everything in the hash table, 92 but for now we just do it simply and remember just the result. 93 */ 94 95 if ((pwd = getpwnam(username)) == NULL) 96 { 97 return(-1); 98 } 99 100 /* enter the result in the hash table */ 101 enter_user(pwd->pw_uid, username, 1); 102 103 /* return our result */ 104 return(pwd->pw_uid); 105} 106 107int enter_user(uid, name, wecare) 108 109register int uid; 110register char *name; 111int wecare; /* 1 = enter it always, 0 = nice to have */ 112 113{ 114 register int hashindex; 115 116#ifdef DEBUG 117 fprintf(stderr, "enter_hash(%d, %s, %d)\n", uid, name, wecare); 118#endif 119 120 hashindex = hashit(uid); 121 122 if (!is_empty_hash(hashindex)) 123 { 124 if (!wecare) 125 return 0; /* Don't clobber a slot for trash */ 126 if (hash_table[hashindex].uid == uid) 127 return(hashindex); /* Fortuitous find */ 128 } 129 130 /* empty or wrong slot -- fill it with new value */ 131 hash_table[hashindex].uid = uid; 132 (void) strncpy(hash_table[hashindex].name, name, MAXLOGNAME - 1); 133 return(hashindex); 134} 135 136/* 137 * Get a userid->name mapping from the system. 138 * If the passwd database is hashed (#define RANDOM_PW), we 139 * just handle this uid. Otherwise we scan the passwd file 140 * and cache any entries we pass over while looking. 141 */ 142 143int get_user(uid) 144 145register int uid; 146 147{ 148 struct passwd *pwd; 149 150#ifdef RANDOM_PW 151 /* no performance penalty for using getpwuid makes it easy */ 152 if ((pwd = getpwuid(uid)) != NULL) 153 { 154 return(enter_user(pwd->pw_uid, pwd->pw_name, 1)); 155 } 156#else 157 158 int from_start = 0; 159 160 /* 161 * If we just called getpwuid each time, things would be very slow 162 * since that just iterates through the passwd file each time. So, 163 * we walk through the file instead (using getpwent) and cache each 164 * entry as we go. Once the right record is found, we cache it and 165 * return immediately. The next time we come in, getpwent will get 166 * the next record. In theory, we never have to read the passwd file 167 * a second time (because we cache everything we read). But in 168 * practice, the cache may not be large enough, so if we don't find 169 * it the first time we have to scan the file a second time. This 170 * is not very efficient, but it will do for now. 171 */ 172 173 while (from_start++ < 2) 174 { 175 while ((pwd = getpwent()) != NULL) 176 { 177 if (pwd->pw_uid == uid) 178 { 179 return(enter_user(pwd->pw_uid, pwd->pw_name, 1)); 180 } 181 (void) enter_user(pwd->pw_uid, pwd->pw_name, 0); 182 } 183 /* try again */ 184 setpwent(); 185 } 186#endif 187 /* if we can't find the name at all, then use the uid as the name */ 188 return(enter_user(uid, itoa7(uid), 1)); 189} 190