1 /* $NetBSD: netgroup_mkdb.c,v 1.11 2001/02/19 23:22:45 cgd Exp $ */ 2 3 /* 4 * Copyright (c) 1994 Christos Zoulas 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Christos Zoulas. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 #include <sys/cdefs.h> 34 #ifndef lint 35 __RCSID("$NetBSD: netgroup_mkdb.c,v 1.11 2001/02/19 23:22:45 cgd Exp $"); 36 #endif 37 38 #include <sys/types.h> 39 #include <sys/param.h> 40 #include <sys/stat.h> 41 #include <stdlib.h> 42 #include <stddef.h> 43 #include <unistd.h> 44 #include <fcntl.h> 45 #include <db.h> 46 #include <err.h> 47 #include <errno.h> 48 #include <stdio.h> 49 #include <string.h> 50 #include <stringlist.h> 51 #define _NETGROUP_PRIVATE 52 #include <netgroup.h> 53 #include <assert.h> 54 55 #include "str.h" 56 #include "util.h" 57 58 #define DEBUG_NG 59 60 #define NEW(a) (a *) emalloc(sizeof(a)) 61 62 struct nentry { 63 int n_type; 64 size_t n_size; /* Buffer size required for printing */ 65 union { 66 char *_name; 67 struct netgroup *_group; 68 } _n; 69 #define n_name _n._name 70 #define n_group _n._group 71 struct nentry *n_next; 72 }; 73 74 75 static void cleanup __P((void)); 76 int main __P((int, char **)); 77 static DB *ng_insert __P((DB *, const char *)); 78 static void ng_reventry __P((DB *, DB *, struct nentry *, char *, 79 size_t, StringList *)); 80 81 static void ng_print __P((struct nentry *, struct string *)); 82 static void ng_rprint __P((DB *, struct string *)); 83 static DB *ng_reverse __P((DB *, size_t)); 84 static DB *ng_load __P((const char *)); 85 static void ng_write __P((DB *, DB *, int)); 86 static void ng_rwrite __P((DB *, DB *, int)); 87 static void usage __P((void)); 88 89 #ifdef DEBUG_NG 90 static int debug = 0; 91 static void ng_dump __P((DB *)); 92 static void ng_rdump __P((DB *)); 93 #endif /* DEBUG_NG */ 94 95 96 static const char ng_empty[] = ""; 97 #define NG_EMPTY(a) ((a) ? (a) : ng_empty) 98 99 static char *dbname = _PATH_NETGROUP_DB; 100 101 int 102 main(argc, argv) 103 int argc; 104 char **argv; 105 { 106 DB *db, *ndb, *hdb, *udb; 107 int ch; 108 char buf[MAXPATHLEN]; 109 char *fname = _PATH_NETGROUP; 110 111 112 while ((ch = getopt(argc, argv, "do:")) != -1) 113 switch (ch) { 114 #ifdef DEBUG_NG 115 case 'd': 116 debug++; 117 break; 118 #endif 119 case 'o': 120 dbname = optarg; 121 break; 122 123 case '?': 124 default: 125 usage(); 126 } 127 128 argc -= optind; 129 argv += optind; 130 131 if (argc == 1) 132 fname = *argv; 133 else if (argc > 1) 134 usage(); 135 136 if (atexit(cleanup)) 137 err(1, "Cannot install exit handler"); 138 139 /* Read and parse the netgroup file */ 140 ndb = ng_load(fname); 141 #ifdef DEBUG_NG 142 if (debug) { 143 (void) fprintf(stderr, "#### Database\n"); 144 ng_dump(ndb); 145 } 146 #endif 147 148 /* Reverse the database by host */ 149 hdb = ng_reverse(ndb, offsetof(struct netgroup, ng_host)); 150 #ifdef DEBUG_NG 151 if (debug) { 152 (void) fprintf(stderr, "#### Reverse by host\n"); 153 ng_rdump(hdb); 154 } 155 #endif 156 157 /* Reverse the database by user */ 158 udb = ng_reverse(ndb, offsetof(struct netgroup, ng_user)); 159 #ifdef DEBUG_NG 160 if (debug) { 161 (void) fprintf(stderr, "#### Reverse by user\n"); 162 ng_rdump(udb); 163 } 164 #endif 165 166 (void) snprintf(buf, sizeof(buf), "%s.tmp", dbname); 167 168 db = dbopen(buf, O_RDWR | O_CREAT | O_EXCL, 169 (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), DB_HASH, NULL); 170 if (!db) 171 err(1, "%s", buf); 172 173 ng_write(db, ndb, _NG_KEYBYNAME); 174 ng_rwrite(db, udb, _NG_KEYBYUSER); 175 ng_rwrite(db, hdb, _NG_KEYBYHOST); 176 177 if ((db->close)(db)) 178 err(1, "Error closing database"); 179 180 if (rename(buf, dbname) == -1) 181 err(1, "Cannot rename `%s' to `%s'", buf, dbname); 182 183 return 0; 184 } 185 186 187 /* 188 * cleanup(): Remove temporary files upon exit 189 */ 190 static void 191 cleanup() 192 { 193 char buf[MAXPATHLEN]; 194 (void) snprintf(buf, sizeof(buf), "%s.tmp", dbname); 195 (void) unlink(buf); 196 } 197 198 199 200 /* 201 * ng_load(): Load the netgroup database from a file 202 */ 203 static DB * 204 ng_load(fname) 205 const char *fname; 206 { 207 FILE *fp; 208 DB *db; 209 char *buf; 210 size_t size; 211 struct nentry *tail, *head, *e; 212 char *p, *name; 213 struct netgroup *ng; 214 DBT data, key; 215 216 /* Open the netgroup file */ 217 if ((fp = fopen(fname, "r")) == NULL) 218 err(1, "%s", fname); 219 220 db = dbopen(NULL, O_RDWR | O_CREAT | O_EXCL, 0, DB_HASH, NULL); 221 222 if (db == NULL) 223 err(1, "dbopen"); 224 225 while ((buf = getline(fp, &size)) != NULL) { 226 tail = head = NULL; 227 p = buf; 228 229 while (p != NULL) { 230 switch (_ng_parse(&p, &name, &ng)) { 231 case _NG_NONE: 232 /* done with this one */ 233 p = NULL; 234 free(buf); 235 if (head == NULL) 236 break; 237 238 key.data = (u_char *) head->n_name; 239 key.size = strlen(head->n_name) + 1; 240 data.data = (u_char *) & head; 241 data.size = sizeof(head); 242 switch ((db->put)(db, &key, &data, 243 R_NOOVERWRITE)) { 244 case 0: 245 break; 246 247 case 1: 248 warnx("Duplicate entry netgroup `%s'\n", 249 head->n_name); 250 break; 251 252 case -1: 253 err(1, "put"); 254 break; 255 256 default: 257 abort(); 258 break; 259 } 260 break; 261 262 case _NG_NAME: 263 e = NEW(struct nentry); 264 e->n_type = _NG_NAME; 265 e->n_name = name; 266 e->n_next = NULL; 267 e->n_size = size; 268 if (tail == NULL) 269 head = tail = e; 270 else { 271 tail->n_next = e; 272 tail = e; 273 } 274 break; 275 276 case _NG_GROUP: 277 if (tail == NULL) { 278 char fmt[BUFSIZ]; 279 _ng_print(fmt, sizeof(fmt), ng); 280 errx(1, "no netgroup key for %s", fmt); 281 } 282 else { 283 e = NEW(struct nentry); 284 e->n_type = _NG_GROUP; 285 e->n_group = ng; 286 e->n_next = NULL; 287 e->n_size = size; 288 tail->n_next = e; 289 tail = e; 290 } 291 break; 292 293 case _NG_ERROR: 294 errx(1, "Fatal error at `%s'", p); 295 break; 296 297 default: 298 abort(); 299 break; 300 } 301 } 302 } 303 (void) fclose(fp); 304 return db; 305 } 306 307 308 /* 309 * ng_insert(): Insert named key into the database, and return its associated 310 * string database 311 */ 312 static DB * 313 ng_insert(db, name) 314 DB *db; 315 const char *name; 316 { 317 DB *xdb = NULL; 318 DBT key, data; 319 320 key.data = (u_char *) name; 321 key.size = strlen(name) + 1; 322 323 switch ((db->get)(db, &key, &data, 0)) { 324 case 0: 325 memcpy(&xdb, data.data, sizeof(xdb)); 326 break; 327 328 case 1: 329 xdb = dbopen(NULL, O_RDWR | O_CREAT | O_EXCL, 0, DB_HASH, NULL); 330 if (xdb == NULL) 331 err(1, "dbopen"); 332 333 data.data = (u_char *) & xdb; 334 data.size = sizeof(xdb); 335 switch ((db->put)(db, &key, &data, R_NOOVERWRITE)) { 336 case 0: 337 break; 338 339 case -1: 340 err(1, "db put `%s'", name); 341 break; 342 343 case 1: 344 default: 345 abort(); 346 } 347 break; 348 349 case -1: 350 err(1, "db get `%s'", name); 351 break; 352 353 default: 354 abort(); 355 break; 356 } 357 358 return xdb; 359 } 360 361 362 /* 363 * ng_reventry(): Recursively add all the netgroups to the group entry. 364 */ 365 static void 366 ng_reventry(db, udb, fe, name, s, ss) 367 DB *db, *udb; 368 struct nentry *fe; 369 char *name; 370 size_t s; 371 StringList *ss; 372 { 373 DBT key, data; 374 struct nentry *e; 375 struct netgroup *ng; 376 char *p; 377 DB *xdb; 378 379 if (sl_find(ss, fe->n_name) != NULL) { 380 warnx("Cycle in netgroup `%s'", name); 381 return; 382 } 383 sl_add(ss, fe->n_name); 384 385 for (e = fe->n_next; e != NULL; e = e->n_next) 386 switch (e->n_type) { 387 case _NG_GROUP: 388 ng = e->n_group; 389 p = _ng_makekey(*((char **)(((char *) ng) + s)), 390 ng->ng_domain, e->n_size); 391 xdb = ng_insert(udb, p); 392 key.data = (u_char *) name; 393 key.size = strlen(name) + 1; 394 data.data = NULL; 395 data.size = 0; 396 switch ((xdb->put)(xdb, &key, &data, R_NOOVERWRITE)) { 397 case 0: 398 case 1: 399 break; 400 401 case -1: 402 err(1, "db put `%s'", name); 403 return; 404 405 default: 406 abort(); 407 break; 408 } 409 free(p); 410 break; 411 412 case _NG_NAME: 413 key.data = (u_char *) e->n_name; 414 key.size = strlen(e->n_name) + 1; 415 switch ((db->get)(db, &key, &data, 0)) { 416 struct nentry *rfe; 417 case 0: 418 (void) memcpy(&rfe, data.data, sizeof(rfe)); 419 ng_reventry(db, udb, rfe, name, s, ss); 420 break; 421 422 case 1: 423 break; 424 425 case -1: 426 err(1, "db get `%s'", e->n_name); 427 return; 428 429 default: 430 abort(); 431 return; 432 } 433 break; 434 435 default: 436 abort(); 437 break; 438 } 439 } 440 441 442 /* 443 * ng_reverse(): Reverse the database 444 */ 445 static DB * 446 ng_reverse(db, s) 447 DB *db; 448 size_t s; 449 { 450 int pos; 451 StringList *sl; 452 DBT key, data; 453 struct nentry *fe; 454 DB *udb = dbopen(NULL, O_RDWR | O_CREAT | O_EXCL, 0, 455 DB_HASH, NULL); 456 457 if (udb == NULL) 458 err(1, "dbopen"); 459 460 for (pos = R_FIRST;; pos = R_NEXT) 461 switch ((db->seq)(db, &key, &data, pos)) { 462 case 0: 463 sl = sl_init(); 464 memcpy(&fe, data.data, sizeof(fe)); 465 ng_reventry(db, udb, fe, (char *) key.data, s, sl); 466 sl_free(sl, 0); 467 break; 468 469 case 1: 470 return udb; 471 472 case -1: 473 err(1, "seq"); 474 return udb; 475 } 476 477 return udb; 478 } 479 480 481 /* 482 * ng_print(): Pretty print a netgroup entry 483 */ 484 static void 485 ng_print(e, str) 486 struct nentry *e; 487 struct string *str; 488 { 489 char *ptr = emalloc(e->n_size); 490 491 if (e->n_next == NULL) { 492 str_append(str, "", ' '); 493 return; 494 } 495 496 for (e = e->n_next; e != NULL; e = e->n_next) { 497 switch (e->n_type) { 498 case _NG_NAME: 499 (void) snprintf(ptr, e->n_size, "%s", e->n_name); 500 break; 501 502 case _NG_GROUP: 503 (void) snprintf(ptr, e->n_size, "(%s,%s,%s)", 504 NG_EMPTY(e->n_group->ng_host), 505 NG_EMPTY(e->n_group->ng_user), 506 NG_EMPTY(e->n_group->ng_domain)); 507 break; 508 509 default: 510 errx(1, "Internal error: Bad netgroup type\n"); 511 break; 512 } 513 str_append(str, ptr, ' '); 514 } 515 free(ptr); 516 } 517 518 519 /* 520 * ng_rprint(): Pretty print all reverse netgroup mappings in the given entry 521 */ 522 static void 523 ng_rprint(db, str) 524 DB *db; 525 struct string *str; 526 { 527 int pos; 528 DBT key, data; 529 530 for (pos = R_FIRST;; pos = R_NEXT) 531 switch ((db->seq)(db, &key, &data, pos)) { 532 case 0: 533 str_append(str, (char *) key.data, ','); 534 break; 535 536 case 1: 537 return; 538 539 default: 540 err(1, "seq"); 541 break; 542 } 543 } 544 545 546 #ifdef DEBUG_NG 547 /* 548 * ng_dump(): Pretty print all netgroups in the given database 549 */ 550 static void 551 ng_dump(db) 552 DB *db; 553 { 554 int pos; 555 DBT key, data; 556 struct nentry *e; 557 struct string buf; 558 559 for (pos = R_FIRST;; pos = R_NEXT) 560 switch ((db->seq)(db, &key, &data, pos)) { 561 case 0: 562 memcpy(&e, data.data, sizeof(e)); 563 str_init(&buf); 564 assert(e->n_type == _NG_NAME); 565 566 ng_print(e, &buf); 567 (void) fprintf(stderr, "%s\t%s\n", e->n_name, 568 buf.s_str ? buf.s_str : ""); 569 str_free(&buf); 570 break; 571 572 case 1: 573 return; 574 575 default: 576 err(1, "seq"); 577 return; 578 } 579 } 580 581 582 /* 583 * ng_rdump(): Pretty print all reverse mappings in the given database 584 */ 585 static void 586 ng_rdump(db) 587 DB *db; 588 { 589 int pos; 590 DBT key, data; 591 DB *xdb; 592 struct string buf; 593 594 for (pos = R_FIRST;; pos = R_NEXT) 595 switch ((db->seq)(db, &key, &data, pos)) { 596 case 0: 597 memcpy(&xdb, data.data, sizeof(xdb)); 598 str_init(&buf); 599 ng_rprint(xdb, &buf); 600 (void) fprintf(stderr, "%s\t%s\n", 601 (char *) key.data, 602 buf.s_str ? buf.s_str : ""); 603 str_free(&buf); 604 break; 605 606 case 1: 607 return; 608 609 default: 610 err(1, "seq"); 611 return; 612 } 613 } 614 #endif /* DEBUG_NG */ 615 616 617 /* 618 * ng_write(): Dump the database into a file. 619 */ 620 static void 621 ng_write(odb, idb, k) 622 DB *odb, *idb; 623 int k; 624 { 625 int pos; 626 DBT key, data; 627 struct nentry *e; 628 struct string skey, sdata; 629 630 for (pos = R_FIRST;; pos = R_NEXT) 631 switch ((idb->seq)(idb, &key, &data, pos)) { 632 case 0: 633 memcpy(&e, data.data, sizeof(e)); 634 str_init(&skey); 635 str_init(&sdata); 636 assert(e->n_type == _NG_NAME); 637 638 str_prepend(&skey, e->n_name, k); 639 ng_print(e, &sdata); 640 key.data = (u_char *) skey.s_str; 641 key.size = skey.s_len + 1; 642 data.data = (u_char *) sdata.s_str; 643 data.size = sdata.s_len + 1; 644 645 switch ((odb->put)(odb, &key, &data, R_NOOVERWRITE)) { 646 case 0: 647 break; 648 649 case -1: 650 err(1, "put"); 651 break; 652 653 case 1: 654 default: 655 abort(); 656 break; 657 } 658 659 str_free(&skey); 660 str_free(&sdata); 661 break; 662 663 case 1: 664 return; 665 666 default: 667 err(1, "seq"); 668 return; 669 } 670 } 671 672 673 /* 674 * ng_rwrite(): Write the database 675 */ 676 static void 677 ng_rwrite(odb, idb, k) 678 DB *odb; 679 DB *idb; 680 int k; 681 { 682 int pos; 683 DBT key, data; 684 DB *xdb; 685 struct string skey, sdata; 686 687 for (pos = R_FIRST;; pos = R_NEXT) 688 switch ((idb->seq)(idb, &key, &data, pos)) { 689 case 0: 690 memcpy(&xdb, data.data, sizeof(xdb)); 691 str_init(&skey); 692 str_init(&sdata); 693 694 str_prepend(&skey, (char *) key.data, k); 695 ng_rprint(xdb, &sdata); 696 key.data = (u_char *) skey.s_str; 697 key.size = skey.s_len + 1; 698 data.data = (u_char *) sdata.s_str; 699 data.size = sdata.s_len + 1; 700 701 switch ((odb->put)(odb, &key, &data, R_NOOVERWRITE)) { 702 case 0: 703 break; 704 705 case -1: 706 err(1, "put"); 707 break; 708 709 case 1: 710 default: 711 abort(); 712 break; 713 } 714 715 str_free(&skey); 716 str_free(&sdata); 717 break; 718 719 case 1: 720 return; 721 722 default: 723 err(1, "seq"); 724 return; 725 } 726 } 727 728 729 /* 730 * usage(): Print usage message and exit 731 */ 732 static void 733 usage() 734 { 735 736 fprintf(stderr, "usage: %s [-o db] file\n", getprogname()); 737 exit(1); 738 } 739