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