arm: fix up style in reloc fix
[glibc.git] / inet / getnetgrent_r.c
blob70cc14cd02cfbbca88aee13e83ed84fbd5f2f2dc
1 /* Copyright (C) 1996-2012 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <http://www.gnu.org/licenses/>. */
18 #include <assert.h>
19 #include <atomic.h>
20 #include <bits/libc-lock.h>
21 #include <errno.h>
22 #include <netdb.h>
23 #include <stdbool.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include "netgroup.h"
27 #include "nsswitch.h"
28 #include <sysdep.h>
29 #include <nscd/nscd_proto.h>
32 /* Protect above variable against multiple uses at the same time. */
33 __libc_lock_define_initialized (static, lock)
35 /* The whole information for the set/get/endnetgrent functions are
36 kept in this structure. */
37 static struct __netgrent dataset;
39 /* The lookup function for the first entry of this service. */
40 extern int __nss_netgroup_lookup (service_user **nipp, const char *name,
41 void **fctp) internal_function;
43 /* Set up NIP to run through the services. Return nonzero if there are no
44 services (left). */
45 static int
46 setup (void **fctp, service_user **nipp)
48 /* Remember the first service_entry, it's always the same. */
49 static bool startp_initialized;
50 static service_user *startp;
51 int no_more;
53 if (!startp_initialized)
55 /* Executing this more than once at the same time must yield the
56 same result every time. So we need no locking. */
57 no_more = __nss_netgroup_lookup (nipp, "setnetgrent", fctp);
58 startp = no_more ? (service_user *) -1 : *nipp;
59 #ifdef PTR_MANGLE
60 PTR_MANGLE (startp);
61 #endif
62 atomic_write_barrier ();
63 startp_initialized = true;
65 else
67 service_user *nip = startp;
68 #ifdef PTR_DEMANGLE
69 PTR_DEMANGLE (nip);
70 #endif
71 if (nip == (service_user *) -1)
72 /* No services at all. */
73 return 1;
75 /* Reset to the beginning of the service list. */
76 *nipp = nip;
77 /* Look up the first function. */
78 no_more = __nss_lookup (nipp, "setnetgrent", NULL, fctp);
80 return no_more;
83 /* Free used memory. */
84 static void
85 free_memory (struct __netgrent *data)
87 while (data->known_groups != NULL)
89 struct name_list *tmp = data->known_groups;
90 data->known_groups = data->known_groups->next;
91 free (tmp);
94 while (data->needed_groups != NULL)
96 struct name_list *tmp = data->needed_groups;
97 data->needed_groups = data->needed_groups->next;
98 free (tmp);
102 static void
103 endnetgrent_hook (struct __netgrent *datap)
105 enum nss_status (*endfct) (struct __netgrent *);
107 if (datap->nip == NULL || datap->nip == (service_user *) -1l)
108 return;
110 endfct = __nss_lookup_function (datap->nip, "endnetgrent");
111 if (endfct != NULL)
112 (void) (*endfct) (datap);
113 datap->nip = NULL;
116 static int
117 internal_function
118 __internal_setnetgrent_reuse (const char *group, struct __netgrent *datap,
119 int *errnop)
121 union
123 enum nss_status (*f) (const char *, struct __netgrent *);
124 void *ptr;
125 } fct;
126 enum nss_status status = NSS_STATUS_UNAVAIL;
127 struct name_list *new_elem;
129 /* Free data from previous service. */
130 endnetgrent_hook (datap);
132 /* Cycle through all the services and run their setnetgrent functions. */
133 int no_more = setup (&fct.ptr, &datap->nip);
134 while (! no_more)
136 assert (datap->data == NULL);
138 /* Ignore status, we force check in `__nss_next2'. */
139 status = DL_CALL_FCT (*fct.f, (group, datap));
141 service_user *old_nip = datap->nip;
142 no_more = __nss_next2 (&datap->nip, "setnetgrent", NULL, &fct.ptr,
143 status, 0);
145 if (status == NSS_STATUS_SUCCESS && ! no_more)
147 enum nss_status (*endfct) (struct __netgrent *);
149 endfct = __nss_lookup_function (old_nip, "endnetgrent");
150 if (endfct != NULL)
151 (void) DL_CALL_FCT (*endfct, (datap));
155 /* Add the current group to the list of known groups. */
156 size_t group_len = strlen (group) + 1;
157 new_elem = (struct name_list *) malloc (sizeof (struct name_list)
158 + group_len);
159 if (new_elem == NULL)
161 *errnop = errno;
162 status = NSS_STATUS_TRYAGAIN;
164 else
166 new_elem->next = datap->known_groups;
167 memcpy (new_elem->name, group, group_len);
168 datap->known_groups = new_elem;
171 return status == NSS_STATUS_SUCCESS;
174 int internal_setnetgrent (const char *group, struct __netgrent *datap);
175 libc_hidden_proto (internal_setnetgrent)
178 internal_setnetgrent (const char *group, struct __netgrent *datap)
180 /* Free list of all netgroup names from last run. */
181 free_memory (datap);
183 return __internal_setnetgrent_reuse (group, datap, &errno);
185 libc_hidden_def (internal_setnetgrent)
186 strong_alias (internal_setnetgrent, __internal_setnetgrent)
189 setnetgrent (const char *group)
191 int result;
193 __libc_lock_lock (lock);
195 if (__nss_not_use_nscd_netgroup > 0
196 && ++__nss_not_use_nscd_netgroup > NSS_NSCD_RETRY)
197 __nss_not_use_nscd_netgroup = 0;
199 if (!__nss_not_use_nscd_netgroup
200 && !__nss_database_custom[NSS_DBSIDX_netgroup])
202 result = __nscd_setnetgrent (group, &dataset);
203 if (result >= 0)
204 goto out;
207 result = internal_setnetgrent (group, &dataset);
209 out:
210 __libc_lock_unlock (lock);
212 return result;
215 void internal_endnetgrent (struct __netgrent *datap);
216 libc_hidden_proto (internal_endnetgrent)
218 void
219 internal_endnetgrent (struct __netgrent *datap)
221 endnetgrent_hook (datap);
222 /* Now free list of all netgroup names from last run. */
223 free_memory (datap);
225 libc_hidden_def (internal_endnetgrent)
226 strong_alias (internal_endnetgrent, __internal_endnetgrent)
229 void
230 endnetgrent (void)
232 __libc_lock_lock (lock);
234 internal_endnetgrent (&dataset);
236 __libc_lock_unlock (lock);
240 int internal_getnetgrent_r (char **hostp, char **userp, char **domainp,
241 struct __netgrent *datap,
242 char *buffer, size_t buflen, int *errnop);
243 libc_hidden_proto (internal_getnetgrent_r)
246 static enum nss_status
247 nscd_getnetgrent (struct __netgrent *datap, char *buffer, size_t buflen,
248 int *errnop)
250 if (datap->cursor >= datap->data + datap->data_size)
251 return NSS_STATUS_UNAVAIL;
253 datap->type = triple_val;
254 datap->val.triple.host = datap->cursor;
255 datap->cursor = (char *) __rawmemchr (datap->cursor, '\0') + 1;
256 datap->val.triple.user = datap->cursor;
257 datap->cursor = (char *) __rawmemchr (datap->cursor, '\0') + 1;
258 datap->val.triple.domain = datap->cursor;
259 datap->cursor = (char *) __rawmemchr (datap->cursor, '\0') + 1;
261 return NSS_STATUS_SUCCESS;
266 internal_getnetgrent_r (char **hostp, char **userp, char **domainp,
267 struct __netgrent *datap,
268 char *buffer, size_t buflen, int *errnop)
270 enum nss_status (*fct) (struct __netgrent *, char *, size_t, int *);
272 /* Initialize status to return if no more functions are found. */
273 enum nss_status status = NSS_STATUS_NOTFOUND;
275 /* Run through available functions, starting with the same function last
276 run. We will repeat each function as long as it succeeds, and then go
277 on to the next service action. */
278 int no_more = datap->nip == NULL;
279 if (! no_more)
281 if (datap->nip == (service_user *) -1l)
282 fct = nscd_getnetgrent;
283 else
285 fct = __nss_lookup_function (datap->nip, "getnetgrent_r");
286 no_more = fct == NULL;
290 while (! no_more)
292 status = DL_CALL_FCT (*fct, (datap, buffer, buflen, &errno));
294 if (status == NSS_STATUS_RETURN)
296 /* This was the last one for this group. Look at next group
297 if available. */
298 int found = 0;
299 while (datap->needed_groups != NULL && ! found)
301 struct name_list *tmp = datap->needed_groups;
302 datap->needed_groups = datap->needed_groups->next;
303 tmp->next = datap->known_groups;
304 datap->known_groups = tmp;
306 found = __internal_setnetgrent_reuse (datap->known_groups->name,
307 datap, errnop);
310 if (found && datap->nip != NULL)
312 fct = __nss_lookup_function (datap->nip, "getnetgrent_r");
313 if (fct != NULL)
314 continue;
317 else if (status == NSS_STATUS_SUCCESS && datap->type == group_val)
319 /* The last entry was a name of another netgroup. */
320 struct name_list *namep;
322 /* Ignore if we've seen the name before. */
323 for (namep = datap->known_groups; namep != NULL;
324 namep = namep->next)
325 if (strcmp (datap->val.group, namep->name) == 0)
326 break;
327 if (namep == NULL)
328 for (namep = datap->needed_groups; namep != NULL;
329 namep = namep->next)
330 if (strcmp (datap->val.group, namep->name) == 0)
331 break;
332 if (namep != NULL)
333 /* Really ignore. */
334 continue;
336 size_t group_len = strlen (datap->val.group) + 1;
337 namep = (struct name_list *) malloc (sizeof (struct name_list)
338 + group_len);
339 if (namep == NULL)
340 /* We are out of memory. */
341 status = NSS_STATUS_RETURN;
342 else
344 namep->next = datap->needed_groups;
345 memcpy (namep->name, datap->val.group, group_len);
346 datap->needed_groups = namep;
347 /* And get the next entry. */
348 continue;
352 break;
355 if (status == NSS_STATUS_SUCCESS)
357 *hostp = (char *) datap->val.triple.host;
358 *userp = (char *) datap->val.triple.user;
359 *domainp = (char *) datap->val.triple.domain;
362 return status == NSS_STATUS_SUCCESS ? 1 : 0;
364 libc_hidden_def (internal_getnetgrent_r)
365 strong_alias (internal_getnetgrent_r, __internal_getnetgrent_r)
367 /* The real entry point. */
369 __getnetgrent_r (char **hostp, char **userp, char **domainp,
370 char *buffer, size_t buflen)
372 enum nss_status status;
374 __libc_lock_lock (lock);
376 status = internal_getnetgrent_r (hostp, userp, domainp, &dataset,
377 buffer, buflen, &errno);
379 __libc_lock_unlock (lock);
381 return status;
383 weak_alias (__getnetgrent_r, getnetgrent_r)
385 /* Test whether given (host,user,domain) triple is in NETGROUP. */
387 innetgr (const char *netgroup, const char *host, const char *user,
388 const char *domain)
390 if (__nss_not_use_nscd_netgroup > 0
391 && ++__nss_not_use_nscd_netgroup > NSS_NSCD_RETRY)
392 __nss_not_use_nscd_netgroup = 0;
394 if (!__nss_not_use_nscd_netgroup
395 && !__nss_database_custom[NSS_DBSIDX_netgroup])
397 int result = __nscd_innetgr (netgroup, host, user, domain);
398 if (result >= 0)
399 return result;
402 union
404 enum nss_status (*f) (const char *, struct __netgrent *);
405 void *ptr;
406 } setfct;
407 void (*endfct) (struct __netgrent *);
408 int (*getfct) (struct __netgrent *, char *, size_t, int *);
409 struct __netgrent entry;
410 int result = 0;
411 const char *current_group = netgroup;
413 memset (&entry, '\0', sizeof (entry));
415 /* Walk through the services until we found an answer or we shall
416 not work further. We can do some optimization here. Since all
417 services must provide the `setnetgrent' function we can do all
418 the work during one walk through the service list. */
419 while (1)
421 int no_more = setup (&setfct.ptr, &entry.nip);
422 while (! no_more)
424 assert (entry.data == NULL);
426 /* Open netgroup. */
427 enum nss_status status = DL_CALL_FCT (*setfct.f,
428 (current_group, &entry));
430 if (status == NSS_STATUS_SUCCESS
431 && (getfct = __nss_lookup_function (entry.nip, "getnetgrent_r"))
432 != NULL)
434 char buffer[1024];
436 while (DL_CALL_FCT (*getfct,
437 (&entry, buffer, sizeof buffer, &errno))
438 == NSS_STATUS_SUCCESS)
440 if (entry.type == group_val)
442 /* Make sure we haven't seen the name before. */
443 struct name_list *namep;
445 for (namep = entry.known_groups; namep != NULL;
446 namep = namep->next)
447 if (strcmp (entry.val.group, namep->name) == 0)
448 break;
449 if (namep == NULL)
450 for (namep = entry.needed_groups; namep != NULL;
451 namep = namep->next)
452 if (strcmp (entry.val.group, namep->name) == 0)
453 break;
454 if (namep == NULL
455 && strcmp (netgroup, entry.val.group) != 0)
457 size_t group_len = strlen (entry.val.group) + 1;
458 namep =
459 (struct name_list *) malloc (sizeof (*namep)
460 + group_len);
461 if (namep == NULL)
463 /* Out of memory, simply return. */
464 result = -1;
465 break;
468 namep->next = entry.needed_groups;
469 memcpy (namep->name, entry.val.group, group_len);
470 entry.needed_groups = namep;
473 else
475 if ((entry.val.triple.host == NULL || host == NULL
476 || __strcasecmp (entry.val.triple.host, host) == 0)
477 && (entry.val.triple.user == NULL || user == NULL
478 || strcmp (entry.val.triple.user, user) == 0)
479 && (entry.val.triple.domain == NULL || domain == NULL
480 || __strcasecmp (entry.val.triple.domain,
481 domain) == 0))
483 result = 1;
484 break;
489 /* If we found one service which does know the given
490 netgroup we don't try further. */
491 status = NSS_STATUS_RETURN;
494 /* Free all resources of the service. */
495 endfct = __nss_lookup_function (entry.nip, "endnetgrent");
496 if (endfct != NULL)
497 DL_CALL_FCT (*endfct, (&entry));
499 if (result != 0)
500 break;
502 /* Look for the next service. */
503 no_more = __nss_next2 (&entry.nip, "setnetgrent", NULL,
504 &setfct.ptr, status, 0);
507 if (result == 0 && entry.needed_groups != NULL)
509 struct name_list *tmp = entry.needed_groups;
510 entry.needed_groups = tmp->next;
511 tmp->next = entry.known_groups;
512 entry.known_groups = tmp;
513 current_group = tmp->name;
514 continue;
517 /* No way out. */
518 break;
521 /* Free the memory. */
522 free_memory (&entry);
524 return result == 1;
526 libc_hidden_def (innetgr)