]> git.ipfire.org Git - thirdparty/glibc.git/blame - nscd/nscd_helper.c
2.5-18.1
[thirdparty/glibc.git] / nscd / nscd_helper.c
CommitLineData
0ecb606c
JJ
1/* Copyright (C) 1998-2002,2003,2004,2005,2006,2007
2 Free Software Foundation, Inc.
c207f23b
UD
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 The GNU C Library 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 GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
20
21#include <assert.h>
22#include <errno.h>
23#include <fcntl.h>
24#include <stdbool.h>
0ecb606c
JJ
25#include <string.h>
26#include <time.h>
c207f23b
UD
27#include <unistd.h>
28#include <sys/mman.h>
29#include <sys/poll.h>
30#include <sys/socket.h>
31#include <sys/stat.h>
0ecb606c 32#include <sys/time.h>
c207f23b
UD
33#include <sys/uio.h>
34#include <sys/un.h>
35#include <not-cancel.h>
36#include <nis/rpcsvc/nis.h>
37
38#include "nscd-client.h"
39
40
0ecb606c
JJ
41ssize_t
42__readall (int fd, void *buf, size_t len)
43{
44 size_t n = len;
45 ssize_t ret;
46 do
47 {
48 ret = TEMP_FAILURE_RETRY (__read (fd, buf, n));
49 if (ret <= 0)
50 break;
51 buf = (char *) buf + ret;
52 n -= ret;
53 }
54 while (n > 0);
55 return ret < 0 ? ret : len - n;
56}
57
58
59ssize_t
60__readvall (int fd, const struct iovec *iov, int iovcnt)
61{
62 ssize_t ret = TEMP_FAILURE_RETRY (__readv (fd, iov, iovcnt));
63 if (ret <= 0)
64 return ret;
65
66 size_t total = 0;
67 for (int i = 0; i < iovcnt; ++i)
68 total += iov[i].iov_len;
69
70 if (ret < total)
71 {
72 struct iovec iov_buf[iovcnt];
73 ssize_t r = ret;
74
75 struct iovec *iovp = memcpy (iov_buf, iov, iovcnt * sizeof (*iov));
76 do
77 {
78 while (iovp->iov_len <= r)
79 {
80 r -= iovp->iov_len;
81 --iovcnt;
82 ++iovp;
83 }
84 iovp->iov_base = (char *) iovp->iov_base + r;
85 iovp->iov_len -= r;
86 r = TEMP_FAILURE_RETRY (__readv (fd, iovp, iovcnt));
87 if (r <= 0)
88 break;
89 ret += r;
90 }
91 while (ret < total);
92 if (r < 0)
93 ret = r;
94 }
95 return ret;
96}
97
98
c207f23b
UD
99static int
100open_socket (void)
101{
102 int sock = __socket (PF_UNIX, SOCK_STREAM, 0);
103 if (sock < 0)
104 return -1;
105
106 /* Make socket non-blocking. */
107 int fl = __fcntl (sock, F_GETFL);
108 if (fl != -1)
109 __fcntl (sock, F_SETFL, fl | O_NONBLOCK);
110
111 struct sockaddr_un sun;
112 sun.sun_family = AF_UNIX;
113 strcpy (sun.sun_path, _PATH_NSCDSOCKET);
114 if (__connect (sock, (struct sockaddr *) &sun, sizeof (sun)) < 0
115 && errno != EINPROGRESS)
116 goto out;
117
118 struct pollfd fds[1];
119 fds[0].fd = sock;
120 fds[0].events = POLLOUT | POLLERR | POLLHUP;
121 if (__poll (fds, 1, 5 * 1000) > 0)
122 /* Success. We do not check for success of the connect call here.
123 If it failed, the following operations will fail. */
124 return sock;
125
126 out:
127 close_not_cancel_no_status (sock);
128
129 return -1;
130}
131
132
133void
134__nscd_unmap (struct mapped_database *mapped)
135{
136 assert (mapped->counter == 0);
62417d7e 137 __munmap ((void *) mapped->head, mapped->mapsize);
c207f23b
UD
138 free (mapped);
139}
140
141
0ecb606c
JJ
142static int
143wait_on_socket (int sock)
144{
145 struct pollfd fds[1];
146 fds[0].fd = sock;
147 fds[0].events = POLLIN | POLLERR | POLLHUP;
148 int n = __poll (fds, 1, 5 * 1000);
149 if (n == -1 && __builtin_expect (errno == EINTR, 0))
150 {
151 /* Handle the case where the poll() call is interrupted by a
152 signal. We cannot just use TEMP_FAILURE_RETRY since it might
153 lead to infinite loops. */
154 struct timeval now;
155 (void) __gettimeofday (&now, NULL);
156 long int end = (now.tv_sec + 5) * 1000 + (now.tv_usec + 500) / 1000;
157 while (1)
158 {
159 long int timeout = end - (now.tv_sec * 1000
160 + (now.tv_usec + 500) / 1000);
161 n = __poll (fds, 1, timeout);
162 if (n != -1 || errno != EINTR)
163 break;
164 (void) __gettimeofday (&now, NULL);
165 }
166 }
167
168 return n;
169}
170
171
c207f23b
UD
172/* Try to get a file descriptor for the shared meory segment
173 containing the database. */
174static struct mapped_database *
175get_mapping (request_type type, const char *key,
176 struct mapped_database **mappedp)
177{
178 struct mapped_database *result = NO_MAPPING;
179#ifdef SCM_RIGHTS
180 const size_t keylen = strlen (key) + 1;
c207f23b
UD
181 int saved_errno = errno;
182
183 int mapfd = -1;
184
185 /* Send the request. */
0ecb606c
JJ
186 struct
187 {
188 request_header req;
189 char key[keylen];
190 } reqdata;
191 size_t real_sizeof_reqdata = sizeof (request_header) + keylen;
c207f23b
UD
192
193 int sock = open_socket ();
194 if (sock < 0)
195 goto out;
196
0ecb606c
JJ
197 reqdata.req.version = NSCD_VERSION;
198 reqdata.req.type = type;
199 reqdata.req.key_len = keylen;
200 memcpy (reqdata.key, key, keylen);
201
202# ifndef MSG_NOSIGNAL
203# define MSG_NOSIGNAL 0
204# endif
205 if (__builtin_expect (TEMP_FAILURE_RETRY (__send (sock, &reqdata,
206 real_sizeof_reqdata,
207 MSG_NOSIGNAL))
208 != real_sizeof_reqdata, 0))
c207f23b
UD
209 /* We cannot even write the request. */
210 goto out_close2;
211
212 /* Room for the data sent along with the file descriptor. We expect
213 the key name back. */
0ecb606c
JJ
214# define resdata reqdata.key
215 struct iovec iov[1];
c207f23b
UD
216 iov[0].iov_base = resdata;
217 iov[0].iov_len = keylen;
218
0ecb606c
JJ
219 union
220 {
221 struct cmsghdr hdr;
222 char bytes[CMSG_SPACE (sizeof (int))];
223 } buf;
c207f23b 224 struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1,
0ecb606c
JJ
225 .msg_control = buf.bytes,
226 .msg_controllen = sizeof (buf) };
c207f23b
UD
227 struct cmsghdr *cmsg = CMSG_FIRSTHDR (&msg);
228
229 cmsg->cmsg_level = SOL_SOCKET;
230 cmsg->cmsg_type = SCM_RIGHTS;
231 cmsg->cmsg_len = CMSG_LEN (sizeof (int));
232
0ecb606c
JJ
233 /* This access is well-aligned since BUF is correctly aligned for an
234 int and CMSG_DATA preserves this alignment. */
c207f23b
UD
235 *(int *) CMSG_DATA (cmsg) = -1;
236
237 msg.msg_controllen = cmsg->cmsg_len;
238
0ecb606c 239 if (wait_on_socket (sock) <= 0)
c207f23b
UD
240 goto out_close2;
241
0ecb606c
JJ
242 if (__builtin_expect (TEMP_FAILURE_RETRY (__recvmsg (sock, &msg, 0))
243 != keylen, 0))
c207f23b
UD
244 goto out_close2;
245
0ecb606c
JJ
246 if (__builtin_expect (CMSG_FIRSTHDR (&msg) == NULL
247 || (CMSG_FIRSTHDR (&msg)->cmsg_len
248 != CMSG_LEN (sizeof (int))), 0))
249 goto out_close2;
aadd7d9d 250
0ecb606c 251 mapfd = *(int *) CMSG_DATA (cmsg);
407c4b9a 252
c207f23b 253 struct stat64 st;
0ecb606c
JJ
254 if (__builtin_expect (strcmp (resdata, key) != 0, 0)
255 || __builtin_expect (fstat64 (mapfd, &st) != 0, 0)
256 || __builtin_expect (st.st_size < sizeof (struct database_pers_head), 0))
c207f23b
UD
257 goto out_close;
258
259 struct database_pers_head head;
0ecb606c
JJ
260 if (__builtin_expect (TEMP_FAILURE_RETRY (__pread (mapfd, &head,
261 sizeof (head), 0))
262 != sizeof (head), 0))
c207f23b
UD
263 goto out_close;
264
0ecb606c
JJ
265 if (__builtin_expect (head.version != DB_VERSION, 0)
266 || __builtin_expect (head.header_size != sizeof (head), 0)
c207f23b
UD
267 /* This really should not happen but who knows, maybe the update
268 thread got stuck. */
0ecb606c
JJ
269 || __builtin_expect (! head.nscd_certainly_running
270 && head.timestamp + MAPPING_TIMEOUT < time (NULL),
271 0))
c207f23b
UD
272 goto out_close;
273
274 size_t size = (sizeof (head) + roundup (head.module * sizeof (ref_t), ALIGN)
275 + head.data_size);
276
0ecb606c 277 if (__builtin_expect (st.st_size < size, 0))
c207f23b
UD
278 goto out_close;
279
280 /* The file is large enough, map it now. */
281 void *mapping = __mmap (NULL, size, PROT_READ, MAP_SHARED, mapfd, 0);
0ecb606c 282 if (__builtin_expect (mapping != MAP_FAILED, 1))
c207f23b
UD
283 {
284 /* Allocate a record for the mapping. */
0ecb606c 285 struct mapped_database *newp = malloc (sizeof (*newp));
c207f23b
UD
286 if (newp == NULL)
287 {
288 /* Ugh, after all we went through the memory allocation failed. */
0ecb606c 289 __munmap (mapping, size);
c207f23b
UD
290 goto out_close;
291 }
292
293 newp->head = mapping;
294 newp->data = ((char *) mapping + head.header_size
295 + roundup (head.module * sizeof (ref_t), ALIGN));
296 newp->mapsize = size;
0ecb606c 297 newp->datasize = head.data_size;
c207f23b
UD
298 /* Set counter to 1 to show it is usable. */
299 newp->counter = 1;
300
301 result = newp;
302 }
303
304 out_close:
305 __close (mapfd);
306 out_close2:
307 __close (sock);
308 out:
309 __set_errno (saved_errno);
310#endif /* SCM_RIGHTS */
311
312 struct mapped_database *oldval = *mappedp;
313 *mappedp = result;
314
315 if (oldval != NULL && atomic_decrement_val (&oldval->counter) == 0)
316 __nscd_unmap (oldval);
317
318 return result;
319}
320
321
322struct mapped_database *
323__nscd_get_map_ref (request_type type, const char *name,
0ecb606c 324 volatile struct locked_map_ptr *mapptr, int *gc_cyclep)
c207f23b
UD
325{
326 struct mapped_database *cur = mapptr->mapped;
327 if (cur == NO_MAPPING)
328 return cur;
329
330 int cnt = 0;
0ecb606c
JJ
331 while (__builtin_expect (atomic_compare_and_exchange_val_acq (&mapptr->lock,
332 1, 0) != 0, 0))
c207f23b
UD
333 {
334 // XXX Best number of rounds?
0ecb606c 335 if (__builtin_expect (++cnt > 5, 0))
c207f23b
UD
336 return NO_MAPPING;
337
338 atomic_delay ();
339 }
340
341 cur = mapptr->mapped;
342
343 if (__builtin_expect (cur != NO_MAPPING, 1))
344 {
345 /* If not mapped or timestamp not updated, request new map. */
346 if (cur == NULL
081fc592 347 || (cur->head->nscd_certainly_running == 0
0ecb606c
JJ
348 && cur->head->timestamp + MAPPING_TIMEOUT < time (NULL))
349 || cur->head->data_size > cur->datasize)
350 cur = get_mapping (type, name,
351 (struct mapped_database **) &mapptr->mapped);
c207f23b
UD
352
353 if (__builtin_expect (cur != NO_MAPPING, 1))
354 {
355 if (__builtin_expect (((*gc_cyclep = cur->head->gc_cycle) & 1) != 0,
356 0))
357 cur = NO_MAPPING;
358 else
359 atomic_increment (&cur->counter);
360 }
361 }
362
363 mapptr->lock = 0;
364
365 return cur;
366}
367
368
0ecb606c
JJ
369/* Don't return const struct datahead *, as eventhough the record
370 is normally constant, it can change arbitrarily during nscd
371 garbage collection. */
372struct datahead *
c207f23b
UD
373__nscd_cache_search (request_type type, const char *key, size_t keylen,
374 const struct mapped_database *mapped)
375{
376 unsigned long int hash = __nis_hash (key, keylen) % mapped->head->module;
0ecb606c 377 size_t datasize = mapped->datasize;
c207f23b
UD
378
379 ref_t work = mapped->head->array[hash];
0ecb606c 380 while (work != ENDREF && work + sizeof (struct hashentry) <= datasize)
c207f23b
UD
381 {
382 struct hashentry *here = (struct hashentry *) (mapped->data + work);
383
0ecb606c
JJ
384#ifndef _STRING_ARCH_unaligned
385 /* Although during garbage collection when moving struct hashentry
386 records around we first copy from old to new location and then
387 adjust pointer from previous hashentry to it, there is no barrier
388 between those memory writes. It is very unlikely to hit it,
389 so check alignment only if a misaligned load can crash the
390 application. */
391 if ((uintptr_t) here & (__alignof__ (*here) - 1))
392 return NULL;
393#endif
394
395 if (type == here->type
396 && keylen == here->len
397 && here->key + keylen <= datasize
398 && memcmp (key, mapped->data + here->key, keylen) == 0
399 && here->packet + sizeof (struct datahead) <= datasize)
c207f23b
UD
400 {
401 /* We found the entry. Increment the appropriate counter. */
0ecb606c 402 struct datahead *dh
c207f23b
UD
403 = (struct datahead *) (mapped->data + here->packet);
404
0ecb606c
JJ
405#ifndef _STRING_ARCH_unaligned
406 if ((uintptr_t) dh & (__alignof__ (*dh) - 1))
407 return NULL;
408#endif
409
c207f23b
UD
410 /* See whether we must ignore the entry or whether something
411 is wrong because garbage collection is in progress. */
0ecb606c 412 if (dh->usable && here->packet + dh->allocsize <= datasize)
c207f23b
UD
413 return dh;
414 }
415
416 work = here->next;
417 }
418
419 return NULL;
420}
421
422
423/* Create a socket connected to a name. */
424int
425__nscd_open_socket (const char *key, size_t keylen, request_type type,
426 void *response, size_t responselen)
427{
428 int saved_errno = errno;
429
430 int sock = open_socket ();
431 if (sock >= 0)
432 {
433 request_header req;
434 req.version = NSCD_VERSION;
435 req.type = type;
436 req.key_len = keylen;
437
438 struct iovec vec[2];
439 vec[0].iov_base = &req;
440 vec[0].iov_len = sizeof (request_header);
441 vec[1].iov_base = (void *) key;
442 vec[1].iov_len = keylen;
443
444 ssize_t nbytes = TEMP_FAILURE_RETRY (__writev (sock, vec, 2));
0ecb606c 445 if (nbytes == (ssize_t) (sizeof (request_header) + keylen)
c207f23b 446 /* Wait for data. */
0ecb606c
JJ
447 && wait_on_socket (sock) > 0)
448 {
449 nbytes = TEMP_FAILURE_RETRY (__read (sock, response, responselen));
450 if (nbytes == (ssize_t) responselen)
451 return sock;
c207f23b
UD
452 }
453
454 close_not_cancel_no_status (sock);
455 }
456
457 __set_errno (saved_errno);
458
459 return -1;
460}