xref: /netbsd-src/usr.bin/getent/getent.c (revision 267197ec1eebfcb9810ea27a89625b6ddf68e3e7)
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