dbc89fb8ecc648c6fbd6e5417ba982f86b7b2232
[platform/upstream/glibc.git] / nscd / cache.c
1 /* Copyright (c) 1998 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
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    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA. */
19
20 #include <atomicity.h>
21 #include <errno.h>
22 #include <error.h>
23 #include <limits.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <rpcsvc/nis.h>
27 #include <sys/param.h>
28 #include <sys/stat.h>
29 #include <sys/uio.h>
30
31 #include "nscd.h"
32 #include "dbg_log.h"
33
34 /* Search the cache for a matching entry and return it when found.  If
35    this fails search the negative cache and return (void *) -1 if this
36    search was successful.  Otherwise return NULL.
37
38    This function must be called with the read-lock held.  */
39 struct hashentry *
40 cache_search (int type, void *key, size_t len, struct database *table)
41 {
42   unsigned long int hash = __nis_hash (key, len) % table->module;
43   struct hashentry *work;
44
45   work = table->array[hash];
46
47   while (work != NULL)
48     {
49       if (type == work->type
50           && len == work->len && memcmp (key, work->key, len) == 0)
51         {
52           /* We found the entry.  Increment the appropriate counter.  */
53           if (work->data == (void *) -1)
54             ++table->neghit;
55           else
56             ++table->poshit;
57
58           return work;
59         }
60
61       work = work->next;
62     }
63
64   return NULL;
65 }
66
67 /* Add a new entry to the cache.  The return value is zero if the function
68    call was successful.
69
70    This function must be called with the read-lock held.
71
72    We modify the table but we nevertheless only acquire a read-lock.
73    This is ok since we use operations which would be safe even without
74    locking, given that the `prune_cache' function never runs.  Using
75    the readlock reduces the chance of conflicts.  */
76 void
77 cache_add (int type, void *key, size_t len, const void *packet, size_t total,
78            void *data, int last, time_t t, struct database *table)
79 {
80   unsigned long int hash = __nis_hash (key, len) % table->module;
81   struct hashentry *newp;
82
83   newp = malloc (sizeof (struct hashentry));
84   if (newp == NULL)
85     error (EXIT_FAILURE, errno, _("while allocating hash table entry"));
86
87   newp->type = type;
88   newp->len = len;
89   newp->key = key;
90   newp->data = data;
91   newp->timeout = t;
92   newp->packet = packet;
93   newp->total = total;
94
95   newp->last = last;
96
97   /* Put the new entry in the first position.  */
98   do
99     newp->next = table->array[hash];
100   while (! compare_and_swap ((volatile long int *) &table->array[hash],
101                              (long int) newp->next, (long int) newp));
102
103   /* Update the statistics.  */
104   if (data == (void *) -1)
105     ++table->negmiss;
106   else if (last)
107     ++table->posmiss;
108 }
109
110 /* Walk through the table and remove all entries which lifetime ended.
111
112    We have a problem here.  To actually remove the entries we must get
113    the write-lock.  But since we want to keep the time we have the
114    lock as short as possible we cannot simply acquire the lock when we
115    start looking for timedout entries.
116
117    Therefore we do it in two stages: first we look for entries which
118    must be invalidated and remember them.  Then we get the lock and
119    actually remove them.  This is complicated by the way we have to
120    free the data structures since some hash table entries share the same
121    data.  */
122 void
123 prune_cache (struct database *table, time_t now)
124 {
125   size_t cnt = table->module;
126   int mark[cnt];
127   int anything = 0;
128   size_t first = cnt + 1;
129   size_t last = 0;
130
131   /* If we check for the modification of the underlying file we invalidate
132      the entries also in this case.  */
133   if (table->check_file)
134     {
135       struct stat st;
136
137       if (stat (table->filename, &st) < 0)
138         {
139           char buf[128];
140           /* We cannot stat() the file, disable file checking.  */
141           dbg_log (_("cannot stat() file `%s': %s"),
142                    table->filename, strerror_r (errno, buf, sizeof (buf)));
143           table->check_file = 0;
144         }
145       else
146         {
147           if (st.st_mtime != table->file_mtime)
148             /* The file changed.  Invalidate all entries.  */
149             now = LONG_MAX;
150         }
151     }
152
153   /* We run through the table and find values which are not valid anymore.
154
155    Note that for the initial step, finding the entries to be removed,
156    we don't need to get any lock.  It is at all timed assured that the
157    linked lists are set up correctly and that no second thread prunes
158    the cache.  */
159   do
160     {
161       struct hashentry *runp = table->array[--cnt];
162
163       mark[cnt] = 0;
164
165       while (runp != NULL)
166         {
167           if (runp->timeout < now)
168             {
169               ++mark[cnt];
170               anything = 1;
171               first = MIN (first, cnt);
172               last = MAX (last, cnt);
173             }
174           runp = runp->next;
175         }
176     }
177   while (cnt > 0);
178
179   if (anything)
180     {
181       struct hashentry *head = NULL;
182
183       /* Now we have to get the write lock since we are about to modify
184          the table.  */
185       pthread_rwlock_wrlock (&table->lock);
186
187       while (first <= last)
188         {
189           if (mark[first] > 0)
190             {
191               struct hashentry *runp;
192
193               while (table->array[first]->timeout < now)
194                 {
195                   table->array[first]->dellist = head;
196                   head = table->array[first];
197                   table->array[first] = head->next;
198                   if (--mark[first] == 0)
199                     break;
200                 }
201
202               runp = table->array[first];
203               while (mark[first] > 0)
204                 {
205                   if (runp->next->timeout < now)
206                     {
207                       runp->next->dellist = head;
208                       head = runp->next;
209                       runp->next = head->next;
210                       --mark[first];
211                     }
212                   else
213                     runp = runp->next;
214                 }
215             }
216           ++first;
217         }
218
219       /* It's all done.  */
220       pthread_rwlock_unlock (&table->lock);
221
222       /* And another run to free the data.  */
223       do
224         {
225           struct hashentry *old = head;
226
227           if (debug_level > 0)
228             {
229               char buf[INET6_ADDRSTRLEN];
230               const char *str;
231
232               if ((old->type == GETHOSTBYADDR || old->type == GETHOSTBYADDRv6)
233                   && (old->last || old->data == (void *) -1))
234                 {
235                   inet_ntop (old->type == GETHOSTBYADDR ? AF_INET : AF_INET6,
236                              old->key, buf, sizeof (buf));
237                   str = buf;
238                 }
239               else
240                 str = old->last ? old->key : (old->data == (void *) -1
241                                               ? old->key : "???");
242
243               dbg_log ("remove %s entry \"%s\"", serv2str[old->type], str);
244             }
245
246           /* Free the data structures.  */
247           if (old->data == (void *) -1)
248             free (old->key);
249           else if (old->last)
250             free (old->data);
251
252           head = head->dellist;
253
254           free (old);
255         }
256       while (head != NULL);
257     }
258 }