]> git.ipfire.org Git - thirdparty/glibc.git/blame - nscd/servicescache.c
Add missing BZ number in ChangeLog.
[thirdparty/glibc.git] / nscd / servicescache.c
CommitLineData
b21fa963 1/* Cache handling for services lookup.
20e498bd 2 Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
b21fa963
UD
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@drepper.com>, 2007.
5
6 This program is free software; you can redistribute it and/or modify
2e2efe65
RM
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; version 2 of the License, or
9 (at your option) any later version.
b21fa963
UD
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software Foundation,
18 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19
20#include <alloca.h>
21#include <assert.h>
22#include <errno.h>
23#include <libintl.h>
24#include <netdb.h>
25#include <unistd.h>
26#include <sys/mman.h>
f8de5057 27#include <kernel-features.h>
b21fa963
UD
28
29#include "nscd.h"
30#include "dbg_log.h"
31
32
33/* This is the standard reply in case the service is disabled. */
34static const serv_response_header disabled =
35{
36 .version = NSCD_VERSION,
37 .found = -1,
38 .s_name_len = 0,
39 .s_proto_len = 0,
40 .s_aliases_cnt = 0,
41 .s_port = -1
42};
43
44/* This is the struct describing how to write this record. */
45const struct iovec serv_iov_disabled =
46{
47 .iov_base = (void *) &disabled,
48 .iov_len = sizeof (disabled)
49};
50
51
52/* This is the standard reply in case we haven't found the dataset. */
53static const serv_response_header notfound =
54{
55 .version = NSCD_VERSION,
56 .found = 0,
57 .s_name_len = 0,
58 .s_proto_len = 0,
59 .s_aliases_cnt = 0,
60 .s_port = -1
61};
62
63
64static void
65cache_addserv (struct database_dyn *db, int fd, request_header *req,
66 const void *key, struct servent *serv, uid_t owner,
20e498bd 67 struct hashentry *const he, struct datahead *dh, int errval)
b21fa963
UD
68{
69 ssize_t total;
70 ssize_t written;
71 time_t t = time (NULL);
72
73 /* We allocate all data in one memory block: the iov vector,
74 the response header and the dataset itself. */
75 struct dataset
76 {
77 struct datahead head;
78 serv_response_header resp;
79 char strdata[0];
80 } *dataset;
81
82 assert (offsetof (struct dataset, resp) == offsetof (struct datahead, data));
83
84 if (serv == NULL)
85 {
86 if (he != NULL && errval == EAGAIN)
87 {
88 /* If we have an old record available but cannot find one
89 now because the service is not available we keep the old
90 record and make sure it does not get removed. */
91 if (reload_count != UINT_MAX)
92 /* Do not reset the value if we never not reload the record. */
93 dh->nreloads = reload_count - 1;
94
95 written = total = 0;
96 }
97 else
98 {
99 /* We have no data. This means we send the standard reply for this
100 case. */
101 total = sizeof (notfound);
102
103 written = TEMP_FAILURE_RETRY (send (fd, &notfound, total,
104 MSG_NOSIGNAL));
105
c52137d3 106 dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len,
20e498bd 107 1);
b21fa963
UD
108 /* If we cannot permanently store the result, so be it. */
109 if (dataset != NULL)
110 {
111 dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
112 dataset->head.recsize = total;
113 dataset->head.notfound = true;
114 dataset->head.nreloads = 0;
115 dataset->head.usable = true;
116
117 /* Compute the timeout time. */
118 dataset->head.timeout = t + db->negtimeout;
119
120 /* This is the reply. */
121 memcpy (&dataset->resp, &notfound, total);
122
123 /* Copy the key data. */
124 memcpy (dataset->strdata, key, req->key_len);
125
126 /* If necessary, we also propagate the data to disk. */
127 if (db->persistent)
128 {
129 // XXX async OK?
130 uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
131 msync ((void *) pval,
132 ((uintptr_t) dataset & pagesize_m1)
133 + sizeof (struct dataset) + req->key_len, MS_ASYNC);
134 }
135
7e71e55f 136 (void) cache_add (req->type, &dataset->strdata, req->key_len,
528741cb 137 &dataset->head, true, db, owner, he == NULL);
b21fa963 138
b21fa963
UD
139 /* Mark the old entry as obsolete. */
140 if (dh != NULL)
141 dh->usable = false;
142 }
b21fa963
UD
143 }
144 }
145 else
146 {
147 /* Determine the I/O structure. */
148 size_t s_name_len = strlen (serv->s_name) + 1;
149 size_t s_proto_len = strlen (serv->s_proto) + 1;
150 uint32_t *s_aliases_len;
151 size_t s_aliases_cnt;
152 char *aliases;
153 char *cp;
154 size_t cnt;
155
156 /* Determine the number of aliases. */
157 s_aliases_cnt = 0;
158 for (cnt = 0; serv->s_aliases[cnt] != NULL; ++cnt)
159 ++s_aliases_cnt;
160 /* Determine the length of all aliases. */
161 s_aliases_len = (uint32_t *) alloca (s_aliases_cnt * sizeof (uint32_t));
162 total = 0;
163 for (cnt = 0; cnt < s_aliases_cnt; ++cnt)
164 {
165 s_aliases_len[cnt] = strlen (serv->s_aliases[cnt]) + 1;
166 total += s_aliases_len[cnt];
167 }
168
5a337776 169 total += (offsetof (struct dataset, strdata)
b21fa963
UD
170 + s_name_len
171 + s_proto_len
172 + s_aliases_cnt * sizeof (uint32_t));
173 written = total;
174
175 /* If we refill the cache, first assume the reconrd did not
176 change. Allocate memory on the cache since it is likely
177 discarded anyway. If it turns out to be necessary to have a
178 new record we can still allocate real memory. */
179 bool alloca_used = false;
180 dataset = NULL;
181
182 if (he == NULL)
20e498bd
UD
183 dataset = (struct dataset *) mempool_alloc (db, total + req->key_len,
184 1);
b21fa963
UD
185
186 if (dataset == NULL)
187 {
188 /* We cannot permanently add the result in the moment. But
189 we can provide the result as is. Store the data in some
190 temporary memory. */
191 dataset = (struct dataset *) alloca (total + req->key_len);
192
193 /* We cannot add this record to the permanent database. */
194 alloca_used = true;
195 }
196
197 dataset->head.allocsize = total + req->key_len;
198 dataset->head.recsize = total - offsetof (struct dataset, resp);
199 dataset->head.notfound = false;
200 dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
201 dataset->head.usable = true;
202
203 /* Compute the timeout time. */
204 dataset->head.timeout = t + db->postimeout;
205
206 dataset->resp.version = NSCD_VERSION;
207 dataset->resp.found = 1;
208 dataset->resp.s_name_len = s_name_len;
209 dataset->resp.s_proto_len = s_proto_len;
210 dataset->resp.s_port = serv->s_port;
211 dataset->resp.s_aliases_cnt = s_aliases_cnt;
212
213 cp = dataset->strdata;
214
215 cp = mempcpy (cp, serv->s_name, s_name_len);
216 cp = mempcpy (cp, serv->s_proto, s_proto_len);
217 cp = mempcpy (cp, s_aliases_len, s_aliases_cnt * sizeof (uint32_t));
218
219 /* Then the aliases. */
220 aliases = cp;
221 for (cnt = 0; cnt < s_aliases_cnt; ++cnt)
222 cp = mempcpy (cp, serv->s_aliases[cnt], s_aliases_len[cnt]);
223
224 assert (cp
225 == dataset->strdata + total - offsetof (struct dataset,
226 strdata));
227
228 char *key_copy = memcpy (cp, key, req->key_len);
229
230 /* Now we can determine whether on refill we have to create a new
231 record or not. */
232 if (he != NULL)
233 {
234 assert (fd == -1);
235
236 if (total + req->key_len == dh->allocsize
237 && total - offsetof (struct dataset, resp) == dh->recsize
238 && memcmp (&dataset->resp, dh->data,
239 dh->allocsize - offsetof (struct dataset, resp)) == 0)
240 {
241 /* The data has not changed. We will just bump the
242 timeout value. Note that the new record has been
243 allocated on the stack and need not be freed. */
244 dh->timeout = dataset->head.timeout;
245 ++dh->nreloads;
246 }
247 else
248 {
249 /* We have to create a new record. Just allocate
250 appropriate memory and copy it. */
251 struct dataset *newp
c52137d3 252 = (struct dataset *) mempool_alloc (db, total + req->key_len,
20e498bd 253 1);
b21fa963
UD
254 if (newp != NULL)
255 {
256 /* Adjust pointers into the memory block. */
257 aliases = (char *) newp + (aliases - (char *) dataset);
c8703f88
UD
258 assert (key_copy != NULL);
259 key_copy = (char *) newp + (key_copy - (char *) dataset);
b21fa963
UD
260
261 dataset = memcpy (newp, dataset, total + req->key_len);
262 alloca_used = false;
263 }
264
265 /* Mark the old record as obsolete. */
266 dh->usable = false;
267 }
268 }
269 else
270 {
271 /* We write the dataset before inserting it to the database
272 since while inserting this thread might block and so would
273 unnecessarily keep the receiver waiting. */
274 assert (fd != -1);
275
276#ifdef HAVE_SENDFILE
277 if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
278 {
279 assert (db->wr_fd != -1);
280 assert ((char *) &dataset->resp > (char *) db->data);
281 assert ((char *) &dataset->resp - (char *) db->head
282 + total
283 <= (sizeof (struct database_pers_head)
284 + db->head->module * sizeof (ref_t)
285 + db->head->data_size));
286 written = sendfileall (fd, db->wr_fd,
287 (char *) &dataset->resp
288 - (char *) db->head, total);
289# ifndef __ASSUME_SENDFILE
290 if (written == -1 && errno == ENOSYS)
291 goto use_write;
292# endif
293 }
294 else
295# ifndef __ASSUME_SENDFILE
296 use_write:
297# endif
298#endif
299 written = writeall (fd, &dataset->resp, total);
300 }
301
302 /* Add the record to the database. But only if it has not been
303 stored on the stack. */
304 if (! alloca_used)
305 {
306 /* If necessary, we also propagate the data to disk. */
307 if (db->persistent)
308 {
309 // XXX async OK?
310 uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
311 msync ((void *) pval,
312 ((uintptr_t) dataset & pagesize_m1)
313 + total + req->key_len, MS_ASYNC);
314 }
315
7e71e55f 316 (void) cache_add (req->type, key_copy, req->key_len,
528741cb 317 &dataset->head, true, db, owner, he == NULL);
b21fa963
UD
318 }
319 }
320
321 if (__builtin_expect (written != total, 0) && debug_level > 0)
322 {
323 char buf[256];
324 dbg_log (_("short write in %s: %s"), __FUNCTION__,
325 strerror_r (errno, buf, sizeof (buf)));
326 }
327}
328
329
330static int
331lookup (int type, char *key, struct servent *resultbufp, char *buffer,
332 size_t buflen, struct servent **serv)
333{
334 char *proto = strrchr (key, '/');
335 if (proto != NULL && proto != key)
336 {
337 key = strndupa (key, proto - key);
338 if (proto[1] == '\0')
339 proto = NULL;
340 else
341 ++proto;
342 }
343
344 if (type == GETSERVBYNAME)
345 return __getservbyname_r (key, proto, resultbufp, buffer, buflen, serv);
346
347 assert (type == GETSERVBYPORT);
348 return __getservbyport_r (atol (key), proto, resultbufp, buffer, buflen,
349 serv);
350}
351
352
353static void
354addservbyX (struct database_dyn *db, int fd, request_header *req,
355 char *key, uid_t uid, struct hashentry *he, struct datahead *dh)
356{
357 /* Search for the entry matching the key. Please note that we don't
358 look again in the table whether the dataset is now available. We
359 simply insert it. It does not matter if it is in there twice. The
360 pruning function only will look at the timestamp. */
361 size_t buflen = 1024;
362 char *buffer = (char *) alloca (buflen);
363 struct servent resultbuf;
364 struct servent *serv;
365 bool use_malloc = false;
366 int errval = 0;
367
368 if (__builtin_expect (debug_level > 0, 0))
369 {
370 if (he == NULL)
371 dbg_log (_("Haven't found \"%s\" in services cache!"), key);
372 else
373 dbg_log (_("Reloading \"%s\" in services cache!"), key);
374 }
375
376 while (lookup (req->type, key, &resultbuf, buffer, buflen, &serv) != 0
377 && (errval = errno) == ERANGE)
378 {
379 errno = 0;
380
381 if (__builtin_expect (buflen > 32768, 0))
382 {
383 char *old_buffer = buffer;
384 buflen *= 2;
385 buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
386 if (buffer == NULL)
387 {
388 /* We ran out of memory. We cannot do anything but
389 sending a negative response. In reality this should
390 never happen. */
391 serv = NULL;
392 buffer = old_buffer;
393
394 /* We set the error to indicate this is (possibly) a
395 temporary error and that it does not mean the entry
396 is not available at all. */
397 errval = EAGAIN;
398 break;
399 }
400 use_malloc = true;
401 }
402 else
403 /* Allocate a new buffer on the stack. If possible combine it
404 with the previously allocated buffer. */
405 buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
406 }
407
408 cache_addserv (db, fd, req, key, serv, uid, he, dh, errval);
409
410 if (use_malloc)
411 free (buffer);
412}
413
414
415void
416addservbyname (struct database_dyn *db, int fd, request_header *req,
417 void *key, uid_t uid)
418{
419 addservbyX (db, fd, req, key, uid, NULL, NULL);
420}
421
422
423void
424readdservbyname (struct database_dyn *db, struct hashentry *he,
425 struct datahead *dh)
426{
427 request_header req =
428 {
429 .type = GETSERVBYNAME,
430 .key_len = he->len
431 };
432
433 addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
434}
435
436
437void
438addservbyport (struct database_dyn *db, int fd, request_header *req,
439 void *key, uid_t uid)
440{
441 addservbyX (db, fd, req, key, uid, NULL, NULL);
442}
443
444
445void
446readdservbyport (struct database_dyn *db, struct hashentry *he,
447 struct datahead *dh)
448{
449 request_header req =
450 {
451 .type = GETSERVBYPORT,
452 .key_len = he->len
453 };
454
455 addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
456}