1 /* $NetBSD: getent.c,v 1.12 2008/02/04 15:30:45 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2004-2006 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Luke Mewburn. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 #ifndef lint 41 __RCSID("$NetBSD: getent.c,v 1.12 2008/02/04 15:30:45 christos Exp $"); 42 #endif /* not lint */ 43 44 #include <sys/socket.h> 45 46 #include <assert.h> 47 #include <ctype.h> 48 #include <errno.h> 49 #include <grp.h> 50 #include <limits.h> 51 #include <netdb.h> 52 #include <pwd.h> 53 #include <stdio.h> 54 #include <stdarg.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 #include <paths.h> 59 #include <err.h> 60 61 #include <arpa/inet.h> 62 #include <arpa/nameser.h> 63 64 #include <net/if.h> 65 #include <net/if_ether.h> 66 67 #include <netinet/in.h> /* for INET6_ADDRSTRLEN */ 68 69 #include <rpc/rpcent.h> 70 71 #include <disktab.h> 72 73 static int usage(void) __attribute__((__noreturn__)); 74 static int parsenum(const char *, unsigned long *); 75 static int disktab(int, char *[]); 76 static int gettytab(int, char *[]); 77 static int ethers(int, char *[]); 78 static int group(int, char *[]); 79 static int hosts(int, char *[]); 80 static int networks(int, char *[]); 81 static int passwd(int, char *[]); 82 static int printcap(int, char *[]); 83 static int protocols(int, char *[]); 84 static int rpc(int, char *[]); 85 static int services(int, char *[]); 86 static int shells(int, char *[]); 87 static int termcap(int, char *[]); 88 89 enum { 90 RV_OK = 0, 91 RV_USAGE = 1, 92 RV_NOTFOUND = 2, 93 RV_NOENUM = 3 94 }; 95 96 static struct getentdb { 97 const char *name; 98 int (*callback)(int, char *[]); 99 } databases[] = { 100 { "disktab", disktab, }, 101 { "ethers", ethers, }, 102 { "gettytab", gettytab, }, 103 { "group", group, }, 104 { "hosts", hosts, }, 105 { "networks", networks, }, 106 { "passwd", passwd, }, 107 { "princap", printcap, }, 108 { "protocols", protocols, }, 109 { "rpc", rpc, }, 110 { "services", services, }, 111 { "shells", shells, }, 112 { "termcap", termcap, }, 113 114 { NULL, NULL, }, 115 }; 116 117 118 int 119 main(int argc, char *argv[]) 120 { 121 struct getentdb *curdb; 122 123 setprogname(argv[0]); 124 125 if (argc < 2) 126 usage(); 127 for (curdb = databases; curdb->name != NULL; curdb++) 128 if (strcmp(curdb->name, argv[1]) == 0) 129 return (*curdb->callback)(argc, argv); 130 131 warn("Unknown database `%s'", argv[1]); 132 usage(); 133 /* NOTREACHED */ 134 } 135 136 static int 137 usage(void) 138 { 139 struct getentdb *curdb; 140 141 (void)fprintf(stderr, "Usage: %s database [key ...]\n", 142 getprogname()); 143 (void)fprintf(stderr, " database may be one of:\n\t"); 144 for (curdb = databases; curdb->name != NULL; curdb++) 145 (void)fprintf(stderr, " %s", curdb->name); 146 (void)fprintf(stderr, "\n"); 147 exit(RV_USAGE); 148 /* NOTREACHED */ 149 } 150 151 static int 152 parsenum(const char *word, unsigned long *result) 153 { 154 unsigned long num; 155 char *ep; 156 157 assert(word != NULL); 158 assert(result != NULL); 159 160 if (!isdigit((unsigned char)word[0])) 161 return 0; 162 errno = 0; 163 num = strtoul(word, &ep, 10); 164 if (num == ULONG_MAX && errno == ERANGE) 165 return 0; 166 if (*ep != '\0') 167 return 0; 168 *result = num; 169 return 1; 170 } 171 172 /* 173 * printfmtstrings -- 174 * vprintf(format, ...), 175 * then the aliases (beginning with prefix, separated by sep), 176 * then a newline 177 */ 178 static void 179 printfmtstrings(char *strings[], const char *prefix, const char *sep, 180 const char *fmt, ...) 181 { 182 va_list ap; 183 const char *curpref; 184 size_t i; 185 186 va_start(ap, fmt); 187 (void)vprintf(fmt, ap); 188 va_end(ap); 189 190 curpref = prefix; 191 for (i = 0; strings[i] != NULL; i++) { 192 (void)printf("%s%s", curpref, strings[i]); 193 curpref = sep; 194 } 195 (void)printf("\n"); 196 } 197 198 199 /* 200 * ethers 201 */ 202 203 static int 204 ethers(int argc, char *argv[]) 205 { 206 char hostname[MAXHOSTNAMELEN + 1], *hp; 207 struct ether_addr ea, *eap; 208 int i, rv; 209 210 assert(argc > 1); 211 assert(argv != NULL); 212 213 #define ETHERSPRINT (void)printf("%-17s %s\n", ether_ntoa(eap), hp) 214 215 rv = RV_OK; 216 if (argc == 2) { 217 warnx("Enumeration not supported on ethers"); 218 rv = RV_NOENUM; 219 } else { 220 for (i = 2; i < argc; i++) { 221 if ((eap = ether_aton(argv[i])) == NULL) { 222 eap = &ea; 223 hp = argv[i]; 224 if (ether_hostton(hp, eap) != 0) { 225 rv = RV_NOTFOUND; 226 break; 227 } 228 } else { 229 hp = hostname; 230 if (ether_ntohost(hp, eap) != 0) { 231 rv = RV_NOTFOUND; 232 break; 233 } 234 } 235 ETHERSPRINT; 236 } 237 } 238 return rv; 239 } 240 241 /* 242 * group 243 */ 244 245 static int 246 group(int argc, char *argv[]) 247 { 248 struct group *gr; 249 unsigned long id; 250 int i, rv; 251 252 assert(argc > 1); 253 assert(argv != NULL); 254 255 #define GROUPPRINT printfmtstrings(gr->gr_mem, ":", ",", "%s:%s:%u", \ 256 gr->gr_name, gr->gr_passwd, gr->gr_gid) 257 258 (void)setgroupent(1); 259 rv = RV_OK; 260 if (argc == 2) { 261 while ((gr = getgrent()) != NULL) 262 GROUPPRINT; 263 } else { 264 for (i = 2; i < argc; i++) { 265 if (parsenum(argv[i], &id)) 266 gr = getgrgid((gid_t)id); 267 else 268 gr = getgrnam(argv[i]); 269 if (gr != NULL) 270 GROUPPRINT; 271 else { 272 rv = RV_NOTFOUND; 273 break; 274 } 275 } 276 } 277 endgrent(); 278 return rv; 279 } 280 281 282 /* 283 * hosts 284 */ 285 286 static void 287 hostsprint(const struct hostent *he) 288 { 289 char buf[INET6_ADDRSTRLEN]; 290 291 assert(he != NULL); 292 if (inet_ntop(he->h_addrtype, he->h_addr, buf, sizeof(buf)) == NULL) 293 (void)strlcpy(buf, "# unknown", sizeof(buf)); 294 printfmtstrings(he->h_aliases, " ", " ", "%-16s %s", buf, he->h_name); 295 } 296 297 static int 298 hosts(int argc, char *argv[]) 299 { 300 struct hostent *he; 301 char addr[IN6ADDRSZ]; 302 int i, rv; 303 304 assert(argc > 1); 305 assert(argv != NULL); 306 307 sethostent(1); 308 rv = RV_OK; 309 if (argc == 2) { 310 while ((he = gethostent()) != NULL) 311 hostsprint(he); 312 } else { 313 for (i = 2; i < argc; i++) { 314 if (inet_pton(AF_INET6, argv[i], (void *)addr) > 0) 315 he = gethostbyaddr(addr, IN6ADDRSZ, AF_INET6); 316 else if (inet_pton(AF_INET, argv[i], (void *)addr) > 0) 317 he = gethostbyaddr(addr, INADDRSZ, AF_INET); 318 else 319 he = gethostbyname(argv[i]); 320 if (he != NULL) 321 hostsprint(he); 322 else { 323 rv = RV_NOTFOUND; 324 break; 325 } 326 } 327 } 328 endhostent(); 329 return rv; 330 } 331 332 333 /* 334 * networks 335 */ 336 337 static void 338 networksprint(const struct netent *ne) 339 { 340 char buf[INET6_ADDRSTRLEN]; 341 struct in_addr ianet; 342 343 assert(ne != NULL); 344 ianet = inet_makeaddr(ne->n_net, 0); 345 if (inet_ntop(ne->n_addrtype, &ianet, buf, sizeof(buf)) == NULL) 346 (void)strlcpy(buf, "# unknown", sizeof(buf)); 347 printfmtstrings(ne->n_aliases, " ", " ", "%-16s %s", ne->n_name, buf); 348 } 349 350 static int 351 networks(int argc, char *argv[]) 352 { 353 struct netent *ne; 354 in_addr_t net; 355 int i, rv; 356 357 assert(argc > 1); 358 assert(argv != NULL); 359 360 setnetent(1); 361 rv = RV_OK; 362 if (argc == 2) { 363 while ((ne = getnetent()) != NULL) 364 networksprint(ne); 365 } else { 366 for (i = 2; i < argc; i++) { 367 net = inet_network(argv[i]); 368 if (net != INADDR_NONE) 369 ne = getnetbyaddr(net, AF_INET); 370 else 371 ne = getnetbyname(argv[i]); 372 if (ne != NULL) 373 networksprint(ne); 374 else { 375 rv = RV_NOTFOUND; 376 break; 377 } 378 } 379 } 380 endnetent(); 381 return rv; 382 } 383 384 385 /* 386 * passwd 387 */ 388 389 static int 390 passwd(int argc, char *argv[]) 391 { 392 struct passwd *pw; 393 unsigned long id; 394 int i, rv; 395 396 assert(argc > 1); 397 assert(argv != NULL); 398 399 #define PASSWDPRINT (void)printf("%s:%s:%u:%u:%s:%s:%s\n", \ 400 pw->pw_name, pw->pw_passwd, pw->pw_uid, \ 401 pw->pw_gid, pw->pw_gecos, pw->pw_dir, pw->pw_shell) 402 403 (void)setpassent(1); 404 rv = RV_OK; 405 if (argc == 2) { 406 while ((pw = getpwent()) != NULL) 407 PASSWDPRINT; 408 } else { 409 for (i = 2; i < argc; i++) { 410 if (parsenum(argv[i], &id)) 411 pw = getpwuid((uid_t)id); 412 else 413 pw = getpwnam(argv[i]); 414 if (pw != NULL) 415 PASSWDPRINT; 416 else { 417 rv = RV_NOTFOUND; 418 break; 419 } 420 } 421 } 422 endpwent(); 423 return rv; 424 } 425 426 static char * 427 mygetent(const char * const * db_array, const char *name) 428 { 429 char *buf = NULL; 430 int error; 431 432 switch (error = cgetent(&buf, db_array, name)) { 433 case -3: 434 warnx("tc= loop in record `%s' in `%s'", name, db_array[0]); 435 break; 436 case -2: 437 warn("system error fetching record `%s' in `%s'", name, 438 db_array[0]); 439 break; 440 case -1: 441 case 0: 442 break; 443 case 1: 444 warnx("tc= reference not found in record for `%s' in `%s'", 445 name, db_array[0]); 446 break; 447 default: 448 warnx("unknown error %d in record `%s' in `%s'", error, name, 449 db_array[0]); 450 break; 451 } 452 return buf; 453 } 454 455 static char * 456 mygetone(const char * const * db_array, int first) 457 { 458 char *buf = NULL; 459 int error; 460 461 switch (error = (first ? cgetfirst : cgetnext)(&buf, db_array)) { 462 case -2: 463 warnx("tc= loop in `%s'", db_array[0]); 464 break; 465 case -1: 466 warn("system error fetching record in `%s'", db_array[0]); 467 break; 468 case 0: 469 case 1: 470 break; 471 case 2: 472 warnx("tc= reference not found in `%s'", db_array[0]); 473 break; 474 default: 475 warnx("unknown error %d in `%s'", error, db_array[0]); 476 break; 477 } 478 return buf; 479 } 480 481 static void 482 capprint(const char *cap) 483 { 484 char *c = strchr(cap, ':'); 485 if (c) 486 if (c == cap) 487 (void)printf("true\n"); 488 else { 489 int l = (int)(c - cap); 490 (void)printf("%*.*s\n", l, l, cap); 491 } 492 else 493 (void)printf("%s\n", cap); 494 } 495 496 static void 497 prettyprint(char *b) 498 { 499 #define TERMWIDTH 65 500 int did = 0; 501 size_t len; 502 char *s, c; 503 504 for (;;) { 505 len = strlen(b); 506 if (len <= TERMWIDTH) { 507 done: 508 if (did) 509 printf("\t:"); 510 printf("%s\n", b); 511 return; 512 } 513 for (s = b + TERMWIDTH; s > b && *s != ':'; s--) 514 continue; 515 if (*s++ != ':') 516 goto done; 517 c = *s; 518 *s = '\0'; 519 if (did) 520 printf("\t:"); 521 did++; 522 printf("%s\\\n", b); 523 *s = c; 524 b = s; 525 } 526 } 527 528 static void 529 handleone(const char * const *db_array, char *b, int recurse, int pretty, 530 int level) 531 { 532 char *tc; 533 534 if (level && pretty) 535 printf("\n"); 536 if (pretty) 537 prettyprint(b); 538 else 539 printf("%s\n", b); 540 if (!recurse || cgetstr(b, "tc", &tc) <= 0) 541 return; 542 543 b = mygetent(db_array, tc); 544 free(tc); 545 546 if (b == NULL) 547 return; 548 549 handleone(db_array, b, recurse, pretty, ++level); 550 free(b); 551 } 552 553 static int 554 handlecap(const char *db, int argc, char *argv[]) 555 { 556 static const char sfx[] = "=#:"; 557 const char *db_array[] = { db, NULL }; 558 char *b, *cap; 559 int i, j, rv, c; 560 int expand = 1, recurse = 0, pretty = 0; 561 562 assert(argc > 1); 563 assert(argv != NULL); 564 565 argc--; 566 argv++; 567 while ((c = getopt(argc, argv, "pnr")) != -1) 568 switch (c) { 569 case 'n': 570 expand = 0; 571 break; 572 case 'r': 573 expand = 0; 574 recurse = 1; 575 break; 576 case 'p': 577 pretty = 1; 578 break; 579 default: 580 usage(); 581 break; 582 } 583 584 argc -= optind; 585 argv += optind; 586 csetexpandtc(expand); 587 rv = RV_OK; 588 if (argc == 0) { 589 for (b = mygetone(db_array, 1); b; b = mygetone(db_array, 0)) { 590 handleone(db_array, b, recurse, pretty, 0); 591 free(b); 592 } 593 } else { 594 if ((b = mygetent(db_array, argv[0])) == NULL) 595 return RV_NOTFOUND; 596 if (argc == 1) 597 handleone(db_array, b, recurse, pretty, 0); 598 else { 599 for (i = 2; i < argc; i++) { 600 for (j = 0; j < sizeof(sfx) - 1; j++) { 601 cap = cgetcap(b, argv[i], sfx[j]); 602 if (cap) { 603 capprint(cap); 604 break; 605 } 606 } 607 if (j == sizeof(sfx) - 1) 608 printf("false\n"); 609 } 610 } 611 free(b); 612 } 613 return rv; 614 } 615 616 /* 617 * gettytab 618 */ 619 620 static int 621 gettytab(int argc, char *argv[]) 622 { 623 return handlecap(_PATH_GETTYTAB, argc, argv); 624 } 625 626 /* 627 * printcap 628 */ 629 630 static int 631 printcap(int argc, char *argv[]) 632 { 633 return handlecap(_PATH_PRINTCAP, argc, argv); 634 } 635 636 /* 637 * disktab 638 */ 639 640 static int 641 disktab(int argc, char *argv[]) 642 { 643 return handlecap(_PATH_DISKTAB, argc, argv); 644 } 645 646 /* 647 * termcap 648 */ 649 650 static int 651 termcap(int argc, char *argv[]) 652 { 653 return handlecap(_PATH_TERMCAP, argc, argv); 654 } 655 /* 656 * protocols 657 */ 658 659 static int 660 protocols(int argc, char *argv[]) 661 { 662 struct protoent *pe; 663 unsigned long id; 664 int i, rv; 665 666 assert(argc > 1); 667 assert(argv != NULL); 668 669 #define PROTOCOLSPRINT printfmtstrings(pe->p_aliases, " ", " ", \ 670 "%-16s %5d", pe->p_name, pe->p_proto) 671 672 setprotoent(1); 673 rv = RV_OK; 674 if (argc == 2) { 675 while ((pe = getprotoent()) != NULL) 676 PROTOCOLSPRINT; 677 } else { 678 for (i = 2; i < argc; i++) { 679 if (parsenum(argv[i], &id)) 680 pe = getprotobynumber((int)id); 681 else 682 pe = getprotobyname(argv[i]); 683 if (pe != NULL) 684 PROTOCOLSPRINT; 685 else { 686 rv = RV_NOTFOUND; 687 break; 688 } 689 } 690 } 691 endprotoent(); 692 return rv; 693 } 694 695 /* 696 * rpc 697 */ 698 699 static int 700 rpc(int argc, char *argv[]) 701 { 702 struct rpcent *re; 703 unsigned long id; 704 int i, rv; 705 706 assert(argc > 1); 707 assert(argv != NULL); 708 709 #define RPCPRINT printfmtstrings(re->r_aliases, " ", " ", \ 710 "%-16s %6d", \ 711 re->r_name, re->r_number) 712 713 setrpcent(1); 714 rv = RV_OK; 715 if (argc == 2) { 716 while ((re = getrpcent()) != NULL) 717 RPCPRINT; 718 } else { 719 for (i = 2; i < argc; i++) { 720 if (parsenum(argv[i], &id)) 721 re = getrpcbynumber((int)id); 722 else 723 re = getrpcbyname(argv[i]); 724 if (re != NULL) 725 RPCPRINT; 726 else { 727 rv = RV_NOTFOUND; 728 break; 729 } 730 } 731 } 732 endrpcent(); 733 return rv; 734 } 735 736 /* 737 * services 738 */ 739 740 static int 741 services(int argc, char *argv[]) 742 { 743 struct servent *se; 744 unsigned long id; 745 char *proto; 746 int i, rv; 747 748 assert(argc > 1); 749 assert(argv != NULL); 750 751 #define SERVICESPRINT printfmtstrings(se->s_aliases, " ", " ", \ 752 "%-16s %5d/%s", \ 753 se->s_name, ntohs(se->s_port), se->s_proto) 754 755 setservent(1); 756 rv = RV_OK; 757 if (argc == 2) { 758 while ((se = getservent()) != NULL) 759 SERVICESPRINT; 760 } else { 761 for (i = 2; i < argc; i++) { 762 proto = strchr(argv[i], '/'); 763 if (proto != NULL) 764 *proto++ = '\0'; 765 if (parsenum(argv[i], &id)) 766 se = getservbyport(htons(id), proto); 767 else 768 se = getservbyname(argv[i], proto); 769 if (se != NULL) 770 SERVICESPRINT; 771 else { 772 rv = RV_NOTFOUND; 773 break; 774 } 775 } 776 } 777 endservent(); 778 return rv; 779 } 780 781 782 /* 783 * shells 784 */ 785 786 static int 787 shells(int argc, char *argv[]) 788 { 789 const char *sh; 790 int i, rv; 791 792 assert(argc > 1); 793 assert(argv != NULL); 794 795 #define SHELLSPRINT (void)printf("%s\n", sh) 796 797 setusershell(); 798 rv = RV_OK; 799 if (argc == 2) { 800 while ((sh = getusershell()) != NULL) 801 SHELLSPRINT; 802 } else { 803 for (i = 2; i < argc; i++) { 804 setusershell(); 805 while ((sh = getusershell()) != NULL) { 806 if (strcmp(sh, argv[i]) == 0) { 807 SHELLSPRINT; 808 break; 809 } 810 } 811 if (sh == NULL) { 812 rv = RV_NOTFOUND; 813 break; 814 } 815 } 816 } 817 endusershell(); 818 return rv; 819 } 820