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