Home | History | Annotate | Line # | Download | only in getent
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