NFSv2/usr/src/etc/ypserv/ypserv.c
#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();
}