update from main archive 961030
[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
16 not, 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 "netgroup.h"
22 #include "nsswitch.h"
23
24
25 /* Protect above variable against multiple uses at the same time.  */
26 __libc_lock_define_initialized (static, lock)
27
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;
33
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
37 {
38   const char *name;
39   struct name_list *next;
40 };
41 struct name_list *known_groups;
42 struct name_list *needed_groups;
43
44
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);
48
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)
54 {
55   int no_more;
56   if (startp == NULL)
57     {
58       no_more = __nss_netgroup_lookup (&nip, func_name, fctp);
59       startp = no_more ? (service_user *) -1 : nip;
60     }
61   else if (startp == (service_user *) -1)
62     /* No services at all.  */
63     return 1;
64   else
65     {
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);
71     }
72   return no_more;
73 }
74 \f
75 /* Free used memory.  */
76 static void
77 free_memory (void)
78 {
79   while (known_groups != NULL)
80     {
81       struct name_list *tmp = known_groups;
82       known_groups = known_groups->next;
83       free (tmp->name);
84       free (tmp);
85     }
86
87   while (needed_groups != NULL)
88     {
89       struct name_list *tmp = needed_groups;
90       needed_groups = needed_groups->next;
91       free (tmp->name);
92       free (tmp);
93     }
94 }
95 \f
96 static int
97 internal_setnetgrent (const char *group)
98 {
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;
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);
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       status == NSS_STATUS_UNAVAIL;
121     }
122   else
123     {
124       new_elem->next = known_groups;
125       known_groups = new_elem;
126     }
127
128   return status == NSS_STATUS_SUCCESS;
129 }
130
131 int
132 setnetgrent (const char *group)
133 {
134   int result;
135
136   __libc_lock_lock (lock);
137
138   /* Free list of all netgroup names from last run.  */
139   free_memory ();
140
141   result = internal_setnetgrent (group);
142
143   __libc_lock_unlock (lock);
144
145   return result;
146 }
147
148
149 void
150 endnetgrent (void)
151 {
152   service_user *old_nip;
153   enum nss_status (*fct) (void);
154   int no_more;
155
156   __libc_lock_lock (lock);
157
158   /* Remember which was the last used service.  */
159   old_nip = nip;
160
161   /* Cycle through all the services and run their setnetgrent functions.  */
162   no_more = setup ((void **) &fct, "endnetgrent", 1);
163   while (! no_more)
164     {
165       /* Ignore status, we force check in `__nss_next'.  */
166       (void) (*fct) ();
167
168       no_more = (nip == old_nip
169                  || __nss_next (&nip, "endnetgrent", (void **) &fct, 0, 1));
170     }
171
172   /* Now free list of all netgroup names from last run.  */
173   free_memory ();
174
175   __libc_lock_unlock (lock);
176 }
177
178
179 int
180 __getnetgrent_r (char **hostp, char **userp, char **domainp,
181                  char *buffer, size_t buflen)
182 {
183   enum nss_status (*fct) (struct __netgrent *, char *, int);
184   struct __netgrent result;
185   int no_more;
186
187   /* Initialize status to return if no more functions are found.  */
188   enum nss_status status = NSS_STATUS_NOTFOUND;
189
190   __libc_lock_lock (lock);
191
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)
197     {
198       status = (*fct) (&result, buffer, buflen);
199
200       if (status == NSS_STATUS_RETURN)
201         {
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)
206             {
207               struct name_list *tmp = needed_groups;
208               needed_groups = needed_groups->next;
209               tmp->next = known_groups;
210               known_groups = tmp;
211
212               found = internal_setnetgrent (known_groups->name);
213             }
214
215           if (found)
216             continue;
217         }
218       else if (status == NSS_STATUS_SUCCESS && result.type == group_val)
219         {
220           /* The last entry was a name of another netgroup.  */
221           struct name_list *namep;
222
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;
230
231           namep = (struct name_list *) malloc (sizeof (struct name_list));
232           if (namep == NULL
233               || (namep->name = __strdup (result.val.group)) == NULL)
234             {
235               /* We are out of memory.  */
236               if (namep != NULL)
237                 free (namep);
238               status = NSS_STATUS_RETURN;
239             }
240           else
241             {
242               namep->next = needed_groups;
243               needed_groups = namep;
244               /* And get the next entry.  */
245               continue;
246             }
247         }
248
249       no_more = __nss_next (&nip, "getnetgrent_r", (void **) &fct, status, 0);
250     }
251
252   if (status == NSS_STATUS_SUCCESS)
253     {
254       *hostp = result.val.triple.host;
255       *userp = result.val.triple.user;
256       *domainp = result.val.triple.domain;
257     }
258
259   __libc_lock_unlock (lock);
260
261   return status == NSS_STATUS_SUCCESS ? 1 : 0;
262 }
263 weak_alias (__getnetgrent_r, getnetgrent_r)
264 \f
265 /* Test whether given (host,user,domain) triple is in NETGROUP.  */
266 int
267 innetgr (const char *netgroup, const char *host, const char *user,
268          const char *domain)
269 {
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;
279
280   __libc_lock_lock (lock);
281
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)
287     {
288       no_more = setup ((void **) &setfct, "setnetgrent", 1);
289       while (! no_more)
290         {
291           enum nss_status status;
292
293           /* Open netgroup.  */
294           status = (*setfct) (current_group);
295           if (status == NSS_STATUS_SUCCESS
296               && __nss_lookup (&nip, "getnetgrent_r", (void **) &getfct) == 0)
297             {
298               char buffer[1024];
299               struct __netgrent entry;
300
301               while ((*getfct) (&entry, buffer, sizeof buffer)
302                      == NSS_STATUS_SUCCESS)
303                 {
304                   if (entry.type == group_val)
305                     {
306                       /* Make sure we haven't seen the name before.  */
307                       struct name_list *namep;
308
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)
314                         {
315                           namep =
316                             (struct name_list *) malloc (sizeof (*namep));
317                           if (namep == NULL
318                               || ((namep->name = __strdup (entry.val.group))
319                                   == NULL))
320                             {
321                               /* Out of memory, simply return.  */
322                               if (namep != NULL)
323                                 free (namep);
324                               result = -1;
325                               break;
326                             }
327
328                           namep->next = needed;
329                           needed = namep;
330                         }
331                     }
332                   else
333                     {
334                       real_entry = 1;
335
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))
342                         {
343                           result = 1;
344                           break;
345                         }
346                     }
347                 }
348
349               if (result != 0)
350                 break;
351
352               /* If we found one service which does know the given
353                  netgroup we don't try further.  */
354               status = NSS_STATUS_RETURN;
355             }
356
357           /* Free all resources of the service.  */
358           if (__nss_lookup (&nip, "endnetgrent", (void **) &endfct) == 0)
359             (*endfct) ();
360
361           /* Look for the next service.  */
362           no_more = __nss_next (&nip, "setnetgrent",
363                                 (void **) &setfct, status, 0);
364         }
365
366       if (result == 0 && needed != NULL)
367         {
368           struct name_list *tmp = needed;
369           needed = tmp->next;
370           tmp->next = known;
371           known = tmp;
372           current_group = known->name;
373           continue;
374         }
375
376       /* No way out.  */
377       break;
378     }
379
380   __libc_lock_unlock (lock);
381
382   /* Free the memory.  */
383   while (known != NULL)
384     {
385       struct name_list *tmp = known;
386       known = known->next;
387       free (tmp->name);
388       free (tmp);
389     }
390   while (needed != NULL)
391     {
392       struct name_list *tmp = needed;
393       needed = needed->next;
394       free (tmp->name);
395       free (tmp);
396     }
397
398   return result == 1;
399 }