yp_dblookup.c revision 15426
1/*
2 * Copyright (c) 1995
3 *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by Bill Paul.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 *	$Id: yp_dblookup.c,v 1.7 1996/04/27 17:50:18 wpaul Exp wpaul $
33 *
34 */
35#include <stdio.h>
36#include <stdlib.h>
37#include <fcntl.h>
38#include <string.h>
39#include <limits.h>
40#include <unistd.h>
41#include <db.h>
42#include <sys/stat.h>
43#include <sys/param.h>
44#include <errno.h>
45#include <paths.h>
46#include <rpcsvc/yp.h>
47#include "yp_extern.h"
48
49int ypdb_debug = 0;
50int yp_errno = YP_TRUE;
51
52#define PERM_SECURE (S_IRUSR|S_IWUSR)
53HASHINFO openinfo = {
54	4096,		/* bsize */
55	32,		/* ffactor */
56	512,		/* nelem */
57	2048 * 512, 	/* cachesize */
58	NULL,		/* hash */
59	0,		/* lorder */
60};
61
62#ifdef DB_CACHE
63#define MAXDBS 20
64#define LASTDB (MAXDBS - 1)
65
66struct dbent {
67	DB *dbp;
68	char *name;
69	char *key;
70	int size;
71};
72
73static struct dbent *dbs[MAXDBS];
74static int numdbs = 0;
75
76/*
77 * Make sure all the DB entries are NULL to start with.
78 */
79void yp_init_dbs()
80{
81	register int i;
82
83	for (i = 0; i < MAXDBS; i++);
84		dbs[i] = NULL;
85	return;
86}
87
88static inline void yp_flush(i)
89	register int i;
90{
91	(void)(dbs[i]->dbp->close)(dbs[i]->dbp);
92	dbs[i]->dbp = NULL;
93	free(dbs[i]->name);
94	dbs[i]->name = NULL;
95	dbs[i]->key = NULL;
96	dbs[i]->size = 0;
97	free(dbs[i]);
98	dbs[i] = NULL;
99	numdbs--;
100}
101
102/*
103 * Close all databases and erase all database names.
104 * Don't free the memory allocated for each DB entry though: we
105 * can just reuse it later.
106 */
107void yp_flush_all()
108{
109	register int i;
110
111	for (i = 0; i < MAXDBS; i++) {
112		if (dbs[i] != NULL && dbs[i]->dbp != NULL) {
113			yp_flush(i);
114		}
115	}
116}
117
118
119/*
120 * Add a DB handle and database name to the cache. We only maintain
121 * fixed number of entries in the cache, so if we're asked to store
122 * a new entry when all our slots are already filled, we have to kick
123 * out the entry in the last slot to make room.
124 */
125static inline void yp_add_db(dbp, name, size)
126	DB *dbp;
127	char *name;
128	int size;
129{
130	register int i;
131	register struct dbent *tmp;
132	static int count = 0;
133
134
135	tmp = dbs[LASTDB];
136
137	/* Rotate */
138	for (i = LASTDB; i > 0; i--)
139		dbs[i] = dbs[i - 1];
140
141	dbs[0] = tmp;
142
143	if (dbs[0]) {
144		if (ypdb_debug)
145			yp_error("table overflow -- releasing last slot");
146		yp_flush(0);
147	}
148
149	/*
150	 * Add the new entry. We allocate memory for the dbent
151	 * structure if we need it. We shoudly only end up calling
152	 * malloc(2) MAXDB times. Once all the slots are filled, we
153	 * hold onto the memory and recycle it.
154	 */
155	if (dbs[0] == NULL) {
156		count++;
157		if (ypdb_debug)
158			yp_error("allocating new DB member (%d)", count);
159		dbs[0] = (struct dbent *)malloc(sizeof(struct dbent));
160		bzero((char *)dbs[0], sizeof(struct dbent));
161	}
162
163	numdbs++;
164	dbs[0]->dbp = dbp;
165	dbs[0]->name = strdup(name);
166	dbs[0]->size = size;
167	return;
168}
169
170/*
171 * Search the list for a database matching 'name.' If we find it,
172 * move it to the head of the list and return its DB handle. If
173 * not, just fail: yp_open_db_cache() will subsequently try to open
174 * the database itself and call yp_add_db() to add it to the
175 * list.
176 *
177 * The search works like this:
178 *
179 * - The caller specifies the name of a database to locate. We try to
180 *   find an entry in our list with a matching name.
181 *
182 * - If the caller doesn't specify a key or size, we assume that the
183 *   first entry that we encounter with a matching name is returned.
184 *   This will result in matches regardless of the pointer index.
185 *
186 * - If the caller also specifies a key and length, we check name
187 *   matches to see if their saved key indexes and lengths also match.
188 *   This lets us return a DB handle that's already positioned at the
189 *   correct location within a database.
190 *
191 * - Once we have a match, it gets migrated to the top of the list
192 *   array so that it will be easier to find if another request for
193 *   the same database comes in later.
194 */
195static inline DB *yp_find_db(name, key, size)
196	char *name;
197	char *key;
198{
199	register int i, j;
200	register struct dbent *tmp;
201
202	for (i = 0; i < numdbs; i++) {
203		if (dbs[i]->name != NULL && !strcmp(dbs[i]->name, name)) {
204			if (size) {
205				if (size != dbs[i]->size ||
206					strncmp(dbs[i]->key, key, size))
207					continue;
208			} else {
209				if (dbs[i]->size) {
210					continue;
211				}
212			}
213			if (i > 0) {
214				tmp = dbs[i];
215				for (j = i; j > 0; j--)
216					dbs[j] = dbs[j - 1];
217				dbs[0] = tmp;
218			}
219			return(dbs[0]->dbp);
220		}
221	}
222	return(NULL);
223}
224
225/*
226 * Open a DB database and cache the handle for later use. We first
227 * check the cache to see if the required database is already open.
228 * If so, we fetch the handle from the cache. If not, we try to open
229 * the database and save the handle in the cache for later use.
230 */
231DB *yp_open_db_cache(domain, map, key, size)
232	const char *domain;
233	const char *map;
234	const char *key;
235	const int size;
236{
237	DB *dbp = NULL;
238	char buf[MAXPATHLEN + 2];
239
240	snprintf(buf, sizeof(buf), "%s/%s", domain, map);
241
242	if ((dbp = yp_find_db((char *)&buf, key, size)) != NULL) {
243		return(dbp);
244	} else {
245		if ((dbp = yp_open_db(domain, map)) != NULL)
246			yp_add_db(dbp, (char *)&buf, size);
247	}
248
249	return (dbp);
250}
251#endif
252
253/*
254 * Open a DB database.
255 */
256DB *yp_open_db(domain, map)
257	const char *domain;
258	const char *map;
259{
260	DB *dbp = NULL;
261	char buf[MAXPATHLEN + 2];
262
263	yp_errno = YP_TRUE;
264
265	if (map[0] == '.' || strchr(map, '/')) {
266		yp_errno = YP_BADARGS;
267		return (NULL);
268	}
269
270#ifdef DB_CACHE
271	if (yp_validdomain(domain)) {
272		yp_errno = YP_NODOM;
273		return(NULL);
274	}
275#endif
276	snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map);
277
278#ifdef DB_CACHE
279again:
280#endif
281	dbp = dbopen(buf,O_RDONLY, PERM_SECURE, DB_HASH, NULL);
282
283	if (dbp == NULL) {
284		switch(errno) {
285#ifdef DB_CACHE
286		case ENFILE:
287			/*
288			 * We ran out of file descriptors. Nuke an
289			 * open one and try again.
290			 */
291			yp_error("ran out of file descriptors");
292			yp_flush(numdbs - 1);
293			goto again;
294			break;
295#endif
296		case ENOENT:
297			yp_errno = YP_NOMAP;
298			break;
299		case EFTYPE:
300			yp_errno = YP_BADDB;
301			break;
302		default:
303			yp_errno = YP_YPERR;
304			break;
305		}
306	}
307
308	return (dbp);
309}
310
311/*
312 * Database access routines.
313 *
314 * - yp_get_record(): retrieve an arbitrary key/data pair given one key
315 *                 to match against.
316 *
317 * - yp_first_record(): retrieve first key/data base in a database.
318 *
319 * - yp_next_record(): retrieve key/data pair that sequentially follows
320 *                   the supplied key value in the database.
321 */
322
323int yp_get_record(domain,map,key,data,allow)
324	const char *domain;
325	const char *map;
326	const DBT *key;
327	DBT *data;
328	int allow;
329{
330	DB *dbp;
331	int rval = 0;
332
333	if (ypdb_debug)
334		yp_error("Looking up key [%.*s] in map [%s]",
335			  key->size, key->data, map);
336
337	/*
338	 * Avoid passing back magic "YP_*" entries unless
339	 * the caller specifically requested them by setting
340	 * the 'allow' flag.
341	 */
342	if (!allow && !strncmp(key->data, "YP_", 3))
343		return(YP_NOKEY);
344
345#ifdef DB_CACHE
346	if ((dbp = yp_open_db_cache(domain, map, NULL, 0)) == NULL) {
347#else
348	if ((dbp = yp_open_db(domain, map)) == NULL) {
349#endif
350		return(yp_errno);
351	}
352
353	if ((rval = (dbp->get)(dbp, key, data, 0)) != 0) {
354#ifdef DB_CACHE
355		dbs[0]->size = 0;
356#else
357		(void)(dbp->close)(dbp);
358#endif
359		if (rval == 1)
360			return(YP_NOKEY);
361		else
362			return(YP_BADDB);
363	}
364
365	if (ypdb_debug)
366		yp_error("Result of lookup: key: [%.*s] data: [%.*s]",
367			 key->size, key->data, data->size, data->data);
368
369#ifdef DB_CACHE
370	if (dbs[0]->size) {
371		dbs[0]->key = key->data;
372		dbs[0]->size = key->size;
373	}
374#else
375	(void)(dbp->close)(dbp);
376#endif
377
378	return(YP_TRUE);
379}
380
381int yp_first_record(dbp,key,data,allow)
382	const DB *dbp;
383	DBT *key;
384	DBT *data;
385	int allow;
386{
387	int rval;
388
389	if (ypdb_debug)
390		yp_error("Retrieving first key in map.");
391
392	if ((rval = (dbp->seq)(dbp,key,data,R_FIRST)) != 0) {
393#ifdef DB_CACHE
394		dbs[0]->size = 0;
395#endif
396		if (rval == 1)
397			return(YP_NOKEY);
398		else
399			return(YP_BADDB);
400	}
401
402	/* Avoid passing back magic "YP_*" records. */
403	while (!strncmp(key->data, "YP_", 3) && !allow) {
404		if ((rval = (dbp->seq)(dbp,key,data,R_NEXT)) != 0) {
405#ifdef DB_CACHE
406			dbs[0]->size = 0;
407#endif
408			if (rval == 1)
409				return(YP_NOKEY);
410			else
411				return(YP_BADDB);
412		}
413	}
414
415	if (ypdb_debug)
416		yp_error("Result of lookup: key: [%.*s] data: [%.*s]",
417			 key->size, key->data, data->size, data->data);
418
419#ifdef DB_CACHE
420	if (dbs[0]->size) {
421		dbs[0]->key = key->data;
422		dbs[0]->size = key->size;
423	}
424#endif
425
426	return(YP_TRUE);
427}
428
429int yp_next_record(dbp,key,data,all,allow)
430	const DB *dbp;
431	DBT *key;
432	DBT *data;
433	int all;
434	int allow;
435{
436	static DBT lkey = { NULL, 0 };
437	static DBT ldata = { NULL, 0 };
438	int rval;
439
440	if (key == NULL || key->data == NULL) {
441		rval = yp_first_record(dbp,key,data,allow);
442		if (rval == YP_NOKEY)
443			return(YP_NOMORE);
444		else
445			return(rval);
446	}
447
448	if (ypdb_debug)
449		yp_error("Retreiving next key, previous was: [%.*s]",
450			  key->size, key->data);
451
452	if (!all) {
453#ifndef DB_CACHE
454		if (key->size != lkey.size ||
455			strncmp(key->data, lkey.data, key->size)) {
456#else
457		if (!dbs[0]->size) {
458#endif
459			(dbp->seq)(dbp,&lkey,&ldata,R_FIRST);
460			while(strncmp((char *)key->data,lkey.data,
461				(int)key->size) || key->size != lkey.size)
462				(dbp->seq)(dbp,&lkey,&ldata,R_NEXT);
463		}
464	}
465
466	if ((dbp->seq)(dbp,key,data,R_NEXT)) {
467#ifdef DB_CACHE
468		dbs[0]->size = 0;
469#endif
470		return(YP_NOMORE);
471	}
472
473	/* Avoid passing back magic "YP_*" records. */
474	while (!strncmp(key->data, "YP_", 3) && !allow)
475		if ((dbp->seq)(dbp,key,data,R_NEXT)) {
476#ifdef DB_CACHE
477			dbs[0]->size = 0;
478#endif
479			return(YP_NOMORE);
480		}
481
482	if (ypdb_debug)
483		yp_error("Result of lookup: key: [%.*s] data: [%.*s]",
484			 key->size, key->data, data->size, data->data);
485
486#ifdef DB_CACHE
487	if (dbs[0]->size) {
488		dbs[0]->key = key->data;
489		dbs[0]->size = key->size;
490	}
491#else
492	lkey.data = key->data;
493	lkey.size = key->size;
494#endif
495
496	return(YP_TRUE);
497}
498