xref: /illumos-gate/usr/src/cmd/ypcmd/yppush.c (revision 2c5ec7a875dcd76853e6618614e990f1e8cdd56d)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  *
21  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
22  * Use is subject to license terms.
23  *
24  * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
25  * All Rights Reserved
26  *
27  * Portions of this source code were derived from Berkeley
28  * 4.3 BSD under license from the Regents of the University of
29  * California.
30  */
31 /*
32  * Copyright (c) 2016 by Delphix. All rights reserved.
33  */
34 #pragma ident	"%Z%%M%	%I%	%E% SMI"
35 
36 #define	_SVID_GETTOD
37 #include	<sys/time.h>
38 extern int gettimeofday(struct timeval *);
39 
40 #include	<sys/types.h>
41 #include	<stdio.h>
42 #include	<string.h>
43 #include	<malloc.h>
44 #include	<errno.h>
45 #include	<signal.h>
46 #include	<limits.h>
47 #include	<stdlib.h>
48 #include	<unistd.h>
49 #include	<sys/types.h>
50 #include	<sys/wait.h>
51 #include	<sys/stat.h>
52 #include	<ctype.h>
53 #include	<dirent.h>
54 #include	<rpc/rpc.h>
55 #include	<rpc/nettype.h>
56 #include	<rpc/rpcb_prot.h>
57 #include	<rpc/rpcb_clnt.h>
58 #include	<sys/systeminfo.h>
59 #include	<sys/select.h>
60 #include	"ypsym.h"
61 #include	"ypdefs.h"
62 #include	"yp_b.h"
63 #include	"shim.h"
64 #include	"yptol.h"
65 
66 
67 #ifdef DEBUG
68 #undef YPPROG
69 #define	YPPROG ((ulong_t)109999)
70 #undef YPBINDPROG
71 #define	YPBINDPROG ((ulong_t)109998)
72 #endif
73 
74 #define	INTER_TRY 12			/* Seconds between tries */
75 #define	PORTMAP_TIME 30			/* Seconds before decide its down */
76 #define	TIMEOUT INTER_TRY*4		/* Total time for timeout */
77 #define	CUR_PAR 4			/* Total  parallal yppushes */
78 #define	MIN_GRACE 25			/* select timeout and minimum grace */
79 #define	GRACE_PERIOD 800		/* Total seconds we'll wait for	*/
80 					/* responses from ypxfrs, yes	*/
81 					/* virginia yp map transfers	*/
82 					/* can take a long time, we	*/
83 					/* only worry if the slave 	*/
84 					/* crashes ...			*/
85 
86 USE_YPDBPATH
87 static char *pusage;
88 static char *domain = NULL;
89 static char *host = NULL;
90 static char my_name[YPMAXPEER +1];
91 static char default_domain_name[YPMAXDOMAIN];
92 static char domain_alias[MAXNAMLEN]; 	/* nickname for domain -	*/
93 					/*	used in sysv filesystems */
94 static char map_alias[MAXNAMLEN];	/* nickname for map -		*/
95 					/*	used in sysv filesystems */
96 static char *map = NULL;
97 static bool verbose = FALSE;
98 static bool onehost = FALSE;
99 static bool oldxfr = FALSE;
100 static bool callback_timeout = FALSE;	/* set when a callback times out */
101 int grace_period = GRACE_PERIOD;
102 int curpar = CUR_PAR;			/* should be set by other stuff */
103 static char ypmapname[1024];		/* Used to check for map's existence */
104 
105 static struct timeval intertry = {
106 	INTER_TRY,			/* Seconds */
107 	0				/* Microseconds */
108 };
109 static struct timeval timeout = {
110 	TIMEOUT,			/* Seconds */
111 	0				/* Microseconds */
112 };
113 static SVCXPRT *transport4;
114 static SVCXPRT *transport6;
115 struct server {
116 	struct server *pnext;
117 	struct dom_binding domb;
118 	char svc_name[YPMAXPEER+1];
119 	unsigned long xactid;
120 	unsigned short state;
121 	unsigned long status;
122 	bool oldvers;
123 	int start_time;
124 };
125 #define	n_conf dom_binding->ypbind_nconf
126 #define	svc_addr dom_binding->ypbind_svcaddr
127 static struct server *server_list = (struct server *)NULL;
128 static struct server *active_list = (struct server *)NULL;
129 
130 /*  State values for server.state field */
131 
132 #define	SSTAT_INIT 0
133 #define	SSTAT_CALLED 1
134 #define	SSTAT_RESPONDED 2
135 #define	SSTAT_PROGNOTREG 3
136 #define	SSTAT_RPC 4
137 #define	SSTAT_RSCRC 5
138 #define	SSTAT_SYSTEM 6
139 
140 static char err_usage[] =
141 "Usage:\n\typpush [-p <par>] [-d <domainname>] [-h <hostname>] [-v] map\n";
142 static char err_bad_args[] =
143 	"The %s argument is bad.\n";
144 static char err_cant_get_kname[] =
145 	"Can't get %s from system call.\n";
146 static char err_null_kname[] =
147 	"The %s hasn't been set on this machine.\n";
148 static char err_bad_domainname[] = "domainname";
149 static char err_cant_bind[] =
150 	"Can't find a yp server for domain %s.  Reason:  %s.\n";
151 static char err_cant_build_serverlist[] =
152 	"Can't build server list from map \"ypservers\".  Reason:  %s.\n";
153 static char err_cant_find_host[] =
154 	"Can't find host %s in map \"ypservers\".\n";
155 /*
156  * State_duple table.  All messages should take 1 arg - the node name.
157  */
158 struct state_duple {
159 	int state;
160 	char *state_msg;
161 };
162 static struct state_duple state_duples[] = {
163 	{SSTAT_INIT, "Internal error trying to talk to %s."},
164 	{SSTAT_CALLED, "%s has been called."},
165 	{SSTAT_RESPONDED, "%s (v1 ypserv) sent an old-style request."},
166 	{SSTAT_PROGNOTREG, "nis server not registered at %s."},
167 	{SSTAT_RPC, "RPC error to %s:  "},
168 	{SSTAT_RSCRC, "Local resource allocation failure - can't talk to %s."},
169 	{SSTAT_SYSTEM, "System error talking to %s:  "},
170 	{0, (char *)NULL}
171 };
172 /*
173  * Status_duple table.  No messages should require any args.
174  */
175 struct status_duple {
176 	long status;
177 	char *status_msg;
178 };
179 static struct status_duple status_duples[] = {
180 	{YPPUSH_SUCC, "Map successfully transferred."},
181 	{YPPUSH_AGE,
182 	    "Transfer not done:  master's version isn't newer."},
183 	{YPPUSH_NOMAP, "Failed - ypxfr there can't find a server for map."},
184 	{YPPUSH_NODOM, "Failed - domain isn't supported."},
185 	{YPPUSH_RSRC, "Failed - local resource allocation failure."},
186 	{YPPUSH_RPC, "Failed - ypxfr had an RPC failure"},
187 	{YPPUSH_MADDR, "Failed - ypxfr couldn't get the map master's address."},
188 	{YPPUSH_YPERR, "Failed - nis server or map format error."},
189 	{YPPUSH_BADARGS, "Failed - args to ypxfr were bad."},
190 	{YPPUSH_DBM, "Failed - dbm operation on map failed."},
191 	{YPPUSH_FILE, "Failed - file I/O operation on map failed"},
192 	{YPPUSH_SKEW, "Failed - map version skew during transfer."},
193 	{YPPUSH_CLEAR,
194 		"Map successfully transferred, but ypxfr \
195 		couldn't send \"Clear map\" to ypserv "},
196 	{YPPUSH_FORCE,
197 	    "Failed - no local order number in map - use -f flag to ypxfr."},
198 	{YPPUSH_XFRERR, "Failed - ypxfr internal error."},
199 	{YPPUSH_REFUSED, "Failed - Transfer request refused."},
200 	{YPPUSH_NOALIAS,
201 		"Failed - System V domain/map alias not in alias file."},
202 	{0, (char *)NULL}
203 };
204 /*
205  * rpcerr_duple table
206  */
207 struct rpcerr_duple {
208 	enum clnt_stat rpc_stat;
209 	char *rpc_msg;
210 };
211 static struct rpcerr_duple rpcerr_duples[] = {
212 	{RPC_SUCCESS, "RPC success"},
213 	{RPC_CANTENCODEARGS, "RPC Can't encode args"},
214 	{RPC_CANTDECODERES, "RPC Can't decode results"},
215 	{RPC_CANTSEND, "RPC Can't send"},
216 	{RPC_CANTRECV, "RPC Can't recv"},
217 	{RPC_TIMEDOUT, "NIS server registered, but does not respond"},
218 	{RPC_VERSMISMATCH, "RPC version mismatch"},
219 	{RPC_AUTHERROR, "RPC auth error"},
220 	{RPC_PROGUNAVAIL, "RPC remote program unavailable"},
221 	{RPC_PROGVERSMISMATCH, "RPC program mismatch"},
222 	{RPC_PROCUNAVAIL, "RPC unknown procedure"},
223 	{RPC_CANTDECODEARGS, "RPC Can't decode args"},
224 	{RPC_UNKNOWNHOST, "unknown host"},
225 	{RPC_RPCBFAILURE, "rpcbind failure (host is down?)"},
226 	{RPC_PROGNOTREGISTERED, "RPC prog not registered"},
227 	{RPC_SYSTEMERROR, "RPC system error"},
228 	{RPC_SUCCESS, (char *)NULL}		/* Duplicate rpc_stat 	*/
229 						/* unused in list-end 	*/
230 						/* entry */
231 };
232 
233 static void get_default_domain_name(void);
234 static void get_command_line_args(int argc, char **argv);
235 static unsigned short send_message(struct server *ps,
236 					unsigned long program, long *err);
237 static void make_server_list(void);
238 static void one_host_list(void);
239 static void add_server(char *sname, int namelen);
240 static int  generate_callback(unsigned long *program);
241 static void xactid_seed(unsigned long *xactid);
242 static void main_loop(unsigned long program);
243 static void listener_exit(unsigned long program, int stat);
244 static void listener_dispatch(struct svc_req *rqstp, SVCXPRT *transp);
245 static void print_state_msg(struct server *s, long e);
246 static void print_callback_msg(struct server *s);
247 static void rpcerr_msg(enum clnt_stat e);
248 static void get_xfr_response(SVCXPRT *transp);
249 
250 #ifdef SYSVCONFIG
251 extern void sysvconfig(void);
252 #endif
253 extern int yp_getalias(char *key, char *key_alias, int maxlen);
254 extern int getdomainname(char *, int);
255 
256 extern struct rpc_createerr rpc_createerr;
257 extern CLIENT *__yp_clnt_create_rsvdport();
258 
259 int
260 main(int argc, char **argv)
261 {
262 	unsigned long program;
263 	struct stat64 sbuf;
264 
265 	get_command_line_args(argc, argv);
266 
267 	if (!domain) {
268 		get_default_domain_name();
269 	}
270 
271 #ifdef SYSVCONFIG
272 	sysvconfig();
273 #endif
274 
275 	if (yp_getalias(domain, domain_alias, NAME_MAX) != 0)
276 		fprintf(stderr, "domain alias for %s not found\n", domain);
277 	if (yp_getalias(map, map_alias, MAXALIASLEN) != 0)
278 		fprintf(stderr, "map alias for %s not found\n", map);
279 
280 	/* check to see if the map exists in this domain */
281 	if (is_yptol_mode())
282 		sprintf(ypmapname, "%s/%s/%s%s.dir", ypdbpath, domain_alias,
283 		    NTOL_PREFIX, map_alias);
284 	else
285 		sprintf(ypmapname, "%s/%s/%s.dir", ypdbpath, domain_alias,
286 		    map_alias);
287 	if (stat64(ypmapname, &sbuf) < 0) {
288 		fprintf(stderr, "yppush: Map does not exist.\n");
289 		exit(1);
290 	}
291 
292 	if (onehost) {
293 		one_host_list();
294 	} else {
295 		make_server_list();
296 	}
297 
298 	/*
299 	 * All process exits after the call to generate_callback should be
300 	 * through listener_exit(program, status), not exit(status), so the
301 	 * transient server can get unregistered with the portmapper.
302 	 */
303 
304 	if (!generate_callback(&program)) {
305 		fprintf(stderr, "Can't set up transient callback server.\n");
306 	}
307 
308 	main_loop(program);
309 
310 	listener_exit(program, 0);
311 
312 	/* NOTREACHED */
313 	return (0);
314 }
315 
316 /*
317  * This does the command line parsing.
318  */
319 static void
320 get_command_line_args(int argc, char **argv)
321 {
322 	pusage = err_usage;
323 	argv++;
324 
325 	if (argc < 2) {
326 		fprintf(stderr, pusage);
327 		exit(1);
328 	}
329 
330 	while (--argc) {
331 		if ((*argv)[0] == '-') {
332 			switch ((*argv)[1]) {
333 			case 'v':
334 				verbose = TRUE;
335 				argv++;
336 				break;
337 			case 'd':
338 				if (argc > 1) {
339 					argv++;
340 					argc--;
341 					domain = *argv;
342 					argv++;
343 					if (((int)strlen(domain)) >
344 					    YPMAXDOMAIN) {
345 						fprintf(stderr,
346 						    err_bad_args,
347 						    err_bad_domainname);
348 						exit(1);
349 					}
350 				} else {
351 					fprintf(stderr, pusage);
352 					exit(1);
353 				}
354 				break;
355 			case 'h':
356 				if (argc > 1) {
357 					onehost = TRUE;
358 					argv++;
359 					argc--;
360 					host = *argv;
361 					argv++;
362 				} else {
363 					fprintf(stderr, pusage);
364 					exit(1);
365 				}
366 				break;
367 
368 			case 'p':
369 
370 				if (argc > 1) {
371 					argv++;
372 					argc--;
373 					if (sscanf(*argv, "%d", &curpar) != 1) {
374 						(void) fprintf(stderr, pusage);
375 						exit(1);
376 					}
377 					argv++;
378 					if (curpar < 1) {
379 						(void) fprintf(stderr, pusage);
380 						exit(1);
381 					}
382 				} else {
383 					(void) fprintf(stderr, pusage);
384 					exit(1);
385 				}
386 				break;
387 
388 			default:
389 				fprintf(stderr, pusage);
390 				exit(1);
391 			}
392 		} else {
393 			if (!map) {
394 				map = *argv;
395 			} else {
396 				fprintf(stderr, pusage);
397 				exit(1);
398 			}
399 			argv++;
400 		}
401 	}
402 
403 	if (!map) {
404 		fprintf(stderr, pusage);
405 		exit(1);
406 	}
407 }
408 
409 /*
410  *  This gets the local kernel domainname, and sets the global domain to it.
411  */
412 static void
413 get_default_domain_name(void)
414 {
415 	if (!getdomainname(default_domain_name, YPMAXDOMAIN)) {
416 		domain = default_domain_name;
417 	} else {
418 		fprintf(stderr, err_cant_get_kname, err_bad_domainname);
419 		exit(1);
420 	}
421 
422 	if ((int)strlen(domain) == 0) {
423 		fprintf(stderr, err_null_kname, err_bad_domainname);
424 		exit(1);
425 	}
426 }
427 
428 /*
429  * This verifies that the hostname supplied by the user is in the map
430  * "ypservers" then calls add_server to make it the only entry on the
431  * list of servers.
432  */
433 static void
434 one_host_list(void)
435 {
436 	char *key;
437 	int keylen;
438 	char *val;
439 	int vallen;
440 	int err;
441 	char *ypservers = "ypservers";
442 
443 	if (verbose) {
444 		printf("Verifying YP server: %s\n", host);
445 		fflush(stdout);
446 	}
447 
448 	if (err = yp_bind(domain_alias)) {
449 		fprintf(stderr, err_cant_bind, domain, yperr_string(err));
450 		exit(1);
451 	}
452 
453 	keylen = strlen(host);
454 
455 	if (yp_match(domain_alias, ypservers, host, keylen,
456 	    &val, &vallen)) {
457 		fprintf(stderr, err_cant_find_host, host);
458 		exit(1);
459 	}
460 
461 	add_server(host, keylen);
462 }
463 
464 /*
465  * This uses yp operations to retrieve each server name in the map
466  *  "ypservers".  add_server is called for each one to add it to the list of
467  *  servers.
468  */
469 static void
470 make_server_list(void)
471 {
472 	char *key;
473 	int keylen;
474 	char *outkey;
475 	int outkeylen;
476 	char *val;
477 	int vallen;
478 	int err;
479 	char *ypservers = "ypservers";
480 	int count;
481 
482 	if (verbose) {
483 		printf("Finding YP servers: ");
484 		fflush(stdout);
485 		count = 4;
486 	}
487 
488 	if (err = yp_bind(domain_alias)) {
489 		fprintf(stderr, err_cant_bind, domain, yperr_string(err));
490 		exit(1);
491 	}
492 
493 	if (err = yp_first(domain_alias, ypservers, &outkey, &outkeylen,
494 	    &val, &vallen)) {
495 		fprintf(stderr, err_cant_build_serverlist, yperr_string(err));
496 		exit(1);
497 	}
498 
499 	for (;;) {
500 		add_server(outkey, outkeylen);
501 		if (verbose) {
502 			printf(" %s", outkey);
503 			fflush(stdout);
504 			if (count++ == 8) {
505 				printf("\n");
506 				count = 0;
507 			}
508 		}
509 		free(val);
510 		key = outkey;
511 		keylen = outkeylen;
512 
513 		if (err = yp_next(domain_alias, ypservers, key, keylen,
514 		    &outkey, &outkeylen, &val, &vallen)) {
515 
516 			if (err == YPERR_NOMORE) {
517 				break;
518 			} else {
519 				fprintf(stderr, err_cant_build_serverlist,
520 				    yperr_string(err));
521 				exit(1);
522 			}
523 		}
524 
525 		free(key);
526 	}
527 	if (count != 0) {
528 		if (verbose)
529 			printf("\n");
530 	}
531 }
532 
533 /*
534  *  This adds a single server to the server list.
535  */
536 static void
537 add_server(char *sname, int namelen)
538 {
539 	struct server *ps;
540 	static unsigned long seq;
541 	static unsigned long xactid = 0;
542 
543 	if (strcmp(sname, my_name) == 0)
544 		return;
545 
546 	if (xactid == 0) {
547 		xactid_seed(&xactid);
548 	}
549 
550 	if ((ps = (struct server *)malloc((unsigned)sizeof (struct server)))
551 		== (struct server *)NULL) {
552 		perror("yppush: malloc failure");
553 		exit(1);
554 	}
555 
556 	sname[namelen] = '\0';
557 	strcpy(ps->svc_name, sname);
558 	ps->state = SSTAT_INIT;
559 	ps->status = 0;
560 	ps->oldvers = FALSE;
561 	ps->xactid = xactid + seq++;
562 	ps->pnext = server_list;
563 	server_list = ps;
564 }
565 
566 /*
567  * This sets the base range for the transaction ids used in speaking the the
568  *  server ypxfr processes.
569  */
570 static void
571 xactid_seed(unsigned long *xactid)
572 {
573 	struct timeval t;
574 
575 	if (gettimeofday(&t) == -1) {
576 		perror("yppush gettimeofday failure");
577 		*xactid = 1234567;
578 	} else {
579 		*xactid = t.tv_sec;
580 	}
581 }
582 
583 /*
584  *  This generates the channel which will be used as the listener process'
585  *  service rendezvous point, and comes up with a transient program number
586  *  for the use of the RPC messages from the ypxfr processes.
587  */
588 static int
589 generate_callback(unsigned long *program)
590 {
591 	unsigned long prognum = 0x40000000, maxprognum;
592 	union {
593 		unsigned long	p;
594 		unsigned char	b[sizeof (unsigned long)];
595 	} u;
596 	int ret, i;
597 	struct netconfig *nc4, *nc6, *nc;
598 	SVCXPRT *trans;
599 
600 	nc4 = getnetconfigent("udp");
601 	nc6 = getnetconfigent("udp6");
602 	if (nc4 == 0 && nc6 == 0) {
603 		fprintf(stderr,
604 		    "yppush: Could not get udp or udp6 netconfig entry\n");
605 		exit(1);
606 	}
607 
608 	transport4 = (nc4 == 0) ? 0 : svc_tli_create(RPC_ANYFD, nc4, 0, 0, 0);
609 	transport6 = (nc6 == 0) ? 0 : svc_tli_create(RPC_ANYFD, nc6, 0, 0, 0);
610 	if (transport4 == 0 && transport6 == 0) {
611 		fprintf(stderr, "yppush: Could not create server handle(s)\n");
612 		exit(1);
613 	}
614 
615 	/* Find the maximum possible program number using an unsigned long */
616 	for (i = 0; i < sizeof (u.b); i++)
617 		u.b[i] = 0xff;
618 	maxprognum = u.p;
619 
620 	if (transport4 != 0) {
621 		trans = transport4;
622 		nc = nc4;
623 	} else {
624 		trans = transport6;
625 		nc = nc6;
626 	}
627 	while (prognum < maxprognum && (ret =
628 	    rpcb_set(prognum, YPPUSHVERS, nc, &trans->xp_ltaddr)) == 0)
629 		prognum++;
630 
631 	if (ret == 0) {
632 		fprintf(stderr, "yppush: Could not create callback service\n");
633 		exit(1);
634 	} else {
635 		if (trans == transport4 && transport6 != 0) {
636 			ret = rpcb_set(prognum, YPPUSHVERS, nc6,
637 			    &transport6->xp_ltaddr);
638 			if (ret == 0) {
639 				fprintf(stderr,
640 			"yppush: Could not create udp6 callback service\n");
641 				exit(1);
642 			}
643 		}
644 		*program = prognum;
645 	}
646 
647 	return (ret);
648 }
649 
650 /*
651  * This is the main loop. Send messages to each server,
652  * and then wait for a response.
653  */
654 
655 
656 int
657 add_to_active()
658 {
659 	struct server  *ps;
660 	ps = server_list;
661 	if (ps == NULL)
662 		return (0);
663 	server_list = server_list->pnext;	/* delete from server_list */
664 	ps->pnext = active_list;
665 	active_list = ps;
666 	return (1);
667 }
668 
669 int
670 delete_active(in)
671 	struct server  *in;
672 {
673 	struct server  *p;
674 	struct server  *n;
675 	if (in == active_list) {
676 		active_list = active_list->pnext;
677 		return (1);
678 	}
679 	p = active_list;
680 	for (n = active_list; n; n = n->pnext) {
681 		if (in == n) {
682 			p->pnext = n->pnext;
683 			return (0);
684 
685 		}
686 		p = n;
687 	}
688 	return (-1);
689 }
690 
691 void
692 main_loop(program)
693 	unsigned long   program;
694 {
695 	pollfd_t	*pollset = NULL;
696 	int		npollfds = 0;
697 	int		pollret;
698 	struct server	*ps;
699 	long		error;
700 	int		hpar;	/* this times par count */
701 	int		i;
702 	int		j;
703 	int		time_now;
704 	int		docb;
705 	int		actives = 0;
706 	int		dead = 0;
707 
708 	if (grace_period < MIN_GRACE)
709 		grace_period = MIN_GRACE;
710 	if (transport4 != 0) {
711 		if (!svc_reg(transport4, program, YPPUSHVERS,
712 				listener_dispatch, 0)) {
713 			fprintf(stderr,
714 			"Can't set up transient udp callback server.\n");
715 		}
716 	}
717 	if (transport6 != 0) {
718 		if (!svc_reg(transport6, program, YPPUSHVERS,
719 				listener_dispatch, 0)) {
720 			fprintf(stderr,
721 			"Can't set up transient udp6 callback server.\n");
722 		}
723 	}
724 	for (;;) {
725 		time_now = time(0);
726 		if (server_list == NULL) {
727 			actives = 0;
728 			dead = 0;
729 			for (ps = active_list; ps; ps = ps->pnext)
730 				if (ps->state == SSTAT_CALLED) {
731 					if ((time_now - ps->start_time) <
732 								grace_period)
733 						actives++;
734 					else
735 						dead++;
736 				}
737 			if (actives == 0) {
738 				if (verbose) {
739 					printf("terminating %d dead\n", dead);
740 					fflush(stdout);
741 				}
742 
743 				for (ps = active_list; ps; ps = ps->pnext)
744 					if (ps->state == SSTAT_CALLED) {
745 						if ((time_now - ps->start_time)
746 							>= grace_period) {
747 							if (verbose) {
748 								printf(
749 		    "no response from %s -- grace of %d seconds expired.\n",
750 		    ps->svc_name, grace_period);
751 								fflush(stdout);
752 							}
753 							fprintf(stderr,
754 		    "No response from ypxfr on %s\n", ps->svc_name);
755 						}
756 					}
757 				break;
758 			}
759 		}
760 		actives = 0;
761 		for (ps = active_list; ps; ps = ps->pnext) {
762 			if (ps->state == SSTAT_CALLED) {
763 				if ((time_now - ps->start_time)
764 						< grace_period) {
765 					actives++;
766 
767 					if (verbose) {
768 						printf(
769 		    "No response yet from ypxfr on %s\n", ps->svc_name);
770 						fflush(stdout);
771 					}
772 				}
773 			} else {
774 				if (verbose) {
775 					printf("Deactivating  %s\n",
776 						ps->svc_name);
777 					fflush(stdout);
778 				}
779 				delete_active(ps);
780 			}
781 		}
782 
783 		/* add someone to the active list keep up with curpar */
784 		for (i = 0; i < (curpar - actives); i++) {
785 			if (add_to_active()) {
786 				ps = active_list;
787 				ps->state = send_message(ps, program, &error);
788 				print_state_msg(ps, error);
789 				if (ps->state != SSTAT_CALLED)
790 					delete_active(ps);	/* zorch it */
791 				else
792 					ps->start_time = time(0); /* set time */
793 			}
794 		}
795 		docb = 0;
796 		for (ps = active_list; ps; ps = ps->pnext)
797 			if (ps->state == SSTAT_CALLED) {
798 				docb = 1;
799 				break;
800 			}
801 		if (docb == 0) {
802 			if (verbose) {
803 				printf("No one to wait for this pass.\n");
804 				fflush(stdout);
805 			}
806 			continue;	/* try curpar more */
807 		}
808 
809 		if (npollfds != svc_max_pollfd) {
810 			pollset = realloc(pollset,
811 					sizeof (pollfd_t) * svc_max_pollfd);
812 			npollfds = svc_max_pollfd;
813 		}
814 
815 		/*
816 		 * Get existing array of pollfd's, should really compress
817 		 * this but it shouldn't get very large (or sparse).
818 		 */
819 		(void) memcpy(pollset, svc_pollfd,
820 					sizeof (pollfd_t) * svc_max_pollfd);
821 
822 		errno = 0;
823 		switch (pollret = poll(pollset, npollfds, MIN_GRACE * 1000)) {
824 		    case -1:
825 			if (errno != EINTR) {
826 				(void) perror("main loop select");
827 			}
828 			break;
829 
830 		    case 0:
831 			if (verbose) {
832 				(void) printf("timeout in main loop select.\n");
833 				fflush(stdout);
834 			}
835 			break;
836 
837 		    default:
838 			svc_getreq_poll(pollset, pollret);
839 			break;
840 		}		/* switch */
841 	}			/* for */
842 }
843 
844 /*
845  * This does the listener process cleanup and process exit.
846  */
847 static void
848 listener_exit(unsigned long program, int stat)
849 {
850 	svc_unreg(program, YPPUSHVERS);
851 	exit(stat);
852 }
853 
854 /*
855  * This is the listener process' RPC service dispatcher.
856  */
857 static void
858 listener_dispatch(struct svc_req *rqstp, SVCXPRT *transp)
859 {
860 	switch (rqstp->rq_proc) {
861 
862 	case YPPUSHPROC_NULL:
863 		if (!svc_sendreply(transp, xdr_void, 0)) {
864 			fprintf(stderr, "Can't reply to rpc call.\n");
865 		}
866 		break;
867 
868 	case YPPUSHPROC_XFRRESP:
869 		get_xfr_response(transp);
870 		break;
871 
872 	default:
873 		svcerr_noproc(transp);
874 		break;
875 	}
876 }
877 
878 
879 /*
880  *  This dumps a server state message to stdout.  It is called in cases where
881  *  we have no expectation of receiving a callback from the remote ypxfr.
882  */
883 static void
884 print_state_msg(struct server *s, long e)
885 {
886 	struct state_duple *sd;
887 
888 	if (s->state == SSTAT_SYSTEM)
889 		return;			/* already printed */
890 
891 	if (!verbose && (s->state == SSTAT_RESPONDED ||
892 	    s->state == SSTAT_CALLED))
893 		return;
894 
895 	for (sd = state_duples; sd->state_msg; sd++) {
896 		if (sd->state == s->state) {
897 			printf(sd->state_msg, s->svc_name);
898 
899 			if (s->state == SSTAT_RPC) {
900 				rpcerr_msg((enum clnt_stat) e);
901 			}
902 
903 			printf("\n");
904 			fflush(stdout);
905 			return;
906 		}
907 	}
908 
909 	fprintf(stderr, "yppush: Bad server state value %d.\n", s->state);
910 }
911 
912 /*
913  *  This dumps a transfer status message to stdout.  It is called in
914  *  response to a received RPC message from the called ypxfr.
915  */
916 static void
917 print_callback_msg(struct server *s)
918 {
919 	register struct status_duple *sd;
920 
921 	if (!verbose &&
922 	    (s->status == YPPUSH_AGE) ||
923 	    (s->status == YPPUSH_SUCC))
924 
925 		return;
926 
927 	for (sd = status_duples; sd->status_msg; sd++) {
928 
929 		if (sd->status == s->status) {
930 			printf("Status received from ypxfr on %s:\n\t%s\n",
931 			    s->svc_name, sd->status_msg);
932 			fflush(stdout);
933 			return;
934 		}
935 	}
936 
937 	fprintf(stderr, "yppush listener: Garbage transaction "
938 	    "status (value %d) from ypxfr on %s.\n",
939 	    (int)s->status, s->svc_name);
940 }
941 
942 /*
943  *  This dumps an RPC error message to stdout.  This is basically a rewrite
944  *  of clnt_perrno, but writes to stdout instead of stderr.
945  */
946 static void
947 rpcerr_msg(enum clnt_stat e)
948 {
949 	struct rpcerr_duple *rd;
950 
951 	for (rd = rpcerr_duples; rd->rpc_msg; rd++) {
952 
953 		if (rd->rpc_stat == e) {
954 			printf(rd->rpc_msg);
955 			return;
956 		}
957 	}
958 
959 	fprintf(stderr, "Bad error code passed to rpcerr_msg: %d.\n", e);
960 }
961 
962 /*
963  * This picks up the response from the ypxfr process which has been started
964  * up on the remote node.  The response status must be non-zero, otherwise
965  * the status will be set to "ypxfr error".
966  */
967 static void
968 get_xfr_response(SVCXPRT *transp)
969 {
970 	struct yppushresp_xfr resp;
971 	register struct server *s;
972 
973 	if (!svc_getargs(transp, (xdrproc_t)xdr_yppushresp_xfr,
974 	    (caddr_t)&resp)) {
975 		svcerr_decode(transp);
976 		return;
977 	}
978 
979 	if (!svc_sendreply(transp, xdr_void, 0)) {
980 		(void) fprintf(stderr, "Can't reply to rpc call.\n");
981 	}
982 
983 	for (s = active_list; s; s = s->pnext) {
984 
985 		if (s->xactid == resp.transid) {
986 			s->status  = resp.status ? resp.status: YPPUSH_XFRERR;
987 			print_callback_msg(s);
988 			s->state = SSTAT_RESPONDED;
989 			return;
990 		}
991 	}
992 }
993 
994 /*
995  * This sends a message to a single ypserv process.  The return value is
996  * a state value.  If the RPC call fails because of a version
997  * mismatch, we'll assume that we're talking to a version 1 ypserv process,
998  * and will send it an old "YPPROC_GET" request, as was defined in the
999  * earlier version of yp_prot.h
1000  */
1001 static unsigned short
1002 send_message(struct server *ps, unsigned long program, long *err)
1003 {
1004 	struct ypreq_newxfr req;
1005 	struct ypreq_xfr oldreq;
1006 	enum clnt_stat s;
1007 	struct rpc_err rpcerr;
1008 
1009 	if ((ps->domb.dom_client = __yp_clnt_create_rsvdport(ps->svc_name,
1010 	    YPPROG, YPVERS, (char *)NULL, 0, 0))  == NULL) {
1011 
1012 		if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED) {
1013 			return (SSTAT_PROGNOTREG);
1014 		} else {
1015 			printf("Error talking to %s: ", ps->svc_name);
1016 			rpcerr_msg(rpc_createerr.cf_stat);
1017 			printf("\n");
1018 			fflush(stdout);
1019 			return (SSTAT_SYSTEM);
1020 		}
1021 	}
1022 
1023 	if (sysinfo(SI_HOSTNAME, my_name, sizeof (my_name)) == -1) {
1024 		return (SSTAT_RSCRC);
1025 	}
1026 
1027 	if (!oldxfr) {
1028 		req.ypxfr_domain = domain;
1029 		req.ypxfr_map = map;
1030 		req.ypxfr_ordernum = 0;
1031 		req.ypxfr_owner = my_name;
1032 		req.name = ps->svc_name;
1033 		/*
1034 		 * the creation of field req.name, instead of ypreq_xfr (old)
1035 		 * req.port, does not make any sense. it doesn't give any
1036 		 * information to receiving ypserv except its own name !!
1037 		 * new ypserv duplicates work for YPPROC_XFR and YPPROC_NEWXFR
1038 		 */
1039 		req.transid = ps->xactid;
1040 		req.proto = program;
1041 		s = (enum clnt_stat) clnt_call(ps->domb.dom_client,
1042 		    YPPROC_NEWXFR, (xdrproc_t)xdr_ypreq_newxfr, (caddr_t)&req,
1043 		    xdr_void, 0, timeout);
1044 	}
1045 
1046 	clnt_geterr(ps->domb.dom_client, &rpcerr);
1047 
1048 	if (s == RPC_PROCUNAVAIL) {
1049 		oldreq.ypxfr_domain = domain;
1050 		oldreq.ypxfr_map = map;
1051 		oldreq.ypxfr_ordernum = 0;
1052 		oldreq.ypxfr_owner = my_name;
1053 		oldreq.transid = ps->xactid;
1054 		oldreq.proto = program;
1055 		oldreq.port = 0;
1056 		s = (enum clnt_stat) clnt_call(ps->domb.dom_client,
1057 		    YPPROC_XFR, (xdrproc_t)xdr_ypreq_xfr, (caddr_t)&oldreq,
1058 		    xdr_void, 0, timeout);
1059 		clnt_geterr(ps->domb.dom_client, &rpcerr);
1060 	}
1061 
1062 	clnt_destroy(ps->domb.dom_client);
1063 
1064 	if (s == RPC_SUCCESS) {
1065 		return (SSTAT_CALLED);
1066 	} else {
1067 		*err = (long)rpcerr.re_status;
1068 		return (SSTAT_RPC);
1069 	}
1070 	/*NOTREACHED*/
1071 }
1072 
1073 /*
1074  * FUNCTION:    is_yptol_mode();
1075  *
1076  * DESCRIPTION: Determines if we should run in N2L or traditional mode based
1077  *              on the presence of the N2L mapping file.
1078  *
1079  *		This is a copy of a function from libnisdb. If more than this
1080  *		one function become required it may be worth linking the
1081  *		entire lib.
1082  *
1083  * INPUTS:      Nothing
1084  *
1085  * OUTPUTS:     TRUE = Run in N2L mode
1086  *              FALSE = Run in traditional mode.
1087  */
1088 bool_t
1089 is_yptol_mode()
1090 {
1091 	struct stat filestat;
1092 
1093 	if (stat(NTOL_MAP_FILE, &filestat) != -1)
1094 		return (TRUE);
1095 
1096 	return (FALSE);
1097 }
1098