xref: /illumos-gate/usr/src/cmd/ypcmd/yppush.c (revision 0173c38a73f34277e0c97a19fedfd25d81ba8380)
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 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 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 int
656 add_to_active()
657 {
658 	struct server  *ps;
659 	ps = server_list;
660 	if (ps == NULL)
661 		return (0);
662 	server_list = server_list->pnext;	/* delete from server_list */
663 	ps->pnext = active_list;
664 	active_list = ps;
665 	return (1);
666 }
667 
668 int
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