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