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