Replace lll_private_futex_* (*) with lll_futex_* (*, LLL_PRIVATE).
[kopensolaris-gnu/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
28 #include "nscd.h"
29 #include "dbg_log.h"
30
31
32 /* This is the standard reply in case the service is disabled.  */
33 static 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.  */
44 const 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.  */
52 static 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
63 static void
64 cache_addserv (struct database_dyn *db, int fd, request_header *req,
65                const void *key, struct servent *serv, uid_t owner,
66                struct hashentry *he, struct datahead *dh, int errval)
67 {
68   ssize_t total;
69   ssize_t written;
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
83   if (serv == NULL)
84     {
85       if (he != NULL && errval == EAGAIN)
86         {
87           /* If we have an old record available but cannot find one
88              now because the service is not available we keep the old
89              record and make sure it does not get removed.  */
90           if (reload_count != UINT_MAX)
91             /* Do not reset the value if we never not reload the record.  */
92             dh->nreloads = reload_count - 1;
93
94           written = total = 0;
95         }
96       else
97         {
98           /* We have no data.  This means we send the standard reply for this
99              case.  */
100           total = sizeof (notfound);
101
102           written = TEMP_FAILURE_RETRY (send (fd, &notfound, total,
103                                               MSG_NOSIGNAL));
104
105           dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
106           /* If we cannot permanently store the result, so be it.  */
107           if (dataset != NULL)
108             {
109               dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
110               dataset->head.recsize = total;
111               dataset->head.notfound = true;
112               dataset->head.nreloads = 0;
113               dataset->head.usable = true;
114
115               /* Compute the timeout time.  */
116               dataset->head.timeout = t + db->negtimeout;
117
118               /* This is the reply.  */
119               memcpy (&dataset->resp, &notfound, total);
120
121               /* Copy the key data.  */
122               memcpy (dataset->strdata, key, req->key_len);
123
124               /* If necessary, we also propagate the data to disk.  */
125               if (db->persistent)
126                 {
127                   // XXX async OK?
128                   uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
129                   msync ((void *) pval,
130                          ((uintptr_t) dataset & pagesize_m1)
131                          + sizeof (struct dataset) + req->key_len, MS_ASYNC);
132                 }
133
134               /* Now get the lock to safely insert the records.  */
135               pthread_rwlock_rdlock (&db->lock);
136
137               if (cache_add (req->type, &dataset->strdata, req->key_len,
138                              &dataset->head, true, db, owner) < 0)
139                 /* Ensure the data can be recovered.  */
140                 dataset->head.usable = false;
141
142               pthread_rwlock_unlock (&db->lock);
143
144               /* Mark the old entry as obsolete.  */
145               if (dh != NULL)
146                 dh->usable = false;
147             }
148           else
149             ++db->head->addfailed;
150         }
151     }
152   else
153     {
154       /* Determine the I/O structure.  */
155       size_t s_name_len = strlen (serv->s_name) + 1;
156       size_t s_proto_len = strlen (serv->s_proto) + 1;
157       uint32_t *s_aliases_len;
158       size_t s_aliases_cnt;
159       char *aliases;
160       char *cp;
161       size_t cnt;
162
163       /* Determine the number of aliases.  */
164       s_aliases_cnt = 0;
165       for (cnt = 0; serv->s_aliases[cnt] != NULL; ++cnt)
166         ++s_aliases_cnt;
167       /* Determine the length of all aliases.  */
168       s_aliases_len = (uint32_t *) alloca (s_aliases_cnt * sizeof (uint32_t));
169       total = 0;
170       for (cnt = 0; cnt < s_aliases_cnt; ++cnt)
171         {
172           s_aliases_len[cnt] = strlen (serv->s_aliases[cnt]) + 1;
173           total += s_aliases_len[cnt];
174         }
175
176       total += (sizeof (struct dataset)
177                 + s_name_len
178                 + s_proto_len
179                 + s_aliases_cnt * sizeof (uint32_t));
180       written = total;
181
182       /* If we refill the cache, first assume the reconrd did not
183          change.  Allocate memory on the cache since it is likely
184          discarded anyway.  If it turns out to be necessary to have a
185          new record we can still allocate real memory.  */
186       bool alloca_used = false;
187       dataset = NULL;
188
189       if (he == NULL)
190         {
191           dataset = (struct dataset *) mempool_alloc (db,
192                                                       total + req->key_len);
193           if (dataset == NULL)
194             ++db->head->addfailed;
195         }
196
197       if (dataset == NULL)
198         {
199           /* We cannot permanently add the result in the moment.  But
200              we can provide the result as is.  Store the data in some
201              temporary memory.  */
202           dataset = (struct dataset *) alloca (total + req->key_len);
203
204           /* We cannot add this record to the permanent database.  */
205           alloca_used = true;
206         }
207
208       dataset->head.allocsize = total + req->key_len;
209       dataset->head.recsize = total - offsetof (struct dataset, resp);
210       dataset->head.notfound = false;
211       dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
212       dataset->head.usable = true;
213
214       /* Compute the timeout time.  */
215       dataset->head.timeout = t + db->postimeout;
216
217       dataset->resp.version = NSCD_VERSION;
218       dataset->resp.found = 1;
219       dataset->resp.s_name_len = s_name_len;
220       dataset->resp.s_proto_len = s_proto_len;
221       dataset->resp.s_port = serv->s_port;
222       dataset->resp.s_aliases_cnt = s_aliases_cnt;
223
224       cp = dataset->strdata;
225
226       cp = mempcpy (cp, serv->s_name, s_name_len);
227       cp = mempcpy (cp, serv->s_proto, s_proto_len);
228       cp = mempcpy (cp, s_aliases_len, s_aliases_cnt * sizeof (uint32_t));
229
230       /* Then the aliases.  */
231       aliases = cp;
232       for (cnt = 0; cnt < s_aliases_cnt; ++cnt)
233         cp = mempcpy (cp, serv->s_aliases[cnt], s_aliases_len[cnt]);
234
235       assert (cp
236               == dataset->strdata + total - offsetof (struct dataset,
237                                                       strdata));
238
239       char *key_copy = memcpy (cp, key, req->key_len);
240
241       /* Now we can determine whether on refill we have to create a new
242          record or not.  */
243       if (he != NULL)
244         {
245           assert (fd == -1);
246
247           if (total + req->key_len == dh->allocsize
248               && total - offsetof (struct dataset, resp) == dh->recsize
249               && memcmp (&dataset->resp, dh->data,
250                          dh->allocsize - offsetof (struct dataset, resp)) == 0)
251             {
252               /* The data has not changed.  We will just bump the
253                  timeout value.  Note that the new record has been
254                  allocated on the stack and need not be freed.  */
255               dh->timeout = dataset->head.timeout;
256               ++dh->nreloads;
257             }
258           else
259             {
260               /* We have to create a new record.  Just allocate
261                  appropriate memory and copy it.  */
262               struct dataset *newp
263                 = (struct dataset *) mempool_alloc (db, total + req->key_len);
264               if (newp != NULL)
265                 {
266                   /* Adjust pointers into the memory block.  */
267                   aliases = (char *) newp + (aliases - (char *) dataset);
268                   assert (key_copy != NULL);
269                   key_copy = (char *) newp + (key_copy - (char *) dataset);
270
271                   dataset = memcpy (newp, dataset, total + req->key_len);
272                   alloca_used = false;
273                 }
274
275               /* Mark the old record as obsolete.  */
276               dh->usable = false;
277             }
278         }
279       else
280         {
281           /* We write the dataset before inserting it to the database
282              since while inserting this thread might block and so would
283              unnecessarily keep the receiver waiting.  */
284           assert (fd != -1);
285
286 #ifdef HAVE_SENDFILE
287           if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
288             {
289               assert (db->wr_fd != -1);
290               assert ((char *) &dataset->resp > (char *) db->data);
291               assert ((char *) &dataset->resp - (char *) db->head
292                       + total
293                       <= (sizeof (struct database_pers_head)
294                           + db->head->module * sizeof (ref_t)
295                           + db->head->data_size));
296               written = sendfileall (fd, db->wr_fd,
297                                      (char *) &dataset->resp
298                                      - (char *) db->head, total);
299 # ifndef __ASSUME_SENDFILE
300               if (written == -1 && errno == ENOSYS)
301                 goto use_write;
302 # endif
303             }
304           else
305 # ifndef __ASSUME_SENDFILE
306           use_write:
307 # endif
308 #endif
309             written = writeall (fd, &dataset->resp, total);
310         }
311
312       /* Add the record to the database.  But only if it has not been
313          stored on the stack.  */
314       if (! alloca_used)
315         {
316           /* If necessary, we also propagate the data to disk.  */
317           if (db->persistent)
318             {
319               // XXX async OK?
320               uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
321               msync ((void *) pval,
322                      ((uintptr_t) dataset & pagesize_m1)
323                      + total + req->key_len, MS_ASYNC);
324             }
325
326           /* Now get the lock to safely insert the records.  */
327           pthread_rwlock_rdlock (&db->lock);
328
329           if (cache_add (req->type, key_copy, req->key_len,
330                          &dataset->head, true, db, owner) < 0)
331             /* Could not allocate memory.  Make sure the
332                data gets discarded.  */
333             dataset->head.usable = false;
334
335           pthread_rwlock_unlock (&db->lock);
336         }
337     }
338
339   if (__builtin_expect (written != total, 0) && debug_level > 0)
340     {
341       char buf[256];
342       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
343                strerror_r (errno, buf, sizeof (buf)));
344     }
345 }
346
347
348 static int
349 lookup (int type, char *key, struct servent *resultbufp, char *buffer,
350         size_t buflen, struct servent **serv)
351 {
352   char *proto = strrchr (key, '/');
353   if (proto != NULL && proto != key)
354     {
355       key = strndupa (key, proto - key);
356       if (proto[1] == '\0')
357         proto = NULL;
358       else
359         ++proto;
360     }
361
362   if (type == GETSERVBYNAME)
363     return __getservbyname_r (key, proto, resultbufp, buffer, buflen, serv);
364
365   assert (type == GETSERVBYPORT);
366   return __getservbyport_r (atol (key), proto, resultbufp, buffer, buflen,
367                             serv);
368 }
369
370
371 static void
372 addservbyX (struct database_dyn *db, int fd, request_header *req,
373             char *key, uid_t uid, struct hashentry *he, struct datahead *dh)
374 {
375   /* Search for the entry matching the key.  Please note that we don't
376      look again in the table whether the dataset is now available.  We
377      simply insert it.  It does not matter if it is in there twice.  The
378      pruning function only will look at the timestamp.  */
379   size_t buflen = 1024;
380   char *buffer = (char *) alloca (buflen);
381   struct servent resultbuf;
382   struct servent *serv;
383   bool use_malloc = false;
384   int errval = 0;
385
386   if (__builtin_expect (debug_level > 0, 0))
387     {
388       if (he == NULL)
389         dbg_log (_("Haven't found \"%s\" in services cache!"), key);
390       else
391         dbg_log (_("Reloading \"%s\" in services cache!"), key);
392     }
393
394   while (lookup (req->type, key, &resultbuf, buffer, buflen, &serv) != 0
395          && (errval = errno) == ERANGE)
396     {
397       errno = 0;
398
399       if (__builtin_expect (buflen > 32768, 0))
400         {
401           char *old_buffer = buffer;
402           buflen *= 2;
403           buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
404           if (buffer == NULL)
405             {
406               /* We ran out of memory.  We cannot do anything but
407                  sending a negative response.  In reality this should
408                  never happen.  */
409               serv = NULL;
410               buffer = old_buffer;
411
412               /* We set the error to indicate this is (possibly) a
413                  temporary error and that it does not mean the entry
414                  is not available at all.  */
415               errval = EAGAIN;
416               break;
417             }
418           use_malloc = true;
419         }
420       else
421         /* Allocate a new buffer on the stack.  If possible combine it
422            with the previously allocated buffer.  */
423         buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
424     }
425
426   cache_addserv (db, fd, req, key, serv, uid, he, dh, errval);
427
428   if (use_malloc)
429     free (buffer);
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 void
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   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 void
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   addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
474 }