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