Update to LGPL v2.1.
[jlayton/glibc.git] / inet / getnetgrent_r.c
1 /* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
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.
8
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.
13
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.  */
18
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"
26
27
28 /* Protect above variable against multiple uses at the same time.  */
29 __libc_lock_define_initialized (static, lock)
30
31 /* This handle for the NSS data base is shared between all
32    set/get/endXXXent functions.  */
33 static service_user *nip;
34
35 /* The whole information for the set/get/endnetgrent functions are
36    kept in this structure.  */
37 static struct __netgrent dataset;
38
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);
42
43
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)
49 {
50   /* Remember the first service_entry, it's always the same.  */
51   static service_user *startp;
52   int no_more;
53
54   if (startp == NULL)
55     {
56       no_more = __nss_netgroup_lookup (&nip, func_name, fctp);
57       startp = no_more ? (service_user *) -1 : nip;
58     }
59   else if (startp == (service_user *) -1)
60     /* No services at all.  */
61     return 1;
62   else
63     {
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);
69     }
70   return no_more;
71 }
72 \f
73 /* Free used memory.  */
74 static void
75 free_memory (struct __netgrent *data)
76 {
77   while (data->known_groups != NULL)
78     {
79       struct name_list *tmp = data->known_groups;
80       data->known_groups = data->known_groups->next;
81       free ((void *) tmp->name);
82       free (tmp);
83     }
84
85   while (data->needed_groups != NULL)
86     {
87       struct name_list *tmp = data->needed_groups;
88       data->needed_groups = data->needed_groups->next;
89       free ((void *) tmp->name);
90       free (tmp);
91     }
92 }
93 \f
94 static int
95 internal_function
96 __internal_setnetgrent_reuse (const char *group, struct __netgrent *datap,
97                               int *errnop)
98 {
99   enum nss_status (*fct) (const char *, struct __netgrent *);
100   enum nss_status status = NSS_STATUS_UNAVAIL;
101   struct name_list *new_elem;
102   int no_more;
103
104   /* Cycle through all the services and run their setnetgrent functions.  */
105   no_more = setup ((void **) &fct, "setnetgrent", 1);
106   while (! no_more)
107     {
108       /* Ignore status, we force check in `__nss_next'.  */
109       status = (*fct) (group, datap);
110
111       no_more = __nss_next (&nip, "setnetgrent", (void **) &fct, status, 0);
112     }
113
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)
117     {
118       if (new_elem != NULL)
119         free (new_elem);
120       *errnop = errno;
121       status = NSS_STATUS_TRYAGAIN;
122     }
123   else
124     {
125       new_elem->next = datap->known_groups;
126       datap->known_groups = new_elem;
127     }
128
129   return status == NSS_STATUS_SUCCESS;
130 }
131
132 int
133 __internal_setnetgrent (const char *group, struct __netgrent *datap)
134 {
135   /* Free list of all netgroup names from last run.  */
136   free_memory (datap);
137
138   return __internal_setnetgrent_reuse (group, datap, &errno);
139 }
140
141 int
142 setnetgrent (const char *group)
143 {
144   int result;
145
146   __libc_lock_lock (lock);
147
148   result = __internal_setnetgrent (group, &dataset);
149
150   __libc_lock_unlock (lock);
151
152   return result;
153 }
154
155
156 void
157 __internal_endnetgrent (struct __netgrent *datap)
158 {
159   service_user *old_nip;
160   enum nss_status (*fct) (struct __netgrent *);
161   int no_more;
162
163   /* Remember which was the last used service.  */
164   old_nip = nip;
165
166   /* Cycle through all the services and run their endnetgrent functions.  */
167   no_more = setup ((void **) &fct, "endnetgrent", 1);
168   while (! no_more)
169     {
170       /* Ignore status, we force check in `__nss_next'.  */
171       (void) (*fct) (datap);
172
173       no_more = (nip == old_nip
174                  || __nss_next (&nip, "endnetgrent", (void **) &fct, 0, 1));
175     }
176
177   /* Now free list of all netgroup names from last run.  */
178   free_memory (datap);
179 }
180
181
182 void
183 endnetgrent (void)
184 {
185   __libc_lock_lock (lock);
186
187   __internal_endnetgrent (&dataset);
188
189   __libc_lock_unlock (lock);
190 }
191
192
193 int
194 __internal_getnetgrent_r (char **hostp, char **userp, char **domainp,
195                           struct __netgrent *datap,
196                           char *buffer, size_t buflen, int *errnop)
197 {
198   enum nss_status (*fct) (struct __netgrent *, char *, size_t, int *);
199   int no_more;
200
201   /* Initialize status to return if no more functions are found.  */
202   enum nss_status status = NSS_STATUS_NOTFOUND;
203
204   /* Run through available functions, starting with the same function last
205      run.  We will repeat each function as long as it succeeds, and then go
206      on to the next service action.  */
207   no_more = setup ((void **) &fct, "getnetgrent_r", 0);
208   while (! no_more)
209     {
210       status = (*fct) (datap, buffer, buflen, &errno);
211
212       if (status == NSS_STATUS_RETURN)
213         {
214           /* This was the last one for this group.  Look at next group
215              if available.  */
216           int found = 0;
217           while (datap->needed_groups != NULL && ! found)
218             {
219               struct name_list *tmp = datap->needed_groups;
220               datap->needed_groups = datap->needed_groups->next;
221               tmp->next = datap->known_groups;
222               datap->known_groups = tmp;
223
224               found = __internal_setnetgrent_reuse (datap->known_groups->name,
225                                                     datap, errnop);
226             }
227
228           if (found)
229             continue;
230         }
231       else if (status == NSS_STATUS_SUCCESS && datap->type == group_val)
232         {
233           /* The last entry was a name of another netgroup.  */
234           struct name_list *namep;
235
236           /* Ignore if we've seen the name before.  */
237           for (namep = datap->known_groups; namep != NULL;
238                namep = namep->next)
239             if (strcmp (datap->val.group, namep->name) == 0)
240               break;
241           if (namep != NULL)
242             /* Really ignore.  */
243             continue;
244
245           namep = (struct name_list *) malloc (sizeof (struct name_list));
246           if (namep == NULL
247               || (namep->name = __strdup (datap->val.group)) == NULL)
248             {
249               /* We are out of memory.  */
250               if (namep != NULL)
251                 free (namep);
252               status = NSS_STATUS_RETURN;
253             }
254           else
255             {
256               namep->next = datap->needed_groups;
257               datap->needed_groups = namep;
258               /* And get the next entry.  */
259               continue;
260             }
261         }
262
263       no_more = __nss_next (&nip, "getnetgrent_r", (void **) &fct, status, 0);
264     }
265
266   if (status == NSS_STATUS_SUCCESS)
267     {
268       *hostp = (char *) datap->val.triple.host;
269       *userp = (char *) datap->val.triple.user;
270       *domainp = (char *) datap->val.triple.domain;
271     }
272
273   return status == NSS_STATUS_SUCCESS ? 1 : 0;
274 }
275
276 /* The real entry point.  */
277 int
278 __getnetgrent_r (char **hostp, char **userp, char **domainp,
279                  char *buffer, size_t buflen)
280 {
281   enum nss_status status;
282
283   __libc_lock_lock (lock);
284
285   status = __internal_getnetgrent_r (hostp, userp, domainp, &dataset,
286                                      buffer, buflen, &errno);
287
288   __libc_lock_unlock (lock);
289
290   return status;
291 }
292 weak_alias (__getnetgrent_r, getnetgrent_r)
293 \f
294 /* Test whether given (host,user,domain) triple is in NETGROUP.  */
295 int
296 innetgr (const char *netgroup, const char *host, const char *user,
297          const char *domain)
298 {
299   int (*setfct) (const char *, struct __netgrent *);
300   void (*endfct) (struct __netgrent *);
301   int (*getfct) (struct __netgrent *, char *, size_t);
302   struct name_list *known = NULL;
303   struct name_list *needed = NULL;
304   int result = 0;
305   int no_more;
306   const char *current_group = netgroup;
307   int real_entry = 0;
308
309   /* Walk through the services until we found an answer or we shall
310      not work further.  We can do some optimization here.  Since all
311      services must provide the `setnetgrent' function we can do all
312      the work during one walk through the service list.  */
313   while (1)
314     {
315       no_more = setup ((void **) &setfct, "setnetgrent", 1);
316       while (! no_more)
317         {
318           enum nss_status status;
319           struct __netgrent entry;
320
321           /* Clear the space for the netgroup data.  */
322           __bzero (&entry, sizeof (entry));
323
324           /* Open netgroup.  */
325           status = (*setfct) (current_group, &entry);
326           if (status == NSS_STATUS_SUCCESS
327               && __nss_lookup (&nip, "getnetgrent_r", (void **) &getfct) == 0)
328             {
329               char buffer[1024];
330
331               while ((*getfct) (&entry, buffer, sizeof buffer)
332                      == NSS_STATUS_SUCCESS)
333                 {
334                   if (entry.type == group_val)
335                     {
336                       /* Make sure we haven't seen the name before.  */
337                       struct name_list *namep;
338
339                       for (namep = known; namep != NULL; namep = namep->next)
340                         if (strcmp (entry.val.group, namep->name) == 0)
341                           break;
342                       if (namep == NULL
343                           && strcmp (netgroup, entry.val.group) != 0)
344                         {
345                           namep =
346                             (struct name_list *) malloc (sizeof (*namep));
347                           if (namep == NULL
348                               || ((namep->name = __strdup (entry.val.group))
349                                   == NULL))
350                             {
351                               /* Out of memory, simply return.  */
352                               if (namep != NULL)
353                                 free (namep);
354                               result = -1;
355                               break;
356                             }
357
358                           namep->next = needed;
359                           needed = namep;
360                         }
361                     }
362                   else
363                     {
364                       real_entry = 1;
365
366                       if ((entry.val.triple.host == NULL || host == NULL
367                            || __strcasecmp (entry.val.triple.host, host) == 0)
368                           && (entry.val.triple.user == NULL || user == NULL
369                               || strcmp (entry.val.triple.user, user) == 0)
370                           && (entry.val.triple.domain == NULL || domain == NULL
371                               || __strcasecmp (entry.val.triple.domain,
372                                                domain) == 0))
373                         {
374                           result = 1;
375                           break;
376                         }
377                     }
378                 }
379
380               if (result != 0)
381                 break;
382
383               /* If we found one service which does know the given
384                  netgroup we don't try further.  */
385               status = NSS_STATUS_RETURN;
386             }
387
388           /* Free all resources of the service.  */
389           if (__nss_lookup (&nip, "endnetgrent", (void **) &endfct) == 0)
390             (*endfct) (&entry);
391
392           /* Look for the next service.  */
393           no_more = __nss_next (&nip, "setnetgrent",
394                                 (void **) &setfct, status, 0);
395         }
396
397       if (result == 0 && needed != NULL)
398         {
399           struct name_list *tmp = needed;
400           needed = tmp->next;
401           tmp->next = known;
402           known = tmp;
403           current_group = known->name;
404           continue;
405         }
406
407       /* No way out.  */
408       break;
409     }
410
411   /* Free the memory.  */
412   while (known != NULL)
413     {
414       struct name_list *tmp = known;
415       known = known->next;
416       free ((void *) tmp->name);
417       free (tmp);
418     }
419   while (needed != NULL)
420     {
421       struct name_list *tmp = needed;
422       needed = needed->next;
423       free ((void *) tmp->name);
424       free (tmp);
425     }
426
427   return result == 1;
428 }