yp_dblookup.c revision 20818
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.2 1996/12/22 15:54:15 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 50static const char rcsid[] = "$Id: yp_dblookup.c,v 1.2 1996/12/22 15:54:15 wpaul Exp $"; 51#endif 52 53int ypdb_debug = 0; 54enum ypstat yp_errno = YP_TRUE; 55 56#define PERM_SECURE (S_IRUSR|S_IWUSR) 57HASHINFO 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 73static int numdbs = 0; 74 75struct dbent { 76 DB *dbp; 77 char *name; 78 char *key; 79 int size; 80 int flags; 81}; 82 83static CIRCLEQ_HEAD(circlehead, circleq_entry) qhead; 84 85struct circleq_entry { 86 struct dbent *dbptr; 87 CIRCLEQ_ENTRY(circleq_entry) links; 88}; 89 90/* 91 * Initialize the circular queue. 92 */ 93void yp_init_dbs() 94{ 95 CIRCLEQ_INIT(&qhead); 96 return; 97} 98 99/* 100 * Dynamically allocate an entry for the circular queue. 101 * Return a NULL pointer on failure. 102 */ 103static struct circleq_entry *yp_malloc_qent() 104{ 105 register struct circleq_entry *q; 106 107 q = (struct circleq_entry *)malloc(sizeof(struct circleq_entry)); 108 if (q == NULL) { 109 yp_error("failed to malloc() circleq entry: %s", 110 strerror(errno)); 111 return(NULL); 112 } 113 bzero((char *)q, sizeof(struct circleq_entry)); 114 q->dbptr = (struct dbent *)malloc(sizeof(struct dbent)); 115 if (q->dbptr == NULL) { 116 yp_error("failed to malloc() circleq entry: %s", 117 strerror(errno)); 118 free(q); 119 return(NULL); 120 } 121 bzero((char *)q->dbptr, sizeof(struct dbent)); 122 123 return(q); 124} 125 126/* 127 * Free a previously allocated circular queue 128 * entry. 129 */ 130static void yp_free_qent(q) 131 struct circleq_entry *q; 132{ 133 /* 134 * First, close the database. In theory, this is also 135 * supposed to free the resources allocated by the DB 136 * package, including the memory pointed to by q->dbptr->key. 137 * This means we don't have to free q->dbptr->key here. 138 */ 139 if (q->dbptr->dbp) { 140 (void)(q->dbptr->dbp->close)(q->dbptr->dbp); 141 q->dbptr->dbp = NULL; 142 } 143 /* 144 * Then free the database name, which was strdup()'ed. 145 */ 146 free(q->dbptr->name); 147 148 /* 149 * Free the rest of the dbent struct. 150 */ 151 free(q->dbptr); 152 q->dbptr = NULL; 153 154 /* 155 * Free the circleq struct. 156 */ 157 free(q); 158 q = NULL; 159 160 return; 161} 162 163/* 164 * Zorch a single entry in the dbent queue and release 165 * all its resources. (This always removes the last entry 166 * in the queue.) 167 */ 168static void yp_flush() 169{ 170 register struct circleq_entry *qptr; 171 172 qptr = qhead.cqh_last; 173 CIRCLEQ_REMOVE(&qhead, qptr, links); 174 yp_free_qent(qptr); 175 numdbs--; 176 177 return; 178} 179 180/* 181 * Close all databases, erase all database names and empty the queue. 182 */ 183void yp_flush_all() 184{ 185 register struct circleq_entry *qptr; 186 187 while(qhead.cqh_first != (void *)&qhead) { 188 qptr = qhead.cqh_first; /* save this */ 189 CIRCLEQ_REMOVE(&qhead, qhead.cqh_first, links); 190 yp_free_qent(qptr); 191 } 192 numdbs = 0; 193 194 return; 195} 196 197static char *inter_string = "YP_INTERDOMAIN"; 198static char *secure_string = "YP_SECURE"; 199static int inter_sz = sizeof("YP_INTERDOMAIN") - 1; 200static int secure_sz = sizeof("YP_SECURE") - 1; 201 202static int yp_setflags(dbp) 203 DB *dbp; 204{ 205 DBT key = { NULL, 0 }, data = { NULL, 0 }; 206 int flags = 0; 207 208 key.data = inter_string; 209 key.size = inter_sz; 210 211 if (!(dbp->get)(dbp, &key, &data, 0)) 212 flags |= YP_INTERDOMAIN; 213 214 key.data = secure_string; 215 key.size = secure_sz; 216 217 if (!(dbp->get)(dbp, &key, &data, 0)) 218 flags |= YP_SECURE; 219 220 return(flags); 221} 222 223int yp_testflag(map, domain, flag) 224 char *map; 225 char *domain; 226 int flag; 227{ 228 char buf[MAXPATHLEN + 2]; 229 register struct circleq_entry *qptr; 230 231 if (map == NULL || domain == NULL) 232 return(0); 233 234 strcpy(buf, domain); 235 strcat(buf, "/"); 236 strcat(buf, map); 237 238 for (qptr = qhead.cqh_first; qptr != (void *)&qhead; 239 qptr = qptr->links.cqe_next) { 240 if (!strcmp(qptr->dbptr->name, buf)) { 241 if (qptr->dbptr->flags & flag) 242 return(1); 243 else 244 return(0); 245 } 246 } 247 248 if (yp_open_db_cache(domain, map, NULL, 0) == NULL) 249 return(0); 250 251 if (qhead.cqh_first->dbptr->flags & flag) 252 return(1); 253 254 return(0); 255} 256 257/* 258 * Add a DB handle and database name to the cache. We only maintain 259 * fixed number of entries in the cache, so if we're asked to store 260 * a new entry when all our slots are already filled, we have to kick 261 * out the entry in the last slot to make room. 262 */ 263static int yp_cache_db(dbp, name, size) 264 DB *dbp; 265 char *name; 266 int size; 267{ 268 register struct circleq_entry *qptr; 269 270 if (numdbs == MAXDBS) { 271 if (ypdb_debug) 272 yp_error("queue overflow -- releasing last slot"); 273 yp_flush(); 274 } 275 276 /* 277 * Allocate a new queue entry. 278 */ 279 280 if ((qptr = yp_malloc_qent()) == NULL) { 281 yp_error("failed to allocate a new cache entry"); 282 return(1); 283 } 284 285 qptr->dbptr->dbp = dbp; 286 qptr->dbptr->name = strdup(name); 287 qptr->dbptr->size = size; 288 qptr->dbptr->key = NULL; 289 290 qptr->dbptr->flags = yp_setflags(dbp); 291 292 CIRCLEQ_INSERT_HEAD(&qhead, qptr, links); 293 numdbs++; 294 295 return(0); 296} 297 298/* 299 * Search the list for a database matching 'name.' If we find it, 300 * move it to the head of the list and return its DB handle. If 301 * not, just fail: yp_open_db_cache() will subsequently try to open 302 * the database itself and call yp_cache_db() to add it to the 303 * list. 304 * 305 * The search works like this: 306 * 307 * - The caller specifies the name of a database to locate. We try to 308 * find an entry in our queue with a matching name. 309 * 310 * - If the caller doesn't specify a key or size, we assume that the 311 * first entry that we encounter with a matching name is returned. 312 * This will result in matches regardless of the key/size values 313 * stored in the queue entry. 314 * 315 * - If the caller also specifies a key and length, we check to see 316 * if the key and length saved in the queue entry also matches. 317 * This lets us return a DB handle that's already positioned at the 318 * correct location within a database. 319 * 320 * - Once we have a match, it gets migrated to the top of the queue 321 * so that it will be easier to find if another request for 322 * the same database comes in later. 323 */ 324static DB *yp_find_db(name, key, size) 325 char *name; 326 char *key; 327 int size; 328{ 329 register struct circleq_entry *qptr; 330 331 for (qptr = qhead.cqh_first; qptr != (void *)&qhead; 332 qptr = qptr->links.cqe_next) { 333 if (!strcmp(qptr->dbptr->name, name)) { 334 if (size) { 335 if (size != qptr->dbptr->size || 336 strncmp(qptr->dbptr->key, key, size)) 337 continue; 338 } else { 339 if (qptr->dbptr->size) 340 continue; 341 } 342 if (qptr != qhead.cqh_first) { 343 CIRCLEQ_REMOVE(&qhead, qptr, links); 344 CIRCLEQ_INSERT_HEAD(&qhead, qptr, links); 345 } 346 return(qptr->dbptr->dbp); 347 } 348 } 349 350 return(NULL); 351} 352 353/* 354 * Open a DB database and cache the handle for later use. We first 355 * check the cache to see if the required database is already open. 356 * If so, we fetch the handle from the cache. If not, we try to open 357 * the database and save the handle in the cache for later use. 358 */ 359DB *yp_open_db_cache(domain, map, key, size) 360 const char *domain; 361 const char *map; 362 const char *key; 363 const int size; 364{ 365 DB *dbp = NULL; 366 char buf[MAXPATHLEN + 2]; 367/* 368 snprintf(buf, sizeof(buf), "%s/%s", domain, map); 369*/ 370 yp_errno = YP_TRUE; 371 372 strcpy(buf, domain); 373 strcat(buf, "/"); 374 strcat(buf, map); 375 376 if ((dbp = yp_find_db((char *)&buf, key, size)) != NULL) { 377 return(dbp); 378 } else { 379 if ((dbp = yp_open_db(domain, map)) != NULL) { 380 if (yp_cache_db(dbp, (char *)&buf, size)) { 381 (void)(dbp->close)(dbp); 382 yp_errno = YP_YPERR; 383 return(NULL); 384 } 385 } 386 } 387 388 return (dbp); 389} 390#endif 391 392/* 393 * Open a DB database. 394 */ 395DB *yp_open_db(domain, map) 396 const char *domain; 397 const char *map; 398{ 399 DB *dbp = NULL; 400 char buf[MAXPATHLEN + 2]; 401 402 yp_errno = YP_TRUE; 403 404 if (map[0] == '.' || strchr(map, '/')) { 405 yp_errno = YP_BADARGS; 406 return (NULL); 407 } 408 409#ifdef DB_CACHE 410 if (yp_validdomain(domain)) { 411 yp_errno = YP_NODOM; 412 return(NULL); 413 } 414#endif 415 snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map); 416 417#ifdef DB_CACHE 418again: 419#endif 420 dbp = dbopen(buf,O_RDONLY, PERM_SECURE, DB_HASH, NULL); 421 422 if (dbp == NULL) { 423 switch(errno) { 424#ifdef DB_CACHE 425 case ENFILE: 426 /* 427 * We ran out of file descriptors. Nuke an 428 * open one and try again. 429 */ 430 yp_error("ran out of file descriptors"); 431 yp_flush(); 432 goto again; 433 break; 434#endif 435 case ENOENT: 436 yp_errno = YP_NOMAP; 437 break; 438 case EFTYPE: 439 yp_errno = YP_BADDB; 440 break; 441 default: 442 yp_errno = YP_YPERR; 443 break; 444 } 445 } 446 447 return (dbp); 448} 449 450/* 451 * Database access routines. 452 * 453 * - yp_get_record(): retrieve an arbitrary key/data pair given one key 454 * to match against. 455 * 456 * - yp_first_record(): retrieve first key/data base in a database. 457 * 458 * - yp_next_record(): retrieve key/data pair that sequentially follows 459 * the supplied key value in the database. 460 */ 461 462#ifdef DB_CACHE 463int yp_get_record(dbp,key,data,allow) 464 DB *dbp; 465#else 466int yp_get_record(domain,map,key,data,allow) 467 const char *domain; 468 const char *map; 469#endif 470 const DBT *key; 471 DBT *data; 472 int allow; 473{ 474#ifndef DB_CACHE 475 DB *dbp; 476#endif 477 int rval = 0; 478#ifndef DB_CACHE 479 static unsigned char buf[YPMAXRECORD]; 480#endif 481 482 if (ypdb_debug) 483 yp_error("Looking up key [%.*s]", 484 key->size, key->data); 485 486 /* 487 * Avoid passing back magic "YP_*" entries unless 488 * the caller specifically requested them by setting 489 * the 'allow' flag. 490 */ 491 if (!allow && !strncmp(key->data, "YP_", 3)) 492 return(YP_NOKEY); 493 494#ifndef DB_CACHE 495 if ((dbp = yp_open_db(domain, map)) == NULL) { 496 return(yp_errno); 497 } 498#endif 499 500 if ((rval = (dbp->get)(dbp, key, data, 0)) != 0) { 501#ifdef DB_CACHE 502 qhead.cqh_first->dbptr->size = 0; 503#else 504 (void)(dbp->close)(dbp); 505#endif 506 if (rval == 1) 507 return(YP_NOKEY); 508 else 509 return(YP_BADDB); 510 } 511 512 if (ypdb_debug) 513 yp_error("Result of lookup: key: [%.*s] data: [%.*s]", 514 key->size, key->data, data->size, data->data); 515 516#ifdef DB_CACHE 517 if (qhead.cqh_first->dbptr->size) { 518 qhead.cqh_first->dbptr->key = key->data; 519 qhead.cqh_first->dbptr->size = key->size; 520 } 521#else 522 bcopy((char *)data->data, (char *)&buf, data->size); 523 data->data = (void *)&buf; 524 (void)(dbp->close)(dbp); 525#endif 526 527 return(YP_TRUE); 528} 529 530int yp_first_record(dbp,key,data,allow) 531 const DB *dbp; 532 DBT *key; 533 DBT *data; 534 int allow; 535{ 536 int rval; 537#ifndef DB_CACHE 538 static unsigned char buf[YPMAXRECORD]; 539#endif 540 541 if (ypdb_debug) 542 yp_error("Retrieving first key in map."); 543 544 if ((rval = (dbp->seq)(dbp,key,data,R_FIRST)) != 0) { 545#ifdef DB_CACHE 546 qhead.cqh_first->dbptr->size = 0; 547#endif 548 if (rval == 1) 549 return(YP_NOKEY); 550 else 551 return(YP_BADDB); 552 } 553 554 /* Avoid passing back magic "YP_*" records. */ 555 while (!strncmp(key->data, "YP_", 3) && !allow) { 556 if ((rval = (dbp->seq)(dbp,key,data,R_NEXT)) != 0) { 557#ifdef DB_CACHE 558 qhead.cqh_first->dbptr->size = 0; 559#endif 560 if (rval == 1) 561 return(YP_NOKEY); 562 else 563 return(YP_BADDB); 564 } 565 } 566 567 if (ypdb_debug) 568 yp_error("Result of lookup: key: [%.*s] data: [%.*s]", 569 key->size, key->data, data->size, data->data); 570 571#ifdef DB_CACHE 572 if (qhead.cqh_first->dbptr->size) { 573 qhead.cqh_first->dbptr->key = key->data; 574 qhead.cqh_first->dbptr->size = key->size; 575 } 576#else 577 bcopy((char *)data->data, (char *)&buf, data->size); 578 data->data = (void *)&buf; 579#endif 580 581 return(YP_TRUE); 582} 583 584int yp_next_record(dbp,key,data,all,allow) 585 const DB *dbp; 586 DBT *key; 587 DBT *data; 588 int all; 589 int allow; 590{ 591 static DBT lkey = { NULL, 0 }; 592 static DBT ldata = { NULL, 0 }; 593 int rval; 594#ifndef DB_CACHE 595 static unsigned char keybuf[YPMAXRECORD]; 596 static unsigned char datbuf[YPMAXRECORD]; 597#endif 598 599 if (key == NULL || !key->size || key->data == NULL) { 600 rval = yp_first_record(dbp,key,data,allow); 601 if (rval == YP_NOKEY) 602 return(YP_NOMORE); 603 else 604 return(rval); 605 } 606 607 if (ypdb_debug) 608 yp_error("Retreiving next key, previous was: [%.*s]", 609 key->size, key->data); 610 611 if (!all) { 612#ifdef DB_CACHE 613 if (qhead.cqh_first->dbptr->key == NULL) { 614#endif 615 (dbp->seq)(dbp,&lkey,&ldata,R_FIRST); 616 while(strncmp((char *)key->data,lkey.data, 617 (int)key->size) || key->size != lkey.size) 618 if ((dbp->seq)(dbp,&lkey,&ldata,R_NEXT)) { 619#ifdef DB_CACHE 620 qhead.cqh_first->dbptr->size = 0; 621#endif 622 return(YP_NOKEY); 623 } 624 625#ifdef DB_CACHE 626 } 627#endif 628 } 629 630 if ((dbp->seq)(dbp,key,data,R_NEXT)) { 631#ifdef DB_CACHE 632 qhead.cqh_first->dbptr->size = 0; 633#endif 634 return(YP_NOMORE); 635 } 636 637 /* Avoid passing back magic "YP_*" records. */ 638 while (!strncmp(key->data, "YP_", 3) && !allow) 639 if ((dbp->seq)(dbp,key,data,R_NEXT)) { 640#ifdef DB_CACHE 641 qhead.cqh_first->dbptr->size = 0; 642#endif 643 return(YP_NOMORE); 644 } 645 646 if (ypdb_debug) 647 yp_error("Result of lookup: key: [%.*s] data: [%.*s]", 648 key->size, key->data, data->size, data->data); 649 650#ifdef DB_CACHE 651 if (qhead.cqh_first->dbptr->size) { 652 qhead.cqh_first->dbptr->key = key->data; 653 qhead.cqh_first->dbptr->size = key->size; 654 } 655#else 656 bcopy((char *)key->data, (char *)&keybuf, key->size); 657 lkey.data = (void *)&keybuf; 658 lkey.size = key->size; 659 bcopy((char *)data->data, (char *)&datbuf, data->size); 660 data->data = (void *)&datbuf; 661#endif 662 663 return(YP_TRUE); 664} 665 666#ifdef DB_CACHE 667/* 668 * Database glue functions. 669 */ 670 671static DB *yp_currmap_db = NULL; 672static int yp_allow_db = 0; 673 674ypstat yp_select_map(map, domain, key, allow) 675 char *map; 676 char *domain; 677 keydat *key; 678 int allow; 679{ 680 yp_currmap_db = yp_open_db_cache(domain, map, key->keydat_val, 681 key->keydat_len); 682 683 yp_allow_db = allow; 684 return(yp_errno); 685} 686 687ypstat yp_getbykey(key, val) 688 keydat *key; 689 valdat *val; 690{ 691 DBT db_key = { NULL, 0 }, db_val = { NULL, 0 }; 692 ypstat rval; 693 694 db_key.data = key->keydat_val; 695 db_key.size = key->keydat_len; 696 697 rval = yp_get_record(yp_currmap_db, 698 &db_key, &db_val, yp_allow_db); 699 700 if (rval == YP_TRUE) { 701 val->valdat_val = db_val.data; 702 val->valdat_len = db_val.size; 703 } 704 705 return(rval); 706} 707 708ypstat yp_firstbykey(key, val) 709 keydat *key; 710 valdat *val; 711{ 712 DBT db_key = { NULL, 0 }, db_val = { NULL, 0 }; 713 ypstat rval; 714 715 rval = yp_first_record(yp_currmap_db, &db_key, &db_val, yp_allow_db); 716 717 if (rval == YP_TRUE) { 718 key->keydat_val = db_key.data; 719 key->keydat_len = db_key.size; 720 val->valdat_val = db_val.data; 721 val->valdat_len = db_val.size; 722 } 723 724 return(rval); 725} 726 727ypstat yp_nextbykey(key, val) 728 keydat *key; 729 valdat *val; 730{ 731 DBT db_key = { NULL, 0 }, db_val = { NULL, 0 }; 732 ypstat rval; 733 734 db_key.data = key->keydat_val; 735 db_key.size = key->keydat_len; 736 737 rval = yp_next_record(yp_currmap_db, &db_key, &db_val, 0, yp_allow_db); 738 739 if (rval == YP_TRUE) { 740 key->keydat_val = db_key.data; 741 key->keydat_len = db_key.size; 742 val->valdat_val = db_val.data; 743 val->valdat_len = db_val.size; 744 } 745 746 return(rval); 747} 748#endif 749