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.13 1996/06/04 04:08:21 wpaul Exp $ 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 49 #ifndef lint 50 static const char rcsid[] = "$Id: yp_dblookup.c,v 1.13 1996/06/04 04:08:21 wpaul Exp $"; 51 #endif 52 53 int ypdb_debug = 0; 54 int yp_errno = YP_TRUE; 55 56 #define PERM_SECURE (S_IRUSR|S_IWUSR) 57 HASHINFO openinfo = { 58 4096, /* bsize */ 59 32, /* ffactor */ 60 256, /* nelem */ 61 2048 * 512, /* cachesize */ 62 NULL, /* hash */ 63 0, /* lorder */ 64 }; 65 66 #ifdef DB_CACHE 67 #define MAXDBS 20 68 #define LASTDB (MAXDBS - 1) 69 70 struct dbent { 71 DB *dbp; 72 char *name; 73 char *key; 74 int size; 75 }; 76 77 static struct dbent *dbs[MAXDBS]; 78 static int numdbs = 0; 79 80 /* 81 * Make sure all the DB entries are NULL to start with. 82 */ 83 void yp_init_dbs() 84 { 85 register int i; 86 87 for (i = 0; i < MAXDBS; i++); 88 dbs[i] = NULL; 89 return; 90 } 91 92 /* 93 * Zorch a single entry in the dbent table and release 94 * all its resources. 95 */ 96 static void yp_flush(i) 97 register int i; 98 { 99 (void)(dbs[i]->dbp->close)(dbs[i]->dbp); 100 dbs[i]->dbp = NULL; 101 free(dbs[i]->name); 102 dbs[i]->name = NULL; 103 dbs[i]->key = NULL; 104 dbs[i]->size = 0; 105 free(dbs[i]); 106 dbs[i] = NULL; 107 numdbs--; 108 } 109 110 /* 111 * Close all databases and erase all database names. 112 */ 113 void yp_flush_all() 114 { 115 register int i; 116 117 for (i = 0; i < MAXDBS; i++) { 118 if (dbs[i] != NULL && dbs[i]->dbp != NULL) { 119 yp_flush(i); 120 } 121 } 122 } 123 124 125 /* 126 * Add a DB handle and database name to the cache. We only maintain 127 * fixed number of entries in the cache, so if we're asked to store 128 * a new entry when all our slots are already filled, we have to kick 129 * out the entry in the last slot to make room. 130 */ 131 static void yp_add_db(dbp, name, size) 132 DB *dbp; 133 char *name; 134 int size; 135 { 136 register int i; 137 register struct dbent *tmp; 138 139 tmp = dbs[LASTDB]; 140 141 /* Rotate */ 142 for (i = LASTDB; i > 0; i--) 143 dbs[i] = dbs[i - 1]; 144 145 dbs[0] = tmp; 146 147 if (dbs[0]) { 148 if (ypdb_debug) 149 yp_error("table overflow -- releasing last slot"); 150 yp_flush(0); 151 } 152 153 /* 154 * Allocate a new entry. 155 */ 156 if (dbs[0] == NULL) { 157 dbs[0] = (struct dbent *)malloc(sizeof(struct dbent)); 158 bzero((char *)dbs[0], sizeof(struct dbent)); 159 } 160 161 numdbs++; 162 dbs[0]->dbp = dbp; 163 dbs[0]->name = strdup(name); 164 dbs[0]->size = size; 165 return; 166 } 167 168 /* 169 * Search the list for a database matching 'name.' If we find it, 170 * move it to the head of the list and return its DB handle. If 171 * not, just fail: yp_open_db_cache() will subsequently try to open 172 * the database itself and call yp_add_db() to add it to the 173 * list. 174 * 175 * The search works like this: 176 * 177 * - The caller specifies the name of a database to locate. We try to 178 * find an entry in our list with a matching name. 179 * 180 * - If the caller doesn't specify a key or size, we assume that the 181 * first entry that we encounter with a matching name is returned. 182 * This will result in matches regardless of the pointer index. 183 * 184 * - If the caller also specifies a key and length, we check name 185 * matches to see if their saved key indexes and lengths also match. 186 * This lets us return a DB handle that's already positioned at the 187 * correct location within a database. 188 * 189 * - Once we have a match, it gets migrated to the top of the list 190 * array so that it will be easier to find if another request for 191 * the same database comes in later. 192 */ 193 static DB *yp_find_db(name, key, size) 194 char *name; 195 char *key; 196 int size; 197 { 198 register int i, j; 199 register struct dbent *tmp; 200 201 for (i = 0; i < numdbs; i++) { 202 if (dbs[i]->name != NULL && !strcmp(dbs[i]->name, name)) { 203 if (size) { 204 if (size != dbs[i]->size || 205 strncmp(dbs[i]->key, key, size)) 206 continue; 207 } else { 208 if (dbs[i]->size) 209 continue; 210 } 211 if (i > 0) { 212 tmp = dbs[i]; 213 for (j = i; j > 0; j--) 214 dbs[j] = dbs[j - 1]; 215 dbs[0] = tmp; 216 } 217 return(dbs[0]->dbp); 218 } 219 } 220 221 return(NULL); 222 } 223 224 /* 225 * Open a DB database and cache the handle for later use. We first 226 * check the cache to see if the required database is already open. 227 * If so, we fetch the handle from the cache. If not, we try to open 228 * the database and save the handle in the cache for later use. 229 */ 230 DB *yp_open_db_cache(domain, map, key, size) 231 const char *domain; 232 const char *map; 233 const char *key; 234 const int size; 235 { 236 DB *dbp = NULL; 237 char buf[MAXPATHLEN + 2]; 238 /* 239 snprintf(buf, sizeof(buf), "%s/%s", domain, map); 240 */ 241 242 strcpy(buf, domain); 243 strcat(buf, "/"); 244 strcat(buf, map); 245 246 if ((dbp = yp_find_db((char *)&buf, key, size)) != NULL) { 247 return(dbp); 248 } else { 249 if ((dbp = yp_open_db(domain, map)) != NULL) 250 yp_add_db(dbp, (char *)&buf, size); 251 } 252 253 return (dbp); 254 } 255 #endif 256 257 /* 258 * Open a DB database. 259 */ 260 DB *yp_open_db(domain, map) 261 const char *domain; 262 const char *map; 263 { 264 DB *dbp = NULL; 265 char buf[MAXPATHLEN + 2]; 266 267 yp_errno = YP_TRUE; 268 269 if (map[0] == '.' || strchr(map, '/')) { 270 yp_errno = YP_BADARGS; 271 return (NULL); 272 } 273 274 #ifdef DB_CACHE 275 if (yp_validdomain(domain)) { 276 yp_errno = YP_NODOM; 277 return(NULL); 278 } 279 #endif 280 snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map); 281 282 #ifdef DB_CACHE 283 again: 284 #endif 285 dbp = dbopen(buf,O_RDONLY, PERM_SECURE, DB_HASH, NULL); 286 287 if (dbp == NULL) { 288 switch(errno) { 289 #ifdef DB_CACHE 290 case ENFILE: 291 /* 292 * We ran out of file descriptors. Nuke an 293 * open one and try again. 294 */ 295 yp_error("ran out of file descriptors"); 296 yp_flush(numdbs - 1); 297 goto again; 298 break; 299 #endif 300 case ENOENT: 301 yp_errno = YP_NOMAP; 302 break; 303 case EFTYPE: 304 yp_errno = YP_BADDB; 305 break; 306 default: 307 yp_errno = YP_YPERR; 308 break; 309 } 310 } 311 312 return (dbp); 313 } 314 315 /* 316 * Database access routines. 317 * 318 * - yp_get_record(): retrieve an arbitrary key/data pair given one key 319 * to match against. 320 * 321 * - yp_first_record(): retrieve first key/data base in a database. 322 * 323 * - yp_next_record(): retrieve key/data pair that sequentially follows 324 * the supplied key value in the database. 325 */ 326 327 int yp_get_record(domain,map,key,data,allow) 328 const char *domain; 329 const char *map; 330 const DBT *key; 331 DBT *data; 332 int allow; 333 { 334 DB *dbp; 335 int rval = 0; 336 337 if (ypdb_debug) 338 yp_error("Looking up key [%.*s] in map [%s]", 339 key->size, key->data, map); 340 341 /* 342 * Avoid passing back magic "YP_*" entries unless 343 * the caller specifically requested them by setting 344 * the 'allow' flag. 345 */ 346 if (!allow && !strncmp(key->data, "YP_", 3)) 347 return(YP_NOKEY); 348 349 #ifdef DB_CACHE 350 if ((dbp = yp_open_db_cache(domain, map, NULL, 0)) == NULL) { 351 #else 352 if ((dbp = yp_open_db(domain, map)) == NULL) { 353 #endif 354 return(yp_errno); 355 } 356 357 if ((rval = (dbp->get)(dbp, key, data, 0)) != 0) { 358 #ifdef DB_CACHE 359 dbs[0]->size = 0; 360 #else 361 (void)(dbp->close)(dbp); 362 #endif 363 if (rval == 1) 364 return(YP_NOKEY); 365 else 366 return(YP_BADDB); 367 } 368 369 if (ypdb_debug) 370 yp_error("Result of lookup: key: [%.*s] data: [%.*s]", 371 key->size, key->data, data->size, data->data); 372 373 #ifdef DB_CACHE 374 if (dbs[0]->size) { 375 dbs[0]->key = key->data; 376 dbs[0]->size = key->size; 377 } 378 #else 379 (void)(dbp->close)(dbp); 380 #endif 381 382 return(YP_TRUE); 383 } 384 385 int yp_first_record(dbp,key,data,allow) 386 const DB *dbp; 387 DBT *key; 388 DBT *data; 389 int allow; 390 { 391 int rval; 392 393 if (ypdb_debug) 394 yp_error("Retrieving first key in map."); 395 396 if ((rval = (dbp->seq)(dbp,key,data,R_FIRST)) != 0) { 397 #ifdef DB_CACHE 398 dbs[0]->size = 0; 399 #endif 400 if (rval == 1) 401 return(YP_NOKEY); 402 else 403 return(YP_BADDB); 404 } 405 406 /* Avoid passing back magic "YP_*" records. */ 407 while (!strncmp(key->data, "YP_", 3) && !allow) { 408 if ((rval = (dbp->seq)(dbp,key,data,R_NEXT)) != 0) { 409 #ifdef DB_CACHE 410 dbs[0]->size = 0; 411 #endif 412 if (rval == 1) 413 return(YP_NOKEY); 414 else 415 return(YP_BADDB); 416 } 417 } 418 419 if (ypdb_debug) 420 yp_error("Result of lookup: key: [%.*s] data: [%.*s]", 421 key->size, key->data, data->size, data->data); 422 423 #ifdef DB_CACHE 424 if (dbs[0]->size) { 425 dbs[0]->key = key->data; 426 dbs[0]->size = key->size; 427 } 428 #endif 429 430 return(YP_TRUE); 431 } 432 433 int yp_next_record(dbp,key,data,all,allow) 434 const DB *dbp; 435 DBT *key; 436 DBT *data; 437 int all; 438 int allow; 439 { 440 static DBT lkey = { NULL, 0 }; 441 static DBT ldata = { NULL, 0 }; 442 int rval; 443 444 if (key == NULL || key->data == NULL) { 445 rval = yp_first_record(dbp,key,data,allow); 446 if (rval == YP_NOKEY) 447 return(YP_NOMORE); 448 else 449 return(rval); 450 } 451 452 if (ypdb_debug) 453 yp_error("Retreiving next key, previous was: [%.*s]", 454 key->size, key->data); 455 456 if (!all) { 457 #ifdef DB_CACHE 458 if (!dbs[0]->key) { 459 #endif 460 (dbp->seq)(dbp,&lkey,&ldata,R_FIRST); 461 while(strncmp((char *)key->data,lkey.data, 462 (int)key->size) || key->size != lkey.size) 463 if ((dbp->seq)(dbp,&lkey,&ldata,R_NEXT)) { 464 #ifdef DB_CACHE 465 dbs[0]->size = 0; 466 #endif 467 return(YP_NOKEY); 468 } 469 470 #ifdef DB_CACHE 471 } 472 #endif 473 } 474 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 /* Avoid passing back magic "YP_*" records. */ 483 while (!strncmp(key->data, "YP_", 3) && !allow) 484 if ((dbp->seq)(dbp,key,data,R_NEXT)) { 485 #ifdef DB_CACHE 486 dbs[0]->size = 0; 487 #endif 488 return(YP_NOMORE); 489 } 490 491 if (ypdb_debug) 492 yp_error("Result of lookup: key: [%.*s] data: [%.*s]", 493 key->size, key->data, data->size, data->data); 494 495 #ifdef DB_CACHE 496 if (dbs[0]->size) { 497 dbs[0]->key = key->data; 498 dbs[0]->size = key->size; 499 } 500 #else 501 lkey.data = key->data; 502 lkey.size = key->size; 503 #endif 504 505 return(YP_TRUE); 506 } 507