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.4 1996/07/07 19:04:33 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.4 1996/07/07 19:04:33 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 #include <sys/queue.h> 68 69 #ifndef MAXDBS 70 #define MAXDBS 20 71 #endif 72 73 static int numdbs = 0; 74 75 struct dbent { 76 DB *dbp; 77 char *name; 78 char *key; 79 int size; 80 }; 81 82 static CIRCLEQ_HEAD(circlehead, circleq_entry) qhead; 83 84 struct circleq_entry { 85 struct dbent *dbptr; 86 CIRCLEQ_ENTRY(circleq_entry) links; 87 }; 88 89 /* 90 * Initialize the circular queue. 91 */ 92 void yp_init_dbs() 93 { 94 CIRCLEQ_INIT(&qhead); 95 return; 96 } 97 98 /* 99 * Dynamically allocate an entry for the circular queue. 100 * Return a NULL pointer on failure. 101 */ 102 static struct circleq_entry *yp_malloc_qent() 103 { 104 register struct circleq_entry *q; 105 106 q = (struct circleq_entry *)malloc(sizeof(struct circleq_entry)); 107 if (q == NULL) { 108 yp_error("failed to malloc() circleq entry: %s", 109 strerror(errno)); 110 return(NULL); 111 } 112 bzero((char *)q, sizeof(struct circleq_entry)); 113 q->dbptr = (struct dbent *)malloc(sizeof(struct dbent)); 114 if (q->dbptr == NULL) { 115 yp_error("failed to malloc() circleq entry: %s", 116 strerror(errno)); 117 free(q); 118 return(NULL); 119 } 120 bzero((char *)q->dbptr, sizeof(struct dbent)); 121 122 return(q); 123 } 124 125 /* 126 * Free a previously allocated circular queue 127 * entry. 128 */ 129 static void yp_free_qent(q) 130 struct circleq_entry *q; 131 { 132 /* 133 * First, close the database. In theory, this is also 134 * supposed to free the resources allocated by the DB 135 * package, including the memory pointed to by q->dbptr->key. 136 * This means we don't have to free q->dbptr->key here. 137 */ 138 if (q->dbptr->dbp) { 139 (void)(q->dbptr->dbp->close)(q->dbptr->dbp); 140 q->dbptr->dbp = NULL; 141 } 142 /* 143 * Then free the database name, which was strdup()'ed. 144 */ 145 free(q->dbptr->name); 146 147 /* 148 * Free the rest of the dbent struct. 149 */ 150 free(q->dbptr); 151 q->dbptr = NULL; 152 153 /* 154 * Free the circleq struct. 155 */ 156 free(q); 157 q = NULL; 158 159 return; 160 } 161 162 /* 163 * Zorch a single entry in the dbent queue and release 164 * all its resources. (This always removes the last entry 165 * in the queue.) 166 */ 167 static void yp_flush() 168 { 169 register struct circleq_entry *qptr; 170 171 qptr = qhead.cqh_last; 172 CIRCLEQ_REMOVE(&qhead, qptr, links); 173 yp_free_qent(qptr); 174 numdbs--; 175 176 return; 177 } 178 179 /* 180 * Close all databases, erase all database names and empty the queue. 181 */ 182 void yp_flush_all() 183 { 184 register struct circleq_entry *qptr; 185 186 while(qhead.cqh_first != (void *)&qhead) { 187 qptr = qhead.cqh_first; /* save this */ 188 CIRCLEQ_REMOVE(&qhead, qhead.cqh_first, links); 189 yp_free_qent(qptr); 190 } 191 numdbs = 0; 192 193 return; 194 } 195 196 197 /* 198 * Add a DB handle and database name to the cache. We only maintain 199 * fixed number of entries in the cache, so if we're asked to store 200 * a new entry when all our slots are already filled, we have to kick 201 * out the entry in the last slot to make room. 202 */ 203 static int yp_cache_db(dbp, name, size) 204 DB *dbp; 205 char *name; 206 int size; 207 { 208 register struct circleq_entry *qptr; 209 210 if (numdbs == MAXDBS) { 211 if (ypdb_debug) 212 yp_error("queue overflow -- releasing last slot"); 213 yp_flush(); 214 } 215 216 /* 217 * Allocate a new queue entry. 218 */ 219 220 if ((qptr = yp_malloc_qent()) == NULL) { 221 yp_error("failed to allocate a new cache entry"); 222 return(1); 223 } 224 225 qptr->dbptr->dbp = dbp; 226 qptr->dbptr->name = strdup(name); 227 qptr->dbptr->size = size; 228 qptr->dbptr->key = NULL; 229 230 CIRCLEQ_INSERT_HEAD(&qhead, qptr, links); 231 numdbs++; 232 233 return(0); 234 } 235 236 /* 237 * Search the list for a database matching 'name.' If we find it, 238 * move it to the head of the list and return its DB handle. If 239 * not, just fail: yp_open_db_cache() will subsequently try to open 240 * the database itself and call yp_cache_db() to add it to the 241 * list. 242 * 243 * The search works like this: 244 * 245 * - The caller specifies the name of a database to locate. We try to 246 * find an entry in our queue with a matching name. 247 * 248 * - If the caller doesn't specify a key or size, we assume that the 249 * first entry that we encounter with a matching name is returned. 250 * This will result in matches regardless of the key/size values 251 * stored in the queue entry. 252 * 253 * - If the caller also specifies a key and length, we check to see 254 * if the key and length saved in the queue entry also matches. 255 * This lets us return a DB handle that's already positioned at the 256 * correct location within a database. 257 * 258 * - Once we have a match, it gets migrated to the top of the queue 259 * so that it will be easier to find if another request for 260 * the same database comes in later. 261 */ 262 static DB *yp_find_db(name, key, size) 263 char *name; 264 char *key; 265 int size; 266 { 267 register struct circleq_entry *qptr; 268 269 for (qptr = qhead.cqh_first; qptr != (void *)&qhead; 270 qptr = qptr->links.cqe_next) { 271 if (!strcmp(qptr->dbptr->name, name)) { 272 if (size) { 273 if (size != qptr->dbptr->size || 274 strncmp(qptr->dbptr->key, key, size)) 275 continue; 276 } else { 277 if (qptr->dbptr->size) 278 continue; 279 } 280 if (qptr != qhead.cqh_first) { 281 CIRCLEQ_REMOVE(&qhead, qptr, links); 282 CIRCLEQ_INSERT_HEAD(&qhead, qptr, links); 283 } 284 return(qptr->dbptr->dbp); 285 } 286 } 287 288 return(NULL); 289 } 290 291 /* 292 * Open a DB database and cache the handle for later use. We first 293 * check the cache to see if the required database is already open. 294 * If so, we fetch the handle from the cache. If not, we try to open 295 * the database and save the handle in the cache for later use. 296 */ 297 DB *yp_open_db_cache(domain, map, key, size) 298 const char *domain; 299 const char *map; 300 const char *key; 301 const int size; 302 { 303 DB *dbp = NULL; 304 char buf[MAXPATHLEN + 2]; 305 /* 306 snprintf(buf, sizeof(buf), "%s/%s", domain, map); 307 */ 308 yp_errno = YP_TRUE; 309 310 strcpy(buf, domain); 311 strcat(buf, "/"); 312 strcat(buf, map); 313 314 if ((dbp = yp_find_db((char *)&buf, key, size)) != NULL) { 315 return(dbp); 316 } else { 317 if ((dbp = yp_open_db(domain, map)) != NULL) { 318 if (yp_cache_db(dbp, (char *)&buf, size)) { 319 (void)(dbp->close)(dbp); 320 yp_errno = YP_YPERR; 321 return(NULL); 322 } 323 } 324 } 325 326 return (dbp); 327 } 328 #endif 329 330 /* 331 * Open a DB database. 332 */ 333 DB *yp_open_db(domain, map) 334 const char *domain; 335 const char *map; 336 { 337 DB *dbp = NULL; 338 char buf[MAXPATHLEN + 2]; 339 340 yp_errno = YP_TRUE; 341 342 if (map[0] == '.' || strchr(map, '/')) { 343 yp_errno = YP_BADARGS; 344 return (NULL); 345 } 346 347 #ifdef DB_CACHE 348 if (yp_validdomain(domain)) { 349 yp_errno = YP_NODOM; 350 return(NULL); 351 } 352 #endif 353 snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map); 354 355 #ifdef DB_CACHE 356 again: 357 #endif 358 dbp = dbopen(buf,O_RDONLY, PERM_SECURE, DB_HASH, NULL); 359 360 if (dbp == NULL) { 361 switch(errno) { 362 #ifdef DB_CACHE 363 case ENFILE: 364 /* 365 * We ran out of file descriptors. Nuke an 366 * open one and try again. 367 */ 368 yp_error("ran out of file descriptors"); 369 yp_flush(); 370 goto again; 371 break; 372 #endif 373 case ENOENT: 374 yp_errno = YP_NOMAP; 375 break; 376 case EFTYPE: 377 yp_errno = YP_BADDB; 378 break; 379 default: 380 yp_errno = YP_YPERR; 381 break; 382 } 383 } 384 385 return (dbp); 386 } 387 388 /* 389 * Database access routines. 390 * 391 * - yp_get_record(): retrieve an arbitrary key/data pair given one key 392 * to match against. 393 * 394 * - yp_first_record(): retrieve first key/data base in a database. 395 * 396 * - yp_next_record(): retrieve key/data pair that sequentially follows 397 * the supplied key value in the database. 398 */ 399 400 int yp_get_record(domain,map,key,data,allow) 401 const char *domain; 402 const char *map; 403 const DBT *key; 404 DBT *data; 405 int allow; 406 { 407 DB *dbp; 408 int rval = 0; 409 #ifndef DB_CACHE 410 static unsigned char buf[YPMAXRECORD]; 411 #endif 412 413 if (ypdb_debug) 414 yp_error("Looking up key [%.*s] in map [%s]", 415 key->size, key->data, map); 416 417 /* 418 * Avoid passing back magic "YP_*" entries unless 419 * the caller specifically requested them by setting 420 * the 'allow' flag. 421 */ 422 if (!allow && !strncmp(key->data, "YP_", 3)) 423 return(YP_NOKEY); 424 425 #ifdef DB_CACHE 426 if ((dbp = yp_open_db_cache(domain, map, NULL, 0)) == NULL) { 427 #else 428 if ((dbp = yp_open_db(domain, map)) == NULL) { 429 #endif 430 return(yp_errno); 431 } 432 433 if ((rval = (dbp->get)(dbp, key, data, 0)) != 0) { 434 #ifdef DB_CACHE 435 qhead.cqh_first->dbptr->size = 0; 436 #else 437 (void)(dbp->close)(dbp); 438 #endif 439 if (rval == 1) 440 return(YP_NOKEY); 441 else 442 return(YP_BADDB); 443 } 444 445 if (ypdb_debug) 446 yp_error("Result of lookup: key: [%.*s] data: [%.*s]", 447 key->size, key->data, data->size, data->data); 448 449 #ifdef DB_CACHE 450 if (qhead.cqh_first->dbptr->size) { 451 qhead.cqh_first->dbptr->key = key->data; 452 qhead.cqh_first->dbptr->size = key->size; 453 } 454 #else 455 bcopy((char *)data->data, (char *)&buf, data->size); 456 data->data = (void *)&buf; 457 (void)(dbp->close)(dbp); 458 #endif 459 460 return(YP_TRUE); 461 } 462 463 int yp_first_record(dbp,key,data,allow) 464 const DB *dbp; 465 DBT *key; 466 DBT *data; 467 int allow; 468 { 469 int rval; 470 #ifndef DB_CACHE 471 static unsigned char buf[YPMAXRECORD]; 472 #endif 473 474 if (ypdb_debug) 475 yp_error("Retrieving first key in map."); 476 477 if ((rval = (dbp->seq)(dbp,key,data,R_FIRST)) != 0) { 478 #ifdef DB_CACHE 479 qhead.cqh_first->dbptr->size = 0; 480 #endif 481 if (rval == 1) 482 return(YP_NOKEY); 483 else 484 return(YP_BADDB); 485 } 486 487 /* Avoid passing back magic "YP_*" records. */ 488 while (!strncmp(key->data, "YP_", 3) && !allow) { 489 if ((rval = (dbp->seq)(dbp,key,data,R_NEXT)) != 0) { 490 #ifdef DB_CACHE 491 qhead.cqh_first->dbptr->size = 0; 492 #endif 493 if (rval == 1) 494 return(YP_NOKEY); 495 else 496 return(YP_BADDB); 497 } 498 } 499 500 if (ypdb_debug) 501 yp_error("Result of lookup: key: [%.*s] data: [%.*s]", 502 key->size, key->data, data->size, data->data); 503 504 #ifdef DB_CACHE 505 if (qhead.cqh_first->dbptr->size) { 506 qhead.cqh_first->dbptr->key = key->data; 507 qhead.cqh_first->dbptr->size = key->size; 508 } 509 #else 510 bcopy((char *)data->data, (char *)&buf, data->size); 511 data->data = (void *)&buf; 512 #endif 513 514 return(YP_TRUE); 515 } 516 517 int yp_next_record(dbp,key,data,all,allow) 518 const DB *dbp; 519 DBT *key; 520 DBT *data; 521 int all; 522 int allow; 523 { 524 static DBT lkey = { NULL, 0 }; 525 static DBT ldata = { NULL, 0 }; 526 int rval; 527 #ifndef DB_CACHE 528 static unsigned char keybuf[YPMAXRECORD]; 529 static unsigned char datbuf[YPMAXRECORD]; 530 #endif 531 532 if (key == NULL || key->data == NULL) { 533 rval = yp_first_record(dbp,key,data,allow); 534 if (rval == YP_NOKEY) 535 return(YP_NOMORE); 536 else 537 return(rval); 538 } 539 540 if (ypdb_debug) 541 yp_error("Retreiving next key, previous was: [%.*s]", 542 key->size, key->data); 543 544 if (!all) { 545 #ifdef DB_CACHE 546 if (qhead.cqh_first->dbptr->key == NULL) { 547 #endif 548 (dbp->seq)(dbp,&lkey,&ldata,R_FIRST); 549 while(strncmp((char *)key->data,lkey.data, 550 (int)key->size) || key->size != lkey.size) 551 if ((dbp->seq)(dbp,&lkey,&ldata,R_NEXT)) { 552 #ifdef DB_CACHE 553 qhead.cqh_first->dbptr->size = 0; 554 #endif 555 return(YP_NOKEY); 556 } 557 558 #ifdef DB_CACHE 559 } 560 #endif 561 } 562 563 if ((dbp->seq)(dbp,key,data,R_NEXT)) { 564 #ifdef DB_CACHE 565 qhead.cqh_first->dbptr->size = 0; 566 #endif 567 return(YP_NOMORE); 568 } 569 570 /* Avoid passing back magic "YP_*" records. */ 571 while (!strncmp(key->data, "YP_", 3) && !allow) 572 if ((dbp->seq)(dbp,key,data,R_NEXT)) { 573 #ifdef DB_CACHE 574 qhead.cqh_first->dbptr->size = 0; 575 #endif 576 return(YP_NOMORE); 577 } 578 579 if (ypdb_debug) 580 yp_error("Result of lookup: key: [%.*s] data: [%.*s]", 581 key->size, key->data, data->size, data->data); 582 583 #ifdef DB_CACHE 584 if (qhead.cqh_first->dbptr->size) { 585 qhead.cqh_first->dbptr->key = key->data; 586 qhead.cqh_first->dbptr->size = key->size; 587 } 588 #else 589 bcopy((char *)key->data, (char *)&keybuf, key->size); 590 lkey.data = (void *)&keybuf; 591 lkey.size = key->size; 592 bcopy((char *)data->data, (char *)&datbuf, data->size); 593 data->data = (void *)&datbuf; 594 #endif 595 596 return(YP_TRUE); 597 } 598