Cache network interface information
[platform/upstream/glibc.git] / nscd / servicescache.c
1 /* Cache handling for services lookup.
2    Copyright (C) 2007, 2008, 2009, 2011 Free Software Foundation, Inc.
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
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.
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>
27 #include <kernel-features.h>
28
29 #include "nscd.h"
30 #include "dbg_log.h"
31
32
33 /* This is the standard reply in case the service is disabled.  */
34 static 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.  */
45 const 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.  */
53 static 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
64 static time_t
65 cache_addserv (struct database_dyn *db, int fd, request_header *req,
66                const void *key, struct servent *serv, uid_t owner,
67                struct hashentry *const he, struct datahead *dh, int errval)
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   time_t timeout = MAX_TIMEOUT_VALUE;
85   if (serv == NULL)
86     {
87       if (he != NULL && errval == EAGAIN)
88         {
89           /* If we have an old record available but cannot find one
90              now because the service is not available we keep the old
91              record and make sure it does not get removed.  */
92           if (reload_count != UINT_MAX)
93             /* Do not reset the value if we never not reload the record.  */
94             dh->nreloads = reload_count - 1;
95
96           /* Reload with the same time-to-live value.  */
97           timeout = dh->timeout = t + db->postimeout;
98
99           written = total = 0;
100         }
101       else
102         {
103           /* We have no data.  This means we send the standard reply for this
104              case.  */
105           written = total = sizeof (notfound);
106
107           if (fd != -1)
108             written = TEMP_FAILURE_RETRY (send (fd, &notfound, total,
109                                                 MSG_NOSIGNAL));
110
111           /* If we cannot permanently store the result, so be it.  */
112           if (__builtin_expect (db->negtimeout == 0, 0))
113             {
114               /* Mark the old entry as obsolete.  */
115               if (dh != NULL)
116                 dh->usable = false;
117             }
118           else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
119                                                   + req->key_len), 1)) != NULL)
120             {
121               dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
122               dataset->head.recsize = total;
123               dataset->head.notfound = true;
124               dataset->head.nreloads = 0;
125               dataset->head.usable = true;
126
127               /* Compute the timeout time.  */
128               timeout = dataset->head.timeout = t + db->negtimeout;
129
130               /* This is the reply.  */
131               memcpy (&dataset->resp, &notfound, total);
132
133               /* Copy the key data.  */
134               memcpy (dataset->strdata, key, req->key_len);
135
136               /* If necessary, we also propagate the data to disk.  */
137               if (db->persistent)
138                 {
139                   // XXX async OK?
140                   uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
141                   msync ((void *) pval,
142                          ((uintptr_t) dataset & pagesize_m1)
143                          + sizeof (struct dataset) + req->key_len, MS_ASYNC);
144                 }
145
146               (void) cache_add (req->type, &dataset->strdata, req->key_len,
147                                 &dataset->head, true, db, owner, he == NULL);
148
149               pthread_rwlock_unlock (&db->lock);
150
151               /* Mark the old entry as obsolete.  */
152               if (dh != NULL)
153                 dh->usable = false;
154             }
155         }
156     }
157   else
158     {
159       /* Determine the I/O structure.  */
160       size_t s_name_len = strlen (serv->s_name) + 1;
161       size_t s_proto_len = strlen (serv->s_proto) + 1;
162       uint32_t *s_aliases_len;
163       size_t s_aliases_cnt;
164       char *aliases;
165       char *cp;
166       size_t cnt;
167
168       /* Determine the number of aliases.  */
169       s_aliases_cnt = 0;
170       for (cnt = 0; serv->s_aliases[cnt] != NULL; ++cnt)
171         ++s_aliases_cnt;
172       /* Determine the length of all aliases.  */
173       s_aliases_len = (uint32_t *) alloca (s_aliases_cnt * sizeof (uint32_t));
174       total = 0;
175       for (cnt = 0; cnt < s_aliases_cnt; ++cnt)
176         {
177           s_aliases_len[cnt] = strlen (serv->s_aliases[cnt]) + 1;
178           total += s_aliases_len[cnt];
179         }
180
181       total += (offsetof (struct dataset, strdata)
182                 + s_name_len
183                 + s_proto_len
184                 + s_aliases_cnt * sizeof (uint32_t));
185       written = total;
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)
195         dataset = (struct dataset *) mempool_alloc (db, total + req->key_len,
196                                                     1);
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.  */
216       timeout = dataset->head.timeout = t + db->postimeout;
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
264                 = (struct dataset *) mempool_alloc (db, total + req->key_len,
265                                                     1);
266               if (newp != NULL)
267                 {
268                   /* Adjust pointers into the memory block.  */
269                   aliases = (char *) newp + (aliases - (char *) dataset);
270                   assert (key_copy != NULL);
271                   key_copy = (char *) newp + (key_copy - (char *) dataset);
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);
293               assert ((char *) &dataset->resp - (char *) db->head
294                       + total
295                       <= (sizeof (struct database_pers_head)
296                           + db->head->module * sizeof (ref_t)
297                           + db->head->data_size));
298               written = sendfileall (fd, db->wr_fd,
299                                      (char *) &dataset->resp
300                                      - (char *) db->head, total);
301 # ifndef __ASSUME_SENDFILE
302               if (written == -1 && errno == ENOSYS)
303                 goto use_write;
304 # endif
305             }
306           else
307 # ifndef __ASSUME_SENDFILE
308           use_write:
309 # endif
310 #endif
311             written = writeall (fd, &dataset->resp, total);
312         }
313
314       /* Add the record to the database.  But only if it has not been
315          stored on the stack.  */
316       if (! alloca_used)
317         {
318           /* If necessary, we also propagate the data to disk.  */
319           if (db->persistent)
320             {
321               // XXX async OK?
322               uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
323               msync ((void *) pval,
324                      ((uintptr_t) dataset & pagesize_m1)
325                      + total + req->key_len, MS_ASYNC);
326             }
327
328           (void) cache_add (req->type, key_copy, req->key_len,
329                             &dataset->head, true, db, owner, he == NULL);
330
331           pthread_rwlock_unlock (&db->lock);
332         }
333     }
334
335   if (__builtin_expect (written != total, 0) && debug_level > 0)
336     {
337       char buf[256];
338       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
339                strerror_r (errno, buf, sizeof (buf)));
340     }
341
342   return timeout;
343 }
344
345
346 static int
347 lookup (int type, char *key, struct servent *resultbufp, char *buffer,
348         size_t buflen, struct servent **serv)
349 {
350   char *proto = strrchr (key, '/');
351   if (proto != NULL && proto != key)
352     {
353       key = strndupa (key, proto - key);
354       if (proto[1] == '\0')
355         proto = NULL;
356       else
357         ++proto;
358     }
359
360   if (type == GETSERVBYNAME)
361     return __getservbyname_r (key, proto, resultbufp, buffer, buflen, serv);
362
363   assert (type == GETSERVBYPORT);
364   return __getservbyport_r (atol (key), proto, resultbufp, buffer, buflen,
365                             serv);
366 }
367
368
369 static time_t
370 addservbyX (struct database_dyn *db, int fd, request_header *req,
371             char *key, uid_t uid, struct hashentry *he, struct datahead *dh)
372 {
373   /* Search for the entry matching the key.  Please note that we don't
374      look again in the table whether the dataset is now available.  We
375      simply insert it.  It does not matter if it is in there twice.  The
376      pruning function only will look at the timestamp.  */
377   size_t buflen = 1024;
378   char *buffer = (char *) alloca (buflen);
379   struct servent resultbuf;
380   struct servent *serv;
381   bool use_malloc = false;
382   int errval = 0;
383
384   if (__builtin_expect (debug_level > 0, 0))
385     {
386       if (he == NULL)
387         dbg_log (_("Haven't found \"%s\" in services cache!"), key);
388       else
389         dbg_log (_("Reloading \"%s\" in services cache!"), key);
390     }
391
392   while (lookup (req->type, key, &resultbuf, buffer, buflen, &serv) != 0
393          && (errval = errno) == ERANGE)
394     {
395       errno = 0;
396
397       if (__builtin_expect (buflen > 32768, 0))
398         {
399           char *old_buffer = buffer;
400           buflen *= 2;
401           buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
402           if (buffer == NULL)
403             {
404               /* We ran out of memory.  We cannot do anything but
405                  sending a negative response.  In reality this should
406                  never happen.  */
407               serv = NULL;
408               buffer = old_buffer;
409
410               /* We set the error to indicate this is (possibly) a
411                  temporary error and that it does not mean the entry
412                  is not available at all.  */
413               errval = EAGAIN;
414               break;
415             }
416           use_malloc = true;
417         }
418       else
419         /* Allocate a new buffer on the stack.  If possible combine it
420            with the previously allocated buffer.  */
421         buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
422     }
423
424   time_t timeout = cache_addserv (db, fd, req, key, serv, uid, he, dh, errval);
425
426   if (use_malloc)
427     free (buffer);
428
429   return timeout;
430 }
431
432
433 void
434 addservbyname (struct database_dyn *db, int fd, request_header *req,
435                void *key, uid_t uid)
436 {
437   addservbyX (db, fd, req, key, uid, NULL, NULL);
438 }
439
440
441 time_t
442 readdservbyname (struct database_dyn *db, struct hashentry *he,
443                  struct datahead *dh)
444 {
445   request_header req =
446     {
447       .type = GETSERVBYNAME,
448       .key_len = he->len
449     };
450
451   return addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
452 }
453
454
455 void
456 addservbyport (struct database_dyn *db, int fd, request_header *req,
457                void *key, uid_t uid)
458 {
459   addservbyX (db, fd, req, key, uid, NULL, NULL);
460 }
461
462
463 time_t
464 readdservbyport (struct database_dyn *db, struct hashentry *he,
465                  struct datahead *dh)
466 {
467   request_header req =
468     {
469       .type = GETSERVBYPORT,
470       .key_len = he->len
471     };
472
473   return addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
474 }