Include atomic.h.
[kopensolaris-gnu/glibc.git] / nss / getXXbyYY_r.c
1 /* Copyright (C) 1996-2002, 2003, 2004, 2006 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 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 <assert.h>
21 #include <atomic.h>
22 #include <errno.h>
23 #include <stdbool.h>
24 #include "nsswitch.h"
25 #ifdef USE_NSCD
26 # include <nscd/nscd_proto.h>
27 #endif
28 #ifdef NEED__RES_HCONF
29 # include <resolv/res_hconf.h>
30 #endif
31 #ifdef NEED__RES
32 # include <resolv.h>
33 #endif
34 /*******************************************************************\
35 |* Here we assume several symbols to be defined:                   *|
36 |*                                                                 *|
37 |* LOOKUP_TYPE   - the return type of the function                 *|
38 |*                                                                 *|
39 |* FUNCTION_NAME - name of the non-reentrant function              *|
40 |*                                                                 *|
41 |* DATABASE_NAME - name of the database the function accesses      *|
42 |*                 (e.g., host, services, ...)                     *|
43 |*                                                                 *|
44 |* ADD_PARAMS    - additional parameter, can vary in number        *|
45 |*                                                                 *|
46 |* ADD_VARIABLES - names of additional parameter                   *|
47 |*                                                                 *|
48 |* Optionally the following vars can be defined:                   *|
49 |*                                                                 *|
50 |* NEED_H_ERRNO  - an extra parameter will be passed to point to   *|
51 |*                 the global `h_errno' variable.                  *|
52 |*                                                                 *|
53 |* NEED__RES     - the global _res variable might be used so we    *|
54 |*                 will have to initialize it if necessary         *|
55 |*                                                                 *|
56 |* PREPROCESS    - code run before anything else                   *|
57 |*                                                                 *|
58 |* POSTPROCESS   - code run after the lookup                       *|
59 |*                                                                 *|
60 \*******************************************************************/
61
62 /* To make the real sources a bit prettier.  */
63 #define REENTRANT_NAME APPEND_R (FUNCTION_NAME)
64 #define APPEND_R(name) APPEND_R1 (name)
65 #define APPEND_R1(name) name##_r
66 #define INTERNAL(name) INTERNAL1 (name)
67 #define INTERNAL1(name) __##name
68 #define NEW(name) NEW1 (name)
69 #define NEW1(name) __new_##name
70
71 #ifdef USE_NSCD
72 # define NSCD_NAME ADD_NSCD (REENTRANT_NAME)
73 # define ADD_NSCD(name) ADD_NSCD1 (name)
74 # define ADD_NSCD1(name) __nscd_##name
75 # define NOT_USENSCD_NAME ADD_NOT_NSCDUSE (DATABASE_NAME)
76 # define ADD_NOT_NSCDUSE(name) ADD_NOT_NSCDUSE1 (name)
77 # define ADD_NOT_NSCDUSE1(name) __nss_not_use_nscd_##name
78 #endif
79
80 #define FUNCTION_NAME_STRING STRINGIZE (FUNCTION_NAME)
81 #define REENTRANT_NAME_STRING STRINGIZE (REENTRANT_NAME)
82 #define DATABASE_NAME_STRING STRINGIZE (DATABASE_NAME)
83 #define STRINGIZE(name) STRINGIZE1 (name)
84 #define STRINGIZE1(name) #name
85
86 #ifndef DB_LOOKUP_FCT
87 # define DB_LOOKUP_FCT CONCAT3_1 (__nss_, DATABASE_NAME, _lookup)
88 # define CONCAT3_1(Pre, Name, Post) CONCAT3_2 (Pre, Name, Post)
89 # define CONCAT3_2(Pre, Name, Post) Pre##Name##Post
90 #endif
91
92 /* Sometimes we need to store error codes in the `h_errno' variable.  */
93 #ifdef NEED_H_ERRNO
94 # define H_ERRNO_PARM , int *h_errnop
95 # define H_ERRNO_VAR , h_errnop
96 # define H_ERRNO_VAR_P h_errnop
97 #else
98 # define H_ERRNO_PARM
99 # define H_ERRNO_VAR
100 # define H_ERRNO_VAR_P NULL
101 #endif
102
103 #ifdef HAVE_AF
104 # define AF_VAL af
105 #else
106 # define AF_VAL AF_INET
107 #endif
108
109 /* Type of the lookup function we need here.  */
110 typedef enum nss_status (*lookup_function) (ADD_PARAMS, LOOKUP_TYPE *, char *,
111                                             size_t, int * H_ERRNO_PARM);
112
113 /* The lookup function for the first entry of this service.  */
114 extern int DB_LOOKUP_FCT (service_user **nip, const char *name, void **fctp)
115      internal_function;
116 libc_hidden_proto (DB_LOOKUP_FCT)
117
118
119 int
120 INTERNAL (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer,
121                            size_t buflen, LOOKUP_TYPE **result H_ERRNO_PARM)
122 {
123   static service_user *startp;
124   static lookup_function start_fct;
125   service_user *nip;
126   union
127   {
128     lookup_function l;
129     void *ptr;
130   } fct;
131
132   int no_more;
133   enum nss_status status = NSS_STATUS_UNAVAIL;
134 #ifdef USE_NSCD
135   int nscd_status;
136 #endif
137 #ifdef NEED_H_ERRNO
138   bool any_service = false;
139 #endif
140
141 #ifdef PREPROCESS
142   PREPROCESS;
143 #endif
144
145 #ifdef HANDLE_DIGITS_DOTS
146   switch (__nss_hostname_digits_dots (name, resbuf, &buffer, NULL,
147                                       buflen, result, &status, AF_VAL,
148                                       H_ERRNO_VAR_P))
149     {
150     case -1:
151       return errno;
152     case 1:
153       goto done;
154     }
155 #endif
156
157 #ifdef USE_NSCD
158   if (NOT_USENSCD_NAME > 0 && ++NOT_USENSCD_NAME > NSS_NSCD_RETRY)
159     NOT_USENSCD_NAME = 0;
160
161   if (!NOT_USENSCD_NAME)
162     {
163       nscd_status = NSCD_NAME (ADD_VARIABLES, resbuf, buffer, buflen, result
164                                H_ERRNO_VAR);
165       if (nscd_status >= 0)
166         return nscd_status;
167     }
168 #endif
169
170   if (startp == NULL)
171     {
172       no_more = DB_LOOKUP_FCT (&nip, REENTRANT_NAME_STRING, &fct.ptr);
173       if (no_more)
174         startp = (service_user *) -1l;
175       else
176         {
177 #ifdef NEED__RES
178           /* The resolver code will really be used so we have to
179              initialize it.  */
180           if (__res_maybe_init (&_res, 0) == -1)
181             {
182               *h_errnop = NETDB_INTERNAL;
183               *result = NULL;
184               return errno;
185             }
186 #endif /* need _res */
187 #ifdef NEED__RES_HCONF
188           if (!_res_hconf.initialized)
189             _res_hconf_init ();
190 #endif /* need _res_hconf */
191
192           start_fct = fct.l;
193           /* Make sure start_fct is written before startp.  */
194           atomic_write_barrier ();
195           startp = nip;
196         }
197     }
198   else
199     {
200       fct.l = start_fct;
201       no_more = (nip = startp) == (service_user *) -1l;
202     }
203
204   while (no_more == 0)
205     {
206 #ifdef NEED_H_ERRNO
207       any_service = true;
208 #endif
209
210       status = DL_CALL_FCT (fct.l, (ADD_VARIABLES, resbuf, buffer, buflen,
211                                     &errno H_ERRNO_VAR));
212
213       /* The status is NSS_STATUS_TRYAGAIN and errno is ERANGE the
214          provided buffer is too small.  In this case we should give
215          the user the possibility to enlarge the buffer and we should
216          not simply go on with the next service (even if the TRYAGAIN
217          action tells us so).  */
218       if (status == NSS_STATUS_TRYAGAIN
219 #ifdef NEED_H_ERRNO
220           && *h_errnop == NETDB_INTERNAL
221 #endif
222           && errno == ERANGE)
223         break;
224
225       no_more = __nss_next (&nip, REENTRANT_NAME_STRING,
226                             &fct.ptr, status, 0);
227     }
228
229 #ifdef HANDLE_DIGITS_DOTS
230 done:
231 #endif
232   *result = status == NSS_STATUS_SUCCESS ? resbuf : NULL;
233 #ifdef NEED_H_ERRNO
234   if (status != NSS_STATUS_SUCCESS && ! any_service)
235     /* We were not able to use any service.  */
236     *h_errnop = NO_RECOVERY;
237 #endif
238 #ifdef POSTPROCESS
239   POSTPROCESS;
240 #endif
241
242   int res;
243   if (status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND)
244     res = 0;
245   /* Don't pass back ERANGE if this is not for a too-small buffer.  */
246   else if (errno == ERANGE && status != NSS_STATUS_TRYAGAIN)
247     res = EINVAL;
248 #ifdef NEED_H_ERRNO
249   /* These functions only set errno if h_errno is NETDB_INTERNAL.  */
250   else if (status == NSS_STATUS_TRYAGAIN && *h_errnop != NETDB_INTERNAL)
251     res = EAGAIN;
252 #endif
253   else
254     return errno;
255
256   __set_errno (res);
257   return res;
258 }
259
260
261 #include <shlib-compat.h>
262 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1_2)
263 #define OLD(name) OLD1 (name)
264 #define OLD1(name) __old_##name
265
266 int
267 attribute_compat_text_section
268 OLD (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer,
269                       size_t buflen, LOOKUP_TYPE **result H_ERRNO_PARM)
270 {
271   int ret = INTERNAL (REENTRANT_NAME) (ADD_VARIABLES, resbuf, buffer,
272                                        buflen, result H_ERRNO_VAR);
273
274   if (ret != 0 || result == NULL)
275     ret = -1;
276
277   return ret;
278 }
279
280 #define do_symbol_version(real, name, version) \
281   compat_symbol (libc, real, name, version)
282 do_symbol_version (OLD (REENTRANT_NAME), REENTRANT_NAME, GLIBC_2_0);
283 #endif
284
285 /* As INTERNAL (REENTRANT_NAME) may be hidden, we need an alias
286    in between so that the REENTRANT_NAME@@GLIBC_2.1.2 is not
287    hidden too.  */
288 strong_alias (INTERNAL (REENTRANT_NAME), NEW (REENTRANT_NAME));
289
290 #define do_default_symbol_version(real, name, version) \
291   versioned_symbol (libc, real, name, version)
292 do_default_symbol_version (NEW (REENTRANT_NAME),
293                            REENTRANT_NAME, GLIBC_2_1_2);
294
295 static_link_warning (REENTRANT_NAME)