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