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