* nscd/servicescache.c: Include kernel-features.h.
[platform/upstream/glibc.git] / nscd / servicescache.c
1 /* Cache handling for services lookup.
2    Copyright (C) 2007 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 void
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 *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   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
106           dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
107           /* If we cannot permanently store the result, so be it.  */
108           if (dataset != NULL)
109             {
110               dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
111               dataset->head.recsize = total;
112               dataset->head.notfound = true;
113               dataset->head.nreloads = 0;
114               dataset->head.usable = true;
115
116               /* Compute the timeout time.  */
117               dataset->head.timeout = t + db->negtimeout;
118
119               /* This is the reply.  */
120               memcpy (&dataset->resp, &notfound, total);
121
122               /* Copy the key data.  */
123               memcpy (dataset->strdata, key, req->key_len);
124
125               /* If necessary, we also propagate the data to disk.  */
126               if (db->persistent)
127                 {
128                   // XXX async OK?
129                   uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
130                   msync ((void *) pval,
131                          ((uintptr_t) dataset & pagesize_m1)
132                          + sizeof (struct dataset) + req->key_len, MS_ASYNC);
133                 }
134
135               /* Now get the lock to safely insert the records.  */
136               pthread_rwlock_rdlock (&db->lock);
137
138               if (cache_add (req->type, &dataset->strdata, req->key_len,
139                              &dataset->head, true, db, owner) < 0)
140                 /* Ensure the data can be recovered.  */
141                 dataset->head.usable = false;
142
143               pthread_rwlock_unlock (&db->lock);
144
145               /* Mark the old entry as obsolete.  */
146               if (dh != NULL)
147                 dh->usable = false;
148             }
149           else
150             ++db->head->addfailed;
151         }
152     }
153   else
154     {
155       /* Determine the I/O structure.  */
156       size_t s_name_len = strlen (serv->s_name) + 1;
157       size_t s_proto_len = strlen (serv->s_proto) + 1;
158       uint32_t *s_aliases_len;
159       size_t s_aliases_cnt;
160       char *aliases;
161       char *cp;
162       size_t cnt;
163
164       /* Determine the number of aliases.  */
165       s_aliases_cnt = 0;
166       for (cnt = 0; serv->s_aliases[cnt] != NULL; ++cnt)
167         ++s_aliases_cnt;
168       /* Determine the length of all aliases.  */
169       s_aliases_len = (uint32_t *) alloca (s_aliases_cnt * sizeof (uint32_t));
170       total = 0;
171       for (cnt = 0; cnt < s_aliases_cnt; ++cnt)
172         {
173           s_aliases_len[cnt] = strlen (serv->s_aliases[cnt]) + 1;
174           total += s_aliases_len[cnt];
175         }
176
177       total += (sizeof (struct dataset)
178                 + s_name_len
179                 + s_proto_len
180                 + s_aliases_cnt * sizeof (uint32_t));
181       written = total;
182
183       /* If we refill the cache, first assume the reconrd did not
184          change.  Allocate memory on the cache since it is likely
185          discarded anyway.  If it turns out to be necessary to have a
186          new record we can still allocate real memory.  */
187       bool alloca_used = false;
188       dataset = NULL;
189
190       if (he == NULL)
191         {
192           dataset = (struct dataset *) mempool_alloc (db,
193                                                       total + req->key_len);
194           if (dataset == NULL)
195             ++db->head->addfailed;
196         }
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       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               if (newp != NULL)
266                 {
267                   /* Adjust pointers into the memory block.  */
268                   aliases = (char *) newp + (aliases - (char *) dataset);
269                   assert (key_copy != NULL);
270                   key_copy = (char *) newp + (key_copy - (char *) dataset);
271
272                   dataset = memcpy (newp, dataset, total + req->key_len);
273                   alloca_used = false;
274                 }
275
276               /* Mark the old record as obsolete.  */
277               dh->usable = false;
278             }
279         }
280       else
281         {
282           /* We write the dataset before inserting it to the database
283              since while inserting this thread might block and so would
284              unnecessarily keep the receiver waiting.  */
285           assert (fd != -1);
286
287 #ifdef HAVE_SENDFILE
288           if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
289             {
290               assert (db->wr_fd != -1);
291               assert ((char *) &dataset->resp > (char *) db->data);
292               assert ((char *) &dataset->resp - (char *) db->head
293                       + total
294                       <= (sizeof (struct database_pers_head)
295                           + db->head->module * sizeof (ref_t)
296                           + db->head->data_size));
297               written = sendfileall (fd, db->wr_fd,
298                                      (char *) &dataset->resp
299                                      - (char *) db->head, total);
300 # ifndef __ASSUME_SENDFILE
301               if (written == -1 && errno == ENOSYS)
302                 goto use_write;
303 # endif
304             }
305           else
306 # ifndef __ASSUME_SENDFILE
307           use_write:
308 # endif
309 #endif
310             written = writeall (fd, &dataset->resp, total);
311         }
312
313       /* Add the record to the database.  But only if it has not been
314          stored on the stack.  */
315       if (! alloca_used)
316         {
317           /* If necessary, we also propagate the data to disk.  */
318           if (db->persistent)
319             {
320               // XXX async OK?
321               uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
322               msync ((void *) pval,
323                      ((uintptr_t) dataset & pagesize_m1)
324                      + total + req->key_len, MS_ASYNC);
325             }
326
327           /* Now get the lock to safely insert the records.  */
328           pthread_rwlock_rdlock (&db->lock);
329
330           if (cache_add (req->type, key_copy, req->key_len,
331                          &dataset->head, true, db, owner) < 0)
332             /* Could not allocate memory.  Make sure the
333                data gets discarded.  */
334             dataset->head.usable = false;
335
336           pthread_rwlock_unlock (&db->lock);
337         }
338     }
339
340   if (__builtin_expect (written != total, 0) && debug_level > 0)
341     {
342       char buf[256];
343       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
344                strerror_r (errno, buf, sizeof (buf)));
345     }
346 }
347
348
349 static int
350 lookup (int type, char *key, struct servent *resultbufp, char *buffer,
351         size_t buflen, struct servent **serv)
352 {
353   char *proto = strrchr (key, '/');
354   if (proto != NULL && proto != key)
355     {
356       key = strndupa (key, proto - key);
357       if (proto[1] == '\0')
358         proto = NULL;
359       else
360         ++proto;
361     }
362
363   if (type == GETSERVBYNAME)
364     return __getservbyname_r (key, proto, resultbufp, buffer, buflen, serv);
365
366   assert (type == GETSERVBYPORT);
367   return __getservbyport_r (atol (key), proto, resultbufp, buffer, buflen,
368                             serv);
369 }
370
371
372 static void
373 addservbyX (struct database_dyn *db, int fd, request_header *req,
374             char *key, uid_t uid, struct hashentry *he, struct datahead *dh)
375 {
376   /* Search for the entry matching the key.  Please note that we don't
377      look again in the table whether the dataset is now available.  We
378      simply insert it.  It does not matter if it is in there twice.  The
379      pruning function only will look at the timestamp.  */
380   size_t buflen = 1024;
381   char *buffer = (char *) alloca (buflen);
382   struct servent resultbuf;
383   struct servent *serv;
384   bool use_malloc = false;
385   int errval = 0;
386
387   if (__builtin_expect (debug_level > 0, 0))
388     {
389       if (he == NULL)
390         dbg_log (_("Haven't found \"%s\" in services cache!"), key);
391       else
392         dbg_log (_("Reloading \"%s\" in services cache!"), key);
393     }
394
395   while (lookup (req->type, key, &resultbuf, buffer, buflen, &serv) != 0
396          && (errval = errno) == ERANGE)
397     {
398       errno = 0;
399
400       if (__builtin_expect (buflen > 32768, 0))
401         {
402           char *old_buffer = buffer;
403           buflen *= 2;
404           buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
405           if (buffer == NULL)
406             {
407               /* We ran out of memory.  We cannot do anything but
408                  sending a negative response.  In reality this should
409                  never happen.  */
410               serv = NULL;
411               buffer = old_buffer;
412
413               /* We set the error to indicate this is (possibly) a
414                  temporary error and that it does not mean the entry
415                  is not available at all.  */
416               errval = EAGAIN;
417               break;
418             }
419           use_malloc = true;
420         }
421       else
422         /* Allocate a new buffer on the stack.  If possible combine it
423            with the previously allocated buffer.  */
424         buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
425     }
426
427   cache_addserv (db, fd, req, key, serv, uid, he, dh, errval);
428
429   if (use_malloc)
430     free (buffer);
431 }
432
433
434 void
435 addservbyname (struct database_dyn *db, int fd, request_header *req,
436                void *key, uid_t uid)
437 {
438   addservbyX (db, fd, req, key, uid, NULL, NULL);
439 }
440
441
442 void
443 readdservbyname (struct database_dyn *db, struct hashentry *he,
444                  struct datahead *dh)
445 {
446   request_header req =
447     {
448       .type = GETSERVBYNAME,
449       .key_len = he->len
450     };
451
452   addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
453 }
454
455
456 void
457 addservbyport (struct database_dyn *db, int fd, request_header *req,
458                void *key, uid_t uid)
459 {
460   addservbyX (db, fd, req, key, uid, NULL, NULL);
461 }
462
463
464 void
465 readdservbyport (struct database_dyn *db, struct hashentry *he,
466                  struct datahead *dh)
467 {
468   request_header req =
469     {
470       .type = GETSERVBYPORT,
471       .key_len = he->len
472     };
473
474   addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
475 }