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