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