update from main archive 961008
[glibc.git] / inet / getnetgrent_r.c
blobff6d5395ede9ddc33fb65cb44e6ed809f1865e31
1 /* Copyright (C) 1996 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 Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 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 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB. If
16 not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA. */
19 #include <libc-lock.h>
20 #include <netdb.h>
21 #include "netgroup.h"
22 #include "nsswitch.h"
25 /* Protect above variable against multiple uses at the same time. */
26 __libc_lock_define_initialized (static, lock)
28 /* This handle for the NSS data base is shared between all
29 set/get/endXXXent functions. */
30 static service_user *nip;
31 /* Remember the first service_entry, it's always the same. */
32 static service_user *startp;
34 /* A netgroup can consist of names of other netgroups. We have to
35 track which netgroups were read and which still have to be read. */
36 struct name_list
38 const char *name;
39 struct name_list *next;
41 struct name_list *known_groups;
42 struct name_list *needed_groups;
45 /* The lookup function for the first entry of this service. */
46 extern int __nss_netgroup_lookup (service_user **nip, const char *name,
47 void **fctp);
49 /* Set up NIP to run through the services. If ALL is zero, use NIP's
50 current location if it's not nil. Return nonzero if there are no
51 services (left). */
52 static enum nss_status
53 setup (void **fctp, const char *func_name, int all)
55 int no_more;
56 if (startp == NULL)
58 no_more = __nss_netgroup_lookup (&nip, func_name, fctp);
59 startp = no_more ? (service_user *) -1 : nip;
61 else if (startp == (service_user *) -1)
62 /* No services at all. */
63 return 1;
64 else
66 if (all || !nip)
67 /* Reset to the beginning of the service list. */
68 nip = startp;
69 /* Look up the first function. */
70 no_more = __nss_lookup (&nip, func_name, fctp);
72 return no_more;
75 /* Free used memory. */
76 static void
77 free_memory (void)
79 while (known_groups != NULL)
81 struct name_list *tmp = known_groups;
82 known_groups = known_groups->next;
83 free (tmp->name);
84 free (tmp);
87 while (needed_groups != NULL)
89 struct name_list *tmp = needed_groups;
90 needed_groups = needed_groups->next;
91 free (tmp->name);
92 free (tmp);
96 static int
97 internal_setnetgrent (const char *group)
99 enum nss_status (*fct) (const char *);
100 enum nss_status status = NSS_STATUS_UNAVAIL;
101 struct name_list *new_elem;
102 int no_more;
104 /* Cycle through all the services and run their setnetgrent functions. */
105 no_more = setup ((void **) &fct, "setnetgrent", 1);
106 while (! no_more)
108 /* Ignore status, we force check in `__nss_next'. */
109 status = (*fct) (group);
111 no_more = __nss_next (&nip, "setnetgrent", (void **) &fct, status, 0);
114 /* Add the current group to the list of known groups. */
115 new_elem = (struct name_list *) malloc (sizeof (struct name_list));
116 if (new_elem == NULL || (new_elem->name = strdup (group)) == NULL)
118 if (new_elem != NULL)
119 free (new_elem);
120 status == NSS_STATUS_UNAVAIL;
122 else
124 new_elem->next = known_groups;
125 known_groups = new_elem;
128 return status == NSS_STATUS_SUCCESS;
132 setnetgrent (const char *group)
134 int result;
136 __libc_lock_lock (lock);
138 /* Free list of all netgroup names from last run. */
139 free_memory ();
141 result = internal_setnetgrent (group);
143 __libc_lock_unlock (lock);
145 return result;
149 void
150 endnetgrent (void)
152 service_user *old_nip;
153 enum nss_status (*fct) (void);
154 int no_more;
156 __libc_lock_lock (lock);
158 /* Remember which was the last used service. */
159 old_nip = nip;
161 /* Cycle through all the services and run their setnetgrent functions. */
162 no_more = setup ((void **) &fct, "endnetgrent", 1);
163 while (! no_more)
165 /* Ignore status, we force check in `__nss_next'. */
166 (void) (*fct) ();
168 no_more = (nip == old_nip
169 || __nss_next (&nip, "endnetgrent", (void **) &fct, 0, 1));
172 /* Now free list of all netgroup names from last run. */
173 free_memory ();
175 __libc_lock_unlock (lock);
180 __getnetgrent_r (char **hostp, char **userp, char **domainp,
181 char *buffer, int buflen)
183 enum nss_status (*fct) (struct __netgrent *, char *, int);
184 struct __netgrent result;
185 int no_more;
187 /* Initialize status to return if no more functions are found. */
188 enum nss_status status = NSS_STATUS_NOTFOUND;
190 __libc_lock_lock (lock);
192 /* Run through available functions, starting with the same function last
193 run. We will repeat each function as long as it succeeds, and then go
194 on to the next service action. */
195 no_more = setup ((void **) &fct, "getnetgrent_r", 0);
196 while (! no_more)
198 status = (*fct) (&result, buffer, buflen);
200 if (status == NSS_STATUS_RETURN)
202 /* This was the last one for this group. Look at next group
203 if available. */
204 int found = 0;
205 while (needed_groups != NULL && ! found)
207 struct name_list *tmp = needed_groups;
208 needed_groups = needed_groups->next;
209 tmp->next = known_groups;
210 known_groups = tmp;
212 found = internal_setnetgrent (known_groups->name);
215 if (found)
216 continue;
218 else if (status == NSS_STATUS_SUCCESS && result.type == group_val)
220 /* The last entry was a name of another netgroup. */
221 struct name_list *namep;
223 /* Ignore if we've seen the name before. */
224 for (namep = known_groups; namep != NULL; namep = namep->next)
225 if (strcmp (result.val.group, namep->name) == 0)
226 break;
227 if (namep != NULL)
228 /* Really ignore. */
229 continue;
231 namep = (struct name_list *) malloc (sizeof (struct name_list));
232 if (namep == NULL
233 || (namep->name = strdup (result.val.group)) == NULL)
235 /* We are out of memory. */
236 if (namep != NULL)
237 free (namep);
238 status = NSS_STATUS_RETURN;
240 else
242 namep->next = needed_groups;
243 needed_groups = namep;
244 /* And get the next entry. */
245 continue;
249 no_more = __nss_next (&nip, "getnetgrent_r", (void **) &fct, status, 0);
252 if (status == NSS_STATUS_SUCCESS)
254 *hostp = result.val.triple.host;
255 *userp = result.val.triple.user;
256 *domainp = result.val.triple.domain;
259 __libc_lock_unlock (lock);
261 return status == NSS_STATUS_SUCCESS ? 1 : 0;
263 weak_alias (__getnetgrent_r, getnetgrent_r)
265 /* Test whether given (host,user,domain) triple is in NETGROUP. */
267 innetgr (const char *netgroup, const char *host, const char *user,
268 const char *domain)
270 int (*setfct) (const char *);
271 void (*endfct) (void);
272 int (*getfct) (struct __netgrent *, char *, int);
273 int result = 0;
274 int no_more;
275 struct name_list *known = NULL;
276 struct name_list *needed = NULL;
277 const char *current_group = netgroup;
278 int real_entry = 0;
280 __libc_lock_lock (lock);
282 /* Walk through the services until we found an answer or we shall
283 not work further. We can do some optimization here. Since all
284 services must provide the `setnetgrent' function we can do all
285 the work during one walk through the service list. */
286 while (1)
288 no_more = setup ((void **) &setfct, "setnetgrent", 1);
289 while (! no_more)
291 enum nss_status status;
293 /* Open netgroup. */
294 status = (*setfct) (current_group);
295 if (status == NSS_STATUS_SUCCESS
296 && __nss_lookup (&nip, "getnetgrent_r", (void **) &getfct) == 0)
298 char buffer[1024];
299 struct __netgrent entry;
301 while ((*getfct) (&entry, buffer, sizeof buffer)
302 == NSS_STATUS_SUCCESS)
304 if (entry.type == group_val)
306 /* Make sure we haven't seen the name before. */
307 struct name_list *namep;
309 for (namep = known; namep != NULL; namep = namep->next)
310 if (strcmp (entry.val.group, namep->name) == 0)
311 break;
312 if (namep == NULL
313 && strcmp (netgroup, entry.val.group) != 0)
315 namep =
316 (struct name_list *) malloc (sizeof (*namep));
317 if (namep == NULL
318 || ((namep->name = strdup (entry.val.group))
319 == NULL))
321 /* Out of memory, simply return. */
322 if (namep != NULL)
323 free (namep);
324 result = -1;
325 break;
328 namep->next = needed;
329 needed = namep;
332 else
334 real_entry = 1;
336 if ((entry.val.triple.host == NULL || host == NULL
337 || strcmp (entry.val.triple.host, host) == 0)
338 && (entry.val.triple.user == NULL || user == NULL
339 || strcmp (entry.val.triple.user, user) == 0)
340 && (entry.val.triple.domain == NULL || domain == NULL
341 || strcmp (entry.val.triple.domain, domain) == 0))
343 result = 1;
344 break;
349 if (result != 0)
350 break;
352 /* If we found one service which does know the given
353 netgroup we don't try further. */
354 status = NSS_STATUS_RETURN;
357 /* Free all resources of the service. */
358 if (__nss_lookup (&nip, "endnetgrent", (void **) &endfct) == 0)
359 (*endfct) ();
361 /* Look for the next service. */
362 no_more = __nss_next (&nip, "setnetgrent",
363 (void **) &setfct, status, 0);
366 if (result == 0 && needed != NULL)
368 struct name_list *tmp = needed;
369 needed = tmp->next;
370 tmp->next = known;
371 known = tmp;
372 current_group = known->name;
373 continue;
376 /* No way out. */
377 break;
380 __libc_lock_unlock (lock);
382 /* Free the memory. */
383 while (known != NULL)
385 struct name_list *tmp = known;
386 known = known->next;
387 free (tmp->name);
388 free (tmp);
390 while (needed != NULL)
392 struct name_list *tmp = needed;
393 needed = needed->next;
394 free (tmp->name);
395 free (tmp);
398 return result == 1;