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