* nscd/nscd_helper.c (get_mapping): Handle short replies instead
[glibc.git] / nscd / nscd_helper.c
blobbab4913e9abd742d86049287b2786389758cf9d4
1 /* Copyright (C) 1998-2002,2003,2004,2005,2006,2007
2 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
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.
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.
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. */
21 #include <assert.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stdbool.h>
25 #include <string.h>
26 #include <time.h>
27 #include <unistd.h>
28 #include <sys/mman.h>
29 #include <sys/poll.h>
30 #include <sys/socket.h>
31 #include <sys/stat.h>
32 #include <sys/time.h>
33 #include <sys/uio.h>
34 #include <sys/un.h>
35 #include <not-cancel.h>
36 #include <nis/rpcsvc/nis.h>
38 #include "nscd-client.h"
41 ssize_t
42 __readall (int fd, void *buf, size_t len)
44 size_t n = len;
45 ssize_t ret;
48 ret = TEMP_FAILURE_RETRY (__read (fd, buf, n));
49 if (ret <= 0)
50 break;
51 buf = (char *) buf + ret;
52 n -= ret;
54 while (n > 0);
55 return ret < 0 ? ret : len - n;
59 ssize_t
60 __readvall (int fd, const struct iovec *iov, int iovcnt)
62 ssize_t ret = TEMP_FAILURE_RETRY (__readv (fd, iov, iovcnt));
63 if (ret <= 0)
64 return ret;
66 size_t total = 0;
67 for (int i = 0; i < iovcnt; ++i)
68 total += iov[i].iov_len;
70 if (ret < total)
72 struct iovec iov_buf[iovcnt];
73 ssize_t r = ret;
75 struct iovec *iovp = memcpy (iov_buf, iov, iovcnt * sizeof (*iov));
78 while (iovp->iov_len <= r)
80 r -= iovp->iov_len;
81 --iovcnt;
82 ++iovp;
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;
91 while (ret < total);
92 if (r < 0)
93 ret = r;
95 return ret;
99 static int
100 open_socket (request_type type, const char *key, size_t keylen)
102 int sock = __socket (PF_UNIX, SOCK_STREAM, 0);
103 if (sock < 0)
104 return -1;
106 struct
108 request_header req;
109 char key[keylen];
110 } reqdata;
111 size_t real_sizeof_reqdata = sizeof (request_header) + keylen;
113 /* Make socket non-blocking. */
114 __fcntl (sock, F_SETFL, O_RDWR | O_NONBLOCK);
116 struct sockaddr_un sun;
117 sun.sun_family = AF_UNIX;
118 strcpy (sun.sun_path, _PATH_NSCDSOCKET);
119 if (__connect (sock, (struct sockaddr *) &sun, sizeof (sun)) < 0
120 && errno != EINPROGRESS)
121 goto out;
123 reqdata.req.version = NSCD_VERSION;
124 reqdata.req.type = type;
125 reqdata.req.key_len = keylen;
127 memcpy (reqdata.key, key, keylen);
129 bool first_try = true;
130 struct timeval tvend;
131 /* Fake initializing tvend. */
132 asm ("" : "=m" (tvend));
133 while (1)
135 #ifndef MSG_NOSIGNAL
136 # define MSG_NOSIGNAL 0
137 #endif
138 ssize_t wres = TEMP_FAILURE_RETRY (__send (sock, &reqdata,
139 real_sizeof_reqdata,
140 MSG_NOSIGNAL));
141 if (__builtin_expect (wres == (ssize_t) real_sizeof_reqdata, 1))
142 /* We managed to send the request. */
143 return sock;
145 if (wres != -1 || errno != EAGAIN)
146 /* Something is really wrong, no chance to continue. */
147 break;
149 /* The daemon is busy wait for it. */
150 int to;
151 struct timeval now;
152 (void) __gettimeofday (&now, NULL);
153 if (first_try)
155 tvend.tv_usec = now.tv_usec;
156 tvend.tv_sec = now.tv_sec + 5;
157 to = 5 * 1000;
158 first_try = false;
160 else
161 to = ((tvend.tv_sec - now.tv_sec) * 1000
162 + (tvend.tv_usec - now.tv_usec) / 1000);
164 struct pollfd fds[1];
165 fds[0].fd = sock;
166 fds[0].events = POLLOUT | POLLERR | POLLHUP;
167 if (__poll (fds, 1, to) <= 0)
168 /* The connection timed out or broke down. */
169 break;
171 /* We try to write again. */
174 out:
175 close_not_cancel_no_status (sock);
177 return -1;
181 void
182 __nscd_unmap (struct mapped_database *mapped)
184 assert (mapped->counter == 0);
185 __munmap ((void *) mapped->head, mapped->mapsize);
186 free (mapped);
190 static int
191 wait_on_socket (int sock)
193 struct pollfd fds[1];
194 fds[0].fd = sock;
195 fds[0].events = POLLIN | POLLERR | POLLHUP;
196 int n = __poll (fds, 1, 5 * 1000);
197 if (n == -1 && __builtin_expect (errno == EINTR, 0))
199 /* Handle the case where the poll() call is interrupted by a
200 signal. We cannot just use TEMP_FAILURE_RETRY since it might
201 lead to infinite loops. */
202 struct timeval now;
203 (void) __gettimeofday (&now, NULL);
204 long int end = (now.tv_sec + 5) * 1000 + (now.tv_usec + 500) / 1000;
205 while (1)
207 long int timeout = end - (now.tv_sec * 1000
208 + (now.tv_usec + 500) / 1000);
209 n = __poll (fds, 1, timeout);
210 if (n != -1 || errno != EINTR)
211 break;
212 (void) __gettimeofday (&now, NULL);
216 return n;
220 /* Try to get a file descriptor for the shared meory segment
221 containing the database. */
222 static struct mapped_database *
223 get_mapping (request_type type, const char *key,
224 struct mapped_database **mappedp)
226 struct mapped_database *result = NO_MAPPING;
227 #ifdef SCM_RIGHTS
228 const size_t keylen = strlen (key) + 1;
229 int saved_errno = errno;
231 int mapfd = -1;
232 char resdata[keylen];
234 /* Open a socket and send the request. */
235 int sock = open_socket (type, key, keylen);
236 if (sock < 0)
237 goto out;
239 /* Room for the data sent along with the file descriptor. We expect
240 the key name back. */
241 struct iovec iov[1];
242 iov[0].iov_base = resdata;
243 iov[0].iov_len = keylen;
245 union
247 struct cmsghdr hdr;
248 char bytes[CMSG_SPACE (sizeof (int))];
249 } buf;
250 struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1,
251 .msg_control = buf.bytes,
252 .msg_controllen = sizeof (buf) };
253 struct cmsghdr *cmsg = CMSG_FIRSTHDR (&msg);
255 cmsg->cmsg_level = SOL_SOCKET;
256 cmsg->cmsg_type = SCM_RIGHTS;
257 cmsg->cmsg_len = CMSG_LEN (sizeof (int));
259 /* This access is well-aligned since BUF is correctly aligned for an
260 int and CMSG_DATA preserves this alignment. */
261 *(int *) CMSG_DATA (cmsg) = -1;
263 msg.msg_controllen = cmsg->cmsg_len;
265 if (wait_on_socket (sock) <= 0)
266 goto out_close2;
268 if (__builtin_expect (TEMP_FAILURE_RETRY (__recvmsg (sock, &msg, 0))
269 != keylen, 0))
270 goto out_close2;
272 mapfd = *(int *) CMSG_DATA (cmsg);
274 if (__builtin_expect (CMSG_FIRSTHDR (&msg) == NULL
275 || (CMSG_FIRSTHDR (&msg)->cmsg_len
276 != CMSG_LEN (sizeof (int))), 0))
277 goto out_close2;
279 struct stat64 st;
280 if (__builtin_expect (strcmp (resdata, key) != 0, 0)
281 || __builtin_expect (fstat64 (mapfd, &st) != 0, 0)
282 || __builtin_expect (st.st_size < sizeof (struct database_pers_head), 0))
283 goto out_close;
285 struct database_pers_head head;
286 if (__builtin_expect (TEMP_FAILURE_RETRY (__pread (mapfd, &head,
287 sizeof (head), 0))
288 != sizeof (head), 0))
289 goto out_close;
291 if (__builtin_expect (head.version != DB_VERSION, 0)
292 || __builtin_expect (head.header_size != sizeof (head), 0)
293 /* This really should not happen but who knows, maybe the update
294 thread got stuck. */
295 || __builtin_expect (! head.nscd_certainly_running
296 && head.timestamp + MAPPING_TIMEOUT < time (NULL),
298 goto out_close;
300 size_t size = (sizeof (head) + roundup (head.module * sizeof (ref_t), ALIGN)
301 + head.data_size);
303 if (__builtin_expect (st.st_size < size, 0))
304 goto out_close;
306 /* The file is large enough, map it now. */
307 void *mapping = __mmap (NULL, size, PROT_READ, MAP_SHARED, mapfd, 0);
308 if (__builtin_expect (mapping != MAP_FAILED, 1))
310 /* Allocate a record for the mapping. */
311 struct mapped_database *newp = malloc (sizeof (*newp));
312 if (newp == NULL)
314 /* Ugh, after all we went through the memory allocation failed. */
315 __munmap (mapping, size);
316 goto out_close;
319 newp->head = mapping;
320 newp->data = ((char *) mapping + head.header_size
321 + roundup (head.module * sizeof (ref_t), ALIGN));
322 newp->mapsize = size;
323 newp->datasize = head.data_size;
324 /* Set counter to 1 to show it is usable. */
325 newp->counter = 1;
327 result = newp;
330 out_close:
331 __close (mapfd);
332 out_close2:
333 __close (sock);
334 out:
335 __set_errno (saved_errno);
336 #endif /* SCM_RIGHTS */
338 struct mapped_database *oldval = *mappedp;
339 *mappedp = result;
341 if (oldval != NULL && atomic_decrement_val (&oldval->counter) == 0)
342 __nscd_unmap (oldval);
344 return result;
348 struct mapped_database *
349 __nscd_get_map_ref (request_type type, const char *name,
350 volatile struct locked_map_ptr *mapptr, int *gc_cyclep)
352 struct mapped_database *cur = mapptr->mapped;
353 if (cur == NO_MAPPING)
354 return cur;
356 int cnt = 0;
357 while (__builtin_expect (atomic_compare_and_exchange_val_acq (&mapptr->lock,
358 1, 0) != 0, 0))
360 // XXX Best number of rounds?
361 if (__builtin_expect (++cnt > 5, 0))
362 return NO_MAPPING;
364 atomic_delay ();
367 cur = mapptr->mapped;
369 if (__builtin_expect (cur != NO_MAPPING, 1))
371 /* If not mapped or timestamp not updated, request new map. */
372 if (cur == NULL
373 || (cur->head->nscd_certainly_running == 0
374 && cur->head->timestamp + MAPPING_TIMEOUT < time (NULL))
375 || cur->head->data_size > cur->datasize)
376 cur = get_mapping (type, name,
377 (struct mapped_database **) &mapptr->mapped);
379 if (__builtin_expect (cur != NO_MAPPING, 1))
381 if (__builtin_expect (((*gc_cyclep = cur->head->gc_cycle) & 1) != 0,
383 cur = NO_MAPPING;
384 else
385 atomic_increment (&cur->counter);
389 mapptr->lock = 0;
391 return cur;
395 /* Don't return const struct datahead *, as eventhough the record
396 is normally constant, it can change arbitrarily during nscd
397 garbage collection. */
398 struct datahead *
399 __nscd_cache_search (request_type type, const char *key, size_t keylen,
400 const struct mapped_database *mapped)
402 unsigned long int hash = __nis_hash (key, keylen) % mapped->head->module;
403 size_t datasize = mapped->datasize;
405 ref_t work = mapped->head->array[hash];
406 while (work != ENDREF && work + sizeof (struct hashentry) <= datasize)
408 struct hashentry *here = (struct hashentry *) (mapped->data + work);
410 #ifndef _STRING_ARCH_unaligned
411 /* Although during garbage collection when moving struct hashentry
412 records around we first copy from old to new location and then
413 adjust pointer from previous hashentry to it, there is no barrier
414 between those memory writes. It is very unlikely to hit it,
415 so check alignment only if a misaligned load can crash the
416 application. */
417 if ((uintptr_t) here & (__alignof__ (*here) - 1))
418 return NULL;
419 #endif
421 if (type == here->type
422 && keylen == here->len
423 && here->key + keylen <= datasize
424 && memcmp (key, mapped->data + here->key, keylen) == 0
425 && here->packet + sizeof (struct datahead) <= datasize)
427 /* We found the entry. Increment the appropriate counter. */
428 struct datahead *dh
429 = (struct datahead *) (mapped->data + here->packet);
431 #ifndef _STRING_ARCH_unaligned
432 if ((uintptr_t) dh & (__alignof__ (*dh) - 1))
433 return NULL;
434 #endif
436 /* See whether we must ignore the entry or whether something
437 is wrong because garbage collection is in progress. */
438 if (dh->usable && here->packet + dh->allocsize <= datasize)
439 return dh;
442 work = here->next;
445 return NULL;
449 /* Create a socket connected to a name. */
451 __nscd_open_socket (const char *key, size_t keylen, request_type type,
452 void *response, size_t responselen)
454 /* This should never happen and it is something the nscd daemon
455 enforces, too. He it helps to limit the amount of stack
456 used. */
457 if (keylen > MAXKEYLEN)
458 return -1;
460 int saved_errno = errno;
462 int sock = open_socket (type, key, keylen);
463 if (sock >= 0)
465 /* Wait for data. */
466 if (wait_on_socket (sock) > 0)
468 ssize_t nbytes = TEMP_FAILURE_RETRY (__read (sock, response,
469 responselen));
470 if (nbytes == (ssize_t) responselen)
471 return sock;
474 close_not_cancel_no_status (sock);
477 __set_errno (saved_errno);
479 return -1;