xref: /illumos-gate/usr/src/cmd/ypcmd/yppush.c (revision cc6c5292fa8a241fe50604cf6a918edfbf7cd7d2)
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  * 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 CLIENT *__yp_clnt_create_rsvdport();
257 
258 int
259 main(int argc, char **argv)
260 {
261 	unsigned long program;
262 	struct stat sbuf;
263 
264 	get_command_line_args(argc, argv);
265 
266 	if (!domain) {
267 		get_default_domain_name();
268 	}
269 
270 #ifdef SYSVCONFIG
271 	sysvconfig();
272 #endif
273 
274 	if (yp_getalias(domain, domain_alias, NAME_MAX) != 0)
275 		fprintf(stderr, "domain alias for %s not found\n", domain);
276 	if (yp_getalias(map, map_alias, MAXALIASLEN) != 0)
277 		fprintf(stderr, "map alias for %s not found\n", map);
278 
279 	/* check to see if the map exists in this domain */
280 	if (is_yptol_mode())
281 		sprintf(ypmapname, "%s/%s/%s%s.dir", ypdbpath, domain_alias,
282 						NTOL_PREFIX, map_alias);
283 	else
284 		sprintf(ypmapname, "%s/%s/%s.dir", ypdbpath, domain_alias,
285 								map_alias);
286 	if (stat(ypmapname, &sbuf) < 0) {
287 		fprintf(stderr, "yppush: Map does not exist.\n");
288 		exit(1);
289 	}
290 
291 	if (onehost) {
292 		one_host_list();
293 	} else {
294 		make_server_list();
295 	}
296 
297 	/*
298 	 * All process exits after the call to generate_callback should be
299 	 * through listener_exit(program, status), not exit(status), so the
300 	 * transient server can get unregistered with the portmapper.
301 	 */
302 
303 	if (!generate_callback(&program)) {
304 		fprintf(stderr, "Can't set up transient callback server.\n");
305 	}
306 
307 	main_loop(program);
308 
309 	listener_exit(program, 0);
310 
311 	/* NOTREACHED */
312 	return (0);
313 }
314 
315 /*
316  * This does the command line parsing.
317  */
318 static void
319 get_command_line_args(int argc, char **argv)
320 {
321 	pusage = err_usage;
322 	argv++;
323 
324 	if (argc < 2) {
325 		fprintf(stderr, pusage);
326 		exit(1);
327 	}
328 
329 	while (--argc) {
330 		if ((*argv)[0] == '-') {
331 			switch ((*argv)[1]) {
332 			case 'v':
333 				verbose = TRUE;
334 				argv++;
335 				break;
336 			case 'd':
337 				if (argc > 1) {
338 					argv++;
339 					argc--;
340 					domain = *argv;
341 					argv++;
342 					if (((int)strlen(domain)) >
343 								YPMAXDOMAIN) {
344 						fprintf(stderr,
345 							err_bad_args,
346 							err_bad_domainname);
347 						exit(1);
348 					}
349 				} else {
350 					fprintf(stderr, pusage);
351 					exit(1);
352 				}
353 				break;
354 			case 'h':
355 				if (argc > 1) {
356 					onehost = TRUE;
357 					argv++;
358 					argc--;
359 					host = *argv;
360 					argv++;
361 				} else {
362 					fprintf(stderr, pusage);
363 					exit(1);
364 				}
365 				break;
366 
367 			case 'p':
368 
369 				if (argc > 1) {
370 					argv++;
371 					argc--;
372 					if (sscanf(*argv, "%d", &curpar) != 1) {
373 						(void) fprintf(stderr, pusage);
374 						exit(1);
375 					}
376 					argv++;
377 					if (curpar < 1) {
378 						(void) fprintf(stderr, pusage);
379 						exit(1);
380 					}
381 				} else {
382 					(void) fprintf(stderr, pusage);
383 					exit(1);
384 				}
385 				break;
386 
387 			default:
388 				fprintf(stderr, pusage);
389 				exit(1);
390 			}
391 		} else {
392 			if (!map) {
393 				map = *argv;
394 			} else {
395 				fprintf(stderr, pusage);
396 				exit(1);
397 			}
398 			argv++;
399 		}
400 	}
401 
402 	if (!map) {
403 		fprintf(stderr, pusage);
404 		exit(1);
405 	}
406 }
407 
408 /*
409  *  This gets the local kernel domainname, and sets the global domain to it.
410  */
411 static void
412 get_default_domain_name(void)
413 {
414 	if (!getdomainname(default_domain_name, YPMAXDOMAIN)) {
415 		domain = default_domain_name;
416 	} else {
417 		fprintf(stderr, err_cant_get_kname, err_bad_domainname);
418 		exit(1);
419 	}
420 
421 	if ((int)strlen(domain) == 0) {
422 		fprintf(stderr, err_null_kname, err_bad_domainname);
423 		exit(1);
424 	}
425 }
426 
427 /*
428  * This verifies that the hostname supplied by the user is in the map
429  * "ypservers" then calls add_server to make it the only entry on the
430  * list of servers.
431  */
432 static void
433 one_host_list(void)
434 {
435 	char *key;
436 	int keylen;
437 	char *val;
438 	int vallen;
439 	int err;
440 	char *ypservers = "ypservers";
441 
442 	if (verbose) {
443 		printf("Verifying YP server: %s\n", host);
444 		fflush(stdout);
445 	}
446 
447 	if (err = yp_bind(domain_alias)) {
448 		fprintf(stderr, err_cant_bind, domain, yperr_string(err));
449 		exit(1);
450 	}
451 
452 	keylen = strlen(host);
453 
454 	if (yp_match(domain_alias, ypservers, host, keylen,
455 			&val, &vallen)) {
456 		fprintf(stderr, err_cant_find_host, host);
457 		exit(1);
458 	}
459 
460 	add_server(host, keylen);
461 }
462 
463 /*
464  * This uses yp operations to retrieve each server name in the map
465  *  "ypservers".  add_server is called for each one to add it to the list of
466  *  servers.
467  */
468 static void
469 make_server_list(void)
470 {
471 	char *key;
472 	int keylen;
473 	char *outkey;
474 	int outkeylen;
475 	char *val;
476 	int vallen;
477 	int err;
478 	char *ypservers = "ypservers";
479 	int count;
480 
481 	if (verbose) {
482 		printf("Finding YP servers: ");
483 		fflush(stdout);
484 		count = 4;
485 	}
486 
487 	if (err = yp_bind(domain_alias)) {
488 		fprintf(stderr, err_cant_bind, domain, yperr_string(err));
489 		exit(1);
490 	}
491 
492 	if (err = yp_first(domain_alias, ypservers, &outkey, &outkeylen,
493 				&val, &vallen)) {
494 		fprintf(stderr, err_cant_build_serverlist, yperr_string(err));
495 		exit(1);
496 	}
497 
498 	for (;;) {
499 		add_server(outkey, outkeylen);
500 		if (verbose) {
501 			printf(" %s", outkey);
502 			fflush(stdout);
503 			if (count++ == 8) {
504 				printf("\n");
505 				count = 0;
506 			}
507 		}
508 		free(val);
509 		key = outkey;
510 		keylen = outkeylen;
511 
512 		if (err = yp_next(domain_alias, ypservers, key, keylen,
513 				&outkey, &outkeylen, &val, &vallen)) {
514 
515 		    if (err == YPERR_NOMORE) {
516 			break;
517 		    } else {
518 			fprintf(stderr, err_cant_build_serverlist,
519 				yperr_string(err));
520 			exit(1);
521 		    }
522 		}
523 
524 		free(key);
525 	}
526 	if (count != 0) {
527 		if (verbose)
528 			printf("\n");
529 	}
530 }
531 
532 /*
533  *  This adds a single server to the server list.
534  */
535 static void
536 add_server(char *sname, int namelen)
537 {
538 	struct server *ps;
539 	static unsigned long seq;
540 	static unsigned long xactid = 0;
541 
542 	if (strcmp(sname, my_name) == 0)
543 		return;
544 
545 	if (xactid == 0) {
546 		xactid_seed(&xactid);
547 	}
548 
549 	if ((ps = (struct server *)malloc((unsigned)sizeof (struct server)))
550 		== (struct server *)NULL) {
551 		perror("yppush: malloc failure");
552 		exit(1);
553 	}
554 
555 	sname[namelen] = '\0';
556 	strcpy(ps->svc_name, sname);
557 	ps->state = SSTAT_INIT;
558 	ps->status = 0;
559 	ps->oldvers = FALSE;
560 	ps->xactid = xactid + seq++;
561 	ps->pnext = server_list;
562 	server_list = ps;
563 }
564 
565 /*
566  * This sets the base range for the transaction ids used in speaking the the
567  *  server ypxfr processes.
568  */
569 static void
570 xactid_seed(unsigned long *xactid)
571 {
572 	struct timeval t;
573 
574 	if (gettimeofday(&t) == -1) {
575 		perror("yppush gettimeofday failure");
576 		*xactid = 1234567;
577 	} else {
578 		*xactid = t.tv_sec;
579 	}
580 }
581 
582 /*
583  *  This generates the channel which will be used as the listener process'
584  *  service rendezvous point, and comes up with a transient program number
585  *  for the use of the RPC messages from the ypxfr processes.
586  */
587 static int
588 generate_callback(unsigned long *program)
589 {
590 	unsigned long prognum = 0x40000000, maxprognum;
591 	union {
592 		unsigned long	p;
593 		unsigned char	b[sizeof (unsigned long)];
594 	} u;
595 	int ret, i;
596 	struct netconfig *nc4, *nc6, *nc;
597 	SVCXPRT *trans;
598 
599 	nc4 = getnetconfigent("udp");
600 	nc6 = getnetconfigent("udp6");
601 	if (nc4 == 0 && nc6 == 0) {
602 		fprintf(stderr,
603 			"yppush: Could not get udp or udp6 netconfig entry\n");
604 		exit(1);
605 	}
606 
607 	transport4 = (nc4 == 0) ? 0 : svc_tli_create(RPC_ANYFD, nc4, 0, 0, 0);
608 	transport6 = (nc6 == 0) ? 0 : svc_tli_create(RPC_ANYFD, nc6, 0, 0, 0);
609 	if (transport4 == 0 && transport6 == 0) {
610 		fprintf(stderr, "yppush: Could not create server handle(s)\n");
611 		exit(1);
612 	}
613 
614 	/* Find the maximum possible program number using an unsigned long */
615 	for (i = 0; i < sizeof (u.b); i++)
616 		u.b[i] = 0xff;
617 	maxprognum = u.p;
618 
619 	if (transport4 != 0) {
620 		trans = transport4;
621 		nc = nc4;
622 	} else {
623 		trans = transport6;
624 		nc = nc6;
625 	}
626 	while (prognum < maxprognum && (ret =
627 		rpcb_set(prognum, YPPUSHVERS, nc, &trans->xp_ltaddr)) == 0)
628 		prognum++;
629 
630 	if (ret == 0) {
631 		fprintf(stderr, "yppush: Could not create callback service\n");
632 		exit(1);
633 	} else {
634 		if (trans == transport4 && transport6 != 0) {
635 			ret = rpcb_set(prognum, YPPUSHVERS, nc6,
636 					&transport6->xp_ltaddr);
637 			if (ret == 0) {
638 				fprintf(stderr,
639 			"yppush: Could not create udp6 callback service\n");
640 				exit(1);
641 			}
642 		}
643 		*program = prognum;
644 	}
645 
646 	return (ret);
647 }
648 
649 /*
650  * This is the main loop. Send messages to each server,
651  * and then wait for a response.
652  */
653 
654 
655 add_to_active()
656 {
657 	struct server  *ps;
658 	ps = server_list;
659 	if (ps == NULL)
660 		return (0);
661 	server_list = server_list->pnext;	/* delete from server_list */
662 	ps->pnext = active_list;
663 	active_list = ps;
664 	return (1);
665 }
666 
667 delete_active(in)
668 	struct server  *in;
669 {
670 	struct server  *p;
671 	struct server  *n;
672 	if (in == active_list) {
673 		active_list = active_list->pnext;
674 		return (1);
675 	}
676 	p = active_list;
677 	for (n = active_list; n; n = n->pnext) {
678 		if (in == n) {
679 			p->pnext = n->pnext;
680 			return (0);
681 
682 		}
683 		p = n;
684 	}
685 	return (-1);
686 }
687 
688 void
689 main_loop(program)
690 	unsigned long   program;
691 {
692 	pollfd_t	*pollset = NULL;
693 	int		npollfds = 0;
694 	int		pollret;
695 	struct server	*ps;
696 	long		error;
697 	int		hpar;	/* this times par count */
698 	int		i;
699 	int		j;
700 	int		time_now;
701 	int		docb;
702 	int		actives = 0;
703 	int		dead = 0;
704 
705 	if (grace_period < MIN_GRACE)
706 		grace_period = MIN_GRACE;
707 	if (transport4 != 0) {
708 		if (!svc_reg(transport4, program, YPPUSHVERS,
709 				listener_dispatch, 0)) {
710 			fprintf(stderr,
711 			"Can't set up transient udp callback server.\n");
712 		}
713 	}
714 	if (transport6 != 0) {
715 		if (!svc_reg(transport6, program, YPPUSHVERS,
716 				listener_dispatch, 0)) {
717 			fprintf(stderr,
718 			"Can't set up transient udp6 callback server.\n");
719 		}
720 	}
721 	for (;;) {
722 		time_now = time(0);
723 		if (server_list == NULL) {
724 			actives = 0;
725 			dead = 0;
726 			for (ps = active_list; ps; ps = ps->pnext)
727 				if (ps->state == SSTAT_CALLED) {
728 					if ((time_now - ps->start_time) <
729 								grace_period)
730 						actives++;
731 					else
732 						dead++;
733 				}
734 			if (actives == 0) {
735 				if (verbose) {
736 					printf("terminating %d dead\n", dead);
737 					fflush(stdout);
738 				}
739 
740 				for (ps = active_list; ps; ps = ps->pnext)
741 					if (ps->state == SSTAT_CALLED) {
742 						if ((time_now - ps->start_time)
743 							>= grace_period) {
744 							if (verbose) {
745 								printf(
746 		    "no response from %s -- grace of %d seconds expired.\n",
747 		    ps->svc_name, grace_period);
748 								fflush(stdout);
749 							}
750 							fprintf(stderr,
751 		    "No response from ypxfr on %s\n", ps->svc_name);
752 						}
753 					}
754 				break;
755 			}
756 		}
757 		actives = 0;
758 		for (ps = active_list; ps; ps = ps->pnext) {
759 			if (ps->state == SSTAT_CALLED) {
760 				if ((time_now - ps->start_time)
761 						< grace_period) {
762 					actives++;
763 
764 					if (verbose) {
765 						printf(
766 		    "No response yet from ypxfr on %s\n", ps->svc_name);
767 						fflush(stdout);
768 					}
769 				}
770 			} else {
771 				if (verbose) {
772 					printf("Deactivating  %s\n",
773 						ps->svc_name);
774 					fflush(stdout);
775 				}
776 				delete_active(ps);
777 			}
778 		}
779 
780 		/* add someone to the active list keep up with curpar */
781 		for (i = 0; i < (curpar - actives); i++) {
782 			if (add_to_active()) {
783 				ps = active_list;
784 				ps->state = send_message(ps, program, &error);
785 				print_state_msg(ps, error);
786 				if (ps->state != SSTAT_CALLED)
787 					delete_active(ps);	/* zorch it */
788 				else
789 					ps->start_time = time(0); /* set time */
790 			}
791 		}
792 		docb = 0;
793 		for (ps = active_list; ps; ps = ps->pnext)
794 			if (ps->state == SSTAT_CALLED) {
795 				docb = 1;
796 				break;
797 			}
798 		if (docb == 0) {
799 			if (verbose) {
800 				printf("No one to wait for this pass.\n");
801 				fflush(stdout);
802 			}
803 			continue;	/* try curpar more */
804 		}
805 
806 		if (npollfds != svc_max_pollfd) {
807 			pollset = realloc(pollset,
808 					sizeof (pollfd_t) * svc_max_pollfd);
809 			npollfds = svc_max_pollfd;
810 		}
811 
812 		/*
813 		 * Get existing array of pollfd's, should really compress
814 		 * this but it shouldn't get very large (or sparse).
815 		 */
816 		(void) memcpy(pollset, svc_pollfd,
817 					sizeof (pollfd_t) * svc_max_pollfd);
818 
819 		errno = 0;
820 		switch (pollret = poll(pollset, npollfds, MIN_GRACE * 1000)) {
821 		    case -1:
822 			if (errno != EINTR) {
823 				(void) perror("main loop select");
824 			}
825 			break;
826 
827 		    case 0:
828 			if (verbose) {
829 				(void) printf("timeout in main loop select.\n");
830 				fflush(stdout);
831 			}
832 			break;
833 
834 		    default:
835 			svc_getreq_poll(pollset, pollret);
836 			break;
837 		}		/* switch */
838 	}			/* for */
839 }
840 
841 /*
842  * This does the listener process cleanup and process exit.
843  */
844 static void
845 listener_exit(unsigned long program, int stat)
846 {
847 	svc_unreg(program, YPPUSHVERS);
848 	exit(stat);
849 }
850 
851 /*
852  * This is the listener process' RPC service dispatcher.
853  */
854 static void
855 listener_dispatch(struct svc_req *rqstp, SVCXPRT *transp)
856 {
857 	switch (rqstp->rq_proc) {
858 
859 	case YPPUSHPROC_NULL:
860 		if (!svc_sendreply(transp, xdr_void, 0)) {
861 		    fprintf(stderr, "Can't reply to rpc call.\n");
862 		}
863 		break;
864 
865 	case YPPUSHPROC_XFRRESP:
866 		get_xfr_response(transp);
867 		break;
868 
869 	default:
870 		svcerr_noproc(transp);
871 		break;
872 	}
873 }
874 
875 
876 /*
877  *  This dumps a server state message to stdout.  It is called in cases where
878  *  we have no expectation of receiving a callback from the remote ypxfr.
879  */
880 static void
881 print_state_msg(struct server *s, long e)
882 {
883 	struct state_duple *sd;
884 
885 	if (s->state == SSTAT_SYSTEM)
886 		return;			/* already printed */
887 
888 	if (!verbose && (s->state == SSTAT_RESPONDED ||
889 				s->state == SSTAT_CALLED))
890 		return;
891 
892 	for (sd = state_duples; sd->state_msg; sd++) {
893 		if (sd->state == s->state) {
894 			printf(sd->state_msg, s->svc_name);
895 
896 			if (s->state == SSTAT_RPC) {
897 				rpcerr_msg((enum clnt_stat) e);
898 			}
899 
900 			printf("\n");
901 			fflush(stdout);
902 			return;
903 		}
904 	}
905 
906 	fprintf(stderr, "yppush: Bad server state value %d.\n", s->state);
907 }
908 
909 /*
910  *  This dumps a transfer status message to stdout.  It is called in
911  *  response to a received RPC message from the called ypxfr.
912  */
913 static void
914 print_callback_msg(struct server *s)
915 {
916 	register struct status_duple *sd;
917 
918 	if (!verbose &&
919 		(s->status == YPPUSH_AGE) ||
920 		(s->status == YPPUSH_SUCC))
921 
922 		return;
923 
924 	for (sd = status_duples; sd->status_msg; sd++) {
925 
926 		if (sd->status == s->status) {
927 			printf("Status received from ypxfr on %s:\n\t%s\n",
928 				s->svc_name, sd->status_msg);
929 			fflush(stdout);
930 			return;
931 		}
932 	}
933 
934 	fprintf(stderr, "yppush listener: Garbage transaction "
935 			"status (value %d) from ypxfr on %s.\n",
936 			(int)s->status, s->svc_name);
937 }
938 
939 /*
940  *  This dumps an RPC error message to stdout.  This is basically a rewrite
941  *  of clnt_perrno, but writes to stdout instead of stderr.
942  */
943 static void
944 rpcerr_msg(enum clnt_stat e)
945 {
946 	struct rpcerr_duple *rd;
947 
948 	for (rd = rpcerr_duples; rd->rpc_msg; rd++) {
949 
950 		if (rd->rpc_stat == e) {
951 			printf(rd->rpc_msg);
952 			return;
953 		}
954 	}
955 
956 	fprintf(stderr, "Bad error code passed to rpcerr_msg: %d.\n", e);
957 }
958 
959 /*
960  * This picks up the response from the ypxfr process which has been started
961  * up on the remote node.  The response status must be non-zero, otherwise
962  * the status will be set to "ypxfr error".
963  */
964 static void
965 get_xfr_response(SVCXPRT *transp)
966 {
967 	struct yppushresp_xfr resp;
968 	register struct server *s;
969 
970 	if (!svc_getargs(transp, (xdrproc_t)xdr_yppushresp_xfr,
971 			(caddr_t)&resp)) {
972 		svcerr_decode(transp);
973 		return;
974 	}
975 
976 	if (!svc_sendreply(transp, xdr_void, 0)) {
977 		(void) fprintf(stderr, "Can't reply to rpc call.\n");
978 	}
979 
980 	for (s = active_list; s; s = s->pnext) {
981 
982 		if (s->xactid == resp.transid) {
983 			s->status  = resp.status ? resp.status: YPPUSH_XFRERR;
984 			print_callback_msg(s);
985 			s->state = SSTAT_RESPONDED;
986 			return;
987 		}
988 	}
989 }
990 
991 /*
992  * This sends a message to a single ypserv process.  The return value is
993  * a state value.  If the RPC call fails because of a version
994  * mismatch, we'll assume that we're talking to a version 1 ypserv process,
995  * and will send him an old "YPPROC_GET" request, as was defined in the
996  * earlier version of yp_prot.h
997  */
998 static unsigned short
999 send_message(struct server *ps, unsigned long program, long *err)
1000 {
1001 	struct ypreq_newxfr req;
1002 	struct ypreq_xfr oldreq;
1003 	enum clnt_stat s;
1004 	struct rpc_err rpcerr;
1005 
1006 	if ((ps->domb.dom_client = __yp_clnt_create_rsvdport(ps->svc_name,
1007 							YPPROG, YPVERS,
1008 							(char *)NULL,
1009 							0, 0))  == NULL) {
1010 
1011 		if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED) {
1012 			return (SSTAT_PROGNOTREG);
1013 		} else {
1014 			printf("Error talking to %s: ", ps->svc_name);
1015 			rpcerr_msg(rpc_createerr.cf_stat);
1016 			printf("\n");
1017 			fflush(stdout);
1018 			return (SSTAT_SYSTEM);
1019 		}
1020 	}
1021 
1022 	if (sysinfo(SI_HOSTNAME, my_name, sizeof (my_name)) == -1) {
1023 		return (SSTAT_RSCRC);
1024 	}
1025 
1026 	if (!oldxfr) {
1027 		req.ypxfr_domain = domain;
1028 		req.ypxfr_map = map;
1029 		req.ypxfr_ordernum = 0;
1030 		req.ypxfr_owner = my_name;
1031 		req.name = ps->svc_name;
1032 		/*
1033 		 * the creation of field req.name, instead of ypreq_xfr (old)
1034 		 * req.port, does not make any sense. it doesn't give any
1035 		 * information to receiving ypserv except its own name !!
1036 		 * new ypserv duplicates work for YPPROC_XFR and YPPROC_NEWXFR
1037 		 */
1038 		req.transid = ps->xactid;
1039 		req.proto = program;
1040 		s = (enum clnt_stat) clnt_call(ps->domb.dom_client,
1041 						YPPROC_NEWXFR,
1042 						(xdrproc_t)xdr_ypreq_newxfr,
1043 						(caddr_t)&req,
1044 						xdr_void, 0, timeout);
1045 	}
1046 
1047 	clnt_geterr(ps->domb.dom_client, &rpcerr);
1048 
1049 	if (s == RPC_PROCUNAVAIL) {
1050 		oldreq.ypxfr_domain = domain;
1051 		oldreq.ypxfr_map = map;
1052 		oldreq.ypxfr_ordernum = 0;
1053 		oldreq.ypxfr_owner = my_name;
1054 		oldreq.transid = ps->xactid;
1055 		oldreq.proto = program;
1056 		oldreq.port = 0;
1057 		s = (enum clnt_stat) clnt_call(ps->domb.dom_client,
1058 						YPPROC_XFR,
1059 						(xdrproc_t)xdr_ypreq_xfr,
1060 						(caddr_t)&oldreq,
1061 						xdr_void, 0, timeout);
1062 		clnt_geterr(ps->domb.dom_client, &rpcerr);
1063 	}
1064 
1065 	clnt_destroy(ps->domb.dom_client);
1066 
1067 	if (s == RPC_SUCCESS) {
1068 		return (SSTAT_CALLED);
1069 	} else {
1070 		*err = (long)rpcerr.re_status;
1071 		return (SSTAT_RPC);
1072 	}
1073 	/*NOTREACHED*/
1074 }
1075 
1076 /*
1077  * FUNCTION:    is_yptol_mode();
1078  *
1079  * DESCRIPTION: Determines if we should run in N2L or traditional mode based
1080  *              on the presence of the N2L mapping file.
1081  *
1082  *		This is a copy of a function from libnisdb. If more than this
1083  *		one function become required it may be worth linking the
1084  *		entire lib.
1085  *
1086  * INPUTS:      Nothing
1087  *
1088  * OUTPUTS:     TRUE = Run in N2L mode
1089  *              FALSE = Run in traditional mode.
1090  */
1091 bool_t
1092 is_yptol_mode()
1093 {
1094 	struct stat filestat;
1095 
1096 	if (stat(NTOL_MAP_FILE, &filestat) != -1)
1097 		return (TRUE);
1098 
1099 	return (FALSE);
1100 }
1101