1 /* $NetBSD: getent.c,v 1.7 2005/08/24 14:31:02 ginsbach Exp $ */ 2 3 /*- 4 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 5 * 6 * Copyright (c) 2004 The NetBSD Foundation, Inc. 7 * All rights reserved. 8 * 9 * This code is derived from software contributed to The NetBSD Foundation 10 * by Luke Mewburn. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <sys/cdefs.h> 35 __FBSDID("$FreeBSD$"); 36 37 #include <sys/socket.h> 38 #include <sys/param.h> 39 #include <arpa/inet.h> 40 #include <arpa/nameser.h> 41 #include <net/if.h> 42 #include <netinet/if_ether.h> 43 #include <netinet/in.h> /* for INET6_ADDRSTRLEN */ 44 #include <rpc/rpcent.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 <stdarg.h> 54 #include <stdint.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <unistd.h> 59 #include <utmpx.h> 60 61 static int usage(void); 62 static int parsenum(const char *, unsigned long *); 63 static int ethers(int, char *[]); 64 static int group(int, char *[]); 65 static int hosts(int, char *[]); 66 static int netgroup(int, char *[]); 67 static int networks(int, char *[]); 68 static int passwd(int, char *[]); 69 static int protocols(int, char *[]); 70 static int rpc(int, char *[]); 71 static int services(int, char *[]); 72 static int shells(int, char *[]); 73 static int utmpx(int, char *[]); 74 75 enum { 76 RV_OK = 0, 77 RV_USAGE = 1, 78 RV_NOTFOUND = 2, 79 RV_NOENUM = 3 80 }; 81 82 static struct getentdb { 83 const char *name; 84 int (*callback)(int, char *[]); 85 } databases[] = { 86 { "ethers", ethers, }, 87 { "group", group, }, 88 { "hosts", hosts, }, 89 { "netgroup", netgroup, }, 90 { "networks", networks, }, 91 { "passwd", passwd, }, 92 { "protocols", protocols, }, 93 { "rpc", rpc, }, 94 { "services", services, }, 95 { "shells", shells, }, 96 { "utmpx", utmpx, }, 97 98 { NULL, NULL, }, 99 }; 100 101 int 102 main(int argc, char *argv[]) 103 { 104 struct getentdb *curdb; 105 106 setprogname(argv[0]); 107 108 if (argc < 2) 109 usage(); 110 for (curdb = databases; curdb->name != NULL; curdb++) { 111 if (strcmp(curdb->name, argv[1]) == 0) { 112 exit(curdb->callback(argc, argv)); 113 } 114 } 115 fprintf(stderr, "Unknown database: %s\n", argv[1]); 116 usage(); 117 /* NOTREACHED */ 118 return RV_USAGE; 119 } 120 121 static int 122 usage(void) 123 { 124 struct getentdb *curdb; 125 126 fprintf(stderr, "Usage: %s database [key ...]\n", 127 getprogname()); 128 fprintf(stderr, " database may be one of:\n\t"); 129 for (curdb = databases; curdb->name != NULL; curdb++) { 130 fprintf(stderr, " %s", curdb->name); 131 } 132 fprintf(stderr, "\n"); 133 exit(RV_USAGE); 134 /* NOTREACHED */ 135 } 136 137 static int 138 parsenum(const char *word, unsigned long *result) 139 { 140 unsigned long num; 141 char *ep; 142 143 assert(word != NULL); 144 assert(result != NULL); 145 146 if (!isdigit((unsigned char)word[0])) 147 return 0; 148 errno = 0; 149 num = strtoul(word, &ep, 10); 150 if (num == ULONG_MAX && errno == ERANGE) 151 return 0; 152 if (*ep != '\0') 153 return 0; 154 *result = num; 155 return 1; 156 } 157 158 /* 159 * printfmtstrings -- 160 * vprintf(format, ...), 161 * then the aliases (beginning with prefix, separated by sep), 162 * then a newline 163 */ 164 static void 165 printfmtstrings(char *strings[], const char *prefix, const char *sep, 166 const char *fmt, ...) 167 { 168 va_list ap; 169 const char *curpref; 170 int i; 171 172 va_start(ap, fmt); 173 vprintf(fmt, ap); 174 175 curpref = prefix; 176 for (i = 0; strings[i] != NULL; i++) { 177 printf("%s%s", curpref, strings[i]); 178 curpref = sep; 179 } 180 printf("\n"); 181 va_end(ap); 182 } 183 184 /* 185 * ethers 186 */ 187 static int 188 ethers(int argc, char *argv[]) 189 { 190 char hostname[MAXHOSTNAMELEN + 1], *hp; 191 struct ether_addr ea, *eap; 192 int i, rv; 193 194 assert(argc > 1); 195 assert(argv != NULL); 196 197 #define ETHERSPRINT printf("%-17s %s\n", ether_ntoa(eap), hp) 198 199 rv = RV_OK; 200 if (argc == 2) { 201 fprintf(stderr, "Enumeration not supported on ethers\n"); 202 rv = RV_NOENUM; 203 } else { 204 for (i = 2; i < argc; i++) { 205 if ((eap = ether_aton(argv[i])) == NULL) { 206 eap = &ea; 207 hp = argv[i]; 208 if (ether_hostton(hp, eap) != 0) { 209 rv = RV_NOTFOUND; 210 break; 211 } 212 } else { 213 hp = hostname; 214 if (ether_ntohost(hp, eap) != 0) { 215 rv = RV_NOTFOUND; 216 break; 217 } 218 } 219 ETHERSPRINT; 220 } 221 } 222 return rv; 223 } 224 225 /* 226 * group 227 */ 228 229 static int 230 group(int argc, char *argv[]) 231 { 232 struct group *gr; 233 unsigned long id; 234 int i, rv; 235 236 assert(argc > 1); 237 assert(argv != NULL); 238 239 #define GROUPPRINT printfmtstrings(gr->gr_mem, ":", ",", "%s:%s:%u", \ 240 gr->gr_name, gr->gr_passwd, gr->gr_gid) 241 242 setgroupent(1); 243 rv = RV_OK; 244 if (argc == 2) { 245 while ((gr = getgrent()) != NULL) 246 GROUPPRINT; 247 } else { 248 for (i = 2; i < argc; i++) { 249 if (parsenum(argv[i], &id)) 250 gr = getgrgid((gid_t)id); 251 else 252 gr = getgrnam(argv[i]); 253 if (gr != NULL) 254 GROUPPRINT; 255 else { 256 rv = RV_NOTFOUND; 257 break; 258 } 259 } 260 } 261 endgrent(); 262 return rv; 263 } 264 265 266 /* 267 * hosts 268 */ 269 270 static void 271 hostsprint(const struct hostent *he) 272 { 273 char buf[INET6_ADDRSTRLEN]; 274 275 assert(he != NULL); 276 if (inet_ntop(he->h_addrtype, he->h_addr, buf, sizeof(buf)) == NULL) 277 strlcpy(buf, "# unknown", sizeof(buf)); 278 printfmtstrings(he->h_aliases, " ", " ", "%-16s %s", buf, he->h_name); 279 } 280 281 static int 282 hosts(int argc, char *argv[]) 283 { 284 struct hostent *he4, *he6; 285 char addr[IN6ADDRSZ]; 286 int i, rv; 287 288 assert(argc > 1); 289 assert(argv != NULL); 290 291 sethostent(1); 292 he4 = he6 = NULL; 293 rv = RV_OK; 294 if (argc == 2) { 295 while ((he4 = gethostent()) != NULL) 296 hostsprint(he4); 297 } else { 298 for (i = 2; i < argc; i++) { 299 if (inet_pton(AF_INET6, argv[i], (void *)addr) > 0) { 300 he6 = gethostbyaddr(addr, IN6ADDRSZ, AF_INET6); 301 if (he6 != NULL) 302 hostsprint(he6); 303 } else if (inet_pton(AF_INET, argv[i], 304 (void *)addr) > 0) { 305 he4 = gethostbyaddr(addr, INADDRSZ, AF_INET); 306 if (he4 != NULL) 307 hostsprint(he4); 308 } else { 309 he6 = gethostbyname2(argv[i], AF_INET6); 310 if (he6 != NULL) 311 hostsprint(he6); 312 he4 = gethostbyname(argv[i]); 313 if (he4 != NULL) 314 hostsprint(he4); 315 } 316 if ( he4 == NULL && he6 == NULL ) { 317 rv = RV_NOTFOUND; 318 break; 319 } 320 } 321 } 322 endhostent(); 323 return rv; 324 } 325 326 /* 327 * networks 328 */ 329 static void 330 networksprint(const struct netent *ne) 331 { 332 char buf[INET6_ADDRSTRLEN]; 333 struct in_addr ianet; 334 335 assert(ne != NULL); 336 ianet = inet_makeaddr(ne->n_net, 0); 337 if (inet_ntop(ne->n_addrtype, &ianet, buf, sizeof(buf)) == NULL) 338 strlcpy(buf, "# unknown", sizeof(buf)); 339 printfmtstrings(ne->n_aliases, " ", " ", "%-16s %s", ne->n_name, buf); 340 } 341 342 static int 343 networks(int argc, char *argv[]) 344 { 345 struct netent *ne; 346 in_addr_t net; 347 int i, rv; 348 349 assert(argc > 1); 350 assert(argv != NULL); 351 352 setnetent(1); 353 rv = RV_OK; 354 if (argc == 2) { 355 while ((ne = getnetent()) != NULL) 356 networksprint(ne); 357 } else { 358 for (i = 2; i < argc; i++) { 359 net = inet_network(argv[i]); 360 if (net != INADDR_NONE) 361 ne = getnetbyaddr(net, AF_INET); 362 else 363 ne = getnetbyname(argv[i]); 364 if (ne != NULL) 365 networksprint(ne); 366 else { 367 rv = RV_NOTFOUND; 368 break; 369 } 370 } 371 } 372 endnetent(); 373 return rv; 374 } 375 376 /* 377 * passwd 378 */ 379 static int 380 passwd(int argc, char *argv[]) 381 { 382 struct passwd *pw; 383 unsigned long id; 384 int i, rv; 385 386 assert(argc > 1); 387 assert(argv != NULL); 388 389 #define PASSWDPRINT printf("%s:%s:%u:%u:%s:%s:%s\n", \ 390 pw->pw_name, pw->pw_passwd, pw->pw_uid, \ 391 pw->pw_gid, pw->pw_gecos, pw->pw_dir, pw->pw_shell) 392 393 setpassent(1); 394 rv = RV_OK; 395 if (argc == 2) { 396 while ((pw = getpwent()) != NULL) 397 PASSWDPRINT; 398 } else { 399 for (i = 2; i < argc; i++) { 400 if (parsenum(argv[i], &id)) 401 pw = getpwuid((uid_t)id); 402 else 403 pw = getpwnam(argv[i]); 404 if (pw != NULL) 405 PASSWDPRINT; 406 else { 407 rv = RV_NOTFOUND; 408 break; 409 } 410 } 411 } 412 endpwent(); 413 return rv; 414 } 415 416 /* 417 * protocols 418 */ 419 static int 420 protocols(int argc, char *argv[]) 421 { 422 struct protoent *pe; 423 unsigned long id; 424 int i, rv; 425 426 assert(argc > 1); 427 assert(argv != NULL); 428 429 #define PROTOCOLSPRINT printfmtstrings(pe->p_aliases, " ", " ", \ 430 "%-16s %5d", pe->p_name, pe->p_proto) 431 432 setprotoent(1); 433 rv = RV_OK; 434 if (argc == 2) { 435 while ((pe = getprotoent()) != NULL) 436 PROTOCOLSPRINT; 437 } else { 438 for (i = 2; i < argc; i++) { 439 if (parsenum(argv[i], &id)) 440 pe = getprotobynumber((int)id); 441 else 442 pe = getprotobyname(argv[i]); 443 if (pe != NULL) 444 PROTOCOLSPRINT; 445 else { 446 rv = RV_NOTFOUND; 447 break; 448 } 449 } 450 } 451 endprotoent(); 452 return rv; 453 } 454 455 /* 456 * rpc 457 */ 458 static int 459 rpc(int argc, char *argv[]) 460 { 461 struct rpcent *re; 462 unsigned long id; 463 int i, rv; 464 465 assert(argc > 1); 466 assert(argv != NULL); 467 468 #define RPCPRINT printfmtstrings(re->r_aliases, " ", " ", \ 469 "%-16s %6d", \ 470 re->r_name, re->r_number) 471 472 setrpcent(1); 473 rv = RV_OK; 474 if (argc == 2) { 475 while ((re = getrpcent()) != NULL) 476 RPCPRINT; 477 } else { 478 for (i = 2; i < argc; i++) { 479 if (parsenum(argv[i], &id)) 480 re = getrpcbynumber((int)id); 481 else 482 re = getrpcbyname(argv[i]); 483 if (re != NULL) 484 RPCPRINT; 485 else { 486 rv = RV_NOTFOUND; 487 break; 488 } 489 } 490 } 491 endrpcent(); 492 return rv; 493 } 494 495 /* 496 * services 497 */ 498 static int 499 services(int argc, char *argv[]) 500 { 501 struct servent *se; 502 unsigned long id; 503 char *proto; 504 int i, rv; 505 506 assert(argc > 1); 507 assert(argv != NULL); 508 509 #define SERVICESPRINT printfmtstrings(se->s_aliases, " ", " ", \ 510 "%-16s %5d/%s", \ 511 se->s_name, ntohs(se->s_port), se->s_proto) 512 513 setservent(1); 514 rv = RV_OK; 515 if (argc == 2) { 516 while ((se = getservent()) != NULL) 517 SERVICESPRINT; 518 } else { 519 for (i = 2; i < argc; i++) { 520 proto = strchr(argv[i], '/'); 521 if (proto != NULL) 522 *proto++ = '\0'; 523 if (parsenum(argv[i], &id)) 524 se = getservbyport(htons((u_short)id), proto); 525 else 526 se = getservbyname(argv[i], proto); 527 if (se != NULL) 528 SERVICESPRINT; 529 else { 530 rv = RV_NOTFOUND; 531 break; 532 } 533 } 534 } 535 endservent(); 536 return rv; 537 } 538 539 /* 540 * shells 541 */ 542 static int 543 shells(int argc, char *argv[]) 544 { 545 const char *sh; 546 int i, rv; 547 548 assert(argc > 1); 549 assert(argv != NULL); 550 551 #define SHELLSPRINT printf("%s\n", sh) 552 553 setusershell(); 554 rv = RV_OK; 555 if (argc == 2) { 556 while ((sh = getusershell()) != NULL) 557 SHELLSPRINT; 558 } else { 559 for (i = 2; i < argc; i++) { 560 setusershell(); 561 while ((sh = getusershell()) != NULL) { 562 if (strcmp(sh, argv[i]) == 0) { 563 SHELLSPRINT; 564 break; 565 } 566 } 567 if (sh == NULL) { 568 rv = RV_NOTFOUND; 569 break; 570 } 571 } 572 } 573 endusershell(); 574 return rv; 575 } 576 577 /* 578 * netgroup 579 */ 580 static int 581 netgroup(int argc, char *argv[]) 582 { 583 char *host, *user, *domain; 584 int first; 585 int rv, i; 586 587 assert(argc > 1); 588 assert(argv != NULL); 589 590 #define NETGROUPPRINT(s) (((s) != NULL) ? (s) : "") 591 592 rv = RV_OK; 593 if (argc == 2) { 594 fprintf(stderr, "Enumeration not supported on netgroup\n"); 595 rv = RV_NOENUM; 596 } else { 597 for (i = 2; i < argc; i++) { 598 setnetgrent(argv[i]); 599 first = 1; 600 while (getnetgrent(&host, &user, &domain) != 0) { 601 if (first) { 602 first = 0; 603 (void)fputs(argv[i], stdout); 604 } 605 (void)printf(" (%s,%s,%s)", 606 NETGROUPPRINT(host), 607 NETGROUPPRINT(user), 608 NETGROUPPRINT(domain)); 609 } 610 if (!first) 611 (void)putchar('\n'); 612 endnetgrent(); 613 } 614 } 615 return rv; 616 } 617 618 /* 619 * utmpx 620 */ 621 622 #define UTMPXPRINTID do { \ 623 size_t i; \ 624 for (i = 0; i < sizeof ut->ut_id; i++) \ 625 printf("%02hhx", ut->ut_id[i]); \ 626 } while (0) 627 628 static void 629 utmpxprint(const struct utmpx *ut) 630 { 631 632 if (ut->ut_type == EMPTY) 633 return; 634 635 printf("[%jd.%06u -- %.24s] ", 636 (intmax_t)ut->ut_tv.tv_sec, (unsigned int)ut->ut_tv.tv_usec, 637 ctime(&ut->ut_tv.tv_sec)); 638 639 switch (ut->ut_type) { 640 case BOOT_TIME: 641 printf("system boot\n"); 642 return; 643 case SHUTDOWN_TIME: 644 printf("system shutdown\n"); 645 return; 646 case OLD_TIME: 647 printf("old system time\n"); 648 return; 649 case NEW_TIME: 650 printf("new system time\n"); 651 return; 652 case USER_PROCESS: 653 printf("user process: id=\""); 654 UTMPXPRINTID; 655 printf("\" pid=\"%d\" user=\"%s\" line=\"%s\" host=\"%s\"\n", 656 ut->ut_pid, ut->ut_user, ut->ut_line, ut->ut_host); 657 break; 658 case INIT_PROCESS: 659 printf("init process: id=\""); 660 UTMPXPRINTID; 661 printf("\" pid=\"%d\"\n", ut->ut_pid); 662 break; 663 case LOGIN_PROCESS: 664 printf("login process: id=\""); 665 UTMPXPRINTID; 666 printf("\" pid=\"%d\" user=\"%s\" line=\"%s\" host=\"%s\"\n", 667 ut->ut_pid, ut->ut_user, ut->ut_line, ut->ut_host); 668 break; 669 case DEAD_PROCESS: 670 printf("dead process: id=\""); 671 UTMPXPRINTID; 672 printf("\" pid=\"%d\"\n", ut->ut_pid); 673 break; 674 default: 675 printf("unknown record type %hu\n", ut->ut_type); 676 break; 677 } 678 } 679 680 static int 681 utmpx(int argc, char *argv[]) 682 { 683 const struct utmpx *ut; 684 const char *file = NULL; 685 int rv = RV_OK, db = 0; 686 687 assert(argc > 1); 688 assert(argv != NULL); 689 690 if (argc == 3 || argc == 4) { 691 if (strcmp(argv[2], "active") == 0) 692 db = UTXDB_ACTIVE; 693 else if (strcmp(argv[2], "lastlogin") == 0) 694 db = UTXDB_LASTLOGIN; 695 else if (strcmp(argv[2], "log") == 0) 696 db = UTXDB_LOG; 697 else 698 rv = RV_USAGE; 699 if (argc == 4) 700 file = argv[3]; 701 } else { 702 rv = RV_USAGE; 703 } 704 705 if (rv == RV_USAGE) { 706 fprintf(stderr, 707 "Usage: %s utmpx active | lastlogin | log [filename]\n", 708 getprogname()); 709 } else if (rv == RV_OK) { 710 if (setutxdb(db, file) != 0) 711 return (RV_NOTFOUND); 712 while ((ut = getutxent()) != NULL) 713 utmpxprint(ut); 714 endutxent(); 715 } 716 return (rv); 717 } 718