1/* Common code for file-based databases in nss_files module.
2 Copyright (C) 1996-2022 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_ARG_OR_NULL herrnop
49# define H_ERRNO_SET(val) (*herrnop = (val))
50#else
51# define H_ERRNO_PROTO
52# define H_ERRNO_ARG
53# define H_ERRNO_ARG_OR_NULL NULL
54# define H_ERRNO_SET(val) ((void) 0)
55#endif
56
57#ifndef EXTRA_ARGS
58# define EXTRA_ARGS
59# define EXTRA_ARGS_DECL
60# define EXTRA_ARGS_VALUE
61#endif
62
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
68/* Open database file if not already opened. */
69static enum nss_status
70internal_setent (FILE **stream)
71{
72 enum nss_status status = NSS_STATUS_SUCCESS;
73
74 if (*stream == NULL)
75 {
76 *stream = __nss_files_fopen (DATAFILE);
77
78 if (*stream == NULL)
79 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
80 }
81 else
82 rewind (*stream);
83
84 return status;
85}
86
87
88/* Thread-safe, exported version of that. */
89enum nss_status
90CONCAT(_nss_files_set,ENTNAME) (int stayopen)
91{
92 return __nss_files_data_setent (CONCAT (nss_file_, ENTNAME), DATAFILE);
93}
94libc_hidden_def (CONCAT (_nss_files_set,ENTNAME))
95
96enum nss_status
97CONCAT(_nss_files_end,ENTNAME) (void)
98{
99 return __nss_files_data_endent (CONCAT (nss_file_, ENTNAME));
100}
101libc_hidden_def (CONCAT (_nss_files_end,ENTNAME))
102
103
104/* Parsing the database file into `struct STRUCTURE' data structures. */
105static enum nss_status
106internal_getent (FILE *stream, struct STRUCTURE *result,
107 char *buffer, size_t buflen, int *errnop H_ERRNO_PROTO
108 EXTRA_ARGS_DECL)
109{
110 struct parser_data *data = (void *) buffer;
111 size_t linebuflen = buffer + buflen - data->linebuffer;
112 int saved_errno = errno; /* Do not clobber errno on success. */
113
114 if (buflen < sizeof *data + 2)
115 {
116 *errnop = ERANGE;
117 H_ERRNO_SET (NETDB_INTERNAL);
118 return NSS_STATUS_TRYAGAIN;
119 }
120
121 while (true)
122 {
123 off64_t original_offset;
124 int ret = __nss_readline (stream, data->linebuffer, linebuflen,
125 &original_offset);
126 if (ret == ENOENT)
127 {
128 /* End of file. */
129 H_ERRNO_SET (HOST_NOT_FOUND);
130 __set_errno (saved_errno);
131 return NSS_STATUS_NOTFOUND;
132 }
133 else if (ret == 0)
134 {
135 ret = __nss_parse_line_result (stream, original_offset,
136 parse_line (line: data->linebuffer,
137 generic_result: result, data, datalen: buflen,
138 errnop EXTRA_ARGS));
139 if (ret == 0)
140 {
141 /* Line has been parsed successfully. */
142 __set_errno (saved_errno);
143 return NSS_STATUS_SUCCESS;
144 }
145 else if (ret == EINVAL)
146 /* If it is invalid, loop to get the next line of the file
147 to parse. */
148 continue;
149 }
150
151 *errnop = ret;
152 H_ERRNO_SET (NETDB_INTERNAL);
153 if (ret == ERANGE)
154 /* Request larger buffer. */
155 return NSS_STATUS_TRYAGAIN;
156 else
157 /* Other read failure. */
158 return NSS_STATUS_UNAVAIL;
159 }
160}
161
162
163/* Return the next entry from the database file, doing locking. */
164enum nss_status
165CONCAT(_nss_files_get,ENTNAME_r) (struct STRUCTURE *result, char *buffer,
166 size_t buflen, int *errnop H_ERRNO_PROTO)
167{
168 /* Return next entry in host file. */
169
170 struct nss_files_per_file_data *data;
171 enum nss_status status = __nss_files_data_open (&data,
172 CONCAT (nss_file_, ENTNAME),
173 DATAFILE,
174 errnop, H_ERRNO_ARG_OR_NULL);
175 if (status != NSS_STATUS_SUCCESS)
176 return status;
177
178 status = internal_getent (stream: data->stream, result, buffer, buflen, errnop
179 H_ERRNO_ARG EXTRA_ARGS_VALUE);
180
181 __nss_files_data_put (data);
182 return status;
183}
184libc_hidden_def (CONCAT (_nss_files_get,ENTNAME_r))
185
186/* Macro for defining lookup functions for this file-based database.
187
188 NAME is the name of the lookup; e.g. `hostbyname'.
189
190 DB_CHAR, KEYPATTERN, KEYSIZE are ignored here but used by db-XXX.c
191 e.g. `1 + sizeof (id) * 4'.
192
193 PROTO is the potentially empty list of other parameters.
194
195 BREAK_IF_MATCH is a block of code which compares `struct STRUCTURE *result'
196 to the lookup key arguments and does `break;' if they match. */
197
198#define DB_LOOKUP(name, db_char, keysize, keypattern, break_if_match, proto...)\
199enum nss_status \
200_nss_files_get##name##_r (proto, \
201 struct STRUCTURE *result, char *buffer, \
202 size_t buflen, int *errnop H_ERRNO_PROTO) \
203{ \
204 enum nss_status status; \
205 FILE *stream = NULL; \
206 \
207 /* Open file. */ \
208 status = internal_setent (&stream); \
209 \
210 if (status == NSS_STATUS_SUCCESS) \
211 { \
212 while ((status = internal_getent (stream, result, buffer, buflen, errnop \
213 H_ERRNO_ARG EXTRA_ARGS_VALUE)) \
214 == NSS_STATUS_SUCCESS) \
215 { break_if_match } \
216 \
217 fclose (stream); \
218 } \
219 \
220 return status; \
221} \
222libc_hidden_def (_nss_files_get##name##_r)
223

source code of glibc/nss/nss_files/files-XXX.c