1/* idcache.c -- map user and group IDs, cached for speed 2 3 Copyright (C) 1985, 1988, 1989, 1990, 1997, 1998, 2003, 2005-2007 4 Free Software Foundation, Inc. 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 18 19#include <config.h> 20 21#include "idcache.h" 22#include <stddef.h> 23#include <stdio.h> 24#include <string.h> 25#include <pwd.h> 26#include <grp.h> 27 28#include <unistd.h> 29 30#include "xalloc.h" 31 32#ifdef __DJGPP__ 33static char digits[] = "0123456789"; 34#endif 35 36struct userid 37{ 38 union 39 { 40 uid_t u; 41 gid_t g; 42 } id; 43 struct userid *next; 44 char name[FLEXIBLE_ARRAY_MEMBER]; 45}; 46 47/* FIXME: provide a function to free any malloc'd storage and reset lists, 48 so that an application can use code like this just before exiting: 49 #ifdef lint 50 idcache_clear (); 51 #endif 52*/ 53 54static struct userid *user_alist; 55 56/* Each entry on list is a user name for which the first lookup failed. */ 57static struct userid *nouser_alist; 58 59/* Use the same struct as for userids. */ 60static struct userid *group_alist; 61 62/* Each entry on list is a group name for which the first lookup failed. */ 63static struct userid *nogroup_alist; 64 65/* Translate UID to a login name, with cache, or NULL if unresolved. */ 66 67char * 68getuser (uid_t uid) 69{ 70 struct userid *tail; 71 struct userid *match = NULL; 72 73 for (tail = user_alist; tail; tail = tail->next) 74 { 75 if (tail->id.u == uid) 76 { 77 match = tail; 78 break; 79 } 80 } 81 82 if (match == NULL) 83 { 84 struct passwd *pwent = getpwuid (uid); 85 char const *name = pwent ? pwent->pw_name : ""; 86 match = xmalloc (offsetof (struct userid, name) + strlen (name) + 1); 87 match->id.u = uid; 88 strcpy (match->name, name); 89 90 /* Add to the head of the list, so most recently used is first. */ 91 match->next = user_alist; 92 user_alist = match; 93 } 94 95 return match->name[0] ? match->name : NULL; 96} 97 98/* Translate USER to a UID, with cache. 99 Return NULL if there is no such user. 100 (We also cache which user names have no passwd entry, 101 so we don't keep looking them up.) */ 102 103uid_t * 104getuidbyname (const char *user) 105{ 106 struct userid *tail; 107 struct passwd *pwent; 108 109 for (tail = user_alist; tail; tail = tail->next) 110 /* Avoid a function call for the most common case. */ 111 if (*tail->name == *user && !strcmp (tail->name, user)) 112 return &tail->id.u; 113 114 for (tail = nouser_alist; tail; tail = tail->next) 115 /* Avoid a function call for the most common case. */ 116 if (*tail->name == *user && !strcmp (tail->name, user)) 117 return NULL; 118 119 pwent = getpwnam (user); 120#ifdef __DJGPP__ 121 /* We need to pretend to be the user USER, to make 122 pwd functions know about an arbitrary user name. */ 123 if (!pwent && strspn (user, digits) < strlen (user)) 124 { 125 setenv ("USER", user, 1); 126 pwent = getpwnam (user); /* now it will succeed */ 127 } 128#endif 129 130 tail = xmalloc (offsetof (struct userid, name) + strlen (user) + 1); 131 strcpy (tail->name, user); 132 133 /* Add to the head of the list, so most recently used is first. */ 134 if (pwent) 135 { 136 tail->id.u = pwent->pw_uid; 137 tail->next = user_alist; 138 user_alist = tail; 139 return &tail->id.u; 140 } 141 142 tail->next = nouser_alist; 143 nouser_alist = tail; 144 return NULL; 145} 146 147/* Translate GID to a group name, with cache, or NULL if unresolved. */ 148 149char * 150getgroup (gid_t gid) 151{ 152 struct userid *tail; 153 struct userid *match = NULL; 154 155 for (tail = group_alist; tail; tail = tail->next) 156 { 157 if (tail->id.g == gid) 158 { 159 match = tail; 160 break; 161 } 162 } 163 164 if (match == NULL) 165 { 166 struct group *grent = getgrgid (gid); 167 char const *name = grent ? grent->gr_name : ""; 168 match = xmalloc (offsetof (struct userid, name) + strlen (name) + 1); 169 match->id.g = gid; 170 strcpy (match->name, name); 171 172 /* Add to the head of the list, so most recently used is first. */ 173 match->next = group_alist; 174 group_alist = match; 175 } 176 177 return match->name[0] ? match->name : NULL; 178} 179 180/* Translate GROUP to a GID, with cache. 181 Return NULL if there is no such group. 182 (We also cache which group names have no group entry, 183 so we don't keep looking them up.) */ 184 185gid_t * 186getgidbyname (const char *group) 187{ 188 struct userid *tail; 189 struct group *grent; 190 191 for (tail = group_alist; tail; tail = tail->next) 192 /* Avoid a function call for the most common case. */ 193 if (*tail->name == *group && !strcmp (tail->name, group)) 194 return &tail->id.g; 195 196 for (tail = nogroup_alist; tail; tail = tail->next) 197 /* Avoid a function call for the most common case. */ 198 if (*tail->name == *group && !strcmp (tail->name, group)) 199 return NULL; 200 201 grent = getgrnam (group); 202#ifdef __DJGPP__ 203 /* We need to pretend to belong to group GROUP, to make 204 grp functions know about an arbitrary group name. */ 205 if (!grent && strspn (group, digits) < strlen (group)) 206 { 207 setenv ("GROUP", group, 1); 208 grent = getgrnam (group); /* now it will succeed */ 209 } 210#endif 211 212 tail = xmalloc (offsetof (struct userid, name) + strlen (group) + 1); 213 strcpy (tail->name, group); 214 215 /* Add to the head of the list, so most recently used is first. */ 216 if (grent) 217 { 218 tail->id.g = grent->gr_gid; 219 tail->next = group_alist; 220 group_alist = tail; 221 return &tail->id.g; 222 } 223 224 tail->next = nogroup_alist; 225 nogroup_alist = tail; 226 return NULL; 227} 228