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