(internal_setent): Rewrite to avoid warning.
[kopensolaris-gnu/glibc.git] / nss / nss_db / db-XXX.c
1 /* Common code for DB-based databases in nss_db module.
2    Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    The GNU C Library 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 GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <db.h>
21 #include <fcntl.h>
22 #include <bits/libc-lock.h>
23 #include "nsswitch.h"
24
25 /* These symbols are defined by the including source file:
26
27    ENTNAME -- database name of the structure and functions (hostent, pwent).
28    STRUCTURE -- struct name, define only if not ENTNAME (passwd, group).
29    DATABASE -- database file name, ("hosts", "passwd")
30
31    NEED_H_ERRNO - defined iff an arg `int *herrnop' is used.
32 */
33
34 #define ENTNAME_r       CONCAT(ENTNAME,_r)
35
36 #include <paths.h>
37 #define DBFILE          _PATH_VARDB DATABASE ".db"
38
39 #ifdef NEED_H_ERRNO
40 #define H_ERRNO_PROTO   , int *herrnop
41 #define H_ERRNO_ARG     , herrnop
42 #define H_ERRNO_SET(val) (*herrnop = (val))
43 #else
44 #define H_ERRNO_PROTO
45 #define H_ERRNO_ARG
46 #define H_ERRNO_SET(val) ((void) 0)
47 #endif
48
49 /* Locks the static variables in this file.  */
50 __libc_lock_define_initialized (static, lock)
51 \f
52 /* Maintenance of the shared handle open on the database.  */
53
54 static DB *db;
55 static int keep_db;
56 static unsigned int entidx;     /* Index for `getENTNAME'. */
57
58 /* Open database file if not already opened.  */
59 static enum nss_status
60 internal_setent (int stayopen)
61 {
62   enum nss_status status = NSS_STATUS_SUCCESS;
63   int err;
64
65   if (db == NULL)
66     {
67       err = __nss_db_open (DBFILE, DB_BTREE, DB_RDONLY, 0, NULL, NULL, &db);
68
69       if (err != 0)
70         {
71           __set_errno (err);
72           status = err == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
73         }
74       else
75         {
76           /* We have to make sure the file is `closed on exec'.  */
77           int fd;
78           int result;
79
80           err = db->fd (db, &fd);
81           if (err != 0)
82             {
83               __set_errno (err);
84               result = -1;
85             }
86           else
87             {
88               int flags = result = fcntl (fd, F_GETFD, 0);
89
90               if (result >= 0)
91                 {
92                   flags |= FD_CLOEXEC;
93                   result = fcntl (fd, F_SETFD, flags);
94                 }
95             }
96           if (result < 0)
97             {
98               /* Something went wrong.  Close the stream and return a
99                  failure.  */
100               db->close (db, 0);
101               db = NULL;
102               status = NSS_STATUS_UNAVAIL;
103             }
104         }
105     }
106
107   /* Remember STAYOPEN flag.  */
108   if (db != NULL)
109     keep_db |= stayopen;
110
111   return status;
112 }
113
114
115 /* Thread-safe, exported version of that.  */
116 enum nss_status
117 CONCAT(_nss_db_set,ENTNAME) (int stayopen)
118 {
119   enum nss_status status;
120
121   __libc_lock_lock (lock);
122
123   status = internal_setent (stayopen);
124
125   /* Reset the sequential index.  */
126   entidx = 0;
127
128   __libc_lock_unlock (lock);
129
130   return status;
131 }
132
133
134 /* Close the database file.  */
135 static void
136 internal_endent (void)
137 {
138   if (db != NULL)
139     {
140       db->close (db, 0);
141       db = NULL;
142     }
143 }
144
145
146 /* Thread-safe, exported version of that.  */
147 enum nss_status
148 CONCAT(_nss_db_end,ENTNAME) (void)
149 {
150   __libc_lock_lock (lock);
151
152   internal_endent ();
153
154   /* Reset STAYOPEN flag.  */
155   keep_db = 0;
156
157   __libc_lock_unlock (lock);
158
159   return NSS_STATUS_SUCCESS;
160 }
161 \f
162 /* Do a database lookup for KEY.  */
163 static enum nss_status
164 lookup (DBT *key, struct STRUCTURE *result,
165         void *buffer, size_t buflen, int *errnop H_ERRNO_PROTO)
166 {
167   char *p;
168   enum nss_status status;
169   int err;
170   DBT value;
171
172   /* Open the database.  */
173   status = internal_setent (keep_db);
174   if (status != NSS_STATUS_SUCCESS)
175     {
176       *errnop = errno;
177       H_ERRNO_SET (NETDB_INTERNAL);
178       return status;
179     }
180
181   /* Succeed iff it matches a value that parses correctly.  */
182   value.flags = 0;
183   err = db->get (db, NULL, key, &value, 0);
184   if (err != 0)
185     {
186       if (err == DB_NOTFOUND)
187         {
188           H_ERRNO_SET (HOST_NOT_FOUND);
189           status = NSS_STATUS_NOTFOUND;
190         }
191       else
192         {
193           *errnop = err;
194           H_ERRNO_SET (NETDB_INTERNAL);
195           status = NSS_STATUS_UNAVAIL;
196         }
197     }
198   else if (buflen < value.size)
199     {
200       /* No room to copy the data to.  */
201       *errnop = ERANGE;
202       H_ERRNO_SET (NETDB_INTERNAL);
203       status = NSS_STATUS_TRYAGAIN;
204     }
205   else
206     {
207       /* Copy the result to a safe place.  */
208       p = (char *) memcpy (buffer, value.data, value.size);
209
210       /* Skip leading blanks.  */
211       while (isspace (*p))
212         ++p;
213
214       err = parse_line (p, result, buffer, buflen, errnop);
215
216       if (err == 0)
217         {
218           /* If the key begins with '0' we are trying to get the next
219              entry.  We want to ignore unparsable lines in this case.  */
220           if (((char *) key->data)[0] == '0')
221             {
222               /* Super magical return value.  We need to tell our caller
223                  that it should continue looping.  This value cannot
224                  happen in other cases.  */
225               status = NSS_STATUS_RETURN;
226             }
227           else
228             {
229               H_ERRNO_SET (HOST_NOT_FOUND);
230               status = NSS_STATUS_NOTFOUND;
231             }
232         }
233       else if (err < 0)
234         {
235           H_ERRNO_SET (NETDB_INTERNAL);
236           status = NSS_STATUS_TRYAGAIN;
237         }
238       else
239         status = NSS_STATUS_SUCCESS;
240     }
241
242   if (! keep_db)
243     internal_endent ();
244
245   return status;
246 }
247
248
249 /* Macro for defining lookup functions for this DB-based database.
250
251    NAME is the name of the lookup; e.g. `pwnam'.
252
253    KEYPATTERN gives `printf' args to construct a key string;
254    e.g. `(".%s", name)'.
255
256    KEYSIZE gives the allocation size of a buffer to construct it in;
257    e.g. `1 + strlen (name)'.
258
259    PROTO describes the arguments for the lookup key;
260    e.g. `const char *name'.
261
262    BREAK_IF_MATCH is ignored, but used by ../nss_files/files-XXX.c.  */
263
264 #define DB_LOOKUP(name, keysize, keypattern, break_if_match, proto...)        \
265 enum nss_status                                                               \
266 _nss_db_get##name##_r (proto,                                                 \
267                        struct STRUCTURE *result,                              \
268                        char *buffer, size_t buflen, int *errnop H_ERRNO_PROTO)\
269 {                                                                             \
270   DBT key;                                                                    \
271   enum nss_status status;                                                     \
272   const size_t size = (keysize) + 1;                                          \
273   key.data = __alloca (size);                                                 \
274   key.size = KEYPRINTF keypattern;                                            \
275   key.flags = 0;                                                              \
276   __libc_lock_lock (lock);                                                    \
277   status = lookup (&key, result, buffer, buflen, errnop H_ERRNO_ARG);         \
278   __libc_lock_unlock (lock);                                                  \
279   return status;                                                              \
280 }
281
282 #define KEYPRINTF(pattern, args...) snprintf (key.data, size, pattern ,##args)
283 \f
284
285
286
287 /* Return the next entry from the database file, doing locking.  */
288 enum nss_status
289 CONCAT(_nss_db_get,ENTNAME_r) (struct STRUCTURE *result, char *buffer,
290                                size_t buflen, int *errnop H_ERRNO_PROTO)
291 {
292   /* Return next entry in host file.  */
293   enum nss_status status;
294   char buf[20];
295   DBT key;
296
297   __libc_lock_lock (lock);
298
299   /* Loop until we find a valid entry or hit EOF.  See above for the
300      special meaning of the status value.  */
301   do
302     {
303       key.size = snprintf (key.data = buf, sizeof buf, "0%u", entidx++);
304       key.flags = 0;
305       status = lookup (&key, result, buffer, buflen, errnop H_ERRNO_ARG);
306       if (status == NSS_STATUS_TRYAGAIN
307 #ifdef NEED_H_ERRNO
308           && *herrnop == NETDB_INTERNAL
309 #endif
310           && *errnop == ERANGE)
311         /* Give the user a chance to get the same entry with a larger
312            buffer.  */
313         --entidx;
314     }
315   while (status == NSS_STATUS_RETURN);
316
317   __libc_lock_unlock (lock);
318
319   return status;
320 }