xref: /illumos-gate/usr/src/cmd/ypcmd/ypwhich.c (revision f83283b9)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  *
22  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
27 /*	  All Rights Reserved   */
28 
29 /*
30  * Portions of this source code were derived from Berkeley
31  * under license from the Regents of the University of
32  * California.
33  */
34 
35 /*
36  * This is a user command which tells which yp server is being used by a
37  * given machine, or which yp server is the master for a named map.
38  *
39  * Usage is:
40  *	ypwhich [-d domain] [-m [mname] [-t] | [-Vn] host]
41  *	ypwhich -x
42  * where:  the -d switch can be used to specify a domain other than the
43  * default domain.  -m tells the master of that map.  mname is a mapname
44  * If the -m option is used, ypwhich will act like a vanilla yp client,
45  * and will not attempt to choose a particular yp server.  On the
46  * other hand, if no -m switch is used, ypwhich will talk directly to the yp
47  * bind process on the named host, or to the local ypbind process if no host
48  * name is specified. -t switch inhibits nickname translation of map names.
49  * -x is to dump the nickname translation table from file /var/yp/nicknames.
50  *
51  */
52 
53 #include <stdio.h>
54 #include <ctype.h>
55 #include <rpc/rpc.h>
56 #include <rpcsvc/yp_prot.h>
57 #include <rpcsvc/ypclnt.h>
58 #include "yp_b.h"
59 #include "ypv2_bind.h"
60 #include <string.h>
61 #include <netdir.h>
62 #include <unistd.h>
63 #include <netdb.h>
64 #include <arpa/inet.h>
65 #include <inet/ip.h>
66 #include <inet/ip6.h>
67 #include <netinet/ip6.h>
68 #include <sys/utsname.h>
69 
70 #define	YPSLEEPTIME 5 /* between two tries of bind */
71 
72 #define	TIMEOUT 30			/* Total seconds for timeout */
73 #define	INTER_TRY 10			/* Seconds between tries */
74 
75 static int translate = TRUE;
76 static int dodump = FALSE;
77 static char *domain = NULL;
78 static char default_domain_name[YPMAXDOMAIN];
79 static char *host = NULL;
80 static int vers = YPBINDVERS;
81 static char default_host_name[256];
82 static bool get_master = FALSE;
83 static bool get_server = FALSE;
84 static char *map = NULL;
85 static char nm[YPMAXMAP+1];
86 static struct timeval timeout = {
87 		TIMEOUT,			/* Seconds */
88 		0				/* Microseconds */
89 };
90 static char nullstring[] = "\000";
91 static char err_usage[] =
92 "Usage:\n\
93 	ypwhich [-d domain] [[-t] -m [mname] | [-Vn] host]\n\
94 	ypwhich -x\n\
95 where\n\
96 	mname may be either a mapname or a nickname for a map.\n\
97 	host if specified, is the machine whose NIS server is to be found.\n\
98 	-t inhibits map nickname translation.\n\
99 	-Vn version of ypbind, V3 is default.\n\
100 	-x dumps the map nickname translation table.\n";
101 static char err_bad_args[] =
102 "ypwhich:  %s argument is bad.\n";
103 static char err_cant_get_kname[] =
104 "ypwhich:  can't get %s back from system call.\n";
105 static char err_null_kname[] =
106 "ypwhich:  the %s hasn't been set on this machine.\n";
107 static char err_bad_mapname[] = "mapname";
108 static char err_bad_domainname[] = "domainname";
109 static char err_bad_hostname[] = "hostname";
110 
111 static void get_command_line_args();
112 static void getdomain();
113 static void getlochost();
114 static void get_server_name();
115 static int call_binder();
116 static void get_map_master();
117 extern void maketable();
118 extern int getmapname();
119 #ifdef DEBUG
120 static void dump_response();
121 #endif
122 static void dump_ypmaps();
123 static void dumpmaps();
124 
125 static bool xdr_yp_inaddr();
126 static bool xdr_old_ypbind_resp();
127 static bool xdr_old_yp_binding();
128 static int old_call_binder();
129 static void print_server();
130 
131 /* need these for call to (remote) V2 ypbind */
132 struct old_ypbind_binding {
133 	struct in_addr ypbind_binding_addr;	/* In network order */
134 	unsigned short int ypbind_binding_port;	/* In network order */
135 };
136 
137 struct old_ypbind_resp {
138 	enum ypbind_resptype ypbind_status;
139 	union {
140 		unsigned long ypbind_error;
141 		struct old_ypbind_binding ypbind_bindinfo;
142 	} ypbind_respbody;
143 };
144 
145 /*
146  * This is the main line for the ypwhich process.
147  */
148 int
main(int argc,char ** argv)149 main(int argc, char **argv)
150 {
151 	get_command_line_args(argc, argv);
152 
153 	if (dodump) {
154 		maketable(dodump);
155 		exit(0);
156 	}
157 
158 	if (!domain) {
159 		getdomain();
160 	}
161 
162 	if (map && translate && (strchr(map, '.') == NULL) &&
163 		(getmapname(map, nm))) {
164 		map = nm;
165 	}
166 
167 	if (get_server) {
168 		if (!host)
169 			getlochost();
170 		get_server_name();
171 	} else {
172 		if (map)
173 			get_map_master();
174 		else
175 			dump_ypmaps();
176 	}
177 
178 	return (0);
179 }
180 
181 /*
182  * This does the command line argument processing.
183  */
184 static void
get_command_line_args(argc,argv)185 get_command_line_args(argc, argv)
186 int argc;
187 char **argv;
188 
189 {
190 	argv++;
191 
192 	if (argc == 1) {
193 		get_server = TRUE;
194 		return;
195 	}
196 
197 	while (--argc) {
198 
199 		if ((*argv)[0] == '-') {
200 
201 			switch ((*argv)[1]) {
202 
203 			case 'V':
204 
205 				vers = atoi(argv[0]+2);
206 				if (vers <  1) {
207 					(void) fprintf(stderr, err_usage);
208 					exit(1);
209 				}
210 				argv++;
211 				break;
212 
213 			case 'm':
214 				get_master = TRUE;
215 				argv++;
216 
217 				if (argc > 1) {
218 
219 					if ((*(argv))[0] == '-') {
220 						break;
221 					}
222 
223 					argc--;
224 					map = *argv;
225 					argv++;
226 
227 					if ((int)strlen(map) > YPMAXMAP) {
228 				(void) fprintf(stderr, err_bad_args,
229 						    err_bad_mapname);
230 						exit(1);
231 					}
232 
233 				}
234 
235 				break;
236 
237 			case 'd':
238 
239 				if (argc > 1) {
240 					argv++;
241 					argc--;
242 					domain = *argv;
243 					argv++;
244 
245 					if ((int)strlen(domain) > YPMAXDOMAIN) {
246 				(void) fprintf(stderr, err_bad_args,
247 				err_bad_domainname);
248 						exit(1);
249 					}
250 
251 				} else {
252 					(void) fprintf(stderr, err_usage);
253 					exit(1);
254 				}
255 
256 				break;
257 
258 			case 't':
259 				translate = FALSE;
260 				argv++;
261 				break;
262 
263 			case 'x':
264 				dodump = TRUE;
265 				argv++;
266 				break;
267 
268 			default:
269 				(void) fprintf(stderr, err_usage);
270 				exit(1);
271 			}
272 
273 		} else {
274 
275 			if (get_server) {
276 				(void) fprintf(stderr, err_usage);
277 				exit(1);
278 			}
279 
280 			get_server = TRUE;
281 			host = *argv;
282 			argv++;
283 
284 			if ((int)strlen(host) > 256) {
285 				(void) fprintf(stderr,
286 			err_bad_args, err_bad_hostname);
287 				exit(1);
288 			}
289 		}
290 	}
291 
292 	if (get_master && get_server) {
293 		(void) fprintf(stderr, err_usage);
294 		exit(1);
295 	}
296 
297 	if (!get_master && !get_server) {
298 		get_server = TRUE;
299 	}
300 }
301 
302 /*
303  * This gets the local default domainname, and makes sure that it's set
304  * to something reasonable.  domain is set here.
305  */
306 static void
getdomain()307 getdomain()
308 {
309 	if (!getdomainname(default_domain_name, YPMAXDOMAIN)) {
310 		domain = default_domain_name;
311 	} else {
312 		(void) fprintf(stderr, err_cant_get_kname, err_bad_domainname);
313 		exit(1);
314 	}
315 
316 	if ((int)strlen(domain) == 0) {
317 		(void) fprintf(stderr, err_null_kname, err_bad_domainname);
318 		exit(1);
319 	}
320 }
321 
322 /*
323  * This gets the local hostname back from the kernel
324  */
325 static void
getlochost()326 getlochost()
327 {
328 	struct utsname utsname;
329 
330 	if (uname(&utsname) != -1) {
331 		strcpy(default_host_name, utsname.nodename);
332 		host = default_host_name;
333 	} else {
334 		(void) fprintf(stderr, err_cant_get_kname, err_bad_hostname);
335 		exit(1);
336 	}
337 
338 }
339 
340 /*
341  * This tries to find the name of the server to which the binder in question
342  * is bound.  If one of the -Vx flags was specified, it will try only for
343  * that protocol version, otherwise, it will start with the current version,
344  * then drop back to the previous version.
345  */
346 static void
get_server_name()347 get_server_name()
348 {
349 	char *notbound = "Domain %s not bound on %s.\n";
350 
351 	if (vers >= 3) {
352 		if (!call_binder(vers))
353 			(void) fprintf(stderr, notbound, domain, host);
354 	} else {
355 		if (!old_call_binder(vers))
356 			(void) fprintf(stderr, notbound, domain, host);
357 	}
358 }
359 
360 extern CLIENT *__clnt_create_loopback();
361 
362 /*
363  * This sends a message to the ypbind process on the node with
364  * the host name
365  */
366 static int
call_binder(vers)367 call_binder(vers)
368 int vers;
369 {
370 	CLIENT *client;
371 	struct ypbind_resp *response;
372 	struct ypbind_domain ypbd;
373 	char errstring[256];
374 	extern struct rpc_createerr rpc_createerr;
375 	int yperr = 0;
376 	struct utsname utsname;
377 	const char *str;
378 
379 	/*
380 	 * CAUTION: Do not go to NIS if the host is the same as the local host
381 	 * XXX: Lots of special magic to distinguish between local and remote
382 	 * case. We want to make sure the local case doesn't hang.
383 	 */
384 
385 	if ((uname(&utsname) != -1) &&
386 		(strcmp(host, utsname.nodename) == 0))
387 		client = __clnt_create_loopback(YPBINDPROG, vers, &yperr);
388 	else
389 		client = clnt_create(host, YPBINDPROG, vers, "netpath");
390 	if (client == NULL) {
391 		if (yperr)
392 			(void) fprintf(stderr,
393 				"ypwhich: %s\n", yperr_string(yperr));
394 		else {
395 			if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED ||
396 				rpc_createerr.cf_stat == RPC_PROGUNAVAIL) {
397 				(void) fprintf(stderr,
398 			"ypwhich: %s is not running ypbind\n", host);
399 			} else if (rpc_createerr.cf_stat == RPC_PMAPFAILURE) {
400 				(void) fprintf(stderr,
401 			"ypwhich: %s is not running rpcbind\n",
402 					host);
403 			} else
404 				(void) clnt_pcreateerror("ypwhich: \
405 clnt_create error");
406 		}
407 		exit(1);
408 	}
409 	ypbd.ypbind_domainname = domain;
410 	ypbd.ypbind_vers = vers;
411 	response = ypbindproc_domain_3(&ypbd, client);
412 
413 	if (response == NULL) {
414 		(void) sprintf(errstring,
415 		    "ypwhich: can't call ypbind on %s", host);
416 		(void) clnt_perror(client, errstring);
417 		exit(1);
418 	}
419 
420 	clnt_destroy(client);
421 
422 	if (response->ypbind_status != YPBIND_SUCC_VAL)  {
423 		return (FALSE);
424 	}
425 
426 	if (response->ypbind_resp_u.ypbind_bindinfo) {
427 		char *server =
428 	response->ypbind_resp_u.ypbind_bindinfo->ypbind_servername;
429 
430 		if (strcmp(server, nullstring) == 0) {
431 		/* depends on a hack in ypbind */
432 			struct nd_hostservlist *nhs = NULL;
433 			struct netconfig *nconf =
434 		response->ypbind_resp_u.ypbind_bindinfo->ypbind_nconf;
435 			struct netbuf *svcaddr =
436 		response->ypbind_resp_u.ypbind_bindinfo->ypbind_svcaddr;
437 
438 			if (netdir_getbyaddr(nconf, &nhs, svcaddr) != ND_OK) {
439 				struct sockaddr_in	*sa4;
440 				struct sockaddr_in6	*sa6;
441 				char			buf[INET6_ADDRSTRLEN];
442 				char			xbuf[IPV6_ADDR_LEN];
443 				int			af;
444 				void			*addr;
445 				XDR			xdrs;
446 
447 				sa4 = (struct sockaddr_in *)svcaddr->buf;
448 				af = ntohs(sa4->sin_family);
449 				if (af != sa4->sin_family) {
450 					xdrmem_create(&xdrs,
451 						(caddr_t)xbuf, IPV6_ADDR_LEN,
452 						XDR_DECODE);
453 					if (af == AF_INET6) {
454 						xdr_opaque(&xdrs,
455 							(caddr_t)svcaddr->buf,
456 							IPV6_ADDR_LEN);
457 						sa6 = (struct sockaddr_in6 *)
458 							xbuf;
459 						addr = &sa6->sin6_addr;
460 					} else {
461 						xdr_opaque(&xdrs,
462 							(caddr_t)svcaddr->buf,
463 							IPV4_ADDR_LEN);
464 						sa4 = (struct sockaddr_in *)
465 							xbuf;
466 						addr = &sa4->sin_addr;
467 					}
468 				} else {
469 					if (af == AF_INET6) {
470 						sa6 = (struct sockaddr_in6 *)
471 							svcaddr->buf;
472 						addr = &sa6->sin6_addr;
473 					} else {
474 						addr = &sa4->sin_addr;
475 					}
476 				}
477 				str = inet_ntop(af, addr, buf, sizeof (buf));
478 				if (str == NULL)
479 					perror("inet_ntop");
480 				else
481 					fprintf(stdout, "%s\n", str);
482 			} else {
483 				str = nhs->h_hostservs->h_host;
484 				if (str == NULL)
485 					str = "<unknown>";
486 				fprintf(stdout, "%s\n", str);
487 			}
488 			netdir_free((char *)nhs, ND_HOSTSERVLIST);
489 		} else {
490 			fprintf(stdout, "%s\n", server);
491 		}
492 	}
493 #ifdef DEBUG
494 	dump_response(response);
495 #endif
496 	return (TRUE);
497 }
498 
499 /*
500  * Serializes/deserializes an in_addr struct.
501  *
502  * Note:  There is a data coupling between the "definition" of a struct
503  * in_addr implicit in this xdr routine, and the true data definition in
504  * <netinet/in.h>.
505  */
xdr_yp_inaddr(xdrs,ps)506 static bool xdr_yp_inaddr(xdrs, ps)
507 	XDR * xdrs;
508 	struct in_addr *ps;
509 
510 {
511 	return (xdr_opaque(xdrs, (caddr_t)&ps->s_addr, 4));
512 }
513 
514 /*
515  * Serializes/deserializes an old ypbind_binding struct.
516  */
xdr_old_yp_binding(xdrs,ps)517 static bool xdr_old_yp_binding(xdrs, ps)
518 	XDR * xdrs;
519 	struct old_ypbind_binding *ps;
520 
521 {
522 	return (xdr_yp_inaddr(xdrs, &ps->ypbind_binding_addr) &&
523 	    xdr_opaque(xdrs, (caddr_t)&ps->ypbind_binding_port, 2));
524 }
525 
526 /*
527  * Serializes/deserializes a ypbind_resp structure.
528  */
xdr_old_ypbind_resp(xdrs,ps)529 static bool xdr_old_ypbind_resp(xdrs, ps)
530 	XDR * xdrs;
531 	struct old_ypbind_resp *ps;
532 
533 {
534 	if (!xdr_enum(xdrs, (enum_t *)&ps->ypbind_status)) {
535 		return (FALSE);
536 	}
537 	switch (ps->ypbind_status) {
538 	case YPBIND_SUCC_VAL:
539 		return (xdr_old_yp_binding(xdrs,
540 				&ps->ypbind_respbody.ypbind_bindinfo));
541 	case YPBIND_FAIL_VAL:
542 		return (xdr_u_long(xdrs,
543 				&ps->ypbind_respbody.ypbind_error));
544 	}
545 	return (FALSE);
546 }
547 /* This sends a message to the old ypbind process on host. */
old_call_binder(vers)548 static int old_call_binder(vers)
549 	int vers;
550 {
551 	CLIENT *client;
552 	struct hostent *hp;
553 	int sock = RPC_ANYSOCK;
554 	enum clnt_stat rpc_stat;
555 	struct old_ypbind_resp response;
556 	char errstring[256];
557 	extern struct rpc_createerr rpc_createerr;
558 	struct in_addr *server;
559 
560 	if ((client = clnt_create(host, YPBINDPROG, vers, "udp")) == NULL) {
561 		if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED) {
562 			(void) printf("ypwhich: %s is not running ypbind\n",
563 				host);
564 			exit(1);
565 		}
566 		if (rpc_createerr.cf_stat == RPC_PMAPFAILURE) {
567 		    (void) printf("ypwhich: %s is not running port mapper\n",
568 				host);
569 			exit(1);
570 		}
571 		(void) clnt_pcreateerror("ypwhich:  clnt_create error");
572 		exit(1);
573 	}
574 
575 	rpc_stat = clnt_call(client, YPBINDPROC_DOMAIN,
576 			(xdrproc_t)xdr_ypdomain_wrap_string, (caddr_t)&domain,
577 			(xdrproc_t)xdr_old_ypbind_resp, (caddr_t)&response,
578 			timeout);
579 
580 	if ((rpc_stat != RPC_SUCCESS) &&
581 	    (rpc_stat != RPC_PROGVERSMISMATCH)) {
582 		(void) sprintf(errstring,
583 		    "ypwhich: can't call ypbind on %s", host);
584 		(void) clnt_perror(client, errstring);
585 		exit(1);
586 	}
587 
588 	clnt_destroy(client);
589 	close(sock);
590 
591 	if ((rpc_stat != RPC_SUCCESS) ||
592 	    (response.ypbind_status != YPBIND_SUCC_VAL)) {
593 		return (FALSE);
594 	}
595 
596 	server = &response.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr;
597 	print_server  (server);
598 
599 	return (TRUE);
600 }
601 
602 /*
603  * For old version:
604  * This translates a server address to a name and prints it.
605  * We'll get a name by using the standard library routine.
606  */
print_server(server)607 static void print_server(server)
608 	struct in_addr *server;
609 {
610 	char buf[256];
611 	struct hostent *hp;
612 
613 	strcpy(buf, inet_ntoa(*server));
614 	hp = gethostbyaddr((char *)&server->s_addr,
615 			sizeof (struct in_addr), AF_INET);
616 
617 	printf("%s\n", hp ? hp->h_name : buf);
618 }
619 
620 #ifdef DEBUG
621 static void
dump_response(which)622 dump_response(which)
623 ypbind_resp * which;
624 {
625 	struct netconfig *nc;
626 	struct netbuf *ua;
627 	ypbind_binding * b;
628 
629 	int i;
630 
631 	{
632 		b = which->ypbind_resp_u.ypbind_bindinfo;
633 		if (b == NULL)
634 			(void) fprintf(stderr, "???NO Binding information\n");
635 		else {
636 			(void) fprintf(stderr,
637 		"server=%s lovers=%ld hivers=%ld\n",
638 			    b->ypbind_servername,
639 				b->ypbind_lo_vers, b->ypbind_hi_vers);
640 			nc = b->ypbind_nconf;
641 			ua = b->ypbind_svcaddr;
642 			if (nc == NULL)
643 				(void) fprintf(stderr,
644 			"ypwhich: NO netconfig information\n");
645 			else {
646 				(void) fprintf(stderr,
647 		"ypwhich: id %s device %s flag %x protofmly %s proto %s\n",
648 		nc->nc_netid, nc->nc_device,
649 		(int)nc->nc_flag, nc->nc_protofmly,
650 		nc->nc_proto);
651 			}
652 			if (ua == NULL)
653 				(void) fprintf(stderr,
654 		"ypwhich: NO netbuf information available from binder\n");
655 			else {
656 				(void) fprintf(stderr,
657 			"maxlen=%d len=%d\naddr=", ua->maxlen, ua->len);
658 				for (i = 0; i < ua->len; i++) {
659 					if (i != (ua->len - 1))
660 						(void) fprintf(stderr,
661 					"%d.", ua->buf[i]);
662 					else
663 						(void) fprintf(stderr,
664 					"%d\n", ua->buf[i]);
665 				}
666 			}
667 		}
668 	}
669 
670 }
671 #endif
672 
673 /*
674  * This translates a server address to a name and prints it.  If the address
675  * is the same as the local address as returned by get_myaddress, the name
676  * is that retrieved from the kernel.  If it's any other address (including
677  * another ip address for the local machine), we'll get a name by using the
678  * standard library routine (which calls the yp).
679  */
680 
681 /*
682  * This asks any yp server for the map's master.
683  */
684 static void
get_map_master()685 get_map_master()
686 {
687 	int err;
688 	char *master;
689 
690 	err = __yp_master_rsvdport(domain, map, &master);
691 
692 	if (err) {
693 		(void) fprintf(stderr,
694 		    "ypwhich:  Can't find the master of %s.  Reason: %s.\n",
695 		    map, yperr_string(err));
696 		exit(1);
697 	} else {
698 		(void) printf("%s\n", master);
699 	}
700 }
701 
702 /*
703  * This enumerates the entries within map "ypmaps" in the domain at global
704  * "domain", and prints them out key and value per single line.  dump_ypmaps
705  * just decides whether we are (probably) able to speak the new YP protocol,
706  * and dispatches to the appropriate function.
707  */
708 static void
dump_ypmaps()709 dump_ypmaps()
710 {
711 	int err;
712 	struct dom_binding *binding;
713 
714 	if (err = __yp_dobind(domain, &binding)) {
715 		(void) fprintf(stderr,
716 		    "dump_ypmaps: Can't bind for domain %s.  Reason: %s\n",
717 		    domain, yperr_string(err));
718 		return;
719 	}
720 
721 	if (binding->dom_binding->ypbind_hi_vers  >= YPVERS) {
722 		dumpmaps(binding);
723 	}
724 }
725 
726 static void
dumpmaps(binding)727 dumpmaps(binding)
728 struct dom_binding *binding;
729 {
730 	enum clnt_stat rpc_stat;
731 	int err;
732 	char *master;
733 	struct ypmaplist *pmpl;
734 	struct ypresp_maplist maplist;
735 
736 	maplist.list = (struct ypmaplist *)NULL;
737 
738 	rpc_stat = clnt_call(binding->dom_client, YPPROC_MAPLIST,
739 	    (xdrproc_t)xdr_ypdomain_wrap_string, (caddr_t)&domain,
740 	    (xdrproc_t)xdr_ypresp_maplist, (caddr_t)&maplist,
741 	    timeout);
742 
743 	if (rpc_stat != RPC_SUCCESS) {
744 		(void) clnt_perror(binding->dom_client,
745 		    "ypwhich(dumpmaps): can't get maplist");
746 		__yp_rel_binding(binding);
747 		exit(1);
748 	}
749 
750 	if (maplist.status != YP_TRUE) {
751 		(void) fprintf(stderr,
752 		    "ypwhich:  Can't get maplist.  Reason:  %s.\n",
753 		    yperr_string(ypprot_err(maplist.status)));
754 		exit(1);
755 	}
756 	__yp_rel_binding(binding);
757 
758 	for (pmpl = maplist.list; pmpl; pmpl = pmpl->ypml_next) {
759 		(void) printf("%s ", pmpl->ypml_name);
760 
761 		err = __yp_master_rsvdport(domain, pmpl->ypml_name, &master);
762 
763 		if (err) {
764 			(void) printf("????????\n");
765 			(void) fprintf(stderr,
766 		"ypwhich:  Can't find the master of %s.  Reason: %s.\n",
767 		pmpl->ypml_name, yperr_string(err));
768 		} else {
769 			(void) printf("%s\n", master);
770 		}
771 	}
772 }
773