(lookup): Preserve errno value.
[kopensolaris-gnu/glibc.git] / hesiod / nss_hesiod / hesiod-grp.c
1 /* Copyright (C) 1997, 1999, 2000, 2002 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Mark Kettenis <kettenis@phys.uva.nl>, 1997.
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 <ctype.h>
21 #include <errno.h>
22 #include <grp.h>
23 #include <hesiod.h>
24 #include <nss.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/param.h>
29
30 #include "nss_hesiod.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 enum nss_status
39 _nss_hesiod_setgrent (int stayopen)
40 {
41   return NSS_STATUS_SUCCESS;
42 }
43
44 enum nss_status
45 _nss_hesiod_endgrent (void)
46 {
47   return NSS_STATUS_SUCCESS;
48 }
49
50 static enum nss_status
51 lookup (const char *name, const char *type, struct group *grp,
52         char *buffer, size_t buflen, int *errnop)
53 {
54   struct parser_data *data = (void *) buffer;
55   size_t linebuflen;
56   void *context;
57   char **list;
58   int parse_res;
59   size_t len;
60   int olderr = errno;
61
62   context = _nss_hesiod_init ();
63   if (context == NULL)
64     return NSS_STATUS_UNAVAIL;
65
66   list = hesiod_resolve (context, name, type);
67   if (list == NULL)
68     {
69       int err = errno;
70       hesiod_end (context);
71       __set_errno (olderr);
72       return err == ENOENT ? NSS_STATUS_NOTFOUND : NSS_STATUS_UNAVAIL;
73     }
74
75   linebuflen = buffer + buflen - data->linebuffer;
76   len = strlen (*list) + 1;
77   if (linebuflen < len)
78     {
79       hesiod_free_list (context, list);
80       hesiod_end (context);
81       *errnop = ERANGE;
82       return NSS_STATUS_TRYAGAIN;
83     }
84
85   memcpy (data->linebuffer, *list, len);
86   hesiod_free_list (context, list);
87   hesiod_end (context);
88
89   parse_res = _nss_files_parse_grent (buffer, grp, data, buflen, errnop);
90   if (parse_res < 1)
91     {
92       __set_errno (olderr);
93       return parse_res == -1 ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND;
94     }
95
96   return NSS_STATUS_SUCCESS;
97 }
98
99 enum nss_status
100 _nss_hesiod_getgrnam_r (const char *name, struct group *grp,
101                         char *buffer, size_t buflen, int *errnop)
102 {
103   return lookup (name, "group", grp, buffer, buflen, errnop);
104 }
105
106 enum nss_status
107 _nss_hesiod_getgrgid_r (gid_t gid, struct group *grp,
108                         char *buffer, size_t buflen, int *errnop)
109 {
110   char gidstr[21];      /* We will probably never have a gid_t with more
111                            than 64 bits.  */
112
113   snprintf (gidstr, sizeof gidstr, "%d", gid);
114
115   return lookup (gidstr, "gid", grp, buffer, buflen, errnop);
116 }
117
118 static int
119 internal_gid_in_list (const gid_t *list, const gid_t g, long int len)
120 {
121   while (len > 0)
122     {
123       if (*list == g)
124         return 1;
125       --len;
126       ++list;
127     }
128   return 0;
129 }
130
131 static enum nss_status
132 internal_gid_from_group (void *context, const char *groupname, gid_t *group)
133 {
134   char **grp_res;
135   enum nss_status status = NSS_STATUS_NOTFOUND;
136
137   grp_res = hesiod_resolve (context, groupname, "group");
138   if (grp_res != NULL && *grp_res != NULL)
139     {
140       char *p = *grp_res;
141
142       while (*p != '\0' && *p != ':')
143         ++p;
144       while (*p != '\0' && *p == ':')
145         ++p;
146       while (*p != '\0' && *p != ':')
147         ++p;
148       while (*p != '\0' && *p == ':')
149         ++p;
150       if (*p == ':')
151         {
152           char *endp;
153           char *q = ++p;
154           long int val;
155
156           q = p;
157           while (*q != '\0' && *q != ':')
158             ++q;
159
160           val = strtol (p, &endp, 10);
161           if (sizeof (gid_t) == sizeof (long int) || (gid_t) val == val)
162             {
163               *group = val;
164               if (endp == q && endp != p)
165                 status = NSS_STATUS_SUCCESS;
166             }
167         }
168       hesiod_free_list (context, grp_res);
169     }
170   return status;
171 }
172
173 enum nss_status
174 _nss_hesiod_initgroups_dyn (const char *user, gid_t group, long int *start,
175                             long int *size, gid_t **groupsp, long int limit,
176                             int *errnop)
177 {
178   enum nss_status status = NSS_STATUS_SUCCESS;
179   char **list = NULL;
180   char *p;
181   void *context;
182   gid_t *groups = *groupsp;
183   int save_errno;
184
185   context = _nss_hesiod_init ();
186   if (context == NULL)
187     return NSS_STATUS_UNAVAIL;
188
189   list = hesiod_resolve (context, user, "grplist");
190
191   if (list == NULL)
192     {
193       hesiod_end (context);
194       return errno == ENOENT ? NSS_STATUS_NOTFOUND : NSS_STATUS_UNAVAIL;
195     }
196
197   if (!internal_gid_in_list (groups, group, *start))
198     {
199       if (__builtin_expect (*start == *size, 0))
200         {
201           /* Need a bigger buffer.  */
202           gid_t *newgroups;
203           long int newsize;
204
205           if (limit > 0 && *size == limit)
206             /* We reached the maximum.  */
207             goto done;
208
209           if (limit <= 0)
210             newsize = 2 * *size;
211           else
212             newsize = MIN (limit, 2 * *size);
213
214           newgroups = realloc (groups, newsize * sizeof (*groups));
215           if (newgroups == NULL)
216             goto done;
217           *groupsp = groups = newgroups;
218           *size = newsize;
219         }
220
221       groups[(*start)++] = group;
222     }
223
224   save_errno = errno;
225
226   p = *list;
227   while (*p != '\0')
228     {
229       char *endp;
230       char *q;
231       long int val;
232
233       status = NSS_STATUS_NOTFOUND;
234
235       q = p;
236       while (*q != '\0' && *q != ':' && *q != ',')
237         ++q;
238
239       if (*q != '\0')
240         *q++ = '\0';
241
242       __set_errno (0);
243       val = strtol (p, &endp, 10);
244       /* Test whether the number is representable in a variable of
245          type `gid_t'.  If not ignore the number.  */
246       if ((sizeof (gid_t) == sizeof (long int) || (gid_t) val == val)
247           && errno == 0)
248         {
249           if (*endp == '\0' && endp != p)
250             {
251               group = val;
252               status = NSS_STATUS_SUCCESS;
253             }
254           else
255             status = internal_gid_from_group (context, p, &group);
256
257           if (status == NSS_STATUS_SUCCESS
258               && !internal_gid_in_list (groups, group, *start))
259             {
260               if (__builtin_expect (*start == *size, 0))
261                 {
262                   /* Need a bigger buffer.  */
263                   gid_t *newgroups;
264                   long int newsize;
265
266                   if (limit > 0 && *size == limit)
267                     /* We reached the maximum.  */
268                     goto done;
269
270                   if (limit <= 0)
271                     newsize = 2 * *size;
272                   else
273                     newsize = MIN (limit, 2 * *size);
274
275                   newgroups = realloc (groups, newsize * sizeof (*groups));
276                   if (newgroups == NULL)
277                     goto done;
278                   *groupsp = groups = newgroups;
279                   *size = newsize;
280                 }
281
282               groups[(*start)++] = group;
283             }
284         }
285
286       p = q;
287     }
288
289   __set_errno (save_errno);
290
291  done:
292   hesiod_free_list (context, list);
293   hesiod_end (context);
294
295   return NSS_STATUS_SUCCESS;
296 }