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