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