1 /* Mail alias file parser in nss_files module.
2 Copyright (C) 1996-2016 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
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 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, see
18 <http://www.gnu.org/licenses/>. */
24 #include <libc-lock.h>
29 #include <kernel-features.h>
33 /* Locks the static variables in this file. */
34 __libc_lock_define_initialized (static, lock
)
36 /* Maintenance of the stream open on the database file. For getXXent
37 operations the stream needs to be held open across calls, the other
38 getXXbyYY operations all use their own stream. */
43 static enum nss_status
44 internal_setent (FILE **stream
)
46 enum nss_status status
= NSS_STATUS_SUCCESS
;
50 *stream
= fopen ("/etc/aliases", "rce");
53 status
= errno
== EAGAIN
? NSS_STATUS_TRYAGAIN
: NSS_STATUS_UNAVAIL
;
56 #if !defined O_CLOEXEC || !defined __ASSUME_O_CLOEXEC
58 if (__have_o_cloexec
<= 0)
61 /* We have to make sure the file is `closed on exec'. */
65 result
= flags
= fcntl (fileno (*stream
), F_GETFD
, 0);
69 if (__have_o_cloexec
== 0)
70 __have_o_cloexec
= (flags
& FD_CLOEXEC
) == 0 ? -1 : 1;
71 if (__have_o_cloexec
< 0)
75 result
= fcntl (fileno (*stream
), F_SETFD
, flags
);
80 /* Something went wrong. Close the stream and return a
84 status
= NSS_STATUS_UNAVAIL
;
97 /* Thread-safe, exported version of that. */
99 _nss_files_setaliasent (void)
101 enum nss_status status
;
103 __libc_lock_lock (lock
);
105 status
= internal_setent (&stream
);
107 __libc_lock_unlock (lock
);
113 /* Close the database file. */
115 internal_endent (FILE **stream
)
125 /* Thread-safe, exported version of that. */
127 _nss_files_endaliasent (void)
129 __libc_lock_lock (lock
);
131 internal_endent (&stream
);
133 __libc_lock_unlock (lock
);
135 return NSS_STATUS_SUCCESS
;
138 /* Parsing the database file into `struct aliasent' data structures. */
139 static enum nss_status
140 get_next_alias (FILE *stream
, const char *match
, struct aliasent
*result
,
141 char *buffer
, size_t buflen
, int *errnop
)
143 enum nss_status status
= NSS_STATUS_NOTFOUND
;
146 result
->alias_members_len
= 0;
150 /* Now we are ready to process the input. We have to read a
151 line and all its continuations and construct the array of
152 string pointers. This pointers and the names itself have to
153 be placed in BUFFER. */
154 char *first_unused
= buffer
;
155 size_t room_left
= buflen
- (buflen
% __alignof__ (char *));
158 /* Check whether the buffer is large enough for even trying to
163 /* Read the first line. It must contain the alias name and
164 possibly some alias names. */
165 first_unused
[room_left
- 1] = '\xff';
166 line
= fgets_unlocked (first_unused
, room_left
, stream
);
168 /* Nothing to read. */
170 else if (first_unused
[room_left
- 1] != '\xff')
172 /* The line is too long for our buffer. */
175 status
= NSS_STATUS_TRYAGAIN
;
182 /* If we are in IGNORE mode and the first character in the
183 line is a white space we ignore the line and start
185 if (ignore
&& isspace (*first_unused
))
188 /* Terminate the line for any case. */
189 cp
= strpbrk (first_unused
, "#\n");
193 /* Skip leading blanks. */
194 while (isspace (*line
))
197 result
->alias_name
= first_unused
;
198 while (*line
!= '\0' && *line
!= ':')
199 *first_unused
++ = *line
++;
200 if (*line
== '\0' || result
->alias_name
== first_unused
)
201 /* No valid name. Ignore the line. */
204 *first_unused
++ = '\0';
205 if (room_left
< (size_t) (first_unused
- result
->alias_name
))
207 room_left
-= first_unused
- result
->alias_name
;
210 /* When we search for a specific alias we can avoid all the
211 difficult parts and compare now with the name we are
212 looking for. If it does not match we simply ignore all
213 lines until the next line containing the start of a new
215 ignore
= (match
!= NULL
216 && __strcasecmp (result
->alias_name
, match
) != 0);
220 while (isspace (*line
))
224 while (*line
!= '\0' && *line
!= ',')
225 *first_unused
++ = *line
++;
227 if (first_unused
!= cp
)
229 /* OK, we can have a regular entry or an include
233 *first_unused
++ = '\0';
235 if (strncmp (cp
, ":include:", 9) != 0)
237 if (room_left
< (first_unused
- cp
) + sizeof (char *))
239 room_left
-= (first_unused
- cp
) + sizeof (char *);
241 ++result
->alias_members_len
;
245 /* Oh well, we have to read the addressed file. */
247 char *old_line
= NULL
;
251 listfile
= fopen (&cp
[9], "rce");
252 /* If the file does not exist we simply ignore
255 && (old_line
= strdup (line
)) != NULL
)
257 while (! feof_unlocked (listfile
))
259 first_unused
[room_left
- 1] = '\xff';
260 line
= fgets_unlocked (first_unused
, room_left
,
264 if (first_unused
[room_left
- 1] != '\xff')
270 /* Parse the line. */
271 cp
= strpbrk (line
, "#\n");
277 while (isspace (*line
))
281 while (*line
!= '\0' && *line
!= ',')
282 *first_unused
++ = *line
++;
287 if (first_unused
!= cp
)
289 *first_unused
++ = '\0';
290 if (room_left
< ((first_unused
- cp
)
291 + __alignof__ (char *)))
296 room_left
-= ((first_unused
- cp
)
297 + __alignof__ (char *));
298 ++result
->alias_members_len
;
301 while (*line
!= '\0');
305 first_unused
[room_left
- 1] = '\0';
306 strncpy (first_unused
, old_line
, room_left
);
311 if (first_unused
[room_left
- 1] != '\0')
319 /* Get the next line. But we must be careful. We
320 must not read the whole line at once since it
321 might belong to the current alias. Simply read
322 the first character. If it is a white space we
323 have a continuation line. Otherwise it is the
324 beginning of a new alias and we can push back the
325 just read character. */
328 ch
= fgetc_unlocked (stream
);
329 if (ch
== EOF
|| ch
== '\n' || !isspace (ch
))
333 /* Now prepare the return. Provide string
334 pointers for the currently selected aliases. */
338 /* Adjust the pointer so it is aligned for
340 first_unused
+= __alignof__ (char *) - 1;
341 first_unused
-= ((first_unused
- (char *) 0)
342 % __alignof__ (char *));
343 result
->alias_members
= (char **) first_unused
;
345 /* Compute addresses of alias entry strings. */
346 cp
= result
->alias_name
;
347 for (cnt
= 0; cnt
< result
->alias_members_len
; ++cnt
)
349 cp
= strchr (cp
, '\0') + 1;
350 result
->alias_members
[cnt
] = cp
;
353 status
= (result
->alias_members_len
== 0
354 ? NSS_STATUS_RETURN
: NSS_STATUS_SUCCESS
);
358 /* The just read character is a white space and so
360 first_unused
[room_left
- 1] = '\xff';
361 line
= fgets_unlocked (first_unused
, room_left
, stream
);
362 if (first_unused
[room_left
- 1] != '\xff')
364 cp
= strpbrk (line
, "#\n");
371 if (status
!= NSS_STATUS_NOTFOUND
)
372 /* We read something. In any case break here. */
381 _nss_files_getaliasent_r (struct aliasent
*result
, char *buffer
, size_t buflen
,
384 /* Return next entry in host file. */
385 enum nss_status status
= NSS_STATUS_SUCCESS
;
387 __libc_lock_lock (lock
);
389 /* Be prepared that the set*ent function was not called before. */
391 status
= internal_setent (&stream
);
393 if (status
== NSS_STATUS_SUCCESS
)
395 result
->alias_local
= 1;
397 /* Read lines until we get a definite result. */
399 status
= get_next_alias (stream
, NULL
, result
, buffer
, buflen
, errnop
);
400 while (status
== NSS_STATUS_RETURN
);
403 __libc_lock_unlock (lock
);
410 _nss_files_getaliasbyname_r (const char *name
, struct aliasent
*result
,
411 char *buffer
, size_t buflen
, int *errnop
)
413 /* Return next entry in host file. */
414 enum nss_status status
= NSS_STATUS_SUCCESS
;
419 __set_errno (EINVAL
);
420 return NSS_STATUS_UNAVAIL
;
423 /* Open the stream. */
424 status
= internal_setent (&stream
);
426 if (status
== NSS_STATUS_SUCCESS
)
428 result
->alias_local
= 1;
430 /* Read lines until we get a definite result. */
432 status
= get_next_alias (stream
, name
, result
, buffer
, buflen
, errnop
);
433 while (status
== NSS_STATUS_RETURN
);
436 internal_endent (&stream
);