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
27 #include <arpa/inet.h>
28 #include <rpcsvc/nis.h>
29 #include <sys/param.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. */
42 cache_search (request_type type
, void *key
, size_t len
, struct database
*table
,
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
];
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)
70 if (nsearched
> table
->maxnsearched
)
71 table
->maxnsearched
= nsearched
;
76 /* Add a new entry to the cache. The return value is zero if the function
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. */
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
));
94 error (EXIT_FAILURE
, errno
, _("while allocating hash table entry"));
102 newp
->packet
= packet
;
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
,
113 /* Update the statistics. */
114 if (data
== (void *) -1)
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
139 prune_cache (struct database
*table
, time_t now
)
141 size_t cnt
= table
->module
;
144 size_t first
= cnt
+ 1;
147 /* If this table is not actually used don't do anything. */
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
)
157 if (stat (table
->filename
, &st
) < 0)
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
)));
165 table
->check_file
= 0;
169 if (st
.st_mtime
!= table
->file_mtime
)
171 /* The file changed. Invalidate all entries. */
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
186 struct hashentry
*runp
= table
->array
[--cnt
];
192 if (runp
->timeout
< now
)
196 first
= MIN (first
, cnt
);
197 last
= MAX (last
, cnt
);
206 struct hashentry
*head
= NULL
;
208 /* Now we have to get the write lock since we are about to modify
210 if (__builtin_expect (pthread_rwlock_trywrlock (&table
->lock
) != 0, 0))
212 ++table
->wrlockdelayed
;
213 pthread_rwlock_wrlock (&table
->lock
);
216 while (first
<= last
)
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
;
228 if (--mark
[first
] == 0)
232 runp
= table
->array
[first
];
233 while (mark
[first
] > 0)
235 if (runp
->next
->timeout
< now
)
237 runp
->next
->dellist
= head
;
239 runp
->next
= head
->next
;
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
;
260 char buf
[INET6_ADDRSTRLEN
];
263 if (runp
->type
== GETHOSTBYADDR
|| runp
->type
== GETHOSTBYADDRv6
)
265 inet_ntop (runp
->type
== GETHOSTBYADDR
? AF_INET
: AF_INET6
,
266 runp
->key
, buf
, sizeof (buf
));
272 dbg_log ("remove %s entry \"%s\"", serv2str
[runp
->type
], str
);
278 /* And another run to free the data. */
281 struct hashentry
*old
= head
;
283 /* Free the data structures. */
284 if (old
->data
== (void *) -1)
289 head
= head
->dellist
;
293 while (head
!= NULL
);