]> git.ipfire.org Git - thirdparty/glibc.git/blob - nss/nss_files/files-alias.c
b18361f66cf9a49f87a92e03cfe955467352bb0e
[thirdparty/glibc.git] / nss / nss_files / files-alias.c
1 /* Mail alias file parser in nss_files module.
2 Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
10
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public
17 License along with the GNU C Library; see the file COPYING.LIB. If not,
18 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
20
21 #include <aliases.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <bits/libc-lock.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29
30 #include "nsswitch.h"
31
32 /* Locks the static variables in this file. */
33 __libc_lock_define_initialized (static, lock)
34 \f
35 /* Maintenance of the shared stream open on the database file. */
36
37 static FILE *stream;
38 static fpos_t position;
39 static enum { none, getent, getby } last_use;
40
41
42 static enum nss_status
43 internal_setent (void)
44 {
45 enum nss_status status = NSS_STATUS_SUCCESS;
46
47 if (stream == NULL)
48 {
49 stream = fopen ("/etc/aliases", "r");
50
51 if (stream == NULL)
52 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
53 else
54 {
55 /* We have to make sure the file is `closed on exec'. */
56 int result, flags;
57
58 result = flags = fcntl (fileno (stream), F_GETFD, 0);
59 if (result >= 0)
60 {
61 flags |= FD_CLOEXEC;
62 result = fcntl (fileno (stream), F_SETFD, flags);
63 }
64 if (result < 0)
65 {
66 /* Something went wrong. Close the stream and return a
67 failure. */
68 fclose (stream);
69 stream = NULL;
70 status = NSS_STATUS_UNAVAIL;
71 }
72 }
73 }
74 else
75 rewind (stream);
76
77 return status;
78 }
79
80
81 /* Thread-safe, exported version of that. */
82 enum nss_status
83 _nss_files_setaliasent (void)
84 {
85 enum nss_status status;
86
87 __libc_lock_lock (lock);
88
89 status = internal_setent ();
90
91 if (status == NSS_STATUS_SUCCESS && fgetpos (stream, &position) < 0)
92 {
93 fclose (stream);
94 stream = NULL;
95 status = NSS_STATUS_UNAVAIL;
96 }
97
98 last_use = getent;
99
100 __libc_lock_unlock (lock);
101
102 return status;
103 }
104
105
106 /* Close the database file. */
107 static void
108 internal_endent (void)
109 {
110 if (stream != NULL)
111 {
112 fclose (stream);
113 stream = NULL;
114 }
115 }
116
117
118 /* Thread-safe, exported version of that. */
119 enum nss_status
120 _nss_files_endaliasent (void)
121 {
122 __libc_lock_lock (lock);
123
124 internal_endent ();
125
126 __libc_lock_unlock (lock);
127
128 return NSS_STATUS_SUCCESS;
129 }
130 \f
131 /* Parsing the database file into `struct aliasent' data structures. */
132 static enum nss_status
133 get_next_alias (const char *match, struct aliasent *result,
134 char *buffer, size_t buflen, int *errnop)
135 {
136 enum nss_status status = NSS_STATUS_NOTFOUND;
137 int ignore = 0;
138
139 result->alias_members_len = 0;
140
141 while (1)
142 {
143 /* Now we are ready to process the input. We have to read a
144 line and all its continuations and construct the array of
145 string pointers. This pointers and the names itself have to
146 be placed in BUFFER. */
147 char *first_unused = buffer;
148 size_t room_left = buflen - (buflen % __alignof__ (char *));
149 char *line;
150
151 /* Read the first line. It must contain the alias name and
152 possibly some alias names. */
153 first_unused[room_left - 1] = '\xff';
154 line = fgets (first_unused, room_left, stream);
155 if (line == NULL)
156 /* Nothing to read. */
157 break;
158 else if (first_unused[room_left - 1] != '\xff')
159 {
160 /* The line is too long for our buffer. */
161 no_more_room:
162 *errnop = ERANGE;
163 status = NSS_STATUS_TRYAGAIN;
164 break;
165 }
166 else
167 {
168 char *cp;
169
170 /* If we are in IGNORE mode and the first character in the
171 line is a white space we ignore the line and start
172 reading the next. */
173 if (ignore && isspace (*first_unused))
174 continue;
175
176 /* Terminate the line for any case. */
177 cp = strpbrk (first_unused, "#\n");
178 if (cp != NULL)
179 *cp = '\0';
180
181 /* Skip leading blanks. */
182 while (isspace (*line))
183 ++line;
184
185 result->alias_name = first_unused;
186 while (*line != '\0' && *line != ':')
187 *first_unused++ = *line++;
188 if (*line == '\0' || result->alias_name == first_unused)
189 /* No valid name. Ignore the line. */
190 continue;
191
192 *first_unused++ = '\0';
193 if (room_left < (size_t) (first_unused - result->alias_name))
194 goto no_more_room;
195 room_left -= first_unused - result->alias_name;
196 ++line;
197
198 /* When we search for a specific alias we can avoid all the
199 difficult parts and compare now with the name we are
200 looking for. If it does not match we simply ignore all
201 lines until the next line containing the start of a new
202 alias is found. */
203 ignore = (match != NULL
204 && __strcasecmp (result->alias_name, match) != 0);
205
206 while (! ignore)
207 {
208 while (isspace (*line))
209 ++line;
210
211 cp = first_unused;
212 while (*line != '\0' && *line != ',')
213 *first_unused++ = *line++;
214
215 if (first_unused != cp)
216 {
217 /* OK, we can have a regular entry or an include
218 request. */
219 if (*line != '\0')
220 ++line;
221 *first_unused++ = '\0';
222
223 if (strncmp (cp, ":include:", 9) != 0)
224 {
225 if (room_left < (first_unused - cp) + sizeof (char *))
226 goto no_more_room;
227 room_left -= (first_unused - cp) + sizeof (char *);
228
229 ++result->alias_members_len;
230 }
231 else
232 {
233 /* Oh well, we have to read the addressed file. */
234 FILE *listfile;
235 char *old_line = NULL;
236
237 first_unused = cp;
238
239 listfile = fopen (&cp[9], "r");
240 /* If the file does not exist we simply ignore
241 the statement. */
242 if (listfile != NULL
243 && (old_line = strdup (line)) != NULL)
244 {
245 while (! feof (listfile))
246 {
247 first_unused[room_left - 1] = '\xff';
248 line = fgets (first_unused, room_left, listfile);
249 if (line == NULL)
250 break;
251 if (first_unused[room_left - 1] != '\xff')
252 {
253 free (old_line);
254 goto no_more_room;
255 }
256
257 /* Parse the line. */
258 cp = strpbrk (line, "#\n");
259 if (cp != NULL)
260 *cp = '\0';
261
262 do
263 {
264 while (isspace (*line))
265 ++line;
266
267 cp = first_unused;
268 while (*line != '\0' && *line != ',')
269 *first_unused++ = *line++;
270
271 if (*line != '\0')
272 ++line;
273
274 if (first_unused != cp)
275 {
276 *first_unused++ = '\0';
277 if (room_left < ((first_unused - cp)
278 + __alignof__ (char *)))
279 {
280 free (old_line);
281 goto no_more_room;
282 }
283 room_left -= ((first_unused - cp)
284 + __alignof__ (char *));
285 ++result->alias_members_len;
286 }
287 }
288 while (*line != '\0');
289 }
290 fclose (listfile);
291
292 first_unused[room_left - 1] = '\0';
293 strncpy (first_unused, old_line, room_left);
294
295 if (old_line != NULL)
296 free (old_line);
297
298 if (first_unused[room_left - 1] != '\0')
299 goto no_more_room;
300 }
301 }
302 }
303
304 if (*line == '\0')
305 {
306 /* Get the next line. But we must be careful. We
307 must not read the whole line at once since it
308 might belong to the current alias. Simply read
309 the first character. If it is a white space we
310 have a continuation line. Otherwise it is the
311 beginning of a new alias and we can push back the
312 just read character. */
313 int ch;
314
315 ch = fgetc (stream);
316 if (ch == EOF || ch == '\n' || !isspace (ch))
317 {
318 size_t cnt;
319
320 /* Now prepare the return. Provide string
321 pointers for the currently selected aliases. */
322 if (ch != EOF)
323 ungetc (ch, stream);
324
325 /* Adjust the pointer so it is aligned for
326 storing pointers. */
327 first_unused += __alignof__ (char *) - 1;
328 first_unused -= ((first_unused - (char *) 0)
329 % __alignof__ (char *));
330 result->alias_members = (char **) first_unused;
331
332 /* Compute addresses of alias entry strings. */
333 cp = result->alias_name;
334 for (cnt = 0; cnt < result->alias_members_len; ++cnt)
335 {
336 cp = strchr (cp, '\0') + 1;
337 result->alias_members[cnt] = cp;
338 }
339
340 status = (result->alias_members_len == 0
341 ? NSS_STATUS_RETURN : NSS_STATUS_SUCCESS);
342 break;
343 }
344
345 /* The just read character is a white space and so
346 can be ignored. */
347 first_unused[room_left - 1] = '\xff';
348 line = fgets (first_unused, room_left, stream);
349 if (first_unused[room_left - 1] != '\xff')
350 goto no_more_room;
351 cp = strpbrk (line, "#\n");
352 if (cp != NULL)
353 *cp = '\0';
354 }
355 }
356 }
357
358 if (status != NSS_STATUS_NOTFOUND)
359 /* We read something. In any case break here. */
360 break;
361 }
362
363 return status;
364 }
365
366
367 enum nss_status
368 _nss_files_getaliasent_r (struct aliasent *result, char *buffer, size_t buflen,
369 int *errnop)
370 {
371 /* Return next entry in host file. */
372 enum nss_status status = NSS_STATUS_SUCCESS;
373
374 __libc_lock_lock (lock);
375
376 /* Be prepared that the set*ent function was not called before. */
377 if (stream == NULL)
378 status = internal_setent ();
379
380 if (status == NSS_STATUS_SUCCESS)
381 {
382 /* If the last use was not by the getent function we need the
383 position the stream. */
384 if (last_use != getent)
385 {
386 if (fsetpos (stream, &position) < 0)
387 status = NSS_STATUS_UNAVAIL;
388 else
389 last_use = getent;
390 }
391
392 if (status == NSS_STATUS_SUCCESS)
393 {
394 result->alias_local = 1;
395
396 /* Read lines until we get a definite result. */
397 do
398 status = get_next_alias (NULL, result, buffer, buflen, errnop);
399 while (status == NSS_STATUS_RETURN);
400
401 /* If we successfully read an entry remember this position. */
402 if (status == NSS_STATUS_SUCCESS)
403 fgetpos (stream, &position);
404 else
405 last_use = none;
406 }
407 }
408
409 __libc_lock_unlock (lock);
410
411 return status;
412 }
413
414
415 enum nss_status
416 _nss_files_getaliasbyname_r (const char *name, struct aliasent *result,
417 char *buffer, size_t buflen, int *errnop)
418 {
419 /* Return next entry in host file. */
420 enum nss_status status = NSS_STATUS_SUCCESS;
421
422 if (name == NULL)
423 {
424 __set_errno (EINVAL);
425 return NSS_STATUS_UNAVAIL;
426 }
427
428 __libc_lock_lock (lock);
429
430 /* Open the stream or rest it. */
431 status = internal_setent ();
432 last_use = getby;
433
434 if (status == NSS_STATUS_SUCCESS)
435 {
436 result->alias_local = 1;
437
438 /* Read lines until we get a definite result. */
439 do
440 status = get_next_alias (name, result, buffer, buflen, errnop);
441 while (status == NSS_STATUS_RETURN);
442 }
443
444 internal_endent ();
445
446 __libc_lock_unlock (lock);
447
448 return status;
449 }