NFSv2/usr/src/etc/ypserv/ypserv.c

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

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

/*
 * ypserv.c
 * This contains the mainline code for the yellowpages server; functions which
 * are called by name only from the mainline; and functions which are called
 * as software signal handlers.  Data structures which are truly process-global
 * are also in this module.
 */

#include "ypsym.h"
#include <sys/ioctl.h>

bool ypdb_xfer_done = FALSE;		/* Set TRUE by ypxfr_proc_exit, 
					 *   FALSE by ypmap_xfer_done */
struct dom_binding xfr_binding;		/* Binding to transfering peer */
struct peer_list_item *xfr_peer = (struct peer_list_item *) NULL;
struct map_xfr_entry *map_xfr_list = (struct map_xfr_entry *) NULL;
struct map_xfr_entry *current_transfer;
bool ypinitialization_done = FALSE;
static int yptime_quantum  = YPTIME_QUANTUM; /* Time between alarm clock
					*   interrupts in seconds. */
unsigned long tick_counter = 0;		/* Ticktocktracker. */
struct timer_action alarm_actions[] = {
	{
		ypping_eligible_map,
		0,
		1
	},
	{				/* Last entry on the list always */
		(PFV) NULL,
		0,
		0
	}
	
};
static char create_failed[] = "ypserv:  Unable to create server for ";
static char register_failed[] = "ypserv:  Unable to register service for ";
char ypdbpath[] = __YP_PATH_PREFIX;
char peer_transport[] = "udp";
char order_key[] = ORDER_KEY;
char ypprivate_domain_name[] = YPPRIVATE_DOMAIN_NAME;
char ypdomains[] = YPDOMAINS;
struct timeval ypintertry = {		/* udp secs between tries in peer comm */
	YPINTERTRY_TIME,		/* Seconds */
	0				/* uSecs */
};
struct timeval yptimeout = {		/* udp total timeout for peer comm */
	YPTOTAL_TIME,			/* Seconds */
	0				/* uSecs */
};
char *domain_special_cases[] = {
	YPPRIVATE_DOMAIN_NAME,
	(char *) NULL			/* Last entry on the list always */
};

/* Tables map_special_cases and special_map_handlers must be parallel */

char *map_special_cases[] = {
	YPDOMAINS,
	YPSERVERS,
	YPMAPS,
	YPHOSTS_BYNAME,
	(char *) NULL			/* Last entry on the list always */
};
PFV special_map_handlers[] = {
	ypnew_ypdomains,
	ypnew_ypservers,
	ypnew_ypmaps,
	ypnew_hosts,
	(PFV) NULL
};
char myhostname[256];
SVCXPRT *udphandle;
SVCXPRT *tcphandle;
struct domain_list_item *pingpeer_curr_domain = NULL;
struct domain_list_item *pingmap_curr_domain = NULL;
	
#ifdef DEVELOPMENT
bool silent = FALSE;			/* Toggle to prevent process from forking
					 *   and turning off stderr messages */
#else
bool silent = TRUE;
#endif

bool log_transfers = FALSE;		/* Toggle to log the results of the
					 *   map transfers to a file in the
					 *   yp database directory.  This is
					 *   used by function ypdo_xfr in module
					 *   ypserv_xfr.c.  It is never set
					 *   TRUE by code; use adb to do it
					 *   before activating the process. */

void ypexit();

/*
 * This is the main line code for the yp server.  It registers the server with
 * rpc and the port mapper, and sets the first poll alarm.
 */
main()
{
	int readfds;
	struct timer_action *palarm_action;

 	ypinit(); 			/* Set up shop */

	/*
	 * This is the process' main loop.  On each iteration of the loop, the
	 * data base transfer-done flag is checked, and, if it is set,
	 * ypmap_xfer_done is called to relink the temp files to become the
	 * current map.  When that is done, the queue of maps to be updated
	 * is checked, and, if non-null, a map transfer process is kicked off
	 * to do the transfer.  Then the list of time - based functions is
	 * checked, and all which have reached their threshold value are
	 * called.  After all the houskeeping is done, the process does a
	 * blocked select on its rpc input queue.  When it wakes up as a result
	 * of a delivered message, it calls svc_retreq to get the rpc data to
	 * the yp dispatcher, which then calls the appropriate ypserv_proc
	 * function.
	 */

	for (;;) {

		if (ypdb_xfer_done) {
		    ypmap_xfer_done();
		}

		if ( (current_transfer == (struct map_xfr_entry *) NULL) &&
		   (map_xfr_list != (struct map_xfr_entry *) NULL) ) {
		    ypxfr_map();
		}

		for (palarm_action = alarm_actions;
			palarm_action->ta_action != NULL;
			palarm_action++) {

			if ((palarm_action->ta_ticks) ==
			    palarm_action->ta_threshold) {
				(palarm_action->ta_action) ();
				palarm_action->ta_ticks = 0;
		    	}
		}

		readfds = svc_fds;
		errno = 0;

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

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

			break;
		}

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

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

	}

}


/*
 * Does all startup processing for the yp server.  This includes setting up
 * shop as an rcp-based server, letting the portmapper know about us, setting
 * up the signal handlers, getting a list of all supported domains, all existing
 * domains, peer servers in each supported domain, maps in each domain, masters
 * for each map, and order numbers for each map.  A return from this function
 * implies that a usable level of functionality has been reached.  All error
 * reporting is done at this level or below.
 */
void
ypinit()
{
	struct timer_action *palarm_action;
	int pid;
	int t;

	if (silent) {
		
		pid = fork();
		
		if (pid == -1) {
			fprintf(stderr, "ypserv:  ypinit fork failure.\n");
			ypexit();
		}
	
		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);
 		}
	}

	gethostname(myhostname, 256);

	if ((udphandle = svcudp_create(RPC_ANYSOCK)) == (SVCXPRT *) NULL) {
		fprintf(stderr, "%s%s.\n", create_failed, "udp");
		ypexit();
	}

	if ((tcphandle = svctcp_create(RPC_ANYSOCK, 1024, 1024)) ==
	    (SVCXPRT *) NULL) {
		fprintf(stderr, "%s%s.\n", create_failed, "tcp");
		ypexit();
	}

	pmap_unset(YPPROG, YPVERS);

	if (!svc_register(udphandle, YPPROG, YPVERS, ypdispatch, IPPROTO_UDP) ) {
		fprintf(stderr, "%s%s.\n", register_failed, "udp");
		ypexit();
	}

	if (!svc_register(tcphandle, YPPROG, YPVERS, ypdispatch, IPPROTO_TCP) ) {
		fprintf(stderr, "%s%s.\n", register_failed, "tcp");
		ypexit();
	}

	if ((int) signal(SIGALRM, yptimer) == -1) {
		fprintf(stderr, "ypserv:  Can't catch alarm signal.\n");
		ypexit();
	}

	if ((int) signal(SIGCHLD, ypxfr_proc_exit) == -1) {
		fprintf(stderr,
		    "ypserv:  Can't catch xfr process exit signal.\n");
		ypexit();
	}

	if (!ypget_all_domains() ) {
		fprintf(stderr,
		    "ypserv:  ypinit can't build list of all domains.\n");
		ypexit();
	}
	
	ypget_supported_domains();
	ypbuild_peer_lists();
	ypget_all_maps();
	ypget_supported_maps();
/*	ypping_all_peers();	*/		/* Obsolete */
	ypinitialization_done = TRUE;
	alarm(yptime_quantum);			/* Start the motor running */
}

/*
 * This dispatches to server action routines based on the input procedure
 * number.  ypdispatch is called from the RPC function svc_getreq.
 */
void
ypdispatch(rqstp, transp)
	struct svc_req *rqstp;
	SVCXPRT *transp;
{

	switch (rqstp->rq_proc) {

	case YPPROC_NULL:

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

		break;

	case YPPROC_DOMAIN:
		ypdomain(rqstp, transp, TRUE);
		break;

	case YPPROC_DOMAIN_NONACK:
		ypdomain(rqstp, transp, FALSE);
		break;

	case YPPROC_MATCH:
		ypmatch(rqstp, transp);
		break;

	case YPPROC_FIRST:
		ypfirst(rqstp, transp);
		break;

	case YPPROC_NEXT:
		ypnext(rqstp, transp);
		break;

	case YPPROC_POLL:
		yppoll(rqstp, transp);
		break;

	case YPPROC_PUSH:
		yppush(rqstp, transp);
		break;

	case YPPROC_PULL:
		yppull(rqstp, transp);
		break;

	case YPPROC_GET:
		ypget(rqstp, transp);
		break;

	default:
		svcerr_noproc(transp);
		break;

	}

	return;
}

/*
 * This increments the global tick counter and tick counters on the time-based
 * functions.  Tick counters associated with the time-based functions latch at
 * their threshold value.  yptimer is a Unix SIGALRM handler, and re-sets the
 * alarm each time it is called.
 */
void
yptimer()
{
	struct timer_action * palarm_action;
	
	tick_counter++;

	for (palarm_action = alarm_actions;
	   palarm_action->ta_action != (PFV) NULL;
	   palarm_action++) {

		if ( (palarm_action->ta_ticks) <
		    palarm_action->ta_threshold) {
			palarm_action->ta_ticks++;
		}
		
	}

	alarm(yptime_quantum);
}

/*
 * This sets the boolean ypdb_xfer_done TRUE.  This is the Unix SIGCHILD
 * handler.
 */
void
ypxfr_proc_exit ()
{
	ypdb_xfer_done = TRUE;
}

/*
 * This tries to get the current map transferred from a peer server to a
 * local temp file.  The actual transfer is accomplished by forking off a
 * map transfer process:  function ypdo_xfr in module ypserv_xfr.c.  The
 * current map's map_xfr_entry will have the transfer process' pid, and
 * the temporary map file name filled in.  
 */
void
ypxfr_map()
{
	struct map_list_item *pmap;
	struct domain_list_item *pdom;
	int pid;

	if (!ypset_current_xfr() )
	    return;				/* Internal error */
	    
	pmap = current_transfer->mx_map;

	if (!pmap) {				/* Map isn't in current list */
		yprelease_current_xfr();
		return;
	}
	
	/* If I am the master peer, I've already got the official copy */
	
	if ((pmap->map_master) &&
	    (!strcmp(pmap->map_master->peer_pname, myhostname) ) ){
		yprelease_current_xfr();
		return;
	}

	pdom = pmap->map_domain;

	if (!pdom->dom_supported) {		/* Domain is unsupported */
		yprelease_current_xfr();
		return;
	}
	
	ypset_current_tmpname();	/* Get a temporary name for the map */
	
	/*
	 * Fork off a map transfer process to do the work, and remember its pid.
	 */
	 
	errno = 0;
	pid = fork();

	if (pid > 0) {
		ypset_current_pid(pid);
	} else if (pid == -1) {
		fprintf(stderr,
		    "ypserv:  ypxfr_map:  fork failure. errno = %d.\n", errno);
	} else {
		ypdo_xfr();
	}
}

/*
 * This is called from the mainline when the SIGCHILD handler has noticed
 * that a child process has exited.  Two sorts of child processes may have
 * exited: either a map transfer process (implying that this is a slave
 * server) or a peer notification process, forked off to send "get" messages
 * to all the peer servers.  In this second case, this is the master ypserv.
 * 
 * A map transfer process exits when a map transfer has completed, either
 * correctly or with errors.  We tell whether the transfer succeeded or
 * failed by reaping the exit status.  In the first (success) case, the map
 * is renamed from its temporary name to its true name, and, if it is a
 * special case map, the appropriate handler function is called.  In the
 * second, failure case, a decision is made on the basis of failure type to
 * either requeue the transfer, or to give up.  In the success case, the
 * transferred map is renamed to its true name.
 * 
 * A peer notification process exits when it has sent a "get" message to all
 * reachable peers.  In this case, we have no interest in any exit status -
 * we just want to reap the exit status to get rid of the zombie.
 */
void
ypmap_xfer_done()
{
	char temp_base[MAXNAMLEN + 1];
	struct map_list_item *pmap;
	int case_index;
	int pid;
	union wait wait_status;
	datum key, val;
	int termsig, retcode;		/* To make debugging easier */
	int error;

	ypdb_xfer_done = FALSE;		/* Prevent further calls for this
					 *   completion */
	pid = 0;

   	if (current_transfer == (struct map_xfr_entry *) NULL) {

		/*
		 * No transfer was in progress - the child must have been a
		 * peer notification processes.  Reap all exit statuses
		 * without looking at them to make sure zombies don't
		 * accumulate at the master server.
		 */
			 
		while (TRUE) {
			pid = wait3(&wait_status, WNOHANG, NULL);

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

		return;
	}

   	if (current_transfer->mx_xfr_pid == 0) {
		
		/*
		 * This represents an error condition.  We really shouldn't
		 * believe we have an active transfer in progress, but not
		 * know the pid of the transfer process.  Recover by
		 * throwing away the exit statuses, and by clearing the
		 * current transfer item.
		 */
	
		while (TRUE) {
			pid = wait3(&wait_status, WNOHANG, NULL);

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

		yprelease_current_xfr();
		return;
	}

	while (pid != current_transfer->mx_xfr_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)) {

		/* Success branch */

		/*
		 * If the map pointer in the current transfer entry has been
		 * NULLed out while the transfer was going on, we are going to
		 * bag the rest of this.  Throw away the temp map files and
		 * the current transfer entry, and return from this point.
		 */
		     
		if (current_transfer->mx_map == (struct map_list_item *) NULL) {
			ypdel_mapfiles(current_transfer->mx_temp_path);
			yprelease_current_xfr();
			return;
		}
	
		/*
		 * Temporarily move the newly transferred map into the target
		 * domain, and make a temporary maplist entry for it, using the
		 * temporary name.  This allows us to use the available
		 * primitives for setting the current map and retrieving the
		 * order number.  If the map is well-formed, we will rename the
		 * map data base files; otherwise we will delete them.  In
		 * either case, we will delete the temporary map list entry.
		 */

		ypmkfilename(current_transfer->mx_map->map_domain->dom_name,
		     current_transfer->mx_temp_name, temp_base);

		if (!yprename_map(current_transfer->mx_temp_path, temp_base) ) {

			/*
			 * The rename has failed;  cleanup and return from
			 * this point.
			 */
			 
			ypdel_mapfiles(current_transfer->mx_temp_path);
			yprelease_current_xfr();
			return;
		 }

		strcpy(current_transfer->mx_temp_path, temp_base);
		 
		if (!ypadd_one_map(current_transfer->mx_temp_name,
		    current_transfer->mx_map->map_domain, NULL) ) {

			/*
			 * Couldn't add the new temp map;  cleanup and return
			 * from this point.
			 */
			 
			ypdel_mapfiles(current_transfer->mx_temp_path);
			yprelease_current_xfr();
			return;
		}

		pmap = yppoint_at_map(current_transfer->mx_temp_name,
		    current_transfer->mx_map->map_domain);
		pmap->map_exists = TRUE;
		
		/*
		 * Test to see if the new map has a retrievable order number.
		 * If it does, overwrite the order number held in the maplist
		 * entry with the new one, and rename the map.
		 */

		 if (ypget_map_order(pmap) ) {

			 /*
			  * Do special-case processing on the map ypdomains in
			  * domain yp_private.  Make sure that there is an
			  * entry for yp_private itself, so the world doesn't
			  * fall apart.  If there's none, cleanup and return
			  * from this point.
			  */

			 if (!strcmp(current_transfer->mx_map->map_name,
			     YPDOMAINS) &&
			     !strcmp(
			         current_transfer->mx_map->map_domain->dom_name,
			         ypprivate_domain_name) ) {

				if (!ypset_current_map(pmap->map_name,
				    pmap->map_domain->dom_name, &error) ) {
					ypdel_mapfiles(
					    current_transfer->mx_temp_path);
					yprelease_current_xfr();
					ypdel_one_map(pmap);
					return;
				}

				key.dptr = ypprivate_domain_name;
				key.dsize = sizeof(YPPRIVATE_DOMAIN_NAME) -1;
				val = fetch(key);

				if (val.dptr == (char *) NULL) {
					ypdel_mapfiles(
					    current_transfer->mx_temp_path);
					yprelease_current_xfr();
					ypdel_one_map(pmap);
					return;
				}				 
			 }

			 /*
			  * The map is well-formed.  Make the base file name
			  * be the true map name, and try to do the rename.
			  */
			  
			 ypmkfilename(
			     current_transfer->mx_map->map_domain->dom_name,
			     current_transfer->mx_map->map_name, temp_base);
			     
			 if (yprename_map(current_transfer->mx_temp_path,
			     temp_base) ) {

				/*
				 * This is the go path.  Copy the order number
				 * from the temp maplist entry to the real
				 * maplist entry, mark the map as existing
				 * and supported, and delete the temporary
				 * maplist entry and the current transfer entry.
				 * Notice that after we ditch the temporary map,
				 * we will reuse the map pointer (pmap) for the
				 * real map.
				 */

				current_transfer->mx_map->map_order =
				    pmap->map_order;
				ypdel_one_map(pmap);
				pmap = current_transfer->mx_map;
				yprelease_current_xfr();
				pmap->map_exists = TRUE;
				pmap->map_supported = TRUE;
				
				/*
		 		 * If the map is in the list of special cases,
				 * call the special case handler.
		 		 */
		 
				if ( (case_index = ypspecial_casep(
				    pmap->map_name, map_special_cases) ) >= 0) {
					special_map_handlers[case_index](pmap);
				}

			 } else {

				/*
				 * The rename from temp name to real name has
				 * failed.  Clean up the temp map files, the
				 * current transfer entry, and the temp maplist
				 * entry.
				 */
				 
				ypdel_mapfiles(current_transfer->mx_temp_path);
				yprelease_current_xfr();
				ypdel_one_map(pmap);
			 }
			 
		 } else {

			/*
			 * The new map is ill-formed - we can't retrieve the
			 * order number from the map.  Do the same cleanup
			 * needed when the final rename failed.
			 */
			
			ypdel_mapfiles(current_transfer->mx_temp_path);
			yprelease_current_xfr();
			ypdel_one_map(pmap);
		 }

	} else {

		/*
		 * Either the map transfer process got clobbered and died, or
		 * it was unsuccessful, indicated the condition through its
		 * exit code, and left under its own volition.  Based on the
		 * the error code, decide whether to release or to requeue the
		 * current map transfer entry.
		 *
		 * Implementation note:  This is a first guess as to which
		 * conditions should result in requeue, and which in giving
		 * up (release).
		 */
		 
		ypdel_mapfiles(current_transfer->mx_temp_path);

		if (termsig) {
				yprequeue_current_xfr();
		} else {
			
			switch (retcode) {

			case YPXFR_EXIT_MAP:
			case YPXFR_EXIT_OLD:
			case YPXFR_EXIT_DOMAIN:
			case YPXFR_EXIT_SKEW:
			case YPXFR_EXIT_FORM:
			case YPXFR_EXIT_PEER: {
			case YPXFR_EXIT_RPC:
				yprelease_current_xfr();
				break;
			}

			case YPXFR_EXIT_DBM:
			case YPXFR_EXIT_FILE:
			case YPXFR_EXIT_RSRC:
			case YPXFR_EXIT_ERR: {
				yprequeue_current_xfr();
				break;
			}

			default: {
				yprelease_current_xfr();
				break;
			}

			}
		}
	}
}

/*
 * This flushes output to stderr, then aborts the server process to leave a
 * core dump.
 */
static void
ypexit()
{
	(void) fflush(stderr);
	(void) abort();
}