chiark / gitweb /
eglibc (2.11.3-4+deb6u3) squeeze-lts; urgency=medium
[eglibc.git] / nss / nss_files / files-XXX.c
1 /* Common code for file-based databases in nss_files module.
2    Copyright (C) 1996-1999,2001,2002,2004,2007,2008
3    Free Software Foundation, Inc.
4    This file is part of the GNU C Library.
5
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.
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    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 #include <stdio.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <bits/libc-lock.h>
26 #include "nsswitch.h"
27
28 #include <kernel-features.h>
29
30 /* These symbols are defined by the including source file:
31
32    ENTNAME -- database name of the structure and functions (hostent, pwent).
33    STRUCTURE -- struct name, define only if not ENTNAME (passwd, group).
34    DATABASE -- string of the database file's name ("hosts", "passwd").
35
36    NEED_H_ERRNO - defined iff an arg `int *herrnop' is used.
37
38    Also see files-parse.c.
39 */
40
41 #define ENTNAME_r       CONCAT(ENTNAME,_r)
42
43 #define DATAFILE        "/etc/" DATABASE
44
45 #ifdef NEED_H_ERRNO
46 # include <netdb.h>
47 # define H_ERRNO_PROTO  , int *herrnop
48 # define H_ERRNO_ARG    , 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_SET(val) ((void) 0)
54 #endif
55
56 #ifndef EXTRA_ARGS
57 # define EXTRA_ARGS
58 # define EXTRA_ARGS_DECL
59 # define EXTRA_ARGS_VALUE
60 #endif
61
62 /* Locks the static variables in this file.  */
63 __libc_lock_define_initialized (static, lock)
64 \f
65 /* Maintenance of the shared stream open on the database file.  */
66
67 static FILE *stream;
68 static fpos_t position;
69 static enum { nouse, getent, getby } last_use;
70 static int keep_stream;
71
72 /* Open database file if not already opened.  */
73 static enum nss_status
74 internal_setent (int stayopen)
75 {
76   enum nss_status status = NSS_STATUS_SUCCESS;
77
78   if (stream == NULL)
79     {
80       stream = fopen (DATAFILE, "re");
81
82       if (stream == NULL)
83         status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
84       else
85         {
86 #if !defined O_CLOEXEC || !defined __ASSUME_O_CLOEXEC
87 # ifdef O_CLOEXEC
88           if (__have_o_cloexec <= 0)
89 # endif
90             {
91               /* We have to make sure the file is  `closed on exec'.  */
92               int result;
93               int flags;
94
95               result = flags = fcntl (fileno (stream), F_GETFD, 0);
96               if (result >= 0)
97                 {
98 # ifdef O_CLOEXEC
99                   if (__have_o_cloexec == 0)
100                     __have_o_cloexec = (flags & FD_CLOEXEC) == 0 ? -1 : 1;
101                   if (__have_o_cloexec < 0)
102 # endif
103                     {
104                       flags |= FD_CLOEXEC;
105                       result = fcntl (fileno (stream), F_SETFD, flags);
106                     }
107                 }
108               if (result < 0)
109                 {
110                   /* Something went wrong.  Close the stream and return a
111                      failure.  */
112                   fclose (stream);
113                   stream = NULL;
114                   status = NSS_STATUS_UNAVAIL;
115                 }
116             }
117 #endif
118         }
119     }
120   else
121     rewind (stream);
122
123   /* Remember STAYOPEN flag.  */
124   if (stream != NULL)
125     keep_stream |= stayopen;
126
127   return status;
128 }
129
130
131 /* Thread-safe, exported version of that.  */
132 enum nss_status
133 CONCAT(_nss_files_set,ENTNAME) (int stayopen)
134 {
135   enum nss_status status;
136
137   __libc_lock_lock (lock);
138
139   status = internal_setent (stayopen);
140
141   if (status == NSS_STATUS_SUCCESS && fgetpos (stream, &position) < 0)
142     {
143       fclose (stream);
144       stream = NULL;
145       status = NSS_STATUS_UNAVAIL;
146     }
147
148   last_use = getent;
149
150   __libc_lock_unlock (lock);
151
152   return status;
153 }
154
155
156 /* Close the database file.  */
157 static void
158 internal_endent (void)
159 {
160   if (stream != NULL)
161     {
162       fclose (stream);
163       stream = NULL;
164     }
165 }
166
167
168 /* Thread-safe, exported version of that.  */
169 enum nss_status
170 CONCAT(_nss_files_end,ENTNAME) (void)
171 {
172   __libc_lock_lock (lock);
173
174   internal_endent ();
175
176   /* Reset STAYOPEN flag.  */
177   keep_stream = 0;
178
179   __libc_lock_unlock (lock);
180
181   return NSS_STATUS_SUCCESS;
182 }
183 \f
184 /* Parsing the database file into `struct STRUCTURE' data structures.  */
185
186 static enum nss_status
187 internal_getent (struct STRUCTURE *result,
188                  char *buffer, size_t buflen, int *errnop H_ERRNO_PROTO
189                  EXTRA_ARGS_DECL)
190 {
191   char *p;
192   struct parser_data *data = (void *) buffer;
193   int linebuflen = buffer + buflen - data->linebuffer;
194   int parse_result;
195
196   if (buflen < sizeof *data + 2)
197     {
198       *errnop = ERANGE;
199       H_ERRNO_SET (NETDB_INTERNAL);
200       return NSS_STATUS_TRYAGAIN;
201     }
202
203   do
204     {
205       /* Terminate the line so that we can test for overflow.  */
206       ((unsigned char *) data->linebuffer)[linebuflen - 1] = '\xff';
207
208       p = fgets_unlocked (data->linebuffer, linebuflen, stream);
209       if (p == NULL)
210         {
211           /* End of file or read error.  */
212           H_ERRNO_SET (HOST_NOT_FOUND);
213           return NSS_STATUS_NOTFOUND;
214         }
215       else if (((unsigned char *) data->linebuffer)[linebuflen - 1] != 0xff)
216         {
217           /* The line is too long.  Give the user the opportunity to
218              enlarge the buffer.  */
219           *errnop = ERANGE;
220           H_ERRNO_SET (NETDB_INTERNAL);
221           return NSS_STATUS_TRYAGAIN;
222         }
223
224       /* Skip leading blanks.  */
225       while (isspace (*p))
226         ++p;
227     }
228   while (*p == '\0' || *p == '#' /* Ignore empty and comment lines.  */
229          /* Parse the line.  If it is invalid, loop to get the next
230             line of the file to parse.  */
231          || ! (parse_result = parse_line (p, result, data, buflen, errnop
232                                           EXTRA_ARGS)));
233
234   if (__builtin_expect (parse_result == -1, 0))
235     {
236       H_ERRNO_SET (NETDB_INTERNAL);
237       return NSS_STATUS_TRYAGAIN;
238     }
239
240   /* Filled in RESULT with the next entry from the database file.  */
241   return NSS_STATUS_SUCCESS;
242 }
243
244
245 /* Return the next entry from the database file, doing locking.  */
246 enum nss_status
247 CONCAT(_nss_files_get,ENTNAME_r) (struct STRUCTURE *result, char *buffer,
248                                   size_t buflen, int *errnop H_ERRNO_PROTO)
249 {
250   /* Return next entry in host file.  */
251   enum nss_status status = NSS_STATUS_SUCCESS;
252
253   __libc_lock_lock (lock);
254
255   /* Be prepared that the set*ent function was not called before.  */
256   if (stream == NULL)
257     {
258       int save_errno = errno;
259
260       status = internal_setent (0);
261
262       __set_errno (save_errno);
263
264       if (status == NSS_STATUS_SUCCESS && fgetpos (stream, &position) < 0)
265         {
266           fclose (stream);
267           stream = NULL;
268           status = NSS_STATUS_UNAVAIL;
269         }
270     }
271
272   if (status == NSS_STATUS_SUCCESS)
273     {
274       /* If the last use was not by the getent function we need the
275          position the stream.  */
276       if (last_use != getent)
277         {
278           if (fsetpos (stream, &position) < 0)
279             status = NSS_STATUS_UNAVAIL;
280           else
281             last_use = getent;
282         }
283
284       if (status == NSS_STATUS_SUCCESS)
285         {
286           status = internal_getent (result, buffer, buflen, errnop
287                                     H_ERRNO_ARG EXTRA_ARGS_VALUE);
288
289           /* Remember this position if we were successful.  If the
290              operation failed we give the user a chance to repeat the
291              operation (perhaps the buffer was too small).  */
292           if (status == NSS_STATUS_SUCCESS)
293             fgetpos (stream, &position);
294           else
295             /* We must make sure we reposition the stream the next call.  */
296             last_use = nouse;
297         }
298     }
299
300   __libc_lock_unlock (lock);
301
302   return status;
303 }
304 \f
305 /* Macro for defining lookup functions for this file-based database.
306
307    NAME is the name of the lookup; e.g. `hostbyname'.
308
309    KEYSIZE and KEYPATTERN are ignored here but used by ../nss_db/db-XXX.c.
310
311    PROTO describes the arguments for the lookup key;
312    e.g. `const char *hostname'.
313
314    BREAK_IF_MATCH is a block of code which compares `struct STRUCTURE *result'
315    to the lookup key arguments and does `break;' if they match.  */
316
317 #define DB_LOOKUP(name, keysize, keypattern, break_if_match, proto...)        \
318 enum nss_status                                                               \
319 _nss_files_get##name##_r (proto,                                              \
320                           struct STRUCTURE *result, char *buffer,             \
321                           size_t buflen, int *errnop H_ERRNO_PROTO)           \
322 {                                                                             \
323   enum nss_status status;                                                     \
324                                                                               \
325   __libc_lock_lock (lock);                                                    \
326                                                                               \
327   /* Reset file pointer to beginning or open file.  */                        \
328   status = internal_setent (keep_stream);                                     \
329                                                                               \
330   if (status == NSS_STATUS_SUCCESS)                                           \
331     {                                                                         \
332       /* Tell getent function that we have repositioned the file pointer.  */ \
333       last_use = getby;                                                       \
334                                                                               \
335       while ((status = internal_getent (result, buffer, buflen, errnop        \
336                                         H_ERRNO_ARG EXTRA_ARGS_VALUE))        \
337              == NSS_STATUS_SUCCESS)                                           \
338         { break_if_match }                                                    \
339                                                                               \
340       if (! keep_stream)                                                      \
341         internal_endent ();                                                   \
342     }                                                                         \
343                                                                               \
344   __libc_lock_unlock (lock);                                                  \
345                                                                               \
346   return status;                                                              \
347 }