Separate internal state between getXXent and getXXbyYY NSS calls (bug 18007)
[glibc.git] / nss / nss_files / files-XXX.c
blobb38672d2fbf83c1bf22d80be24c0612d4e8c3fa6
1 /* Common code for file-based databases in nss_files module.
2 Copyright (C) 1996-2014 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
19 #include <stdio.h>
20 #include <ctype.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <bits/libc-lock.h>
24 #include "nsswitch.h"
26 #include <kernel-features.h>
28 /* These symbols are defined by the including source file:
30 ENTNAME -- database name of the structure and functions (hostent, pwent).
31 STRUCTURE -- struct name, define only if not ENTNAME (passwd, group).
32 DATABASE -- string of the database file's name ("hosts", "passwd").
34 NEED_H_ERRNO - defined iff an arg `int *herrnop' is used.
36 Also see files-parse.c.
39 #define ENTNAME_r CONCAT(ENTNAME,_r)
41 #define DATAFILE "/etc/" DATABASE
43 #ifdef NEED_H_ERRNO
44 # include <netdb.h>
45 # define H_ERRNO_PROTO , int *herrnop
46 # define H_ERRNO_ARG , herrnop
47 # define H_ERRNO_SET(val) (*herrnop = (val))
48 #else
49 # define H_ERRNO_PROTO
50 # define H_ERRNO_ARG
51 # define H_ERRNO_SET(val) ((void) 0)
52 #endif
54 #ifndef EXTRA_ARGS
55 # define EXTRA_ARGS
56 # define EXTRA_ARGS_DECL
57 # define EXTRA_ARGS_VALUE
58 #endif
60 /* Locks the static variables in this file. */
61 __libc_lock_define_initialized (static, lock)
63 /* Maintenance of the stream open on the database file. For getXXent
64 operations the stream needs to be held open across calls, the other
65 getXXbyYY operations all use their own stream. */
67 static FILE *stream;
69 /* Open database file if not already opened. */
70 static enum nss_status
71 internal_setent (FILE **stream)
73 enum nss_status status = NSS_STATUS_SUCCESS;
75 if (*stream == NULL)
77 *stream = fopen (DATAFILE, "rce");
79 if (*stream == NULL)
80 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
81 else
83 #if !defined O_CLOEXEC || !defined __ASSUME_O_CLOEXEC
84 # ifdef O_CLOEXEC
85 if (__have_o_cloexec <= 0)
86 # endif
88 /* We have to make sure the file is `closed on exec'. */
89 int result;
90 int flags;
92 result = flags = fcntl (fileno (*stream), F_GETFD, 0);
93 if (result >= 0)
95 # ifdef O_CLOEXEC
96 if (__have_o_cloexec == 0)
97 __have_o_cloexec = (flags & FD_CLOEXEC) == 0 ? -1 : 1;
98 if (__have_o_cloexec < 0)
99 # endif
101 flags |= FD_CLOEXEC;
102 result = fcntl (fileno (*stream), F_SETFD, flags);
105 if (result < 0)
107 /* Something went wrong. Close the stream and return a
108 failure. */
109 fclose (*stream);
110 *stream = NULL;
111 status = NSS_STATUS_UNAVAIL;
114 #endif
117 else
118 rewind (*stream);
120 return status;
124 /* Thread-safe, exported version of that. */
125 enum nss_status
126 CONCAT(_nss_files_set,ENTNAME) (int stayopen)
128 enum nss_status status;
130 __libc_lock_lock (lock);
132 status = internal_setent (&stream);
134 __libc_lock_unlock (lock);
136 return status;
140 /* Close the database file. */
141 static void
142 internal_endent (FILE **stream)
144 if (*stream != NULL)
146 fclose (*stream);
147 *stream = NULL;
152 /* Thread-safe, exported version of that. */
153 enum nss_status
154 CONCAT(_nss_files_end,ENTNAME) (void)
156 __libc_lock_lock (lock);
158 internal_endent (&stream);
160 __libc_lock_unlock (lock);
162 return NSS_STATUS_SUCCESS;
166 typedef enum
168 gcr_ok = 0,
169 gcr_error = -1,
170 gcr_overflow = -2
171 } get_contents_ret;
173 /* Hack around the fact that fgets only accepts int sizes. */
174 static get_contents_ret
175 get_contents (char *linebuf, size_t len, FILE *stream)
177 size_t remaining_len = len;
178 char *curbuf = linebuf;
182 int curlen = ((remaining_len > (size_t) INT_MAX) ? INT_MAX
183 : remaining_len);
184 char *p = fgets_unlocked (curbuf, curlen, stream);
186 ((unsigned char *) curbuf)[curlen - 1] = 0xff;
188 /* EOF or read error. */
189 if (p == NULL)
190 return gcr_error;
192 /* Done reading in the line. */
193 if (((unsigned char *) curbuf)[curlen - 1] == 0xff)
194 return gcr_ok;
196 /* Drop the terminating '\0'. */
197 remaining_len -= curlen - 1;
198 curbuf += curlen - 1;
200 /* fgets copies one less than the input length. Our last iteration is of
201 REMAINING_LEN and once that is done, REMAINING_LEN is decremented by
202 REMAINING_LEN - 1, leaving the result as 1. */
203 while (remaining_len > 1);
205 /* This means that the current buffer was not large enough. */
206 return gcr_overflow;
209 /* Parsing the database file into `struct STRUCTURE' data structures. */
210 static enum nss_status
211 internal_getent (FILE *stream, struct STRUCTURE *result,
212 char *buffer, size_t buflen, int *errnop H_ERRNO_PROTO
213 EXTRA_ARGS_DECL)
215 char *p;
216 struct parser_data *data = (void *) buffer;
217 size_t linebuflen = buffer + buflen - data->linebuffer;
218 int parse_result;
220 if (buflen < sizeof *data + 2)
222 *errnop = ERANGE;
223 H_ERRNO_SET (NETDB_INTERNAL);
224 return NSS_STATUS_TRYAGAIN;
229 get_contents_ret r = get_contents (data->linebuffer, linebuflen, stream);
231 if (r == gcr_error)
233 /* End of file or read error. */
234 H_ERRNO_SET (HOST_NOT_FOUND);
235 return NSS_STATUS_NOTFOUND;
238 if (r == gcr_overflow)
240 /* The line is too long. Give the user the opportunity to
241 enlarge the buffer. */
242 *errnop = ERANGE;
243 H_ERRNO_SET (NETDB_INTERNAL);
244 return NSS_STATUS_TRYAGAIN;
247 /* Everything OK. Now skip leading blanks. */
248 p = data->linebuffer;
249 while (isspace (*p))
250 ++p;
252 while (*p == '\0' || *p == '#' /* Ignore empty and comment lines. */
253 /* Parse the line. If it is invalid, loop to get the next
254 line of the file to parse. */
255 || ! (parse_result = parse_line (p, result, data, buflen, errnop
256 EXTRA_ARGS)));
258 if (__builtin_expect (parse_result == -1, 0))
260 H_ERRNO_SET (NETDB_INTERNAL);
261 return NSS_STATUS_TRYAGAIN;
264 /* Filled in RESULT with the next entry from the database file. */
265 return NSS_STATUS_SUCCESS;
269 /* Return the next entry from the database file, doing locking. */
270 enum nss_status
271 CONCAT(_nss_files_get,ENTNAME_r) (struct STRUCTURE *result, char *buffer,
272 size_t buflen, int *errnop H_ERRNO_PROTO)
274 /* Return next entry in host file. */
275 enum nss_status status = NSS_STATUS_SUCCESS;
277 __libc_lock_lock (lock);
279 /* Be prepared that the set*ent function was not called before. */
280 if (stream == NULL)
282 int save_errno = errno;
284 status = internal_setent (&stream);
286 __set_errno (save_errno);
289 if (status == NSS_STATUS_SUCCESS)
290 status = internal_getent (stream, result, buffer, buflen, errnop
291 H_ERRNO_ARG EXTRA_ARGS_VALUE);
293 __libc_lock_unlock (lock);
295 return status;
298 /* Macro for defining lookup functions for this file-based database.
300 NAME is the name of the lookup; e.g. `hostbyname'.
302 DB_CHAR, KEYPATTERN, KEYSIZE are ignored here but used by db-XXX.c
303 e.g. `1 + sizeof (id) * 4'.
305 PROTO is the potentially empty list of other parameters.
307 BREAK_IF_MATCH is a block of code which compares `struct STRUCTURE *result'
308 to the lookup key arguments and does `break;' if they match. */
310 #define DB_LOOKUP(name, db_char, keysize, keypattern, break_if_match, proto...)\
311 enum nss_status \
312 _nss_files_get##name##_r (proto, \
313 struct STRUCTURE *result, char *buffer, \
314 size_t buflen, int *errnop H_ERRNO_PROTO) \
316 enum nss_status status; \
317 FILE *stream = NULL; \
319 /* Open file. */ \
320 status = internal_setent (&stream); \
322 if (status == NSS_STATUS_SUCCESS) \
324 while ((status = internal_getent (stream, result, buffer, buflen, errnop \
325 H_ERRNO_ARG EXTRA_ARGS_VALUE)) \
326 == NSS_STATUS_SUCCESS) \
327 { break_if_match } \
329 internal_endent (&stream); \
332 return status; \