Updated to fedora-glibc-20071212T1051
[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 #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               else
276                 ++db->head->addfailed;
277
278               /* Mark the old record as obsolete.  */
279               dh->usable = false;
280             }
281         }
282       else
283         {
284           /* We write the dataset before inserting it to the database
285              since while inserting this thread might block and so would
286              unnecessarily keep the receiver waiting.  */
287           assert (fd != -1);
288
289 #ifdef HAVE_SENDFILE
290           if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
291             {
292               assert (db->wr_fd != -1);
293               assert ((char *) &dataset->resp > (char *) db->data);
294               assert ((char *) &dataset->resp - (char *) db->head
295                       + total
296                       <= (sizeof (struct database_pers_head)
297                           + db->head->module * sizeof (ref_t)
298                           + db->head->data_size));
299               written = sendfileall (fd, db->wr_fd,
300                                      (char *) &dataset->resp
301                                      - (char *) db->head, total);
302 # ifndef __ASSUME_SENDFILE
303               if (written == -1 && errno == ENOSYS)
304                 goto use_write;
305 # endif
306             }
307           else
308 # ifndef __ASSUME_SENDFILE
309           use_write:
310 # endif
311 #endif
312             written = writeall (fd, &dataset->resp, total);
313         }
314
315       /* Add the record to the database.  But only if it has not been
316          stored on the stack.  */
317       if (! alloca_used)
318         {
319           /* If necessary, we also propagate the data to disk.  */
320           if (db->persistent)
321             {
322               // XXX async OK?
323               uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
324               msync ((void *) pval,
325                      ((uintptr_t) dataset & pagesize_m1)
326                      + total + req->key_len, MS_ASYNC);
327             }
328
329           /* Now get the lock to safely insert the records.  */
330           pthread_rwlock_rdlock (&db->lock);
331
332           if (cache_add (req->type, key_copy, req->key_len,
333                          &dataset->head, true, db, owner) < 0)
334             /* Could not allocate memory.  Make sure the
335                data gets discarded.  */
336             dataset->head.usable = false;
337
338           pthread_rwlock_unlock (&db->lock);
339         }
340     }
341
342   if (__builtin_expect (written != total, 0) && debug_level > 0)
343     {
344       char buf[256];
345       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
346                strerror_r (errno, buf, sizeof (buf)));
347     }
348 }
349
350
351 static int
352 lookup (int type, char *key, struct servent *resultbufp, char *buffer,
353         size_t buflen, struct servent **serv)
354 {
355   char *proto = strrchr (key, '/');
356   if (proto != NULL && proto != key)
357     {
358       key = strndupa (key, proto - key);
359       if (proto[1] == '\0')
360         proto = NULL;
361       else
362         ++proto;
363     }
364
365   if (type == GETSERVBYNAME)
366     return __getservbyname_r (key, proto, resultbufp, buffer, buflen, serv);
367
368   assert (type == GETSERVBYPORT);
369   return __getservbyport_r (atol (key), proto, resultbufp, buffer, buflen,
370                             serv);
371 }
372
373
374 static void
375 addservbyX (struct database_dyn *db, int fd, request_header *req,
376             char *key, uid_t uid, struct hashentry *he, struct datahead *dh)
377 {
378   /* Search for the entry matching the key.  Please note that we don't
379      look again in the table whether the dataset is now available.  We
380      simply insert it.  It does not matter if it is in there twice.  The
381      pruning function only will look at the timestamp.  */
382   size_t buflen = 1024;
383   char *buffer = (char *) alloca (buflen);
384   struct servent resultbuf;
385   struct servent *serv;
386   bool use_malloc = false;
387   int errval = 0;
388
389   if (__builtin_expect (debug_level > 0, 0))
390     {
391       if (he == NULL)
392         dbg_log (_("Haven't found \"%s\" in services cache!"), key);
393       else
394         dbg_log (_("Reloading \"%s\" in services cache!"), key);
395     }
396
397   while (lookup (req->type, key, &resultbuf, buffer, buflen, &serv) != 0
398          && (errval = errno) == ERANGE)
399     {
400       errno = 0;
401
402       if (__builtin_expect (buflen > 32768, 0))
403         {
404           char *old_buffer = buffer;
405           buflen *= 2;
406           buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
407           if (buffer == NULL)
408             {
409               /* We ran out of memory.  We cannot do anything but
410                  sending a negative response.  In reality this should
411                  never happen.  */
412               serv = NULL;
413               buffer = old_buffer;
414
415               /* We set the error to indicate this is (possibly) a
416                  temporary error and that it does not mean the entry
417                  is not available at all.  */
418               errval = EAGAIN;
419               break;
420             }
421           use_malloc = true;
422         }
423       else
424         /* Allocate a new buffer on the stack.  If possible combine it
425            with the previously allocated buffer.  */
426         buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
427     }
428
429   cache_addserv (db, fd, req, key, serv, uid, he, dh, errval);
430
431   if (use_malloc)
432     free (buffer);
433 }
434
435
436 void
437 addservbyname (struct database_dyn *db, int fd, request_header *req,
438                void *key, uid_t uid)
439 {
440   addservbyX (db, fd, req, key, uid, NULL, NULL);
441 }
442
443
444 void
445 readdservbyname (struct database_dyn *db, struct hashentry *he,
446                  struct datahead *dh)
447 {
448   request_header req =
449     {
450       .type = GETSERVBYNAME,
451       .key_len = he->len
452     };
453
454   addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
455 }
456
457
458 void
459 addservbyport (struct database_dyn *db, int fd, request_header *req,
460                void *key, uid_t uid)
461 {
462   addservbyX (db, fd, req, key, uid, NULL, NULL);
463 }
464
465
466 void
467 readdservbyport (struct database_dyn *db, struct hashentry *he,
468                  struct datahead *dh)
469 {
470   request_header req =
471     {
472       .type = GETSERVBYPORT,
473       .key_len = he->len
474     };
475
476   addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
477 }