2003-09-17 Uwe Reimann <Uwe_Reimann@gmx.net> Hans-Peter Nilsson <hp@axis.com>
[glibc.git] / inet / getnetgrent_r.c
blobdee90ad38993109e05ae11e51ae95a8da1b50c21
1 /* Copyright (C) 1996, 1997, 1998, 1999, 2002 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, write to the Free
16 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17 02111-1307 USA. */
19 #include <bits/libc-lock.h>
20 #include <errno.h>
21 #include <netdb.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include "netgroup.h"
25 #include "nsswitch.h"
28 /* Protect above variable against multiple uses at the same time. */
29 __libc_lock_define_initialized (static, lock)
31 /* This handle for the NSS data base is shared between all
32 set/get/endXXXent functions. */
33 static service_user *nip;
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 **nip, const char *name,
41 void **fctp) internal_function;
44 /* Set up NIP to run through the services. If ALL is zero, use NIP's
45 current location if it's not nil. Return nonzero if there are no
46 services (left). */
47 static enum nss_status
48 setup (void **fctp, const char *func_name, int all)
50 /* Remember the first service_entry, it's always the same. */
51 static service_user *startp;
52 int no_more;
54 if (startp == NULL)
56 no_more = __nss_netgroup_lookup (&nip, func_name, fctp);
57 startp = no_more ? (service_user *) -1 : nip;
59 else if (startp == (service_user *) -1)
60 /* No services at all. */
61 return 1;
62 else
64 if (all || !nip)
65 /* Reset to the beginning of the service list. */
66 nip = startp;
67 /* Look up the first function. */
68 no_more = __nss_lookup (&nip, func_name, fctp);
70 return no_more;
73 /* Free used memory. */
74 static void
75 free_memory (struct __netgrent *data)
77 while (data->known_groups != NULL)
79 struct name_list *tmp = data->known_groups;
80 data->known_groups = data->known_groups->next;
81 free ((void *) tmp->name);
82 free (tmp);
85 while (data->needed_groups != NULL)
87 struct name_list *tmp = data->needed_groups;
88 data->needed_groups = data->needed_groups->next;
89 free ((void *) tmp->name);
90 free (tmp);
94 static int
95 internal_function
96 __internal_setnetgrent_reuse (const char *group, struct __netgrent *datap,
97 int *errnop)
99 union
101 enum nss_status (*f) (const char *, struct __netgrent *);
102 void *ptr;
103 } fct;
104 enum nss_status status = NSS_STATUS_UNAVAIL;
105 struct name_list *new_elem;
106 int no_more;
108 /* Cycle through all the services and run their setnetgrent functions. */
109 no_more = setup (&fct.ptr, "setnetgrent", 1);
110 while (! no_more)
112 /* Ignore status, we force check in `__nss_next'. */
113 status = (*fct.f) (group, datap);
115 no_more = __nss_next (&nip, "setnetgrent", &fct.ptr, status, 0);
118 /* Add the current group to the list of known groups. */
119 new_elem = (struct name_list *) malloc (sizeof (struct name_list));
120 if (new_elem == NULL || (new_elem->name = __strdup (group)) == NULL)
122 if (new_elem != NULL)
123 free (new_elem);
124 *errnop = errno;
125 status = NSS_STATUS_TRYAGAIN;
127 else
129 new_elem->next = datap->known_groups;
130 datap->known_groups = new_elem;
133 return status == NSS_STATUS_SUCCESS;
136 static int
137 internal_setnetgrent (const char *group, struct __netgrent *datap)
139 /* Free list of all netgroup names from last run. */
140 free_memory (datap);
142 return __internal_setnetgrent_reuse (group, datap, &errno);
144 strong_alias (internal_setnetgrent, __internal_setnetgrent)
147 setnetgrent (const char *group)
149 int result;
151 __libc_lock_lock (lock);
153 result = internal_setnetgrent (group, &dataset);
155 __libc_lock_unlock (lock);
157 return result;
161 static void
162 internal_endnetgrent (struct __netgrent *datap)
164 service_user *old_nip;
165 union
167 enum nss_status (*f) (struct __netgrent *);
168 void *ptr;
169 } fct;
170 int no_more;
172 /* Remember which was the last used service. */
173 old_nip = nip;
175 /* Cycle through all the services and run their endnetgrent functions. */
176 no_more = setup (&fct.ptr, "endnetgrent", 1);
177 while (! no_more)
179 /* Ignore status, we force check in `__nss_next'. */
180 (void) (*fct.f) (datap);
182 no_more = (nip == old_nip
183 || __nss_next (&nip, "endnetgrent", &fct.ptr, 0, 1));
186 /* Now free list of all netgroup names from last run. */
187 free_memory (datap);
189 strong_alias (internal_endnetgrent, __internal_endnetgrent)
192 void
193 endnetgrent (void)
195 __libc_lock_lock (lock);
197 internal_endnetgrent (&dataset);
199 __libc_lock_unlock (lock);
203 static int
204 internal_getnetgrent_r (char **hostp, char **userp, char **domainp,
205 struct __netgrent *datap,
206 char *buffer, size_t buflen, int *errnop)
208 union
210 enum nss_status (*f) (struct __netgrent *, char *, size_t, int *);
211 void *ptr;
212 } fct;
213 int no_more;
215 /* Initialize status to return if no more functions are found. */
216 enum nss_status status = NSS_STATUS_NOTFOUND;
218 /* Run through available functions, starting with the same function last
219 run. We will repeat each function as long as it succeeds, and then go
220 on to the next service action. */
221 no_more = setup (&fct.ptr, "getnetgrent_r", 0);
222 while (! no_more)
224 status = (*fct.f) (datap, buffer, buflen, &errno);
226 if (status == NSS_STATUS_RETURN)
228 /* This was the last one for this group. Look at next group
229 if available. */
230 int found = 0;
231 while (datap->needed_groups != NULL && ! found)
233 struct name_list *tmp = datap->needed_groups;
234 datap->needed_groups = datap->needed_groups->next;
235 tmp->next = datap->known_groups;
236 datap->known_groups = tmp;
238 found = __internal_setnetgrent_reuse (datap->known_groups->name,
239 datap, errnop);
242 if (found)
243 continue;
245 else if (status == NSS_STATUS_SUCCESS && datap->type == group_val)
247 /* The last entry was a name of another netgroup. */
248 struct name_list *namep;
250 /* Ignore if we've seen the name before. */
251 for (namep = datap->known_groups; namep != NULL;
252 namep = namep->next)
253 if (strcmp (datap->val.group, namep->name) == 0)
254 break;
255 if (namep != NULL)
256 /* Really ignore. */
257 continue;
259 namep = (struct name_list *) malloc (sizeof (struct name_list));
260 if (namep == NULL
261 || (namep->name = __strdup (datap->val.group)) == NULL)
263 /* We are out of memory. */
264 if (namep != NULL)
265 free (namep);
266 status = NSS_STATUS_RETURN;
268 else
270 namep->next = datap->needed_groups;
271 datap->needed_groups = namep;
272 /* And get the next entry. */
273 continue;
277 no_more = __nss_next (&nip, "getnetgrent_r", &fct.ptr, status, 0);
280 if (status == NSS_STATUS_SUCCESS)
282 *hostp = (char *) datap->val.triple.host;
283 *userp = (char *) datap->val.triple.user;
284 *domainp = (char *) datap->val.triple.domain;
287 return status == NSS_STATUS_SUCCESS ? 1 : 0;
289 strong_alias (internal_getnetgrent_r, __internal_getnetgrent_r)
291 /* The real entry point. */
293 __getnetgrent_r (char **hostp, char **userp, char **domainp,
294 char *buffer, size_t buflen)
296 enum nss_status status;
298 __libc_lock_lock (lock);
300 status = internal_getnetgrent_r (hostp, userp, domainp, &dataset,
301 buffer, buflen, &errno);
303 __libc_lock_unlock (lock);
305 return status;
307 weak_alias (__getnetgrent_r, getnetgrent_r)
309 /* Test whether given (host,user,domain) triple is in NETGROUP. */
311 innetgr (const char *netgroup, const char *host, const char *user,
312 const char *domain)
314 union
316 int (*f) (const char *, struct __netgrent *);
317 void *ptr;
318 } setfct;
319 union
321 void (*f) (struct __netgrent *);
322 void *ptr;
323 } endfct;
324 union
326 int (*f) (struct __netgrent *, char *, size_t, int *);
327 void *ptr;
328 } getfct;
329 struct name_list *known = NULL;
330 struct name_list *needed = NULL;
331 int result = 0;
332 int no_more;
333 const char *current_group = netgroup;
334 int real_entry = 0;
336 /* Walk through the services until we found an answer or we shall
337 not work further. We can do some optimization here. Since all
338 services must provide the `setnetgrent' function we can do all
339 the work during one walk through the service list. */
340 while (1)
342 no_more = setup (&setfct.ptr, "setnetgrent", 1);
343 while (! no_more)
345 enum nss_status status;
346 struct __netgrent entry;
348 /* Clear the space for the netgroup data. */
349 __bzero (&entry, sizeof (entry));
351 /* Open netgroup. */
352 status = (*setfct.f) (current_group, &entry);
353 if (status == NSS_STATUS_SUCCESS
354 && __nss_lookup (&nip, "getnetgrent_r", &getfct.ptr) == 0)
356 char buffer[1024];
358 while ((*getfct.f) (&entry, buffer, sizeof buffer, &errno)
359 == NSS_STATUS_SUCCESS)
361 if (entry.type == group_val)
363 /* Make sure we haven't seen the name before. */
364 struct name_list *namep;
366 for (namep = known; namep != NULL; namep = namep->next)
367 if (strcmp (entry.val.group, namep->name) == 0)
368 break;
369 if (namep == NULL
370 && strcmp (netgroup, entry.val.group) != 0)
372 namep =
373 (struct name_list *) malloc (sizeof (*namep));
374 if (namep == NULL
375 || ((namep->name = __strdup (entry.val.group))
376 == NULL))
378 /* Out of memory, simply return. */
379 if (namep != NULL)
380 free (namep);
381 result = -1;
382 break;
385 namep->next = needed;
386 needed = namep;
389 else
391 real_entry = 1;
393 if ((entry.val.triple.host == NULL || host == NULL
394 || __strcasecmp (entry.val.triple.host, host) == 0)
395 && (entry.val.triple.user == NULL || user == NULL
396 || strcmp (entry.val.triple.user, user) == 0)
397 && (entry.val.triple.domain == NULL || domain == NULL
398 || __strcasecmp (entry.val.triple.domain,
399 domain) == 0))
401 result = 1;
402 break;
407 if (result != 0)
408 break;
410 /* If we found one service which does know the given
411 netgroup we don't try further. */
412 status = NSS_STATUS_RETURN;
415 /* Free all resources of the service. */
416 if (__nss_lookup (&nip, "endnetgrent", &endfct.ptr) == 0)
417 (*endfct.f) (&entry);
419 /* Look for the next service. */
420 no_more = __nss_next (&nip, "setnetgrent",
421 &setfct.ptr, status, 0);
424 if (result == 0 && needed != NULL)
426 struct name_list *tmp = needed;
427 needed = tmp->next;
428 tmp->next = known;
429 known = tmp;
430 current_group = known->name;
431 continue;
434 /* No way out. */
435 break;
438 /* Free the memory. */
439 while (known != NULL)
441 struct name_list *tmp = known;
442 known = known->next;
443 free ((void *) tmp->name);
444 free (tmp);
446 while (needed != NULL)
448 struct name_list *tmp = needed;
449 needed = needed->next;
450 free ((void *) tmp->name);
451 free (tmp);
454 return result == 1;
456 libc_hidden_def (innetgr)