* nscd/nscd-client.h (__nscd_cache_search): Remove const qualifier
[kopensolaris-gnu/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
192   int sock = open_socket ();
193   if (sock < 0)
194     goto out;
195
196   reqdata.req.version = NSCD_VERSION;
197   reqdata.req.type = type;
198   reqdata.req.key_len = keylen;
199   memcpy (reqdata.key, key, keylen);
200
201 # ifndef MSG_NOSIGNAL
202 #  define MSG_NOSIGNAL 0
203 # endif
204   if (__builtin_expect (TEMP_FAILURE_RETRY (__send (sock, &reqdata,
205                                                     sizeof (reqdata),
206                                                     MSG_NOSIGNAL))
207                         != sizeof (reqdata), 0))
208     /* We cannot even write the request.  */
209     goto out_close2;
210
211   /* Room for the data sent along with the file descriptor.  We expect
212      the key name back.  */
213 # define resdata reqdata.key
214   struct iovec iov[1];
215   iov[0].iov_base = resdata;
216   iov[0].iov_len = keylen;
217
218   union
219   {
220     struct cmsghdr hdr;
221     char bytes[CMSG_SPACE (sizeof (int))];
222   } buf;
223   struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1,
224                         .msg_control = buf.bytes,
225                         .msg_controllen = sizeof (buf) };
226   struct cmsghdr *cmsg = CMSG_FIRSTHDR (&msg);
227
228   cmsg->cmsg_level = SOL_SOCKET;
229   cmsg->cmsg_type = SCM_RIGHTS;
230   cmsg->cmsg_len = CMSG_LEN (sizeof (int));
231
232   /* This access is well-aligned since BUF is correctly aligned for an
233      int and CMSG_DATA preserves this alignment.  */
234   *(int *) CMSG_DATA (cmsg) = -1;
235
236   msg.msg_controllen = cmsg->cmsg_len;
237
238   if (wait_on_socket (sock) <= 0)
239     goto out_close2;
240
241   if (__builtin_expect (TEMP_FAILURE_RETRY (__recvmsg (sock, &msg, 0))
242                         != keylen, 0))
243     goto out_close2;
244
245   mapfd = *(int *) CMSG_DATA (cmsg);
246
247   if (__builtin_expect (CMSG_FIRSTHDR (&msg)->cmsg_len
248                         != CMSG_LEN (sizeof (int)), 0))
249     goto out_close;
250
251   struct stat64 st;
252   if (__builtin_expect (strcmp (resdata, key) != 0, 0)
253       || __builtin_expect (fstat64 (mapfd, &st) != 0, 0)
254       || __builtin_expect (st.st_size < sizeof (struct database_pers_head), 0))
255     goto out_close;
256
257   struct database_pers_head head;
258   if (__builtin_expect (TEMP_FAILURE_RETRY (__pread (mapfd, &head,
259                                                      sizeof (head), 0))
260                         != sizeof (head), 0))
261     goto out_close;
262
263   if (__builtin_expect (head.version != DB_VERSION, 0)
264       || __builtin_expect (head.header_size != sizeof (head), 0)
265       /* This really should not happen but who knows, maybe the update
266          thread got stuck.  */
267       || __builtin_expect (! head.nscd_certainly_running
268                            && head.timestamp + MAPPING_TIMEOUT < time (NULL),
269                            0))
270     goto out_close;
271
272   size_t size = (sizeof (head) + roundup (head.module * sizeof (ref_t), ALIGN)
273                  + head.data_size);
274
275   if (__builtin_expect (st.st_size < size, 0))
276     goto out_close;
277
278   /* The file is large enough, map it now.  */
279   void *mapping = __mmap (NULL, size, PROT_READ, MAP_SHARED, mapfd, 0);
280   if (__builtin_expect (mapping != MAP_FAILED, 1))
281     {
282       /* Allocate a record for the mapping.  */
283       struct mapped_database *newp = malloc (sizeof (*newp));
284       if (newp == NULL)
285         {
286           /* Ugh, after all we went through the memory allocation failed.  */
287           __munmap (mapping, size);
288           goto out_close;
289         }
290
291       newp->head = mapping;
292       newp->data = ((char *) mapping + head.header_size
293                     + roundup (head.module * sizeof (ref_t), ALIGN));
294       newp->mapsize = size;
295       newp->datasize = head.data_size;
296       /* Set counter to 1 to show it is usable.  */
297       newp->counter = 1;
298
299       result = newp;
300     }
301
302  out_close:
303   __close (mapfd);
304  out_close2:
305   __close (sock);
306  out:
307   __set_errno (saved_errno);
308 #endif  /* SCM_RIGHTS */
309
310   struct mapped_database *oldval = *mappedp;
311   *mappedp = result;
312
313   if (oldval != NULL && atomic_decrement_val (&oldval->counter) == 0)
314     __nscd_unmap (oldval);
315
316   return result;
317 }
318
319
320 struct mapped_database *
321 __nscd_get_map_ref (request_type type, const char *name,
322                     volatile struct locked_map_ptr *mapptr, int *gc_cyclep)
323 {
324   struct mapped_database *cur = mapptr->mapped;
325   if (cur == NO_MAPPING)
326     return cur;
327
328   int cnt = 0;
329   while (__builtin_expect (atomic_compare_and_exchange_val_acq (&mapptr->lock,
330                                                                 1, 0) != 0, 0))
331     {
332       // XXX Best number of rounds?
333       if (__builtin_expect (++cnt > 5, 0))
334         return NO_MAPPING;
335
336       atomic_delay ();
337     }
338
339   cur = mapptr->mapped;
340
341   if (__builtin_expect (cur != NO_MAPPING, 1))
342     {
343       /* If not mapped or timestamp not updated, request new map.  */
344       if (cur == NULL
345           || (cur->head->nscd_certainly_running == 0
346               && cur->head->timestamp + MAPPING_TIMEOUT < time (NULL))
347           || cur->head->data_size > cur->datasize)
348         cur = get_mapping (type, name,
349                            (struct mapped_database **) &mapptr->mapped);
350
351       if (__builtin_expect (cur != NO_MAPPING, 1))
352         {
353           if (__builtin_expect (((*gc_cyclep = cur->head->gc_cycle) & 1) != 0,
354                                 0))
355             cur = NO_MAPPING;
356           else
357             atomic_increment (&cur->counter);
358         }
359     }
360
361   mapptr->lock = 0;
362
363   return cur;
364 }
365
366
367 /* Don't return const struct datahead *, as eventhough the record
368    is normally constant, it can change arbitrarily during nscd
369    garbage collection.  */
370 struct datahead *
371 __nscd_cache_search (request_type type, const char *key, size_t keylen,
372                      const struct mapped_database *mapped)
373 {
374   unsigned long int hash = __nis_hash (key, keylen) % mapped->head->module;
375   size_t datasize = mapped->datasize;
376
377   ref_t work = mapped->head->array[hash];
378   while (work != ENDREF && work + sizeof (struct hashentry) <= datasize)
379     {
380       struct hashentry *here = (struct hashentry *) (mapped->data + work);
381
382 #ifndef _STRING_ARCH_unaligned
383       /* Although during garbage collection when moving struct hashentry
384          records around we first copy from old to new location and then
385          adjust pointer from previous hashentry to it, there is no barrier
386          between those memory writes.  It is very unlikely to hit it,
387          so check alignment only if a misaligned load can crash the
388          application.  */
389       if ((uintptr_t) here & (__alignof__ (*here) - 1))
390         return NULL;
391 #endif
392
393       if (type == here->type
394           && keylen == here->len
395           && here->key + keylen <= datasize
396           && memcmp (key, mapped->data + here->key, keylen) == 0
397           && here->packet + sizeof (struct datahead) <= datasize)
398         {
399           /* We found the entry.  Increment the appropriate counter.  */
400           struct datahead *dh
401             = (struct datahead *) (mapped->data + here->packet);
402
403 #ifndef _STRING_ARCH_unaligned
404           if ((uintptr_t) dh & (__alignof__ (*dh) - 1))
405             return NULL;
406 #endif
407
408           /* See whether we must ignore the entry or whether something
409              is wrong because garbage collection is in progress.  */
410           if (dh->usable && here->packet + dh->allocsize <= datasize)
411             return dh;
412         }
413
414       work = here->next;
415     }
416
417   return NULL;
418 }
419
420
421 /* Create a socket connected to a name. */
422 int
423 __nscd_open_socket (const char *key, size_t keylen, request_type type,
424                     void *response, size_t responselen)
425 {
426   int saved_errno = errno;
427
428   int sock = open_socket ();
429   if (sock >= 0)
430     {
431       request_header req;
432       req.version = NSCD_VERSION;
433       req.type = type;
434       req.key_len = keylen;
435
436       struct iovec vec[2];
437       vec[0].iov_base = &req;
438       vec[0].iov_len = sizeof (request_header);
439       vec[1].iov_base = (void *) key;
440       vec[1].iov_len = keylen;
441
442       ssize_t nbytes = TEMP_FAILURE_RETRY (__writev (sock, vec, 2));
443       if (nbytes == (ssize_t) (sizeof (request_header) + keylen)
444           /* Wait for data.  */
445           && wait_on_socket (sock) > 0)
446         {
447           nbytes = TEMP_FAILURE_RETRY (__read (sock, response, responselen));
448           if (nbytes == (ssize_t) responselen)
449             return sock;
450         }
451
452       close_not_cancel_no_status (sock);
453     }
454
455   __set_errno (saved_errno);
456
457   return -1;
458 }