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