Add hidden_def.
[glibc.git] / nscd / cache.c
blob10b04c3c02465a3bb075c54a9c2616c83a3d35c6
1 /* Copyright (c) 1998, 1999, 2003 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
20 #include <atomic.h>
21 #include <errno.h>
22 #include <error.h>
23 #include <limits.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <libintl.h>
27 #include <arpa/inet.h>
28 #include <rpcsvc/nis.h>
29 #include <sys/param.h>
30 #include <sys/stat.h>
31 #include <sys/uio.h>
33 #include "nscd.h"
34 #include "dbg_log.h"
36 /* Search the cache for a matching entry and return it when found. If
37 this fails search the negative cache and return (void *) -1 if this
38 search was successful. Otherwise return NULL.
40 This function must be called with the read-lock held. */
41 struct hashentry *
42 cache_search (request_type type, void *key, size_t len, struct database *table,
43 uid_t owner)
45 unsigned long int hash = __nis_hash (key, len) % table->module;
46 struct hashentry *work;
47 unsigned long int nsearched = 0;
49 work = table->array[hash];
51 while (work != NULL)
53 ++nsearched;
55 if (type == work->type && len == work->len
56 && memcmp (key, work->key, len) == 0 && work->owner == owner)
58 /* We found the entry. Increment the appropriate counter. */
59 if (work->data == (void *) -1)
60 ++table->neghit;
61 else
62 ++table->poshit;
64 break;
67 work = work->next;
70 if (nsearched > table->maxnsearched)
71 table->maxnsearched = nsearched;
73 return work;
76 /* Add a new entry to the cache. The return value is zero if the function
77 call was successful.
79 This function must be called with the read-lock held.
81 We modify the table but we nevertheless only acquire a read-lock.
82 This is ok since we use operations which would be safe even without
83 locking, given that the `prune_cache' function never runs. Using
84 the readlock reduces the chance of conflicts. */
85 void
86 cache_add (int type, void *key, size_t len, const void *packet, size_t total,
87 void *data, int last, time_t t, struct database *table, uid_t owner)
89 unsigned long int hash = __nis_hash (key, len) % table->module;
90 struct hashentry *newp;
92 newp = malloc (sizeof (struct hashentry));
93 if (newp == NULL)
94 error (EXIT_FAILURE, errno, _("while allocating hash table entry"));
96 newp->type = type;
97 newp->len = len;
98 newp->key = key;
99 newp->owner = owner;
100 newp->data = data;
101 newp->timeout = t;
102 newp->packet = packet;
103 newp->total = total;
105 newp->last = last;
107 /* Put the new entry in the first position. */
109 newp->next = table->array[hash];
110 while (atomic_compare_and_exchange_bool_acq (&table->array[hash], newp,
111 newp->next));
113 /* Update the statistics. */
114 if (data == (void *) -1)
115 ++table->negmiss;
116 else if (last)
117 ++table->posmiss;
119 /* Instead of slowing down the normal process for statistics
120 collection we accept living with some incorrect data. */
121 unsigned long int nentries = ++table->nentries;
122 if (nentries > table->maxnentries)
123 table->maxnentries = nentries;
126 /* Walk through the table and remove all entries which lifetime ended.
128 We have a problem here. To actually remove the entries we must get
129 the write-lock. But since we want to keep the time we have the
130 lock as short as possible we cannot simply acquire the lock when we
131 start looking for timedout entries.
133 Therefore we do it in two stages: first we look for entries which
134 must be invalidated and remember them. Then we get the lock and
135 actually remove them. This is complicated by the way we have to
136 free the data structures since some hash table entries share the same
137 data. */
138 void
139 prune_cache (struct database *table, time_t now)
141 size_t cnt = table->module;
142 int mark[cnt];
143 int anything = 0;
144 size_t first = cnt + 1;
145 size_t last = 0;
147 /* If this table is not actually used don't do anything. */
148 if (cnt == 0)
149 return;
151 /* If we check for the modification of the underlying file we invalidate
152 the entries also in this case. */
153 if (table->check_file)
155 struct stat st;
157 if (stat (table->filename, &st) < 0)
159 char buf[128];
160 /* We cannot stat() the file, disable file checking if the
161 file does not exist. */
162 dbg_log (_("cannot stat() file `%s': %s"),
163 table->filename, strerror_r (errno, buf, sizeof (buf)));
164 if (errno == ENOENT)
165 table->check_file = 0;
167 else
169 if (st.st_mtime != table->file_mtime)
171 /* The file changed. Invalidate all entries. */
172 now = LONG_MAX;
173 table->file_mtime = st.st_mtime;
178 /* We run through the table and find values which are not valid anymore.
180 Note that for the initial step, finding the entries to be removed,
181 we don't need to get any lock. It is at all timed assured that the
182 linked lists are set up correctly and that no second thread prunes
183 the cache. */
186 struct hashentry *runp = table->array[--cnt];
188 mark[cnt] = 0;
190 while (runp != NULL)
192 if (runp->timeout < now)
194 ++mark[cnt];
195 anything = 1;
196 first = MIN (first, cnt);
197 last = MAX (last, cnt);
199 runp = runp->next;
202 while (cnt > 0);
204 if (anything)
206 struct hashentry *head = NULL;
208 /* Now we have to get the write lock since we are about to modify
209 the table. */
210 if (__builtin_expect (pthread_rwlock_trywrlock (&table->lock) != 0, 0))
212 ++table->wrlockdelayed;
213 pthread_rwlock_wrlock (&table->lock);
216 while (first <= last)
218 if (mark[first] > 0)
220 struct hashentry *runp;
222 while (table->array[first]->timeout < now)
224 table->array[first]->dellist = head;
225 head = table->array[first];
226 table->array[first] = head->next;
227 --table->nentries;
228 if (--mark[first] == 0)
229 break;
232 runp = table->array[first];
233 while (mark[first] > 0)
235 if (runp->next->timeout < now)
237 runp->next->dellist = head;
238 head = runp->next;
239 runp->next = head->next;
240 --mark[first];
241 --table->nentries;
243 else
244 runp = runp->next;
247 ++first;
250 /* It's all done. */
251 pthread_rwlock_unlock (&table->lock);
253 /* One extra pass if we do debugging. */
254 if (__builtin_expect (debug_level > 0, 0))
256 struct hashentry *runp = head;
258 while (runp != NULL)
260 char buf[INET6_ADDRSTRLEN];
261 const char *str;
263 if (runp->type == GETHOSTBYADDR || runp->type == GETHOSTBYADDRv6)
265 inet_ntop (runp->type == GETHOSTBYADDR ? AF_INET : AF_INET6,
266 runp->key, buf, sizeof (buf));
267 str = buf;
269 else
270 str = runp->key;
272 dbg_log ("remove %s entry \"%s\"", serv2str[runp->type], str);
274 runp = runp->next;
278 /* And another run to free the data. */
281 struct hashentry *old = head;
283 /* Free the data structures. */
284 if (old->data == (void *) -1)
285 free (old->key);
286 else if (old->last)
287 free (old->data);
289 head = head->dellist;
291 free (old);
293 while (head != NULL);