]> git.ipfire.org Git - thirdparty/glibc.git/blob - nss/nss_files/files-alias.c
Update.
[thirdparty/glibc.git] / nss / nss_files / files-alias.c
1 /* Mail alias file parser in nss_files module.
2 Copyright (C) 1996, 1997 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)
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] = '\0';
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] != '\0')
159 {
160 /* The line is too long for our buffer. */
161 no_more_room:
162 __set_errno (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 && strcmp (result->alias_name, match) != 0;
204
205 while (! ignore)
206 {
207 while (isspace (*line))
208 ++line;
209
210 cp = first_unused;
211 while (*line != '\0' && *line != ',')
212 *first_unused++ = *line++;
213
214 if (first_unused != cp)
215 {
216 /* OK, we can have a regular entry or an include
217 request. */
218 if (*line != '\0')
219 ++line;
220 *first_unused++ = '\0';
221
222 if (strncmp (cp, ":include:", 9) != 0)
223 {
224 if (room_left < (first_unused - cp) + sizeof (char *))
225 goto no_more_room;
226 room_left -= (first_unused - cp) + sizeof (char *);
227
228 ++result->alias_members_len;
229 }
230 else
231 {
232 /* Oh well, we have to read the addressed file. */
233 FILE *listfile;
234 char *old_line = NULL;
235
236 first_unused = cp;
237
238 listfile = fopen (&cp[9], "r");
239 /* If the file does not exist we simply ignore
240 the statement. */
241 if (listfile != NULL
242 && (old_line = strdup (line)) != NULL)
243 {
244 while (! feof (listfile))
245 {
246 first_unused[room_left - 1] = '\0';
247 line = fgets (first_unused, room_left, listfile);
248 if (line == NULL)
249 break;
250 if (first_unused[room_left - 1] != '\0')
251 {
252 free (old_line);
253 goto no_more_room;
254 }
255
256 /* Parse the line. */
257 cp = strpbrk (line, "#\n");
258 if (cp != NULL)
259 *cp = '\0';
260
261 do
262 {
263 while (isspace (*line))
264 ++line;
265
266 cp = first_unused;
267 while (*line != '\0' && *line != ',')
268 *first_unused++ = *line++;
269
270 if (*line != '\0')
271 ++line;
272
273 if (first_unused != cp)
274 {
275 *first_unused++ = '\0';
276 if (room_left < ((first_unused - cp)
277 + __alignof__ (char *)))
278 {
279 free (old_line);
280 goto no_more_room;
281 }
282 room_left -= ((first_unused - cp)
283 + __alignof__ (char *));
284 ++result->alias_members_len;
285 }
286 }
287 while (*line != '\0');
288 }
289 fclose (listfile);
290
291 first_unused[room_left - 1] = '\0';
292 strncpy (first_unused, old_line, room_left);
293
294 if (old_line != NULL)
295 free (old_line);
296
297 if (first_unused[room_left - 1] != '\0')
298 goto no_more_room;
299 }
300 }
301 }
302
303 if (*line == '\0')
304 {
305 /* Get the next line. But we must be careful. We
306 must not read the whole line at once since it
307 might belong to the current alias. Simply read
308 the first character. If it is a white space we
309 have a continuation line. Otherwise it is the
310 beginning of a new alias and we can push back the
311 just read character. */
312 int ch;
313
314 ch = fgetc (stream);
315 if (ch == EOF || ch == '\n' || !isspace (ch))
316 {
317 size_t cnt;
318
319 /* Now prepare the return. Provide string
320 pointers for the currently selected aliases. */
321 if (ch != EOF)
322 ungetc (ch, stream);
323
324 /* Adjust the pointer so it is aligned for
325 storing pointers. */
326 first_unused += __alignof__ (char *) - 1;
327 first_unused -= ((first_unused - (char *) 0)
328 % __alignof__ (char *));
329 result->alias_members = (char **) first_unused;
330
331 /* Compute addresses of alias entry strings. */
332 cp = result->alias_name;
333 for (cnt = 0; cnt < result->alias_members_len; ++cnt)
334 {
335 cp = strchr (cp, '\0') + 1;
336 result->alias_members[cnt] = cp;
337 }
338
339 status = (result->alias_members_len == 0
340 ? NSS_STATUS_RETURN : NSS_STATUS_SUCCESS);
341 break;
342 }
343
344 /* The just read character is a white space and so
345 can be ignored. */
346 first_unused[room_left - 1] = '\0';
347 line = fgets (first_unused, room_left, stream);
348 if (first_unused[room_left - 1] != '\0')
349 goto no_more_room;
350 cp = strpbrk (line, "#\n");
351 if (cp != NULL)
352 *cp = '\0';
353 }
354 }
355 }
356
357 if (status != NSS_STATUS_NOTFOUND)
358 /* We read something. In any case break here. */
359 break;
360 }
361
362 return status;
363 }
364
365
366 enum nss_status
367 _nss_files_getaliasent_r (struct aliasent *result, char *buffer, size_t buflen)
368 {
369 /* Return next entry in host file. */
370 enum nss_status status = NSS_STATUS_SUCCESS;
371
372 __libc_lock_lock (lock);
373
374 /* Be prepared that the set*ent function was not called before. */
375 if (stream == NULL)
376 status = internal_setent ();
377
378 if (status == NSS_STATUS_SUCCESS)
379 {
380 /* If the last use was not by the getent function we need the
381 position the stream. */
382 if (last_use != getent)
383 if (fsetpos (stream, &position) < 0)
384 status = NSS_STATUS_UNAVAIL;
385 else
386 last_use = getent;
387
388 if (status == NSS_STATUS_SUCCESS)
389 {
390 result->alias_local = 1;
391
392 /* Read lines until we get a definite result. */
393 do
394 status = get_next_alias (NULL, result, buffer, buflen);
395 while (status == NSS_STATUS_RETURN);
396
397 /* If we successfully read an entry remember this position. */
398 if (status == NSS_STATUS_SUCCESS)
399 fgetpos (stream, &position);
400 else
401 last_use = none;
402 }
403 }
404
405 __libc_lock_unlock (lock);
406
407 return status;
408 }
409
410
411 enum nss_status
412 _nss_files_getaliasbyname_r (const char *name, struct aliasent *result,
413 char *buffer, size_t buflen)
414 {
415 /* Return next entry in host file. */
416 enum nss_status status = NSS_STATUS_SUCCESS;
417
418 if (name == NULL)
419 {
420 __set_errno (EINVAL);
421 return NSS_STATUS_UNAVAIL;
422 }
423
424 __libc_lock_lock (lock);
425
426 /* Open the stream or rest it. */
427 status = internal_setent ();
428 last_use = getby;
429
430 if (status == NSS_STATUS_SUCCESS)
431 {
432 result->alias_local = 1;
433
434 /* Read lines until we get a definite result. */
435 do
436 status = get_next_alias (name, result, buffer, buflen);
437 while (status == NSS_STATUS_RETURN);
438 }
439
440 internal_endent ();
441
442 __libc_lock_unlock (lock);
443
444 return status;
445 }