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