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