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