1 | /* Copyright (c) 1998-2021 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998. |
4 | |
5 | This program is free software; you can redistribute it and/or modify |
6 | it under the terms of the GNU General Public License as published |
7 | by the Free Software Foundation; version 2 of the License, or |
8 | (at your option) any later version. |
9 | |
10 | This program 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 |
13 | GNU General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU General Public License |
16 | along with this program; if not, see <https://www.gnu.org/licenses/>. */ |
17 | |
18 | #include <ctype.h> |
19 | #include <errno.h> |
20 | #include <error.h> |
21 | #include <libintl.h> |
22 | #include <malloc.h> |
23 | #include <pwd.h> |
24 | #include <stdio.h> |
25 | #include <stdio_ext.h> |
26 | #include <stdlib.h> |
27 | #include <string.h> |
28 | #include <unistd.h> |
29 | #include <sys/param.h> |
30 | #include <sys/types.h> |
31 | |
32 | #include "dbg_log.h" |
33 | #include "nscd.h" |
34 | |
35 | |
36 | /* Names of the databases. */ |
37 | const char *const dbnames[lastdb] = |
38 | { |
39 | [pwddb] = "passwd" , |
40 | [grpdb] = "group" , |
41 | [hstdb] = "hosts" , |
42 | [servdb] = "services" , |
43 | [netgrdb] = "netgroup" |
44 | }; |
45 | |
46 | |
47 | static int |
48 | find_db (const char *name) |
49 | { |
50 | for (int cnt = 0; cnt < lastdb; ++cnt) |
51 | if (strcmp (name, dbnames[cnt]) == 0) |
52 | return cnt; |
53 | |
54 | error (0, 0, _("database %s is not supported" ), name); |
55 | return -1; |
56 | } |
57 | |
58 | int |
59 | nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb]) |
60 | { |
61 | FILE *fp; |
62 | char *line, *cp, *entry, *arg1, *arg2; |
63 | size_t len; |
64 | int cnt; |
65 | const unsigned int initial_error_message_count = error_message_count; |
66 | |
67 | /* Open the configuration file. */ |
68 | fp = fopen (fname, "r" ); |
69 | if (fp == NULL) |
70 | return -1; |
71 | |
72 | /* The stream is not used by more than one thread. */ |
73 | (void) __fsetlocking (fp, FSETLOCKING_BYCALLER); |
74 | |
75 | line = NULL; |
76 | len = 0; |
77 | |
78 | do |
79 | { |
80 | ssize_t n = getline (&line, &len, fp); |
81 | if (n < 0) |
82 | break; |
83 | if (line[n - 1] == '\n') |
84 | line[n - 1] = '\0'; |
85 | |
86 | /* Because the file format does not know any form of quoting we |
87 | can search forward for the next '#' character and if found |
88 | make it terminating the line. */ |
89 | *strchrnul (line, '#') = '\0'; |
90 | |
91 | /* If the line is blank it is ignored. */ |
92 | if (line[0] == '\0') |
93 | continue; |
94 | |
95 | entry = line; |
96 | while (isspace (*entry) && *entry != '\0') |
97 | ++entry; |
98 | cp = entry; |
99 | while (!isspace (*cp) && *cp != '\0') |
100 | ++cp; |
101 | arg1 = cp; |
102 | ++arg1; |
103 | *cp = '\0'; |
104 | if (strlen (entry) == 0) |
105 | error (0, 0, _("Parse error: %s" ), line); |
106 | while (isspace (*arg1) && *arg1 != '\0') |
107 | ++arg1; |
108 | cp = arg1; |
109 | while (!isspace (*cp) && *cp != '\0') |
110 | ++cp; |
111 | arg2 = cp; |
112 | ++arg2; |
113 | *cp = '\0'; |
114 | if (strlen (arg2) > 0) |
115 | { |
116 | while (isspace (*arg2) && *arg2 != '\0') |
117 | ++arg2; |
118 | cp = arg2; |
119 | while (!isspace (*cp) && *cp != '\0') |
120 | ++cp; |
121 | *cp = '\0'; |
122 | } |
123 | |
124 | if (strcmp (entry, "positive-time-to-live" ) == 0) |
125 | { |
126 | int idx = find_db (arg1); |
127 | if (idx >= 0) |
128 | dbs[idx].postimeout = atol (arg2); |
129 | } |
130 | else if (strcmp (entry, "negative-time-to-live" ) == 0) |
131 | { |
132 | int idx = find_db (arg1); |
133 | if (idx >= 0) |
134 | dbs[idx].negtimeout = atol (arg2); |
135 | } |
136 | else if (strcmp (entry, "suggested-size" ) == 0) |
137 | { |
138 | int idx = find_db (arg1); |
139 | if (idx >= 0) |
140 | dbs[idx].suggested_module |
141 | = atol (arg2) ?: DEFAULT_SUGGESTED_MODULE; |
142 | } |
143 | else if (strcmp (entry, "enable-cache" ) == 0) |
144 | { |
145 | int idx = find_db (arg1); |
146 | if (idx >= 0) |
147 | { |
148 | if (strcmp (arg2, "no" ) == 0) |
149 | dbs[idx].enabled = 0; |
150 | else if (strcmp (arg2, "yes" ) == 0) |
151 | dbs[idx].enabled = 1; |
152 | } |
153 | } |
154 | else if (strcmp (entry, "check-files" ) == 0) |
155 | { |
156 | int idx = find_db (arg1); |
157 | if (idx >= 0) |
158 | { |
159 | if (strcmp (arg2, "no" ) == 0) |
160 | dbs[idx].check_file = 0; |
161 | else if (strcmp (arg2, "yes" ) == 0) |
162 | dbs[idx].check_file = 1; |
163 | } |
164 | } |
165 | else if (strcmp (entry, "max-db-size" ) == 0) |
166 | { |
167 | int idx = find_db (arg1); |
168 | if (idx >= 0) |
169 | dbs[idx].max_db_size = atol (arg2) ?: DEFAULT_MAX_DB_SIZE; |
170 | } |
171 | else if (strcmp (entry, "logfile" ) == 0) |
172 | set_logfile (arg1); |
173 | else if (strcmp (entry, "debug-level" ) == 0) |
174 | { |
175 | int level = atoi (arg1); |
176 | if (level > 0) |
177 | debug_level = level; |
178 | } |
179 | else if (strcmp (entry, "threads" ) == 0) |
180 | { |
181 | if (nthreads == -1) |
182 | nthreads = MAX (atol (arg1), lastdb); |
183 | } |
184 | else if (strcmp (entry, "max-threads" ) == 0) |
185 | { |
186 | max_nthreads = MAX (atol (arg1), lastdb); |
187 | } |
188 | else if (strcmp (entry, "server-user" ) == 0) |
189 | { |
190 | if (!arg1) |
191 | error (0, 0, _("Must specify user name for server-user option" )); |
192 | else |
193 | { |
194 | free ((char *) server_user); |
195 | server_user = xstrdup (arg1); |
196 | } |
197 | } |
198 | else if (strcmp (entry, "stat-user" ) == 0) |
199 | { |
200 | if (arg1 == NULL) |
201 | error (0, 0, _("Must specify user name for stat-user option" )); |
202 | else |
203 | { |
204 | free ((char *) stat_user); |
205 | stat_user = xstrdup (arg1); |
206 | |
207 | struct passwd *pw = getpwnam (stat_user); |
208 | if (pw != NULL) |
209 | stat_uid = pw->pw_uid; |
210 | } |
211 | } |
212 | else if (strcmp (entry, "persistent" ) == 0) |
213 | { |
214 | int idx = find_db (arg1); |
215 | if (idx >= 0) |
216 | { |
217 | if (strcmp (arg2, "no" ) == 0) |
218 | dbs[idx].persistent = 0; |
219 | else if (strcmp (arg2, "yes" ) == 0) |
220 | dbs[idx].persistent = 1; |
221 | } |
222 | } |
223 | else if (strcmp (entry, "shared" ) == 0) |
224 | { |
225 | int idx = find_db (arg1); |
226 | if (idx >= 0) |
227 | { |
228 | if (strcmp (arg2, "no" ) == 0) |
229 | dbs[idx].shared = 0; |
230 | else if (strcmp (arg2, "yes" ) == 0) |
231 | dbs[idx].shared = 1; |
232 | } |
233 | } |
234 | else if (strcmp (entry, "reload-count" ) == 0) |
235 | { |
236 | if (strcasecmp (arg1, "unlimited" ) == 0) |
237 | reload_count = UINT_MAX; |
238 | else |
239 | { |
240 | unsigned long int count = strtoul (arg1, NULL, 0); |
241 | if (count > UINT8_MAX - 1) |
242 | reload_count = UINT_MAX; |
243 | else |
244 | reload_count = count; |
245 | } |
246 | } |
247 | else if (strcmp (entry, "paranoia" ) == 0) |
248 | { |
249 | if (strcmp (arg1, "no" ) == 0) |
250 | paranoia = 0; |
251 | else if (strcmp (arg1, "yes" ) == 0) |
252 | paranoia = 1; |
253 | } |
254 | else if (strcmp (entry, "restart-interval" ) == 0) |
255 | { |
256 | if (arg1 != NULL) |
257 | restart_interval = atol (arg1); |
258 | else |
259 | error (0, 0, _("Must specify value for restart-interval option" )); |
260 | } |
261 | else if (strcmp (entry, "auto-propagate" ) == 0) |
262 | { |
263 | int idx = find_db (arg1); |
264 | if (idx >= 0) |
265 | { |
266 | if (strcmp (arg2, "no" ) == 0) |
267 | dbs[idx].propagate = 0; |
268 | else if (strcmp (arg2, "yes" ) == 0) |
269 | dbs[idx].propagate = 1; |
270 | } |
271 | } |
272 | else |
273 | error (0, 0, _("Unknown option: %s %s %s" ), entry, arg1, arg2); |
274 | } |
275 | while (!feof_unlocked (fp)); |
276 | |
277 | if (paranoia) |
278 | { |
279 | restart_time = time (NULL) + restart_interval; |
280 | |
281 | /* Save the old current workding directory if we are in paranoia |
282 | mode. We have to change back to it. */ |
283 | oldcwd = get_current_dir_name (); |
284 | if (oldcwd == NULL) |
285 | { |
286 | error (0, 0, _("\ |
287 | cannot get current working directory: %s; disabling paranoia mode" ), |
288 | strerror (errno)); |
289 | paranoia = 0; |
290 | } |
291 | } |
292 | |
293 | /* Enforce sanity. */ |
294 | if (max_nthreads < nthreads) |
295 | max_nthreads = nthreads; |
296 | |
297 | for (cnt = 0; cnt < lastdb; ++cnt) |
298 | { |
299 | size_t datasize = (sizeof (struct database_pers_head) |
300 | + roundup (dbs[cnt].suggested_module |
301 | * sizeof (ref_t), ALIGN) |
302 | + (dbs[cnt].suggested_module |
303 | * DEFAULT_DATASIZE_PER_BUCKET)); |
304 | if (datasize > dbs[cnt].max_db_size) |
305 | { |
306 | error (0, 0, _("maximum file size for %s database too small" ), |
307 | dbnames[cnt]); |
308 | dbs[cnt].max_db_size = datasize; |
309 | } |
310 | |
311 | } |
312 | |
313 | /* Free the buffer. */ |
314 | free (line); |
315 | /* Close configuration file. */ |
316 | fclose (fp); |
317 | |
318 | return error_message_count != initial_error_message_count; |
319 | } |
320 | |