Don't set errno in case of lookup failure.
[kopensolaris-gnu/glibc.git] / nis / nss_nis / nis-initgroups.c
1 /* Copyright (C) 1998, 1999, 2000, 2002 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Thorsten Kukuk <kukuk@suse.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 Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the 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    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    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 #include <sys/param.h>
29
30 #include "nss-nis.h"
31
32 /* Get the declaration of the parser function.  */
33 #define ENTNAME grent
34 #define STRUCTURE group
35 #define EXTERN_PARSER
36 #include <nss/nss_files/files-parse.c>
37
38 struct response_t
39 {
40   char *val;
41   struct response_t *next;
42 };
43
44 struct intern_t
45 {
46   struct response_t *start;
47   struct response_t *next;
48 };
49 typedef struct intern_t intern_t;
50
51 static int
52 saveit (int instatus, char *inkey, int inkeylen, char *inval,
53         int invallen, char *indata)
54 {
55   intern_t *intern = (intern_t *) indata;
56
57   if (instatus != YP_TRUE)
58     return instatus;
59
60   if (inkey && inkeylen > 0 && inval && invallen > 0)
61     {
62       if (intern->start == NULL)
63         {
64           intern->start = malloc (sizeof (struct response_t));
65           if (intern->start == NULL)
66             return YP_FALSE;
67           intern->next = intern->start;
68         }
69       else
70         {
71           intern->next->next = malloc (sizeof (struct response_t));
72           if (intern->next->next == NULL)
73             return YP_FALSE;
74           intern->next = intern->next->next;
75         }
76       intern->next->next = NULL;
77       intern->next->val = malloc (invallen + 1);
78       if (intern->next->val == NULL)
79         return YP_FALSE;
80       strncpy (intern->next->val, inval, invallen);
81       intern->next->val[invallen] = '\0';
82     }
83
84   return 0;
85 }
86
87 static enum nss_status
88 internal_setgrent (intern_t *intern)
89 {
90   char *domainname;
91   struct ypall_callback ypcb;
92   enum nss_status status;
93
94   if (yp_get_default_domain (&domainname))
95     return NSS_STATUS_UNAVAIL;
96
97   intern->start = NULL;
98
99   ypcb.foreach = saveit;
100   ypcb.data = (char *) intern;
101   status = yperr2nss (yp_all (domainname, "group.byname", &ypcb));
102   intern->next = intern->start;
103
104   return status;
105 }
106
107 static enum nss_status
108 internal_getgrent_r (struct group *grp, char *buffer, size_t buflen,
109                      int *errnop, intern_t *intern)
110 {
111   struct parser_data *data = (void *) buffer;
112   int parse_res;
113   char *p;
114
115   if (intern->start == NULL)
116     internal_setgrent (intern);
117
118   /* Get the next entry until we found a correct one. */
119   do
120     {
121       if (intern->next == NULL)
122         return NSS_STATUS_NOTFOUND;
123
124       p = strncpy (buffer, intern->next->val, buflen);
125       while (isspace (*p))
126         ++p;
127
128       parse_res = _nss_files_parse_grent (p, grp, data, buflen, errnop);
129       if (parse_res == -1)
130         return NSS_STATUS_TRYAGAIN;
131       intern->next = intern->next->next;
132     }
133   while (!parse_res);
134
135   return NSS_STATUS_SUCCESS;
136 }
137
138 enum nss_status
139 _nss_nis_initgroups_dyn (const char *user, gid_t group, long int *start,
140                          long int *size, gid_t **groupsp, long int limit,
141                          int *errnop)
142 {
143   struct group grpbuf, *g;
144   size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
145   char *tmpbuf;
146   enum nss_status status;
147   intern_t intern = { NULL, NULL };
148   gid_t *groups = *groupsp;
149
150   status = internal_setgrent (&intern);
151   if (status != NSS_STATUS_SUCCESS)
152     return status;
153
154   tmpbuf = __alloca (buflen);
155
156   do
157     {
158       while ((status =
159               internal_getgrent_r (&grpbuf, tmpbuf, buflen, errnop,
160                                    &intern)) == NSS_STATUS_TRYAGAIN
161              && *errnop == ERANGE)
162         {
163           buflen *= 2;
164           tmpbuf = __alloca (buflen);
165         }
166
167       if (status != NSS_STATUS_SUCCESS)
168         goto done;
169
170
171       g = &grpbuf;
172       if (g->gr_gid != group)
173         {
174           char **m;
175
176           for (m = g->gr_mem; *m != NULL; ++m)
177             if (strcmp (*m, user) == 0)
178               {
179                 /* Matches user.  Insert this group.  */
180                 if (*start == *size)
181                   {
182                     /* Need a bigger buffer.  */
183                     gid_t *newgroups;
184                     long int newsize;
185
186                     if (limit > 0 && *size == limit)
187                       /* We reached the maximum.  */
188                       goto done;
189
190                     if (limit <= 0)
191                       newsize = 2 * *size;
192                     else
193                       newsize = MIN (limit, 2 * *size);
194
195                     newgroups = realloc (groups, newsize * sizeof (*groups));
196                     if (newgroups == NULL)
197                       goto done;
198                     *groupsp = groups = newgroups;
199                     *size = newsize;
200                   }
201
202                 groups[*start] = g->gr_gid;
203                 *start += 1;
204
205                 break;
206               }
207         }
208     }
209   while (status == NSS_STATUS_SUCCESS);
210
211 done:
212   while (intern.start != NULL)
213     {
214       if (intern.start->val != NULL)
215         free (intern.start->val);
216       intern.next = intern.start;
217       intern.start = intern.start->next;
218       free (intern.next);
219     }
220
221   return NSS_STATUS_SUCCESS;
222 }