Fix lock handling in memory hander of nscd.
[platform/upstream/glibc.git] / nscd / servicescache.c
1 /* Cache handling for services lookup.
2    Copyright (C) 2007, 2008, 2009 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 *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   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                                    1);
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               (void) cache_add (req->type, &dataset->strdata, req->key_len,
137                                 &dataset->head, true, db, owner, he == NULL);
138
139               /* Mark the old entry as obsolete.  */
140               if (dh != NULL)
141                 dh->usable = false;
142             }
143         }
144     }
145   else
146     {
147       /* Determine the I/O structure.  */
148       size_t s_name_len = strlen (serv->s_name) + 1;
149       size_t s_proto_len = strlen (serv->s_proto) + 1;
150       uint32_t *s_aliases_len;
151       size_t s_aliases_cnt;
152       char *aliases;
153       char *cp;
154       size_t cnt;
155
156       /* Determine the number of aliases.  */
157       s_aliases_cnt = 0;
158       for (cnt = 0; serv->s_aliases[cnt] != NULL; ++cnt)
159         ++s_aliases_cnt;
160       /* Determine the length of all aliases.  */
161       s_aliases_len = (uint32_t *) alloca (s_aliases_cnt * sizeof (uint32_t));
162       total = 0;
163       for (cnt = 0; cnt < s_aliases_cnt; ++cnt)
164         {
165           s_aliases_len[cnt] = strlen (serv->s_aliases[cnt]) + 1;
166           total += s_aliases_len[cnt];
167         }
168
169       total += (offsetof (struct dataset, strdata)
170                 + s_name_len
171                 + s_proto_len
172                 + s_aliases_cnt * sizeof (uint32_t));
173       written = total;
174
175       /* If we refill the cache, first assume the reconrd did not
176          change.  Allocate memory on the cache since it is likely
177          discarded anyway.  If it turns out to be necessary to have a
178          new record we can still allocate real memory.  */
179       bool alloca_used = false;
180       dataset = NULL;
181
182       if (he == NULL)
183         dataset = (struct dataset *) mempool_alloc (db, total + req->key_len,
184                                                     1);
185
186       if (dataset == NULL)
187         {
188           /* We cannot permanently add the result in the moment.  But
189              we can provide the result as is.  Store the data in some
190              temporary memory.  */
191           dataset = (struct dataset *) alloca (total + req->key_len);
192
193           /* We cannot add this record to the permanent database.  */
194           alloca_used = true;
195         }
196
197       dataset->head.allocsize = total + req->key_len;
198       dataset->head.recsize = total - offsetof (struct dataset, resp);
199       dataset->head.notfound = false;
200       dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
201       dataset->head.usable = true;
202
203       /* Compute the timeout time.  */
204       dataset->head.timeout = t + db->postimeout;
205
206       dataset->resp.version = NSCD_VERSION;
207       dataset->resp.found = 1;
208       dataset->resp.s_name_len = s_name_len;
209       dataset->resp.s_proto_len = s_proto_len;
210       dataset->resp.s_port = serv->s_port;
211       dataset->resp.s_aliases_cnt = s_aliases_cnt;
212
213       cp = dataset->strdata;
214
215       cp = mempcpy (cp, serv->s_name, s_name_len);
216       cp = mempcpy (cp, serv->s_proto, s_proto_len);
217       cp = mempcpy (cp, s_aliases_len, s_aliases_cnt * sizeof (uint32_t));
218
219       /* Then the aliases.  */
220       aliases = cp;
221       for (cnt = 0; cnt < s_aliases_cnt; ++cnt)
222         cp = mempcpy (cp, serv->s_aliases[cnt], s_aliases_len[cnt]);
223
224       assert (cp
225               == dataset->strdata + total - offsetof (struct dataset,
226                                                       strdata));
227
228       char *key_copy = memcpy (cp, key, req->key_len);
229
230       /* Now we can determine whether on refill we have to create a new
231          record or not.  */
232       if (he != NULL)
233         {
234           assert (fd == -1);
235
236           if (total + req->key_len == dh->allocsize
237               && total - offsetof (struct dataset, resp) == dh->recsize
238               && memcmp (&dataset->resp, dh->data,
239                          dh->allocsize - offsetof (struct dataset, resp)) == 0)
240             {
241               /* The data has not changed.  We will just bump the
242                  timeout value.  Note that the new record has been
243                  allocated on the stack and need not be freed.  */
244               dh->timeout = dataset->head.timeout;
245               ++dh->nreloads;
246             }
247           else
248             {
249               /* We have to create a new record.  Just allocate
250                  appropriate memory and copy it.  */
251               struct dataset *newp
252                 = (struct dataset *) mempool_alloc (db, total + req->key_len,
253                                                     1);
254               if (newp != NULL)
255                 {
256                   /* Adjust pointers into the memory block.  */
257                   aliases = (char *) newp + (aliases - (char *) dataset);
258                   assert (key_copy != NULL);
259                   key_copy = (char *) newp + (key_copy - (char *) dataset);
260
261                   dataset = memcpy (newp, dataset, total + req->key_len);
262                   alloca_used = false;
263                 }
264
265               /* Mark the old record as obsolete.  */
266               dh->usable = false;
267             }
268         }
269       else
270         {
271           /* We write the dataset before inserting it to the database
272              since while inserting this thread might block and so would
273              unnecessarily keep the receiver waiting.  */
274           assert (fd != -1);
275
276 #ifdef HAVE_SENDFILE
277           if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
278             {
279               assert (db->wr_fd != -1);
280               assert ((char *) &dataset->resp > (char *) db->data);
281               assert ((char *) &dataset->resp - (char *) db->head
282                       + total
283                       <= (sizeof (struct database_pers_head)
284                           + db->head->module * sizeof (ref_t)
285                           + db->head->data_size));
286               written = sendfileall (fd, db->wr_fd,
287                                      (char *) &dataset->resp
288                                      - (char *) db->head, total);
289 # ifndef __ASSUME_SENDFILE
290               if (written == -1 && errno == ENOSYS)
291                 goto use_write;
292 # endif
293             }
294           else
295 # ifndef __ASSUME_SENDFILE
296           use_write:
297 # endif
298 #endif
299             written = writeall (fd, &dataset->resp, total);
300         }
301
302       /* Add the record to the database.  But only if it has not been
303          stored on the stack.  */
304       if (! alloca_used)
305         {
306           /* If necessary, we also propagate the data to disk.  */
307           if (db->persistent)
308             {
309               // XXX async OK?
310               uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
311               msync ((void *) pval,
312                      ((uintptr_t) dataset & pagesize_m1)
313                      + total + req->key_len, MS_ASYNC);
314             }
315
316           (void) cache_add (req->type, key_copy, req->key_len,
317                             &dataset->head, true, db, owner, he == NULL);
318         }
319     }
320
321   if (__builtin_expect (written != total, 0) && debug_level > 0)
322     {
323       char buf[256];
324       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
325                strerror_r (errno, buf, sizeof (buf)));
326     }
327 }
328
329
330 static int
331 lookup (int type, char *key, struct servent *resultbufp, char *buffer,
332         size_t buflen, struct servent **serv)
333 {
334   char *proto = strrchr (key, '/');
335   if (proto != NULL && proto != key)
336     {
337       key = strndupa (key, proto - key);
338       if (proto[1] == '\0')
339         proto = NULL;
340       else
341         ++proto;
342     }
343
344   if (type == GETSERVBYNAME)
345     return __getservbyname_r (key, proto, resultbufp, buffer, buflen, serv);
346
347   assert (type == GETSERVBYPORT);
348   return __getservbyport_r (atol (key), proto, resultbufp, buffer, buflen,
349                             serv);
350 }
351
352
353 static void
354 addservbyX (struct database_dyn *db, int fd, request_header *req,
355             char *key, uid_t uid, struct hashentry *he, struct datahead *dh)
356 {
357   /* Search for the entry matching the key.  Please note that we don't
358      look again in the table whether the dataset is now available.  We
359      simply insert it.  It does not matter if it is in there twice.  The
360      pruning function only will look at the timestamp.  */
361   size_t buflen = 1024;
362   char *buffer = (char *) alloca (buflen);
363   struct servent resultbuf;
364   struct servent *serv;
365   bool use_malloc = false;
366   int errval = 0;
367
368   if (__builtin_expect (debug_level > 0, 0))
369     {
370       if (he == NULL)
371         dbg_log (_("Haven't found \"%s\" in services cache!"), key);
372       else
373         dbg_log (_("Reloading \"%s\" in services cache!"), key);
374     }
375
376   while (lookup (req->type, key, &resultbuf, buffer, buflen, &serv) != 0
377          && (errval = errno) == ERANGE)
378     {
379       errno = 0;
380
381       if (__builtin_expect (buflen > 32768, 0))
382         {
383           char *old_buffer = buffer;
384           buflen *= 2;
385           buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
386           if (buffer == NULL)
387             {
388               /* We ran out of memory.  We cannot do anything but
389                  sending a negative response.  In reality this should
390                  never happen.  */
391               serv = NULL;
392               buffer = old_buffer;
393
394               /* We set the error to indicate this is (possibly) a
395                  temporary error and that it does not mean the entry
396                  is not available at all.  */
397               errval = EAGAIN;
398               break;
399             }
400           use_malloc = true;
401         }
402       else
403         /* Allocate a new buffer on the stack.  If possible combine it
404            with the previously allocated buffer.  */
405         buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
406     }
407
408   cache_addserv (db, fd, req, key, serv, uid, he, dh, errval);
409
410   if (use_malloc)
411     free (buffer);
412 }
413
414
415 void
416 addservbyname (struct database_dyn *db, int fd, request_header *req,
417                void *key, uid_t uid)
418 {
419   addservbyX (db, fd, req, key, uid, NULL, NULL);
420 }
421
422
423 void
424 readdservbyname (struct database_dyn *db, struct hashentry *he,
425                  struct datahead *dh)
426 {
427   request_header req =
428     {
429       .type = GETSERVBYNAME,
430       .key_len = he->len
431     };
432
433   addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
434 }
435
436
437 void
438 addservbyport (struct database_dyn *db, int fd, request_header *req,
439                void *key, uid_t uid)
440 {
441   addservbyX (db, fd, req, key, uid, NULL, NULL);
442 }
443
444
445 void
446 readdservbyport (struct database_dyn *db, struct hashentry *he,
447                  struct datahead *dh)
448 {
449   request_header req =
450     {
451       .type = GETSERVBYPORT,
452       .key_len = he->len
453     };
454
455   addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
456 }