Set errno to ENOENT if we have no more entries.
[kopensolaris-gnu/glibc.git] / nis / nss_nis / nis-initgroups.c
1 /* Copyright (C) 1998 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <nss.h>
21 #include <grp.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <rpcsvc/yp.h>
27 #include <rpcsvc/ypclnt.h>
28
29 #include "nss-nis.h"
30
31 /* Get the declaration of the parser function.  */
32 #define ENTNAME grent
33 #define STRUCTURE group
34 #define EXTERN_PARSER
35 #include <nss/nss_files/files-parse.c>
36
37 struct response_t
38 {
39   char *val;
40   struct response_t *next;
41 };
42
43 struct intern_t
44 {
45   struct response_t *start;
46   struct response_t *next;
47 };
48 typedef struct intern_t intern_t;
49
50 static int
51 saveit (int instatus, char *inkey, int inkeylen, char *inval,
52         int invallen, char *indata)
53 {
54   intern_t *intern = (intern_t *) indata;
55
56   if (instatus != YP_TRUE)
57     return instatus;
58
59   if (inkey && inkeylen > 0 && inval && invallen > 0)
60     {
61       if (intern->start == NULL)
62         {
63           intern->start = malloc (sizeof (struct response_t));
64           intern->next = intern->start;
65         }
66       else
67         {
68           intern->next->next = malloc (sizeof (struct response_t));
69           intern->next = intern->next->next;
70         }
71       intern->next->next = NULL;
72       intern->next->val = malloc (invallen + 1);
73       strncpy (intern->next->val, inval, invallen);
74       intern->next->val[invallen] = '\0';
75     }
76
77   return 0;
78 }
79
80 static enum nss_status
81 internal_setgrent (intern_t *intern)
82 {
83   char *domainname;
84   struct ypall_callback ypcb;
85   enum nss_status status;
86
87   if (yp_get_default_domain (&domainname))
88     return NSS_STATUS_UNAVAIL;
89
90   intern->start = NULL;
91
92   ypcb.foreach = saveit;
93   ypcb.data = (char *) intern;
94   status = yperr2nss (yp_all (domainname, "group.byname", &ypcb));
95   intern->next = intern->start;
96
97   return status;
98 }
99
100 static enum nss_status
101 internal_getgrent_r (struct group *grp, char *buffer, size_t buflen,
102                      int *errnop, intern_t *intern)
103 {
104   struct parser_data *data = (void *) buffer;
105   int parse_res;
106   char *p;
107
108   if (intern->start == NULL)
109     internal_setgrent (intern);
110
111   /* Get the next entry until we found a correct one. */
112   do
113     {
114       if (intern->next == NULL)
115         {
116           *errnop = ENOENT;
117           return NSS_STATUS_NOTFOUND;
118         }
119       p = strncpy (buffer, intern->next->val, buflen);
120       while (isspace (*p))
121         ++p;
122
123       parse_res = _nss_files_parse_grent (p, grp, data, buflen, errnop);
124       if (parse_res == -1)
125         return NSS_STATUS_TRYAGAIN;
126       intern->next = intern->next->next;
127     }
128   while (!parse_res);
129
130   return NSS_STATUS_SUCCESS;
131 }
132
133 enum nss_status
134 _nss_nis_initgroups (const char *user, gid_t group, long int *start,
135                      long int *size, gid_t *groups, long int limit,
136                      int *errnop)
137 {
138   struct group grpbuf, *g;
139   size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
140   char *tmpbuf;
141   enum nss_status status;
142   intern_t intern = { NULL, NULL };
143
144   status = internal_setgrent (&intern);
145   if (status != NSS_STATUS_SUCCESS)
146     return status;
147
148   tmpbuf = __alloca (buflen);
149
150   do
151     {
152       while ((status =
153               internal_getgrent_r (&grpbuf, tmpbuf, buflen, errnop,
154                                    &intern)) == NSS_STATUS_TRYAGAIN
155              && *errnop == ERANGE)
156         {
157           buflen *= 2;
158           tmpbuf = __alloca (buflen);
159         }
160
161       if (status != NSS_STATUS_SUCCESS)
162         goto done;
163
164
165       g = &grpbuf;
166       if (g->gr_gid != group)
167         {
168           char **m;
169
170           for (m = g->gr_mem; *m != NULL; ++m)
171             if (strcmp (*m, user) == 0)
172               {
173                 /* Matches user.  Insert this group.  */
174                 if (*start == *size && limit <= 0)
175                   {
176                     /* Need a bigger buffer.  */
177                     groups = realloc (groups, *size * sizeof (*groups));
178                     if (groups == NULL)
179                       goto done;
180                     *size *= 2;
181                   }
182
183                 groups[*start] = g->gr_gid;
184                 *start += 1;
185
186                 if (*start == limit)
187                   /* Can't take any more groups; stop searching.  */
188                   goto done;
189
190                 break;
191               }
192         }
193     }
194   while (status == NSS_STATUS_SUCCESS);
195
196 done:
197   while (intern.start != NULL)
198     {
199       if (intern.start->val != NULL)
200         free (intern.start->val);
201       intern.next = intern.start;
202       intern.start = intern.start->next;
203       free (intern.next);
204     }
205
206   return NSS_STATUS_SUCCESS;
207 }