1 /* Copyright (c) 1998, 1999, 2003-2006, 2007 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2 as
7 published by the Free Software Foundation.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
27 #include <arpa/inet.h>
28 #include <rpcsvc/nis.h>
30 #include <sys/param.h>
38 /* Number of times a value is reloaded without being used. UINT_MAX
40 unsigned int reload_count
= DEFAULT_RELOAD_LIMIT
;
43 static void (*const readdfcts
[LASTREQ
]) (struct database_dyn
*,
47 [GETPWBYNAME
] = readdpwbyname
,
48 [GETPWBYUID
] = readdpwbyuid
,
49 [GETGRBYNAME
] = readdgrbyname
,
50 [GETGRBYGID
] = readdgrbygid
,
51 [GETHOSTBYNAME
] = readdhstbyname
,
52 [GETHOSTBYNAMEv6
] = readdhstbynamev6
,
53 [GETHOSTBYADDR
] = readdhstbyaddr
,
54 [GETHOSTBYADDRv6
] = readdhstbyaddrv6
,
56 [INITGROUPS
] = readdinitgroups
,
57 [GETSERVBYNAME
] = readdservbyname
,
58 [GETSERVBYPORT
] = readdservbyport
62 /* Search the cache for a matching entry and return it when found. If
63 this fails search the negative cache and return (void *) -1 if this
64 search was successful. Otherwise return NULL.
66 This function must be called with the read-lock held. */
68 cache_search (request_type type
, void *key
, size_t len
,
69 struct database_dyn
*table
, uid_t owner
)
71 unsigned long int hash
= __nis_hash (key
, len
) % table
->head
->module
;
73 unsigned long int nsearched
= 0;
74 struct datahead
*result
= NULL
;
76 ref_t work
= table
->head
->array
[hash
];
77 while (work
!= ENDREF
)
81 struct hashentry
*here
= (struct hashentry
*) (table
->data
+ work
);
83 if (type
== here
->type
&& len
== here
->len
84 && memcmp (key
, table
->data
+ here
->key
, len
) == 0
85 && here
->owner
== owner
)
87 /* We found the entry. Increment the appropriate counter. */
89 = (struct datahead
*) (table
->data
+ here
->packet
);
91 /* See whether we must ignore the entry. */
94 /* We do not synchronize the memory here. The statistics
95 data is not crucial, we synchronize only once in a while
96 in the cleanup threads. */
98 ++table
->head
->neghit
;
101 ++table
->head
->poshit
;
103 if (dh
->nreloads
!= 0)
115 if (nsearched
> table
->head
->maxnsearched
)
116 table
->head
->maxnsearched
= nsearched
;
121 /* Add a new entry to the cache. The return value is zero if the function
124 This function must be called with the read-lock held.
126 We modify the table but we nevertheless only acquire a read-lock.
127 This is ok since we use operations which would be safe even without
128 locking, given that the `prune_cache' function never runs. Using
129 the readlock reduces the chance of conflicts. */
131 cache_add (int type
, const void *key
, size_t len
, struct datahead
*packet
,
132 bool first
, struct database_dyn
*table
,
135 if (__builtin_expect (debug_level
>= 2, 0))
138 char buf
[INET6_ADDRSTRLEN
+ 1];
139 if (type
== GETHOSTBYADDR
|| type
== GETHOSTBYADDRv6
)
140 str
= inet_ntop (type
== GETHOSTBYADDR
? AF_INET
: AF_INET6
,
141 key
, buf
, sizeof (buf
));
145 dbg_log (_("add new entry \"%s\" of type %s for %s to cache%s"),
146 str
, serv2str
[type
], dbnames
[table
- dbs
],
147 first
? _(" (first)") : "");
150 unsigned long int hash
= __nis_hash (key
, len
) % table
->head
->module
;
151 struct hashentry
*newp
;
153 newp
= mempool_alloc (table
, sizeof (struct hashentry
));
154 /* If we cannot allocate memory, just do not do anything. */
157 ++table
->head
->addfailed
;
164 newp
->key
= (char *) key
- table
->data
;
165 assert (newp
->key
+ newp
->len
<= table
->head
->first_free
);
167 newp
->packet
= (char *) packet
- table
->data
;
169 /* Put the new entry in the first position. */
171 newp
->next
= table
->head
->array
[hash
];
172 while (atomic_compare_and_exchange_bool_acq (&table
->head
->array
[hash
],
173 (ref_t
) ((char *) newp
175 (ref_t
) newp
->next
));
177 /* Update the statistics. */
178 if (packet
->notfound
)
179 ++table
->head
->negmiss
;
181 ++table
->head
->posmiss
;
183 /* We depend on this value being correct and at least as high as the
184 real number of entries. */
185 atomic_increment (&table
->head
->nentries
);
187 /* It does not matter that we are not loading the just increment
188 value, this is just for statistics. */
189 unsigned long int nentries
= table
->head
->nentries
;
190 if (nentries
> table
->head
->maxnentries
)
191 table
->head
->maxnentries
= nentries
;
193 if (table
->persistent
)
195 msync ((void *) table
->head
,
196 (char *) &table
->head
->array
[hash
] - (char *) table
->head
197 + sizeof (ref_t
), MS_ASYNC
);
202 /* Walk through the table and remove all entries which lifetime ended.
204 We have a problem here. To actually remove the entries we must get
205 the write-lock. But since we want to keep the time we have the
206 lock as short as possible we cannot simply acquire the lock when we
207 start looking for timedout entries.
209 Therefore we do it in two stages: first we look for entries which
210 must be invalidated and remember them. Then we get the lock and
211 actually remove them. This is complicated by the way we have to
212 free the data structures since some hash table entries share the same
215 prune_cache (struct database_dyn
*table
, time_t now
, int fd
)
217 size_t cnt
= table
->head
->module
;
219 /* If this table is not actually used don't do anything. */
224 /* Reply to the INVALIDATE initiator. */
226 writeall (fd
, &resp
, sizeof (resp
));
231 /* This function can be called from the cleanup thread but also in
232 response to an invalidate command. Make sure only one thread is
233 running. When not serving INVALIDATE request, no need for the
234 second to wait around. */
237 if (pthread_mutex_trylock (&table
->prunelock
) != 0)
238 /* The work is already being done. */
242 pthread_mutex_lock (&table
->prunelock
);
244 /* If we check for the modification of the underlying file we invalidate
245 the entries also in this case. */
246 if (table
->check_file
)
250 if (stat64 (table
->filename
, &st
) < 0)
253 /* We cannot stat() the file, disable file checking if the
254 file does not exist. */
255 dbg_log (_("cannot stat() file `%s': %s"),
256 table
->filename
, strerror_r (errno
, buf
, sizeof (buf
)));
258 table
->check_file
= 0;
262 if (st
.st_mtime
!= table
->file_mtime
)
264 /* The file changed. Invalidate all entries. */
266 table
->file_mtime
= st
.st_mtime
;
271 /* We run through the table and find values which are not valid anymore.
273 Note that for the initial step, finding the entries to be removed,
274 we don't need to get any lock. It is at all timed assured that the
275 linked lists are set up correctly and that no second thread prunes
278 size_t first
= cnt
+ 1;
280 char *const data
= table
->data
;
283 if (__builtin_expect (debug_level
> 2, 0))
284 dbg_log (_("pruning %s cache; time %ld"),
285 dbnames
[table
- dbs
], (long int) now
);
289 ref_t run
= table
->head
->array
[--cnt
];
291 while (run
!= ENDREF
)
293 struct hashentry
*runp
= (struct hashentry
*) (data
+ run
);
294 struct datahead
*dh
= (struct datahead
*) (data
+ runp
->packet
);
296 /* Some debug support. */
297 if (__builtin_expect (debug_level
> 2, 0))
299 char buf
[INET6_ADDRSTRLEN
];
302 if (runp
->type
== GETHOSTBYADDR
|| runp
->type
== GETHOSTBYADDRv6
)
304 inet_ntop (runp
->type
== GETHOSTBYADDR
? AF_INET
: AF_INET6
,
305 data
+ runp
->key
, buf
, sizeof (buf
));
309 str
= data
+ runp
->key
;
311 dbg_log (_("considering %s entry \"%s\", timeout %" PRIu64
),
312 serv2str
[runp
->type
], str
, dh
->timeout
);
315 /* Check whether the entry timed out. */
316 if (dh
->timeout
< now
)
318 /* This hash bucket could contain entries which need to
322 first
= MIN (first
, cnt
);
323 last
= MAX (last
, cnt
);
325 /* We only have to look at the data of the first entries
326 since the count information is kept in the data part
331 /* At this point there are two choices: we reload the
332 value or we discard it. Do not change NRELOADS if
333 we never not reload the record. */
334 if ((reload_count
!= UINT_MAX
335 && __builtin_expect (dh
->nreloads
>= reload_count
, 0))
336 /* We always remove negative entries. */
338 /* Discard everything if the user explicitly
342 /* Remove the value. */
345 /* We definitely have some garbage entries now. */
350 /* Reload the value. We do this only for the
351 initially used key, not the additionally
352 added derived value. */
353 assert (runp
->type
< LASTREQ
354 && readdfcts
[runp
->type
] != NULL
);
356 readdfcts
[runp
->type
] (table
, runp
, dh
);
358 /* If the entry has been replaced, we might need
374 /* Reply to the INVALIDATE initiator that the cache has been
377 writeall (fd
, &resp
, sizeof (resp
));
382 struct hashentry
*head
= NULL
;
384 /* Now we have to get the write lock since we are about to modify
386 if (__builtin_expect (pthread_rwlock_trywrlock (&table
->lock
) != 0, 0))
388 ++table
->head
->wrlockdelayed
;
389 pthread_rwlock_wrlock (&table
->lock
);
392 while (first
<= last
)
396 ref_t
*old
= &table
->head
->array
[first
];
397 ref_t run
= table
->head
->array
[first
];
399 while (run
!= ENDREF
)
401 struct hashentry
*runp
= (struct hashentry
*) (data
+ run
);
403 = (struct datahead
*) (data
+ runp
->packet
);
407 /* We need the list only for debugging but it is
408 more costly to avoid creating the list than
410 runp
->dellist
= head
;
413 /* No need for an atomic operation, we have the
415 --table
->head
->nentries
;
417 run
= *old
= runp
->next
;
431 pthread_rwlock_unlock (&table
->lock
);
433 /* Make sure the data is saved to disk. */
434 if (table
->persistent
)
436 data
+ table
->head
->first_free
- (char *) table
->head
,
439 /* One extra pass if we do debugging. */
440 if (__builtin_expect (debug_level
> 0, 0))
442 struct hashentry
*runp
= head
;
446 char buf
[INET6_ADDRSTRLEN
];
449 if (runp
->type
== GETHOSTBYADDR
|| runp
->type
== GETHOSTBYADDRv6
)
451 inet_ntop (runp
->type
== GETHOSTBYADDR
? AF_INET
: AF_INET6
,
452 data
+ runp
->key
, buf
, sizeof (buf
));
456 str
= data
+ runp
->key
;
458 dbg_log ("remove %s entry \"%s\"", serv2str
[runp
->type
], str
);
460 runp
= runp
->dellist
;
465 /* Run garbage collection if any entry has been removed or replaced. */
469 pthread_mutex_unlock (&table
->prunelock
);