2000-03-26 Roland McGrath <roland@baalperazim.frob.com>
[kopensolaris-gnu/glibc.git] / nss / getXXent_r.c
1 /* Copyright (C) 1996,97,98,99,2000 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
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 <errno.h>
21 #include <bits/libc-lock.h>
22
23 #include "nsswitch.h"
24
25 /*******************************************************************\
26 |* Here we assume several symbols to be defined:                   *|
27 |*                                                                 *|
28 |* LOOKUP_TYPE   - the return type of the function                 *|
29 |*                                                                 *|
30 |* SETFUNC_NAME  - name of the non-reentrant setXXXent function    *|
31 |*                                                                 *|
32 |* GETFUNC_NAME  - name of the non-reentrant getXXXent function    *|
33 |*                                                                 *|
34 |* ENDFUNC_NAME  - name of the non-reentrant endXXXent function    *|
35 |*                                                                 *|
36 |* DATABASE_NAME - name of the database the function accesses      *|
37 |*                 (e.g., host, services, ...)                     *|
38 |*                                                                 *|
39 |* Optionally the following vars can be defined:                   *|
40 |*                                                                 *|
41 |* STAYOPEN      - variable declaration for setXXXent function     *|
42 |*                                                                 *|
43 |* STAYOPEN_VAR  - variable name for setXXXent function            *|
44 |*                                                                 *|
45 |* NEED_H_ERRNO  - an extra parameter will be passed to point to   *|
46 |*                 the global `h_errno' variable.                  *|
47 |*                                                                 *|
48 \*******************************************************************/
49
50 /* To make the real sources a bit prettier.  */
51 #define REENTRANT_GETNAME APPEND_R (GETFUNC_NAME)
52 #define APPEND_R(Name) CONCAT2_2 (Name, _r)
53 #define INTERNAL(Name) CONCAT2_2 (__, Name)
54 #define CONCAT2_1(Pre, Post) CONCAT2_2 (Pre, Post)
55 #define CONCAT2_2(Pre, Post) Pre##Post
56
57 #define SETFUNC_NAME_STRING STRINGIZE (SETFUNC_NAME)
58 #define GETFUNC_NAME_STRING STRINGIZE (REENTRANT_GETNAME)
59 #define ENDFUNC_NAME_STRING STRINGIZE (ENDFUNC_NAME)
60 #define DATABASE_NAME_STRING STRINGIZE (DATABASE_NAME)
61 #define STRINGIZE(Name) STRINGIZE1 (Name)
62 #define STRINGIZE1(Name) #Name
63
64 #define DB_LOOKUP_FCT CONCAT3_1 (__nss_, DATABASE_NAME, _lookup)
65 #define CONCAT3_1(Pre, Name, Post) CONCAT3_2 (Pre, Name, Post)
66 #define CONCAT3_2(Pre, Name, Post) Pre##Name##Post
67
68 /* Sometimes we need to store error codes in the `h_errno' variable.  */
69 #ifdef NEED_H_ERRNO
70 # define H_ERRNO_PARM , int *h_errnop
71 # define H_ERRNO_VAR , &h_errno
72 #else
73 # define H_ERRNO_PARM
74 # define H_ERRNO_VAR
75 #endif
76
77 /* Some databases take the `stayopen' flag.  */
78 #ifdef STAYOPEN
79 # define STAYOPEN_TMP CONCAT2_1 (STAYOPEN, _tmp)
80 # define STAYOPEN_TMPVAR CONCAT2_1 (STAYOPEN_VAR, _tmp)
81 #else
82 # define STAYOPEN void
83 # define STAYOPEN_VAR
84 # define STAYOPEN_TMPVAR
85 #endif
86
87 /* Prototype for the setXXXent functions we use here.  */
88 typedef enum nss_status (*set_function) (STAYOPEN);
89
90 /* Prototype for the endXXXent functions we use here.  */
91 typedef enum nss_status (*end_function) (void);
92
93 /* Prototype for the setXXXent functions we use here.  */
94 typedef enum nss_status (*get_function) (LOOKUP_TYPE *, char *, size_t, int *
95                                          H_ERRNO_PARM);
96
97
98 /* This handle for the NSS data base is shared between all
99    set/get/endXXXent functions.  */
100 static service_user *nip;
101 /* Remember the last service used since the last call to  `endXXent'.  */
102 static service_user *last_nip;
103 /* Remember the first service_entry, it's always the same.  */
104 static service_user *startp;
105
106 #ifdef STAYOPEN_TMP
107 /* We need to remember the last `stayopen' flag given by the user
108    since the `setent' function is only called for the first available
109    service.  */
110 static STAYOPEN_TMP;
111 #endif
112
113 /* Protect above variable against multiple uses at the same time.  */
114 __libc_lock_define_initialized (static, lock)
115
116 /* The lookup function for the first entry of this service.  */
117 extern int DB_LOOKUP_FCT (service_user **nip, const char *name, void **fctp);
118
119 /* Set up NIP to run through the services.  If ALL is zero, use NIP's
120    current location if it's not nil.  Return nonzero if there are no
121    services (left).  */
122 static enum nss_status
123 setup (void **fctp, const char *func_name, int all)
124 {
125   int no_more;
126   if (startp == NULL)
127     {
128       no_more = DB_LOOKUP_FCT (&nip, func_name, fctp);
129       startp = no_more ? (service_user *) -1l : nip;
130     }
131   else if (startp == (service_user *) -1l)
132     /* No services at all.  */
133     return 1;
134   else
135     {
136       if (all || !nip)
137         /* Reset to the beginning of the service list.  */
138         nip = startp;
139       /* Look up the first function.  */
140       no_more = __nss_lookup (&nip, func_name, fctp);
141     }
142   return no_more;
143 }
144 \f
145 void
146 SETFUNC_NAME (STAYOPEN)
147 {
148   set_function fct;
149   int no_more;
150
151 #ifdef NEED__RES
152   if ((_res.options & RES_INIT) == 0 && res_init () == -1)
153     {
154       __set_h_errno (NETDB_INTERNAL);
155       return;
156     }
157 #endif /* need _res */
158
159   __libc_lock_lock (lock);
160
161   /* Cycle through the services and run their `setXXent' functions until
162      we find an available service.  */
163   no_more = setup ((void **) &fct, SETFUNC_NAME_STRING, 1);
164   while (! no_more)
165     {
166       int is_last_nip = nip == last_nip;
167       enum nss_status status = DL_CALL_FCT (fct, (STAYOPEN_VAR));
168
169       no_more = __nss_next (&nip, SETFUNC_NAME_STRING, (void **) &fct,
170                             status, 0);
171       if (is_last_nip)
172         last_nip = nip;
173     }
174
175 #ifdef STAYOPEN_TMP
176   STAYOPEN_TMPVAR = STAYOPEN_VAR;
177 #endif
178
179   __libc_lock_unlock (lock);
180 }
181
182
183 void
184 ENDFUNC_NAME (void)
185 {
186   end_function fct;
187   int no_more;
188
189 #ifdef NEED__RES
190   if ((_res.options & RES_INIT) == 0 && res_init () == -1)
191     {
192       __set_h_errno (NETDB_INTERNAL);
193       return;
194     }
195 #endif /* need _res */
196
197   __libc_lock_lock (lock);
198
199   /* Cycle through all the services and run their endXXent functions.  */
200   no_more = setup ((void **) &fct, ENDFUNC_NAME_STRING, 1);
201   while (! no_more)
202     {
203       /* Ignore status, we force check in __NSS_NEXT.  */
204       DL_CALL_FCT (fct, ());
205
206       if (nip == last_nip)
207         /* We have processed all services which were used.  */
208         break;
209
210       no_more = __nss_next (&nip, ENDFUNC_NAME_STRING, (void **) &fct, 0, 1);
211     }
212   last_nip = nip = NULL;
213
214   __libc_lock_unlock (lock);
215 }
216
217
218 int
219 INTERNAL (REENTRANT_GETNAME) (LOOKUP_TYPE *resbuf, char *buffer, size_t buflen,
220                               LOOKUP_TYPE **result H_ERRNO_PARM)
221 {
222   get_function fct;
223   int no_more;
224   enum nss_status status;
225
226 #ifdef NEED__RES
227   if ((_res.options & RES_INIT) == 0 && res_init () == -1)
228     {
229       __set_h_errno (NETDB_INTERNAL);
230       *result = NULL;
231       return errno;
232     }
233 #endif /* need _res */
234
235   /* Initialize status to return if no more functions are found.  */
236   status = NSS_STATUS_NOTFOUND;
237
238   __libc_lock_lock (lock);
239
240   /* Run through available functions, starting with the same function last
241      run.  We will repeat each function as long as it succeeds, and then go
242      on to the next service action.  */
243   no_more = setup ((void **) &fct, GETFUNC_NAME_STRING, 0);
244   while (! no_more)
245     {
246       int is_last_nip = nip == last_nip;
247
248       status = DL_CALL_FCT (fct,
249                              (resbuf, buffer, buflen, &errno H_ERRNO_VAR));
250
251       /* The the status is NSS_STATUS_TRYAGAIN and errno is ERANGE the
252          provided buffer is too small.  In this case we should give
253          the user the possibility to enlarge the buffer and we should
254          not simply go on with the next service (even if the TRYAGAIN
255          action tells us so).  */
256       if (status == NSS_STATUS_TRYAGAIN
257 #ifdef NEED_H_ERRNO
258           && *h_errnop == NETDB_INTERNAL
259 #endif
260           && errno == ERANGE)
261         break;
262
263       do
264         {
265           no_more = __nss_next (&nip, GETFUNC_NAME_STRING, (void **) &fct,
266                                 status, 0);
267
268           if (is_last_nip)
269             last_nip = nip;
270
271           if (! no_more)
272             {
273               /* Call the `setXXent' function.  This wasn't done before.  */
274               set_function sfct;
275
276               no_more = __nss_lookup (&nip, SETFUNC_NAME_STRING,
277                                       (void **) &sfct);
278
279               if (! no_more)
280                 status = DL_CALL_FCT (sfct, (STAYOPEN_TMPVAR));
281               else
282                 status = NSS_STATUS_NOTFOUND;
283             }
284         }
285       while (! no_more && status != NSS_STATUS_SUCCESS);
286     }
287
288   __libc_lock_unlock (lock);
289
290   *result = status == NSS_STATUS_SUCCESS ? resbuf : NULL;
291   return status == NSS_STATUS_SUCCESS ? 0 : errno;
292 }
293
294
295 #include <shlib-compat.h>
296 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1_2)
297 #define OLD(name) OLD1 (name)
298 #define OLD1(name) __old_##name
299
300 int
301 OLD (REENTRANT_GETNAME) (LOOKUP_TYPE *resbuf, char *buffer, size_t buflen,
302                          LOOKUP_TYPE **result H_ERRNO_PARM)
303 {
304   int ret = INTERNAL (REENTRANT_GETNAME) (resbuf, buffer, buflen,
305                                           result H_ERRNO_VAR);
306
307   if (ret != 0)
308     ret = -1;
309
310   return ret;
311 }
312
313 #define do_symbol_version(real, name, version) \
314   compat_symbol (libc, real, name, version)
315 do_symbol_version (OLD (REENTRANT_GETNAME), REENTRANT_GETNAME, GLIBC_2_0);
316
317 #define do_default_symbol_version(real, name, version) \
318   versioned_symbol (libc, real, name, version)
319 do_default_symbol_version (INTERNAL (REENTRANT_GETNAME),
320                            REENTRANT_GETNAME, GLIBC_2_1_2);
321 #else
322 #define do_weak_alias(n1, n2) weak_alias (n1, n2)
323 do_weak_alias (INTERNAL (REENTRANT_GETNAME), REENTRANT_GETNAME)
324 #endif