1/* Common code for file-based databases in nss_files module.
2 Copyright (C) 1996-2019 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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, see
17 <http://www.gnu.org/licenses/>. */
18
19#include <stdio.h>
20#include <ctype.h>
21#include <errno.h>
22#include <fcntl.h>
23#include <libc-lock.h>
24#include "nsswitch.h"
25
26#include <kernel-features.h>
27
28/* These symbols are defined by the including source file:
29
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").
33
34 NEED_H_ERRNO - defined iff an arg `int *herrnop' is used.
35
36 Also see files-parse.c.
37*/
38
39#define ENTNAME_r CONCAT(ENTNAME,_r)
40
41#define DATAFILE "/etc/" DATABASE
42
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
53
54#ifndef EXTRA_ARGS
55# define EXTRA_ARGS
56# define EXTRA_ARGS_DECL
57# define EXTRA_ARGS_VALUE
58#endif
59
60/* Locks the static variables in this file. */
61__libc_lock_define_initialized (static, lock)
62
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. */
66
67static FILE *stream;
68
69/* Open database file if not already opened. */
70static enum nss_status
71internal_setent (FILE **stream)
72{
73 enum nss_status status = NSS_STATUS_SUCCESS;
74
75 if (*stream == NULL)
76 {
77 *stream = fopen (DATAFILE, "rce");
78
79 if (*stream == NULL)
80 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
81 }
82 else
83 rewind (*stream);
84
85 return status;
86}
87
88
89/* Thread-safe, exported version of that. */
90enum nss_status
91CONCAT(_nss_files_set,ENTNAME) (int stayopen)
92{
93 enum nss_status status;
94
95 __libc_lock_lock (lock);
96
97 status = internal_setent (&stream);
98
99 __libc_lock_unlock (lock);
100
101 return status;
102}
103
104
105/* Close the database file. */
106static void
107internal_endent (FILE **stream)
108{
109 if (*stream != NULL)
110 {
111 fclose (*stream);
112 *stream = NULL;
113 }
114}
115
116
117/* Thread-safe, exported version of that. */
118enum nss_status
119CONCAT(_nss_files_end,ENTNAME) (void)
120{
121 __libc_lock_lock (lock);
122
123 internal_endent (&stream);
124
125 __libc_lock_unlock (lock);
126
127 return NSS_STATUS_SUCCESS;
128}
129
130
131/* Parsing the database file into `struct STRUCTURE' data structures. */
132static enum nss_status
133internal_getent (FILE *stream, struct STRUCTURE *result,
134 char *buffer, size_t buflen, int *errnop H_ERRNO_PROTO
135 EXTRA_ARGS_DECL)
136{
137 char *p;
138 struct parser_data *data = (void *) buffer;
139 size_t linebuflen = buffer + buflen - data->linebuffer;
140 int parse_result;
141
142 if (buflen < sizeof *data + 2)
143 {
144 *errnop = ERANGE;
145 H_ERRNO_SET (NETDB_INTERNAL);
146 return NSS_STATUS_TRYAGAIN;
147 }
148
149 while (true)
150 {
151 ssize_t r = __libc_readline_unlocked
152 (stream, data->linebuffer, linebuflen);
153 if (r < 0)
154 {
155 *errnop = errno;
156 H_ERRNO_SET (NETDB_INTERNAL);
157 if (*errnop == ERANGE)
158 /* Request larger buffer. */
159 return NSS_STATUS_TRYAGAIN;
160 else
161 /* Other read failure. */
162 return NSS_STATUS_UNAVAIL;
163 }
164 else if (r == 0)
165 {
166 /* End of file. */
167 H_ERRNO_SET (HOST_NOT_FOUND);
168 return NSS_STATUS_NOTFOUND;
169 }
170
171 /* Everything OK. Now skip leading blanks. */
172 p = data->linebuffer;
173 while (isspace (*p))
174 ++p;
175
176 /* Ignore empty and comment lines. */
177 if (*p == '\0' || *p == '#')
178 continue;
179
180 /* Parse the line. */
181 *errnop = EINVAL;
182 parse_result = parse_line (p, result, data, buflen, errnop EXTRA_ARGS);
183
184 if (parse_result == -1)
185 {
186 if (*errnop == ERANGE)
187 {
188 /* Return to the original file position at the beginning
189 of the line, so that the next call can read it again
190 if necessary. */
191 if (__fseeko64 (stream, -r, SEEK_CUR) != 0)
192 {
193 if (errno == ERANGE)
194 *errnop = EINVAL;
195 else
196 *errnop = errno;
197 H_ERRNO_SET (NETDB_INTERNAL);
198 return NSS_STATUS_UNAVAIL;
199 }
200 }
201 H_ERRNO_SET (NETDB_INTERNAL);
202 return NSS_STATUS_TRYAGAIN;
203 }
204
205 /* Return the data if parsed successfully. */
206 if (parse_result != 0)
207 return NSS_STATUS_SUCCESS;
208
209 /* If it is invalid, loop to get the next line of the file to
210 parse. */
211 }
212}
213
214
215/* Return the next entry from the database file, doing locking. */
216enum nss_status
217CONCAT(_nss_files_get,ENTNAME_r) (struct STRUCTURE *result, char *buffer,
218 size_t buflen, int *errnop H_ERRNO_PROTO)
219{
220 /* Return next entry in host file. */
221 enum nss_status status = NSS_STATUS_SUCCESS;
222
223 __libc_lock_lock (lock);
224
225 /* Be prepared that the set*ent function was not called before. */
226 if (stream == NULL)
227 {
228 int save_errno = errno;
229
230 status = internal_setent (&stream);
231
232 __set_errno (save_errno);
233 }
234
235 if (status == NSS_STATUS_SUCCESS)
236 status = internal_getent (stream, result, buffer, buflen, errnop
237 H_ERRNO_ARG EXTRA_ARGS_VALUE);
238
239 __libc_lock_unlock (lock);
240
241 return status;
242}
243
244/* Macro for defining lookup functions for this file-based database.
245
246 NAME is the name of the lookup; e.g. `hostbyname'.
247
248 DB_CHAR, KEYPATTERN, KEYSIZE are ignored here but used by db-XXX.c
249 e.g. `1 + sizeof (id) * 4'.
250
251 PROTO is the potentially empty list of other parameters.
252
253 BREAK_IF_MATCH is a block of code which compares `struct STRUCTURE *result'
254 to the lookup key arguments and does `break;' if they match. */
255
256#define DB_LOOKUP(name, db_char, keysize, keypattern, break_if_match, proto...)\
257enum nss_status \
258_nss_files_get##name##_r (proto, \
259 struct STRUCTURE *result, char *buffer, \
260 size_t buflen, int *errnop H_ERRNO_PROTO) \
261{ \
262 enum nss_status status; \
263 FILE *stream = NULL; \
264 \
265 /* Open file. */ \
266 status = internal_setent (&stream); \
267 \
268 if (status == NSS_STATUS_SUCCESS) \
269 { \
270 while ((status = internal_getent (stream, result, buffer, buflen, errnop \
271 H_ERRNO_ARG EXTRA_ARGS_VALUE)) \
272 == NSS_STATUS_SUCCESS) \
273 { break_if_match } \
274 \
275 internal_endent (&stream); \
276 } \
277 \
278 return status; \
279}
280