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