1 /* idcache.c -- map user and group IDs, cached for speed
3 Copyright (C) 1985, 1988-1990, 1997-1998, 2003, 2005-2007, 2009-2011 Free
4 Software Foundation, Inc.
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.
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.
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/>. */
33 static char digits[] = "0123456789";
44 char name[FLEXIBLE_ARRAY_MEMBER];
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:
54 static struct userid *user_alist;
56 /* Each entry on list is a user name for which the first lookup failed. */
57 static struct userid *nouser_alist;
59 /* Use the same struct as for userids. */
60 static struct userid *group_alist;
62 /* Each entry on list is a group name for which the first lookup failed. */
63 static struct userid *nogroup_alist;
65 /* Translate UID to a login name, with cache, or NULL if unresolved. */
71 struct userid *match = NULL;
73 for (tail = user_alist; tail; tail = tail->next)
75 if (tail->id.u == uid)
84 struct passwd *pwent = getpwuid (uid);
85 char const *name = pwent ? pwent->pw_name : "";
86 match = xmalloc (offsetof (struct userid, name) + strlen (name) + 1);
88 strcpy (match->name, name);
90 /* Add to the head of the list, so most recently used is first. */
91 match->next = user_alist;
95 return match->name[0] ? match->name : NULL;
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.) */
104 getuidbyname (const char *user)
107 struct passwd *pwent;
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))
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))
119 pwent = getpwnam (user);
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))
125 setenv ("USER", user, 1);
126 pwent = getpwnam (user); /* now it will succeed */
130 tail = xmalloc (offsetof (struct userid, name) + strlen (user) + 1);
131 strcpy (tail->name, user);
133 /* Add to the head of the list, so most recently used is first. */
136 tail->id.u = pwent->pw_uid;
137 tail->next = user_alist;
142 tail->next = nouser_alist;
147 /* Translate GID to a group name, with cache, or NULL if unresolved. */
153 struct userid *match = NULL;
155 for (tail = group_alist; tail; tail = tail->next)
157 if (tail->id.g == gid)
166 struct group *grent = getgrgid (gid);
167 char const *name = grent ? grent->gr_name : "";
168 match = xmalloc (offsetof (struct userid, name) + strlen (name) + 1);
170 strcpy (match->name, name);
172 /* Add to the head of the list, so most recently used is first. */
173 match->next = group_alist;
177 return match->name[0] ? match->name : NULL;
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.) */
186 getgidbyname (const char *group)
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))
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))
201 grent = getgrnam (group);
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))
207 setenv ("GROUP", group, 1);
208 grent = getgrnam (group); /* now it will succeed */
212 tail = xmalloc (offsetof (struct userid, name) + strlen (group) + 1);
213 strcpy (tail->name, group);
215 /* Add to the head of the list, so most recently used is first. */
218 tail->id.g = grent->gr_gid;
219 tail->next = group_alist;
224 tail->next = nogroup_alist;
225 nogroup_alist = tail;