1/* Common code for file-based databases in nss_files module.
2 Copyright (C) 1996-2020 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 <https://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#include <nss_files.h>
26
27#include <kernel-features.h>
28
29/* These symbols are defined by the including source file:
30
31 ENTNAME -- database name of the structure and functions (hostent, pwent).
32 STRUCTURE -- struct name, define only if not ENTNAME (passwd, group).
33 DATABASE -- string of the database file's name ("hosts", "passwd").
34
35 NEED_H_ERRNO - defined iff an arg `int *herrnop' is used.
36
37 Also see files-parse.c.
38*/
39
40#define ENTNAME_r CONCAT(ENTNAME,_r)
41
42#define DATAFILE "/etc/" DATABASE
43
44#ifdef NEED_H_ERRNO
45# include <netdb.h>
46# define H_ERRNO_PROTO , int *herrnop
47# define H_ERRNO_ARG , herrnop
48# define H_ERRNO_SET(val) (*herrnop = (val))
49#else
50# define H_ERRNO_PROTO
51# define H_ERRNO_ARG
52# define H_ERRNO_SET(val) ((void) 0)
53#endif
54
55#ifndef EXTRA_ARGS
56# define EXTRA_ARGS
57# define EXTRA_ARGS_DECL
58# define EXTRA_ARGS_VALUE
59#endif
60
61/* Locks the static variables in this file. */
62__libc_lock_define_initialized (static, lock)
63
64/* Maintenance of the stream open on the database file. For getXXent
65 operations the stream needs to be held open across calls, the other
66 getXXbyYY operations all use their own stream. */
67
68static FILE *stream;
69
70/* Open database file if not already opened. */
71static enum nss_status
72internal_setent (FILE **stream)
73{
74 enum nss_status status = NSS_STATUS_SUCCESS;
75
76 if (*stream == NULL)
77 {
78 *stream = __nss_files_fopen (DATAFILE);
79
80 if (*stream == NULL)
81 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
82 }
83 else
84 rewind (*stream);
85
86 return status;
87}
88
89
90/* Thread-safe, exported version of that. */
91enum nss_status
92CONCAT(_nss_files_set,ENTNAME) (int stayopen)
93{
94 enum nss_status status;
95
96 __libc_lock_lock (lock);
97
98 status = internal_setent (&stream);
99
100 __libc_lock_unlock (lock);
101
102 return status;
103}
104
105
106/* Close the database file. */
107static void
108internal_endent (FILE **stream)
109{
110 if (*stream != NULL)
111 {
112 fclose (*stream);
113 *stream = NULL;
114 }
115}
116
117
118/* Thread-safe, exported version of that. */
119enum nss_status
120CONCAT(_nss_files_end,ENTNAME) (void)
121{
122 __libc_lock_lock (lock);
123
124 internal_endent (&stream);
125
126 __libc_lock_unlock (lock);
127
128 return NSS_STATUS_SUCCESS;
129}
130
131
132/* Parsing the database file into `struct STRUCTURE' data structures. */
133static enum nss_status
134internal_getent (FILE *stream, struct STRUCTURE *result,
135 char *buffer, size_t buflen, int *errnop H_ERRNO_PROTO
136 EXTRA_ARGS_DECL)
137{
138 struct parser_data *data = (void *) buffer;
139 size_t linebuflen = buffer + buflen - data->linebuffer;
140 int saved_errno = errno; /* Do not clobber errno on success. */
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 off64_t original_offset;
152 int ret = __nss_readline (stream, data->linebuffer, linebuflen,
153 &original_offset);
154 if (ret == ENOENT)
155 {
156 /* End of file. */
157 H_ERRNO_SET (HOST_NOT_FOUND);
158 __set_errno (saved_errno);
159 return NSS_STATUS_NOTFOUND;
160 }
161 else if (ret == 0)
162 {
163 ret = __nss_parse_line_result (stream, original_offset,
164 parse_line (data->linebuffer,
165 result, data, buflen,
166 errnop EXTRA_ARGS));
167 if (ret == 0)
168 {
169 /* Line has been parsed successfully. */
170 __set_errno (saved_errno);
171 return NSS_STATUS_SUCCESS;
172 }
173 else if (ret == EINVAL)
174 /* If it is invalid, loop to get the next line of the file
175 to parse. */
176 continue;
177 }
178
179 *errnop = ret;
180 H_ERRNO_SET (NETDB_INTERNAL);
181 if (ret == ERANGE)
182 /* Request larger buffer. */
183 return NSS_STATUS_TRYAGAIN;
184 else
185 /* Other read failure. */
186 return NSS_STATUS_UNAVAIL;
187 }
188}
189
190
191/* Return the next entry from the database file, doing locking. */
192enum nss_status
193CONCAT(_nss_files_get,ENTNAME_r) (struct STRUCTURE *result, char *buffer,
194 size_t buflen, int *errnop H_ERRNO_PROTO)
195{
196 /* Return next entry in host file. */
197 enum nss_status status = NSS_STATUS_SUCCESS;
198
199 __libc_lock_lock (lock);
200
201 /* Be prepared that the set*ent function was not called before. */
202 if (stream == NULL)
203 {
204 int save_errno = errno;
205
206 status = internal_setent (&stream);
207
208 __set_errno (save_errno);
209 }
210
211 if (status == NSS_STATUS_SUCCESS)
212 status = internal_getent (stream, result, buffer, buflen, errnop
213 H_ERRNO_ARG EXTRA_ARGS_VALUE);
214
215 __libc_lock_unlock (lock);
216
217 return status;
218}
219
220/* Macro for defining lookup functions for this file-based database.
221
222 NAME is the name of the lookup; e.g. `hostbyname'.
223
224 DB_CHAR, KEYPATTERN, KEYSIZE are ignored here but used by db-XXX.c
225 e.g. `1 + sizeof (id) * 4'.
226
227 PROTO is the potentially empty list of other parameters.
228
229 BREAK_IF_MATCH is a block of code which compares `struct STRUCTURE *result'
230 to the lookup key arguments and does `break;' if they match. */
231
232#define DB_LOOKUP(name, db_char, keysize, keypattern, break_if_match, proto...)\
233enum nss_status \
234_nss_files_get##name##_r (proto, \
235 struct STRUCTURE *result, char *buffer, \
236 size_t buflen, int *errnop H_ERRNO_PROTO) \
237{ \
238 enum nss_status status; \
239 FILE *stream = NULL; \
240 \
241 /* Open file. */ \
242 status = internal_setent (&stream); \
243 \
244 if (status == NSS_STATUS_SUCCESS) \
245 { \
246 while ((status = internal_getent (stream, result, buffer, buflen, errnop \
247 H_ERRNO_ARG EXTRA_ARGS_VALUE)) \
248 == NSS_STATUS_SUCCESS) \
249 { break_if_match } \
250 \
251 internal_endent (&stream); \
252 } \
253 \
254 return status; \
255}
256