(saveit): Improve memory. No need to allocate multiple blocks.
[kopensolaris-gnu/glibc.git] / nis / nss_nis / nis-initgroups.c
1 /* Copyright (C) 1998-2000, 2002, 2003, 2004 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   struct response_t *next;
42   char val[0];
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       struct response_t *newp = malloc (sizeof (struct response_t)
64                                         + invallen + 1);
65       if (newp == NULL)
66         return YP_FALSE; /* We have no error code for out of memory */
67
68       if (intern->start == NULL)
69         intern->start = newp;
70       else
71         intern->next->next = newp;
72       intern->next = newp;
73
74       newp->next = NULL;
75       *((char *) mempcpy (newp->val, inval, invallen)) = '\0';
76     }
77
78   return 0;
79 }
80
81 static enum nss_status
82 internal_setgrent (intern_t *intern)
83 {
84   char *domainname;
85   struct ypall_callback ypcb;
86   enum nss_status status;
87
88   if (yp_get_default_domain (&domainname))
89     return NSS_STATUS_UNAVAIL;
90
91   intern->start = NULL;
92
93   ypcb.foreach = saveit;
94   ypcb.data = (char *) intern;
95   status = yperr2nss (yp_all (domainname, "group.byname", &ypcb));
96   intern->next = intern->start;
97
98   return status;
99 }
100
101 static enum nss_status
102 internal_getgrent_r (struct group *grp, char *buffer, size_t buflen,
103                      int *errnop, intern_t *intern)
104 {
105   struct parser_data *data = (void *) buffer;
106   int parse_res;
107   char *p;
108
109   if (intern->start == NULL)
110     internal_setgrent (intern);
111
112   /* Get the next entry until we found a correct one. */
113   do
114     {
115       if (intern->next == NULL)
116         return NSS_STATUS_NOTFOUND;
117
118       p = strncpy (buffer, intern->next->val, buflen);
119       while (isspace (*p))
120         ++p;
121
122       parse_res = _nss_files_parse_grent (p, grp, data, buflen, errnop);
123       if (parse_res == -1)
124         return NSS_STATUS_TRYAGAIN;
125       intern->next = intern->next->next;
126     }
127   while (!parse_res);
128
129   return NSS_STATUS_SUCCESS;
130 }
131
132 enum nss_status
133 _nss_nis_initgroups_dyn (const char *user, gid_t group, long int *start,
134                          long int *size, gid_t **groupsp, long int limit,
135                          int *errnop)
136 {
137   struct group grpbuf, *g;
138   size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
139   char *tmpbuf;
140   enum nss_status status;
141   intern_t intern = { NULL, NULL };
142   gid_t *groups = *groupsp;
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         tmpbuf = extend_alloca (tmpbuf, buflen, 2 * buflen);
157
158       if (status != NSS_STATUS_SUCCESS)
159         goto done;
160
161
162       g = &grpbuf;
163       if (g->gr_gid != group)
164         {
165           char **m;
166
167           for (m = g->gr_mem; *m != NULL; ++m)
168             if (strcmp (*m, user) == 0)
169               {
170                 /* Matches user.  Insert this group.  */
171                 if (*start == *size)
172                   {
173                     /* Need a bigger buffer.  */
174                     gid_t *newgroups;
175                     long int newsize;
176
177                     if (limit > 0 && *size == limit)
178                       /* We reached the maximum.  */
179                       goto done;
180
181                     if (limit <= 0)
182                       newsize = 2 * *size;
183                     else
184                       newsize = MIN (limit, 2 * *size);
185
186                     newgroups = realloc (groups, newsize * sizeof (*groups));
187                     if (newgroups == NULL)
188                       goto done;
189                     *groupsp = groups = newgroups;
190                     *size = newsize;
191                   }
192
193                 groups[*start] = g->gr_gid;
194                 *start += 1;
195
196                 break;
197               }
198         }
199     }
200   while (status == NSS_STATUS_SUCCESS);
201
202 done:
203   while (intern.start != NULL)
204     {
205       intern.next = intern.start;
206       intern.start = intern.start->next;
207       free (intern.next);
208     }
209
210   return NSS_STATUS_SUCCESS;
211 }