NFSv2/usr/src/etc/ypbind.c

Compare this file to the similar file:
Show the results in this format:

#ifndef lint
static	char sccsid[] = "@(#)ypbind.c 1.1 85/05/30 Copyr 1985 Sun Micro";
#endif

/*
 * This constructs a list of servers by domains, and keeps more-or-less up to
 * date track of those server's reachability.
  */

#include <dbm.h>			/* Pull this in first */
#undef NULL				/* Remove dbm.h's definition of NULL */
extern void dbmclose();			/* Refer to dbm routine not in dbm.h */
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <rpc/rpc.h>
#include <sys/dir.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <rpc/pmap_clnt.h>
#include <rpcsvc/yp_prot.h>
#include <rpcsvc/ypclnt.h>

/*
 * The domain struct is the data structure used by the yp binder to remember
 * mappings of domain to a server.  The list of domains is pointed to by
 * known_domains.  Domains are added when the yp binder gets binding requests
 * for domains which are not currently on the list.  Once on the list, an
 * entry stays on the list forever.  Bindings are initially made by means of
 * a broadcast method, using functions ypbind_broadcast_bind and
 * ypbind_broadcast_ack.  This means of binding is re-done any time the domain
 * becomes unbound, which happens when a server doesn't respond to a ping.
 * current_domain is used to communicate among the various functions in this
 * module; it is set by ypbind_get_binding.
 */
struct domain {
	struct domain *dom_pnext;
	char dom_name[MAXNAMLEN + 1];
	bool dom_boundp;
	struct in_addr dom_serv_addr;
	unsigned short int dom_serv_port;
	int dom_report_success;		/* Controls msg to /dev/console*/
};
struct domain *known_domains = (struct domain *) NULL;
struct domain *current_domain;		/* Used by ypbind_broadcast_ack, set
					 *    by all callers of clnt_broadcast */
struct domain *broadcast_domain;	/* Set by ypbind_get_binding, used
					 *   by the mainline. */
bool broadcast_done = FALSE;		/* Set TRUE by the interrupt handler,
					 *   FALSE by the mainline. */
bool broadcast_binding_succeeded;	/* Set TRUE or FALSE or by the interrupt
					 *   handler, used by the mainline. */
int broadcaster_pid = 0;		/* Set non-zero by ypbind_get_binding,
					 *   0 by the mainline. */
SVCXPRT *tcphandle;
SVCXPRT *udphandle;

#define BINDING_TRIES 4			/* Number of times we'll broadcast to
					 *   try to bind default domain.  */
#ifdef  DEBUG
#define YPTIMEOUT 120			/* Total seconds for timeout */
#else
#define YPTIMEOUT 9			/* Total seconds for timeout */
#endif
static struct timeval total_timeout = {
	YPTIMEOUT,			/* Seconds */
	0				/* Microseconds */
	};
#define SETDOMINTERTRY 20
#define SETDOMTOTTIM 60
#ifdef DEVELOPMENT
int silent = FALSE;
#else
int silent = TRUE;
#endif

extern int svc_fds;
extern int errno;

void ypbind_dispatch();
void ypbind_get_binding();
void ypbind_set_binding();
void ypbind_send_setdom();
struct domain *ypbind_point_to_domain();
bool ypbind_broadcast_ack();
void ypbind_ping();
void ypbind_init_default();
void broadcast_proc_exit();

extern bool xdr_ypdomain_wrap_string();
extern bool xdr_ypbind_resp();

main()
{
	int pid;
	int t;
	int readfds;
	char *pname;
	bool true;

	pmap_unset(YPBINDPROG, YPBINDVERS);
	ypbind_init_default();

	if (silent) {
		
		pid = fork();
		
		if (pid == -1) {
			fprintf(stderr, "ypbind:  fork failure.\n");
			fflush(stderr);
			abort();
		}
	
		if (pid != 0) {
			exit(0);
		}
	
		for (t = 0; t < 20; t++) {
			close(t);
		}
	
 		open("/", 0);
 		dup2(0, 1);
 		dup2(0, 2);
 		t = open("/dev/tty", 2);
	
 		if (t >= 0) {
 			ioctl(t, TIOCNOTTY, (char *)0);
 			close(t);
 		}
	}

	if ((int) signal(SIGCHLD, broadcast_proc_exit) == -1) {
		fprintf(stderr,
		    "ypbind:  Can't catch broadcast process exit signal.\n");
		fflush(stderr);
		abort();
	}

	if ((tcphandle = svctcp_create(RPC_ANYSOCK, 1024, 1024)) == NULL) {
		fprintf(stderr, "ypbind:  can't create tcp service.\n");
		fflush(stderr);
		abort();
	}

	if (!svc_register(tcphandle, YPBINDPROG, YPBINDVERS,
	    ypbind_dispatch, IPPROTO_TCP) ) {
		fprintf(stderr, "ypbind:  can't register tcp service.\n");
		fflush(stderr);
		abort();
	}

	if ((udphandle = svcudp_create(RPC_ANYSOCK)) == (SVCXPRT *) NULL) {
		fprintf(stderr, "ypbind:  can't create udp service.\n");
		fflush(stderr);
		abort();
	}

	if (!svc_register(udphandle, YPBINDPROG, YPBINDVERS,
	    ypbind_dispatch, IPPROTO_UDP) ) {
		fprintf(stderr, "ypbind:  can't register udp service.\n");
		fflush(stderr);
		abort();
	}

	for (;;) {

		if (broadcast_done) {
			broadcast_done = FALSE;
			broadcaster_pid = 0;
			current_domain = broadcast_domain;

			if (broadcast_binding_succeeded) {
				current_domain->dom_report_success = -1;
			}
		}

		readfds = svc_fds;
		errno = 0;

		switch ( (int) select(32, &readfds, NULL, NULL, NULL) ) {

		case -1:  {
		
			if (errno != EINTR) {
			    fprintf (stderr,
			    "ypbind:  bad fds bits in main loop select mask.\n");
			}

			break;
		}

		case 0:  {
			fprintf (stderr,
			    "ypbind:  invalid timeout in main loop select.\n");
			break;
		}

		default:  {
			svc_getreq (readfds);
			break;
		}
		
		}
	}
}

/*
 * This dispatches to server action routines based on the input procedure number.
 * Called from rpc library level upon the receipt of a packet asking for 
 * services from program YPPROG and version YPVERS.
 */
void
ypbind_dispatch(rqstp, transp)
	struct svc_req *rqstp;
	SVCXPRT *transp;
{

	switch (rqstp->rq_proc) {

	case YPBINDPROC_NULL:

		if (!svc_sendreply(transp, xdr_void, 0) ) {
			fprintf(stderr, "ypbind:  Can't reply to rpc call.\n");
		}

		break;

	case YPBINDPROC_DOMAIN:
		ypbind_get_binding(rqstp, transp);
		break;

	case YPBINDPROC_SETDOM:
		ypbind_set_binding(rqstp, transp);
		break;

	default:
		svcerr_noproc(transp);
		break;

	}
}

/*
 * This is a Unix SIGCHILD handler which notices when a broadcaster child
 * process has exited, and retrieves the exit status.  The outputs from this
 * function are broadcast_done, which will be set TRUE, and
 * broadcast_binding_succeeded, which will be set TRUE if the child process
 * exited with a 0 status, and FALSE otherwise.
 */

void
broadcast_proc_exit()
{
	int pid;
	union wait wait_status;
	int termsig, retcode;

	broadcast_done = TRUE;
	pid = 0;

	while (pid != broadcaster_pid) {
		pid = wait3(&wait_status, WNOHANG, NULL);

		if (pid == 0) {
			return;
		} else if (pid == -1) {
			return;
		}
	}

	termsig = wait_status.w_termsig;
	retcode = wait_status.w_retcode;

	if ((termsig == 0) && (retcode == 0)) {
		broadcast_binding_succeeded = TRUE;
	} else {
		broadcast_binding_succeeded = FALSE;
	}
}

/*
 * This returns the current binding for a passed domain.
 */
void
ypbind_get_binding(rqstp, transp)
	struct svc_req *rqstp;
	register SVCXPRT *transp;
{
	char domain_name[YPMAXDOMAIN + 1];
	char *pdomain_name = domain_name;
	char *pname;
	struct ypbind_resp response;
	bool true;
	char outstring[YPMAXDOMAIN + 256];

	if (!svc_getargs(transp, xdr_ypdomain_wrap_string, &pdomain_name) ) {
		svcerr_decode(transp);
		return;
	}

	if ( (current_domain = ypbind_point_to_domain(pdomain_name) ) !=
	    (struct domain *) NULL) {

		/*
		 * Ping the server to make sure it is up.
		 */
		 
		if (current_domain->dom_boundp) {
			ypbind_ping(current_domain);
		}

		/*
		 * Bound or not, return the current state of the binding.
		 */

		if (current_domain->dom_boundp) {
			response.ypbind_status = YPBIND_SUCC_VAL;
			response.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr =
			    current_domain->dom_serv_addr;
			response.ypbind_respbody.ypbind_bindinfo.ypbind_binding_port = 
			    current_domain->dom_serv_port;
		} else {
			response.ypbind_status = YPBIND_FAIL_VAL;
			response.ypbind_respbody.ypbind_error =
			    YPBIND_ERR_NOSERV;
		}
		    
	} else {
		response.ypbind_status = YPBIND_FAIL_VAL;
		response.ypbind_respbody.ypbind_error = YPBIND_ERR_RESC;
	}

	if (!svc_sendreply(transp, xdr_ypbind_resp, &response) ) {
		fprintf(stderr, "ypbind:  Can't respond to rpc request.\n");
	}

	if (!svc_freeargs(transp, xdr_ypdomain_wrap_string, &pdomain_name) ) {
		fprintf(stderr,
		    "ypbind:  ypbind_get_binding can't free args.\n");
	}

	if ((current_domain) && (!current_domain->dom_boundp) &&
	    (!broadcaster_pid)) {
		/*
		 * The current domain is unbound, and there is no broadcaster 
		 * process active now.  Fork off a child who will yell out on 
		 * the net.  Because of the flavor of request we're making of 
		 * the server, we only expect positive ("I do serve this
		 * domain") responses.
		 */
		broadcast_domain = current_domain;
		broadcast_domain->dom_report_success++;
		pname = current_domain->dom_name;
				
		if ( (broadcaster_pid = fork() ) == 0) {
			(void) clnt_broadcast(YPPROG, YPVERS,
			    YPPROC_DOMAIN_NONACK, xdr_ypdomain_wrap_string,
			    &pname, xdr_int, &true, ypbind_broadcast_ack);
			    
			if (current_domain->dom_boundp) {
				
				/*
				 * Send out a set domain request to our parent
				 */
				ypbind_send_setdom(pname,
				    current_domain->dom_serv_addr,
				    current_domain->dom_serv_port);
				    
				if (current_domain->dom_report_success > 0) {
					sprintf(outstring,
					    "yp: server for domain \"%s\" OK",
					    pname);
					writeit(outstring);
				}
					
				exit(0);
			} else {
				sprintf(outstring,
				    "\
yp: server not responding for domain \"%s\"; still trying",
				    pname);
				writeit(outstring);
				exit(1);
			}

		} else if (broadcaster_pid == -1) {
			fprintf(stderr,
			"ypbind:  broadcaster fork failure.\n");
			broadcaster_pid = 0;
		}
	}
}

static int
writeit(s)
	char *s;
{
	FILE *f;

	if ((f = fopen("/dev/console", "w")) != NULL) {
		fprintf(f, "%s.\n", s);
		fclose(f);
	}
	
}

/*
 * This sends a ypbind "Set domain" message back to our parent.
 */
void
ypbind_send_setdom(dom, addr, port)
	char *dom;
	struct in_addr addr;
	unsigned short int port;
{
	struct ypbind_setdom req;
	struct sockaddr_in myaddr;
	int socket;
	struct timeval timeout;
	struct timeval intertry;
	CLIENT *client;

	strcpy(req.ypsetdom_domain, dom);
	req.ypsetdom_addr = addr;
	req.ypsetdom_port = port;
	get_myaddress(&myaddr);
	myaddr.sin_port = htons(udphandle->xp_port);
	socket = RPC_ANYSOCK;
	timeout.tv_sec = SETDOMTOTTIM;
	intertry.tv_sec = SETDOMINTERTRY;
	timeout.tv_usec = intertry.tv_usec = 0;

	if ((client = clntudp_create (&myaddr, YPBINDPROG, YPBINDVERS,
	    intertry, &socket) ) != NULL) {

		clnt_call(client, YPBINDPROC_SETDOM, xdr_ypbind_setdom,
		    &req, xdr_void, 0, timeout);
		clnt_destroy(client);
		close(socket);
	}
}

/*
 * This sets the internet address and port for the passed domain to the
 * passed values, and marks the domain as supported.
 */
void
ypbind_set_binding(rqstp, transp)
	struct svc_req *rqstp;
	register SVCXPRT *transp;
{
	struct ypbind_setdom req;

	if (!svc_getargs(transp, xdr_ypbind_setdom, &req) ) {
		svcerr_decode(transp);
		return;
	}

	if (!svc_sendreply(transp, xdr_void, 0) ) {
		fprintf(stderr, "ypbind:  Can't reply to rpc call.\n");
	}

	if ( (current_domain = ypbind_point_to_domain(req.ypsetdom_domain) ) !=
	    (struct domain *) NULL) {
		current_domain->dom_serv_addr = req.ypsetdom_addr;
		current_domain->dom_serv_port = req.ypsetdom_port;
		current_domain->dom_boundp = TRUE;
	}
}
/*
 * This returns a pointer to a domain entry.  If no such domain existed on
 * the list previously, an entry will be allocated, initialized, and linked
 * to the list.  Note:  If no memory can be malloc-ed for the domain structure,
 * the functional value will be (struct domain *) NULL.
 */
static struct domain *
ypbind_point_to_domain(pname)
	register char *pname;
{
	register struct domain *pdom;
	
	for (pdom = known_domains; pdom != (struct domain *)NULL;
	    pdom = pdom->dom_pnext) {
		if (!strcmp(pname, pdom->dom_name))
			return (pdom);
	}
	
	/* Not found.  Add it to the list */
	
	if (pdom = (struct domain *)malloc(sizeof (struct domain))) {
		pdom->dom_pnext = known_domains;
		known_domains = pdom;
		strcpy(pdom->dom_name, pname);
		pdom->dom_boundp = FALSE;
		pdom->dom_report_success = -1;
	}
	
	return (pdom);
}

/*
 * This is called by the broadcast rpc routines to process the responses 
 * coming back from the broadcast request. Since the form of the request 
 * which is used in ypbind_broadcast_bind is "respond only in the positive  
 * case", the internet address of the responding server will be picked up 
 * from the saddr parameter, and stuffed into the domain.  The domain's
 * boundp field will be set TRUE.  Because this function returns TRUE, 
 * the first responding server will be the bound server for the domain.
 */
bool
ypbind_broadcast_ack(ptrue, saddr)
	bool *ptrue;
	struct sockaddr_in *saddr;
{
	current_domain->dom_boundp = TRUE;
	current_domain->dom_serv_addr = saddr->sin_addr;
	current_domain->dom_serv_port = saddr->sin_port;
	return(TRUE);
}

/*
 * This checks to see if a server bound to a named domain is still alive and
 * well.  If he's not, boundp in the domain structure is set to FALSE.
 * pdom->boundp may change state.  Note:  Any rpc or resource error, or
 * heavily loaded server machine, or queued-up server, or busy disk, or ....
 * may toggle dom_boundp to FALSE.
 */
void
ypbind_ping(pdom)
       register struct domain *pdom;
{
        u_long port;  /* may be different from pdom->dom_serv_port */
        struct sockaddr_in addr;
        enum clnt_stat clnt_stat;

        addr.sin_addr = pdom->dom_serv_addr;
        addr.sin_family = AF_INET;

        if ((clnt_stat = pmap_rmtcall(&addr, YPPROG, YPVERS, YPPROC_NULL,
            xdr_void, 0, xdr_void, 0, total_timeout, &port)) != RPC_SUCCESS) {
                pdom->dom_boundp = FALSE;
        } else {
                pdom->dom_serv_port = htons((u_short)port);
        }
}

/*
 * Preloads the default domain's domain binding. A domain binding for the
 * local node's default domain will be set up, and a binding to a server
 * may additionally be made.
 */
static void
ypbind_init_default()
{
	char domain[256];
	char *pname = domain;
	int true;
	int binding_tries = 0;

	if (getdomainname(domain, 256) == 0) {
		current_domain = ypbind_point_to_domain(domain);

		if (current_domain == (struct domain *) NULL) {
			abort();
		}
		
		while ( (!current_domain->dom_boundp) &&
		    (binding_tries < BINDING_TRIES) ) {
			(void) clnt_broadcast(YPPROG, YPVERS,
			    YPPROC_DOMAIN_NONACK, xdr_ypdomain_wrap_string,
			    &pname, xdr_int, &true, ypbind_broadcast_ack);
			binding_tries++;
		}
	}
}