Add prototype for host database functions.
[kopensolaris-gnu/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
123    This function must be called with the write-lock held.  */
124 void
125 prune_cache (struct database *table, time_t now)
126 {
127   size_t cnt = table->module;
128   int mark[cnt];
129   int anything = 0;
130   size_t first = cnt + 1;
131   size_t last = 0;
132
133   /* If we check for the modification of the underlying file we invalidate
134      the entries also in this case.  */
135   if (table->check_file)
136     {
137       struct stat st;
138
139       if (stat (table->filename, &st) < 0)
140         {
141           char buf[128];
142           /* We cannot stat() the file, disable file checking.  */
143           dbg_log (_("cannot stat() file `%s': %s"),
144                    table->filename, strerror_r (errno, buf, sizeof (buf)));
145           table->check_file = 0;
146         }
147       else
148         {
149           if (st.st_mtime != table->file_mtime)
150             /* The file changed.  Invalidate all entries.  */
151             now = LONG_MAX;
152         }
153     }
154
155   /* We run through the table and find values which are not valid anymore.
156
157    Note that for the initial step, finding the entries to be removed,
158    we don't need to get any lock.  It is at all timed assured that the
159    linked lists are set up correctly and that no second thread prunes
160    the cache.  */
161   do
162     {
163       struct hashentry *runp = table->array[--cnt];
164
165       mark[cnt] = 0;
166
167       while (runp != NULL)
168         {
169           if (runp->timeout < now)
170             {
171               ++mark[cnt];
172               anything = 1;
173               first = MIN (first, cnt);
174               last = MAX (last, cnt);
175             }
176           runp = runp->next;
177         }
178     }
179   while (cnt > 0);
180
181   if (anything)
182     {
183       struct hashentry *head = NULL;
184
185       /* Now we have to get the write lock since we are about to modify
186          the table.  */
187       pthread_rwlock_wrlock (&table->lock);
188
189       while (first <= last)
190         {
191           if (mark[first] > 0)
192             {
193               struct hashentry *runp;
194
195               while (table->array[first]->timeout < now)
196                 {
197                   table->array[first]->dellist = head;
198                   head = table->array[first];
199                   table->array[first] = head->next;
200                   if (--mark[first] == 0)
201                     break;
202                 }
203
204               runp = table->array[first];
205               while (mark[first] > 0)
206                 {
207                   if (runp->next->timeout < now)
208                     {
209                       runp->next->dellist = head;
210                       head = runp->next;
211                       runp->next = head->next;
212                       --mark[first];
213                     }
214                   else
215                     runp = runp->next;
216                 }
217             }
218           ++first;
219         }
220
221       /* It's all done.  */
222       pthread_rwlock_unlock (&table->lock);
223
224       /* And another run to free the data.  */
225       do
226         {
227           struct hashentry *old = head;
228
229           if (debug_level > 0)
230             dbg_log ("remove %s entry \"%s\"",
231                      serv2str[old->type],
232                      old->last
233                      ? old->key : old->data == (void *) -1 ? old->key : "???");
234
235           /* Free the data structures.  */
236           if (old->data == (void *) -1)
237             free (old->key);
238           else if (old->last)
239             free (old->data);
240
241           head = head->dellist;
242
243           free (old);
244         }
245       while (head != NULL);
246     }
247 }