c952fa12c8d87631711ceb7aad884f8010bacdce
[kopensolaris-gnu/glibc.git] / nscd / servicescache.c
1 /* Cache handling for services lookup.
2    Copyright (C) 2007, 2008 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                                    IDX_result_data);
108           /* If we cannot permanently store the result, so be it.  */
109           if (dataset != NULL)
110             {
111               dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
112               dataset->head.recsize = total;
113               dataset->head.notfound = true;
114               dataset->head.nreloads = 0;
115               dataset->head.usable = true;
116
117               /* Compute the timeout time.  */
118               dataset->head.timeout = t + db->negtimeout;
119
120               /* This is the reply.  */
121               memcpy (&dataset->resp, &notfound, total);
122
123               /* Copy the key data.  */
124               memcpy (dataset->strdata, key, req->key_len);
125
126               /* If necessary, we also propagate the data to disk.  */
127               if (db->persistent)
128                 {
129                   // XXX async OK?
130                   uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
131                   msync ((void *) pval,
132                          ((uintptr_t) dataset & pagesize_m1)
133                          + sizeof (struct dataset) + req->key_len, MS_ASYNC);
134                 }
135
136               /* Now get the lock to safely insert the records.  */
137               pthread_rwlock_rdlock (&db->lock);
138
139               (void) cache_add (req->type, &dataset->strdata, req->key_len,
140                                 &dataset->head, true, db, owner, he == NULL);
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                                                       IDX_result_data);
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                                                     IDX_result_data);
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               else
277                 ++db->head->addfailed;
278
279               /* Mark the old record as obsolete.  */
280               dh->usable = false;
281             }
282         }
283       else
284         {
285           /* We write the dataset before inserting it to the database
286              since while inserting this thread might block and so would
287              unnecessarily keep the receiver waiting.  */
288           assert (fd != -1);
289
290 #ifdef HAVE_SENDFILE
291           if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
292             {
293               assert (db->wr_fd != -1);
294               assert ((char *) &dataset->resp > (char *) db->data);
295               assert ((char *) &dataset->resp - (char *) db->head
296                       + total
297                       <= (sizeof (struct database_pers_head)
298                           + db->head->module * sizeof (ref_t)
299                           + db->head->data_size));
300               written = sendfileall (fd, db->wr_fd,
301                                      (char *) &dataset->resp
302                                      - (char *) db->head, total);
303 # ifndef __ASSUME_SENDFILE
304               if (written == -1 && errno == ENOSYS)
305                 goto use_write;
306 # endif
307             }
308           else
309 # ifndef __ASSUME_SENDFILE
310           use_write:
311 # endif
312 #endif
313             written = writeall (fd, &dataset->resp, total);
314         }
315
316       /* Add the record to the database.  But only if it has not been
317          stored on the stack.  */
318       if (! alloca_used)
319         {
320           /* If necessary, we also propagate the data to disk.  */
321           if (db->persistent)
322             {
323               // XXX async OK?
324               uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
325               msync ((void *) pval,
326                      ((uintptr_t) dataset & pagesize_m1)
327                      + total + req->key_len, MS_ASYNC);
328             }
329
330           /* Now get the lock to safely insert the records.  */
331           pthread_rwlock_rdlock (&db->lock);
332
333           (void) cache_add (req->type, key_copy, req->key_len,
334                             &dataset->head, true, db, owner, he == NULL);
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 }