xref: /netbsd-src/usr.bin/getent/getent.c (revision 3816d47b2c42fcd6e549e3407f842a5b1a1d23ad)
1 /*	$NetBSD: getent.c,v 1.16 2009/04/12 10:27:08 lukem 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.16 2009/04/12 10:27:08 lukem 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 	{	"printcap",	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, rv, c;
598 	size_t	j;
599 	int	expand = 1, recurse = 0, pretty = 0;
600 
601 	assert(argc > 1);
602 	assert(argv != NULL);
603 
604 	argc--;
605 	argv++;
606 	while ((c = getopt(argc, argv, "pnr")) != -1)
607 		switch (c) {
608 		case 'n':
609 			expand = 0;
610 			break;
611 		case 'r':
612 			expand = 0;
613 			recurse = 1;
614 			break;
615 		case 'p':
616 			pretty = 1;
617 			break;
618 		default:
619 			usage();
620 			break;
621 		}
622 
623 	argc -= optind;
624 	argv += optind;
625 	csetexpandtc(expand);
626 	rv = RV_OK;
627 	if (argc == 0) {
628 		for (b = mygetone(db_array, 1); b; b = mygetone(db_array, 0)) {
629 			handleone(db_array, b, recurse, pretty, 0);
630 			free(b);
631 		}
632 	} else {
633 		if ((b = mygetent(db_array, argv[0])) == NULL)
634 			return RV_NOTFOUND;
635 		if (argc == 1)
636 			handleone(db_array, b, recurse, pretty, 0);
637 		else {
638 			for (i = 2; i < argc; i++) {
639 				for (j = 0; j < sizeof(sfx) - 1; j++) {
640 					cap = cgetcap(b, argv[i], sfx[j]);
641 					if (cap) {
642 						capprint(cap);
643 						break;
644 					}
645 				}
646 				if (j == sizeof(sfx) - 1)
647 					printf("false\n");
648 			}
649 		}
650 		free(b);
651 	}
652 	return rv;
653 }
654 
655 		/*
656 		 * gettytab
657 		 */
658 
659 static int
660 gettytab(int argc, char *argv[])
661 {
662 	return handlecap(_PATH_GETTYTAB, argc, argv);
663 }
664 
665 		/*
666 		 * printcap
667 		 */
668 
669 static int
670 printcap(int argc, char *argv[])
671 {
672 	return handlecap(_PATH_PRINTCAP, argc, argv);
673 }
674 
675 		/*
676 		 * disktab
677 		 */
678 
679 static int
680 disktab(int argc, char *argv[])
681 {
682 	return handlecap(_PATH_DISKTAB, argc, argv);
683 }
684 
685 		/*
686 		 * termcap
687 		 */
688 
689 static int
690 termcap(int argc, char *argv[])
691 {
692 	return handlecap(_PATH_TERMCAP, argc, argv);
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 		/*
735 		 * rpc
736 		 */
737 
738 static int
739 rpc(int argc, char *argv[])
740 {
741 	struct rpcent	*re;
742 	unsigned long	id;
743 	int		i, rv;
744 
745 	assert(argc > 1);
746 	assert(argv != NULL);
747 
748 #define RPCPRINT	printfmtstrings(re->r_aliases, "  ", " ", \
749 				"%-16s  %6d", \
750 				re->r_name, re->r_number)
751 
752 	setrpcent(1);
753 	rv = RV_OK;
754 	if (argc == 2) {
755 		while ((re = getrpcent()) != NULL)
756 			RPCPRINT;
757 	} else {
758 		for (i = 2; i < argc; i++) {
759 			if (parsenum(argv[i], &id))
760 				re = getrpcbynumber((int)id);
761 			else
762 				re = getrpcbyname(argv[i]);
763 			if (re != NULL)
764 				RPCPRINT;
765 			else {
766 				rv = RV_NOTFOUND;
767 				break;
768 			}
769 		}
770 	}
771 	endrpcent();
772 	return rv;
773 }
774 
775 		/*
776 		 * services
777 		 */
778 
779 static int
780 services(int argc, char *argv[])
781 {
782 	struct servent	*se;
783 	unsigned long	id;
784 	char		*proto;
785 	int		i, rv;
786 
787 	assert(argc > 1);
788 	assert(argv != NULL);
789 
790 #define SERVICESPRINT	printfmtstrings(se->s_aliases, "  ", " ", \
791 			    "%-16s  %5d/%s", \
792 			    se->s_name, ntohs(se->s_port), se->s_proto)
793 
794 	setservent(1);
795 	rv = RV_OK;
796 	if (argc == 2) {
797 		while ((se = getservent()) != NULL)
798 			SERVICESPRINT;
799 	} else {
800 		for (i = 2; i < argc; i++) {
801 			proto = strchr(argv[i], '/');
802 			if (proto != NULL)
803 				*proto++ = '\0';
804 			if (parsenum(argv[i], &id))
805 				se = getservbyport(htons(id), proto);
806 			else
807 				se = getservbyname(argv[i], proto);
808 			if (se != NULL)
809 				SERVICESPRINT;
810 			else {
811 				rv = RV_NOTFOUND;
812 				break;
813 			}
814 		}
815 	}
816 	endservent();
817 	return rv;
818 }
819 
820 
821 		/*
822 		 * shells
823 		 */
824 
825 static int
826 shells(int argc, char *argv[])
827 {
828 	const char	*sh;
829 	int		i, rv;
830 
831 	assert(argc > 1);
832 	assert(argv != NULL);
833 
834 #define SHELLSPRINT	(void)printf("%s\n", sh)
835 
836 	setusershell();
837 	rv = RV_OK;
838 	if (argc == 2) {
839 		while ((sh = getusershell()) != NULL)
840 			SHELLSPRINT;
841 	} else {
842 		for (i = 2; i < argc; i++) {
843 			setusershell();
844 			while ((sh = getusershell()) != NULL) {
845 				if (strcmp(sh, argv[i]) == 0) {
846 					SHELLSPRINT;
847 					break;
848 				}
849 			}
850 			if (sh == NULL) {
851 				rv = RV_NOTFOUND;
852 				break;
853 			}
854 		}
855 	}
856 	endusershell();
857 	return rv;
858 }
859