]> git.ipfire.org Git - thirdparty/glibc.git/blob - nscd/nscd_helper.c
.
[thirdparty/glibc.git] / nscd / nscd_helper.c
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.
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>
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>
37
38 #include "nscd-client.h"
39
40
41 ssize_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
59 ssize_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
99 static int
100 open_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
133 void
134 __nscd_unmap (struct mapped_database *mapped)
135 {
136 assert (mapped->counter == 0);
137 __munmap ((void *) mapped->head, mapped->mapsize);
138 free (mapped);
139 }
140
141
142 static int
143 wait_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
172 /* Try to get a file descriptor for the shared meory segment
173 containing the database. */
174 static struct mapped_database *
175 get_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;
181 int saved_errno = errno;
182
183 int mapfd = -1;
184
185 /* Send the request. */
186 struct
187 {
188 request_header req;
189 char key[keylen];
190 } reqdata;
191 size_t real_sizeof_reqdata = sizeof (request_header) + keylen;
192
193 int sock = open_socket ();
194 if (sock < 0)
195 goto out;
196
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))
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. */
214 # define resdata reqdata.key
215 struct iovec iov[1];
216 iov[0].iov_base = resdata;
217 iov[0].iov_len = keylen;
218
219 union
220 {
221 struct cmsghdr hdr;
222 char bytes[CMSG_SPACE (sizeof (int))];
223 } buf;
224 struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1,
225 .msg_control = buf.bytes,
226 .msg_controllen = sizeof (buf) };
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
233 /* This access is well-aligned since BUF is correctly aligned for an
234 int and CMSG_DATA preserves this alignment. */
235 *(int *) CMSG_DATA (cmsg) = -1;
236
237 msg.msg_controllen = cmsg->cmsg_len;
238
239 if (wait_on_socket (sock) <= 0)
240 goto out_close2;
241
242 if (__builtin_expect (TEMP_FAILURE_RETRY (__recvmsg (sock, &msg, 0))
243 != keylen, 0))
244 goto out_close2;
245
246 if (__builtin_expect (CMSG_FIRSTHDR (&msg) == NULL
247 || (CMSG_FIRSTHDR (&msg)->cmsg_len
248 != CMSG_LEN (sizeof (int))), 0))
249 goto out_close2;
250
251 mapfd = *(int *) CMSG_DATA (cmsg);
252
253 struct stat64 st;
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))
257 goto out_close;
258
259 struct database_pers_head head;
260 if (__builtin_expect (TEMP_FAILURE_RETRY (__pread (mapfd, &head,
261 sizeof (head), 0))
262 != sizeof (head), 0))
263 goto out_close;
264
265 if (__builtin_expect (head.version != DB_VERSION, 0)
266 || __builtin_expect (head.header_size != sizeof (head), 0)
267 /* This really should not happen but who knows, maybe the update
268 thread got stuck. */
269 || __builtin_expect (! head.nscd_certainly_running
270 && head.timestamp + MAPPING_TIMEOUT < time (NULL),
271 0))
272 goto out_close;
273
274 size_t size = (sizeof (head) + roundup (head.module * sizeof (ref_t), ALIGN)
275 + head.data_size);
276
277 if (__builtin_expect (st.st_size < size, 0))
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);
282 if (__builtin_expect (mapping != MAP_FAILED, 1))
283 {
284 /* Allocate a record for the mapping. */
285 struct mapped_database *newp = malloc (sizeof (*newp));
286 if (newp == NULL)
287 {
288 /* Ugh, after all we went through the memory allocation failed. */
289 __munmap (mapping, size);
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;
297 newp->datasize = head.data_size;
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
322 struct mapped_database *
323 __nscd_get_map_ref (request_type type, const char *name,
324 volatile struct locked_map_ptr *mapptr, int *gc_cyclep)
325 {
326 struct mapped_database *cur = mapptr->mapped;
327 if (cur == NO_MAPPING)
328 return cur;
329
330 int cnt = 0;
331 while (__builtin_expect (atomic_compare_and_exchange_val_acq (&mapptr->lock,
332 1, 0) != 0, 0))
333 {
334 // XXX Best number of rounds?
335 if (__builtin_expect (++cnt > 5, 0))
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
347 || (cur->head->nscd_certainly_running == 0
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);
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
369 /* Don't return const struct datahead *, as eventhough the record
370 is normally constant, it can change arbitrarily during nscd
371 garbage collection. */
372 struct datahead *
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;
377 size_t datasize = mapped->datasize;
378
379 ref_t work = mapped->head->array[hash];
380 while (work != ENDREF && work + sizeof (struct hashentry) <= datasize)
381 {
382 struct hashentry *here = (struct hashentry *) (mapped->data + work);
383
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)
400 {
401 /* We found the entry. Increment the appropriate counter. */
402 struct datahead *dh
403 = (struct datahead *) (mapped->data + here->packet);
404
405 #ifndef _STRING_ARCH_unaligned
406 if ((uintptr_t) dh & (__alignof__ (*dh) - 1))
407 return NULL;
408 #endif
409
410 /* See whether we must ignore the entry or whether something
411 is wrong because garbage collection is in progress. */
412 if (dh->usable && here->packet + dh->allocsize <= datasize)
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. */
424 int
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));
445 if (nbytes == (ssize_t) (sizeof (request_header) + keylen)
446 /* Wait for data. */
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;
452 }
453
454 close_not_cancel_no_status (sock);
455 }
456
457 __set_errno (saved_errno);
458
459 return -1;
460 }