Performace improvements/simplifications/cleanups:

- Make the child process reaper signal-driven. (Previously, we called reaper()
  once a second each time we went through the select() loop. This was
  convenient, but inefficient.)

- Increase main select() timeout from 1 second to 60 seconds and use
  this as the ping timer instead of using timestamps in the _dom_binding
  structure. This nd the reaper() change noted above makes ypbind a little
  less CPU-intensive.

- Don't flag EINTR's from select() as errors since they will happen as a
  result of incoming SIGCHLD's interrupting select().

- Prevent possible resource hogging. Currently we malloc() memory
  each time a user process asks us to establish a binding for a domain,
  but we never free it. This could lead to serious memory leakage if a
  'clever' user did something like ask ypwhich to check the bindings
  for domains 0.0.0.0.0.0.0.0.0.0 through 9.9.9.9.9.9.9.9.9.9 inclusive.
  (This would also make a mess out of the /var/yp/binding directory.)

  We now avoid this silliness by a) limiting the maximum number of
  simultaneous bindings we can manage to 200, and b) free()ing _dom_binding
  structures of secondary domains whose servers have stopped responding.
  We unlink the /var/yp/binding/domain.vers files for the free()ed
  domains too.

  (This is safe to do since a client can prod us into reestablishing the
  binding, at which time we'll simply allocate a new _dom_binding structure
  for it.)

  We keep count of the total number of domains. If asked to
  allocate more than the maximum, we return an error. I have yet to hear
  of anybody needing 200 simultaneous NIS bindings, so this should be
  enough. (I chose the number 200 arbitrarily. It can be increased if need
  be.)

- Changed "server not responding"/"server OK" messages to display server
  IP addresses again since it looks spiffier.

- Use daemon() to daemonify ourselves,

- Added a SIGTERM handler that removes all binding files and unregisters
  the ypbind service from the portmapper when a SIGTERM in received.

- The comment 'blow away everything in BINDINGDIR' has no associated code.
  Give it some: clean out /var/yp/binding at startup (if it exists).

This completes my ypbind wishlist. Barring bug fixes, I shouldn't need to
go poking around in here anymore. (Of course, this means I can start
working on my ypserv whishlist now... :)
This commit is contained in:
wpaul 1995-05-10 23:02:41 +00:00
parent fd82b3cc6a
commit 78ce3864de

View File

@ -28,7 +28,7 @@
*/
#ifndef LINT
static char rcsid[] = "$Id: ypbind.c,v 1.8 1995/04/26 19:03:14 wpaul Exp $";
static char rcsid[] = "$Id: ypbind.c,v 1.9 1995/05/03 18:34:22 wpaul Exp $";
#endif
#include <sys/param.h>
@ -65,17 +65,6 @@ static char rcsid[] = "$Id: ypbind.c,v 1.8 1995/04/26 19:03:14 wpaul Exp $";
#define BINDINGDIR "/var/yp/binding"
#endif
/*
* Ping the server once every PING_INTERVAL seconds to make sure it's
* still there.
*/
#ifndef PING_INTERVAL
#define PING_INTERVAL 60
#endif
#ifndef FAIL_THRESHOLD
#define FAIL_THRESHOLD 20
#endif
struct _dom_binding {
struct _dom_binding *dom_pnext;
char dom_domain[YPMAXDOMAIN + 1];
@ -86,7 +75,6 @@ struct _dom_binding {
int dom_alive;
int dom_broadcasting;
int dom_default;
time_t dom_check;
};
extern bool_t xdr_domainname(), xdr_ypbind_resp();
@ -98,8 +86,10 @@ void *ypbindproc_null_2 __P((SVCXPRT *, void *, CLIENT *));
bool_t *ypbindproc_setdom_2 __P((SVCXPRT *, struct ypbind_setdom *, CLIENT *));
void rpc_received __P((char *, struct sockaddr_in *, int ));
void broadcast __P((struct _dom_binding *));
int ping __P((struct _dom_binding *, int));
int ping __P((struct _dom_binding *));
void handle_children __P(( int ));
void reaper __P((int));
void terminate __P((int));
static char *broad_domain;
char *domainname;
@ -113,9 +103,18 @@ int ypsecuremode = 0;
/* No more than MAX_CHILDREN child broadcasters at a time. */
#define MAX_CHILDREN 5
/* No more than MAX_DOMAINS simultaneous domains */
#define MAX_DOMAINS 200
#ifndef FAIL_THRESHOLD
#define FAIL_THRESHOLD 20
#endif
#define KILLME_MAGIC 0xdeaddead
int child_fds[FD_SETSIZE];
static int fd[2];
int children = 0;
int domains = 0;
SVCXPRT *udptransp, *tcptransp;
@ -150,6 +149,12 @@ CLIENT *clnt;
break;
if(ypdb==NULL) {
if (domains > MAX_DOMAINS) {
syslog(LOG_WARNING, "domain limit (%d) exceeded",
MAX_DOMAINS);
res.ypbind_respbody.ypbind_error = YPBIND_ERR_RESC;
return;
}
ypdb = (struct _dom_binding *)malloc(sizeof *ypdb);
if (ypdb == NULL) {
syslog(LOG_WARNING, "malloc: %s", strerror(errno));
@ -162,17 +167,15 @@ CLIENT *clnt;
ypdb->dom_alive = 0;
ypdb->dom_default = 0;
ypdb->dom_lockfd = -1;
sprintf(path, "%s/%s.%ld", BINDINGDIR, ypdb->dom_domain, ypdb->dom_vers);
sprintf(path, "%s/%s.%ld", BINDINGDIR,
ypdb->dom_domain, ypdb->dom_vers);
unlink(path);
ypdb->dom_pnext = ypbindlist;
ypbindlist = ypdb;
return &res;
domains++;
}
if(ypdb->dom_alive==0)
return &res;
if (ping(ypdb, 1))
if (ping(ypdb))
return &res;
res.ypbind_status = YPBIND_SUCC_VAL;
@ -298,6 +301,22 @@ int sig;
wait3(&st, WNOHANG, NULL);
}
void terminate(sig)
int sig;
{
struct _dom_binding *ypdb;
char path[MAXPATHLEN];
for(ypdb=ypbindlist; ypdb; ypdb=ypdb->dom_pnext) {
close(ypdb->dom_lockfd);
sprintf(path, "%s/%s.%ld", BINDINGDIR,
ypdb->dom_domain, ypdb->dom_vers);
unlink(path);
}
pmap_unset(YPBINDPROG, YPBINDVERS);
exit(0);
}
void
main(argc, argv)
int argc;
@ -307,6 +326,8 @@ char **argv;
struct timeval tv;
fd_set fdsr;
int i;
DIR *dird;
struct dirent *dirp;
yp_get_default_domain(&domainname);
if( domainname[0] == '\0') {
@ -323,45 +344,48 @@ char **argv;
ypsecuremode++;
}
/* blow away everything in BINDINGDIR */
/* blow away everything in BINDINGDIR (if it exists) */
if ((dird = opendir(BINDINGDIR)) != NULL) {
char path[MAXPATHLEN];
while ((dirp = readdir(dird)) != NULL)
if (strcmp(dirp->d_name, ".") &&
strcmp(dirp->d_name, "..")) {
sprintf(path,"%s/%s",BINDINGDIR,dirp->d_name);
unlink(path);
}
closedir(dird);
}
#ifdef DAEMON
switch(fork()) {
case 0:
break;
case -1:
if (daemon(0,0)) {
perror("fork");
exit(1);
default:
exit(0);
}
setsid();
#endif
pmap_unset(YPBINDPROG, YPBINDVERS);
udptransp = svcudp_create(RPC_ANYSOCK);
if (udptransp == NULL) {
fprintf(stderr, "cannot create udp service.");
fprintf(stderr, "cannot create udp service.\n");
exit(1);
}
if (!svc_register(udptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
IPPROTO_UDP)) {
fprintf(stderr, "unable to register (YPBINDPROG, YPBINDVERS, udp).");
fprintf(stderr, "unable to register (YPBINDPROG, YPBINDVERS, udp).\n");
exit(1);
}
tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0);
if (tcptransp == NULL) {
fprintf(stderr, "cannot create tcp service.");
fprintf(stderr, "cannot create tcp service.\n");
exit(1);
}
if (!svc_register(tcptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
IPPROTO_TCP)) {
fprintf(stderr, "unable to register (YPBINDPROG, YPBINDVERS, tcp).");
fprintf(stderr, "unable to register (YPBINDPROG, YPBINDVERS, tcp).\n");
exit(1);
}
@ -378,9 +402,10 @@ char **argv;
ypbindlist->dom_lockfd = -1;
ypbindlist->client_handle = NULL;
ypbindlist->dom_default = 1;
sprintf(path, "%s/%s.%ld", BINDINGDIR, ypbindlist->dom_domain,
ypbindlist->dom_vers);
(void)unlink(path);
domains++;
signal(SIGCHLD, reaper);
signal(SIGTERM, terminate);
/* Initialize children fds. */
for (i = 0; i < FD_SETSIZE; i++)
@ -388,6 +413,9 @@ char **argv;
openlog(argv[0], LOG_PID, LOG_DAEMON);
/* Kick off the default domain */
broadcast(ypbindlist);
while(1) {
fdsr = svc_fdset;
@ -395,17 +423,20 @@ char **argv;
if (child_fds[i] > 0 )
FD_SET(child_fds[i], &fdsr);
tv.tv_sec = 1;
tv.tv_sec = 60;
tv.tv_usec = 0;
switch(select(_rpc_dtablesize(), &fdsr, NULL, NULL, &tv)) {
case 0:
checkwork();
reaper();
break;
case -1:
syslog(LOG_WARNING, "select: %s", strerror(errno));
break;
if (errno == EINTR)
continue;
else {
syslog(LOG_WARNING, "select: %s", strerror(errno));
break;
}
default:
for(i = 0; i < FD_SETSIZE; i++) {
if (child_fds[i] > 0 && FD_ISSET(child_fds[i],&fdsr)) {
@ -427,19 +458,9 @@ void
checkwork()
{
struct _dom_binding *ypdb;
time_t t;
time(&t);
for(ypdb=ypbindlist; ypdb; ypdb=ypdb->dom_pnext) {
if (!ypdb->dom_alive && !ypdb->dom_broadcasting) {
if (!ypdb->dom_default)
ypdb->dom_alive = 1;
ypdb->dom_broadcasting = 1;
broadcast(ypdb);
}
if (ypdb->dom_alive && ypdb->dom_check < t)
ping(ypdb, 0);
}
for(ypdb=ypbindlist; ypdb; ypdb=ypdb->dom_pnext)
ping(ypdb);
}
/* The clnt_broadcast() callback mechanism sucks. */
@ -527,16 +548,22 @@ struct _dom_binding *ypdb;
enum clnt_stat stat;
int i;
if (children > MAX_CHILDREN)
if (children > MAX_CHILDREN || ypdb->dom_broadcasting)
return;
broad_domain = ypdb->dom_domain;
if (pipe(fd) < 0) {
syslog(LOG_WARNING, "pipe: %s",strerror(errno));
return;
}
if (ypdb->dom_vers = -1 && (long)ypdb->dom_server_addr.sin_addr.s_addr)
syslog(LOG_WARNING, "NIS server [%s] for domain %s not responding",
inet_ntoa(ypdb->dom_server_addr.sin_addr), ypdb->dom_domain);
broad_domain = ypdb->dom_domain;
ypdb->dom_broadcasting = 1;
flock(ypdb->dom_lockfd, LOCK_UN);
switch(fork()) {
case 0:
close(fd[0]);
@ -564,10 +591,10 @@ struct _dom_binding *ypdb;
broadcast_result);
if (stat != RPC_SUCCESS) {
syslog(LOG_WARNING, "NIS server for domain %s not responding",
ypdb->dom_domain);
bzero((char *)&ypdb->dom_server_addr,
sizeof(struct sockaddr_in));
if (!ypdb->dom_default)
ypdb->dom_server_addr.sin_addr.s_addr = KILLME_MAGIC;
if (tell_parent(&ypdb->dom_domain, &ypdb->dom_server_addr))
syslog(LOG_WARNING, "lost connection to parent");
}
@ -578,8 +605,8 @@ struct _dom_binding *ypdb;
* The right way to check if a server is alive.
* Attempt to get a client handle pointing to the server and send a
* YPPROC_DOMAIN_NONACK. If we don't get a response, we invalidate
* this binding entry, which will cause checkwork() to dispatch a
* broadcaster process. Note that we treat non-default domains
* this binding entry and send out a broadcast to try to establish
* a new binding. Note that we treat non-default domains
* specially: once bound, we keep tabs on our server, but if it
* goes away and fails to respond after one round of broadcasting, we
* abandon it until a client specifically references it again. We make
@ -587,9 +614,8 @@ struct _dom_binding *ypdb;
* need it to keep the system on its feet.
*/
int
ping(ypdb, force)
ping(ypdb)
struct _dom_binding *ypdb;
int force;
{
bool_t out;
struct timeval interval, timeout;
@ -605,9 +631,6 @@ int force;
if (ypdb->dom_broadcasting)
return(1);
if (!ypdb->dom_default && ypdb->dom_vers == -1 && !force)
return(1);
if (ypdb->client_handle == NULL) {
if ((ypdb->client_handle = clntudp_bufcreate(
&ypdb->dom_server_addr, YPPROG, YPVERS,
@ -617,7 +640,7 @@ int force;
ypdb->client_handle = NULL;
ypdb->dom_alive = 0;
ypdb->dom_vers = -1;
flock(ypdb->dom_lockfd, LOCK_UN);
broadcast(ypdb);
return(1);
}
}
@ -628,14 +651,9 @@ int force;
ypdb->client_handle = NULL;
ypdb->dom_alive = 0;
ypdb->dom_vers = -1;
flock(ypdb->dom_lockfd, LOCK_UN);
broadcast(ypdb);
return(1);
}
/*
* We pinged successfully. Reset the timer.
*/
time(&t);
ypdb->dom_check = t + PING_INTERVAL;
return(0);
}
@ -645,7 +663,7 @@ char *dom;
struct sockaddr_in *raddrp;
int force;
{
struct _dom_binding *ypdb;
struct _dom_binding *ypdb, *prev = NULL;
struct iovec iov[2];
struct ypbind_resp ybr;
char path[MAXPATHLEN];
@ -657,9 +675,11 @@ int force;
if(dom==NULL)
return;
for(ypdb=ypbindlist; ypdb; ypdb=ypdb->dom_pnext)
for(ypdb=ypbindlist; ypdb; ypdb=ypdb->dom_pnext) {
if( strcmp(ypdb->dom_domain, dom) == 0)
break;
prev = ypdb;
}
/* if in securemode, check originating port number */
if (ypsecuremode && (ntohs(raddrp->sin_port) >= IPPORT_RESERVED)) {
@ -676,6 +696,21 @@ int force;
if (raddrp->sin_addr.s_addr == (long)0) {
ypdb->dom_broadcasting = 0;
ypdb->dom_alive = 0;
broadcast(ypdb);
return;
}
if (raddrp->sin_addr.s_addr == KILLME_MAGIC) {
if (prev == NULL)
ypbindlist = ypdb->dom_pnext;
else
prev->dom_pnext = ypdb->dom_pnext;
sprintf(path, "%s/%s.%ld", BINDINGDIR,
ypdb->dom_domain, YPVERS); /* XXX dom_vers can't */
close(ypdb->dom_lockfd); /* be trusted here */
unlink(path);
free(ypdb);
domains--;
return;
}
@ -699,8 +734,8 @@ int force;
/* We've recovered from a crash: inform the world. */
if (ypdb->dom_vers = -1 && ypdb->dom_server_addr.sin_addr.s_addr)
syslog(LOG_WARNING, "NIS server for domain %s OK",
ypdb->dom_domain);
syslog(LOG_WARNING, "NIS server [%s] for domain %s OK",
inet_ntoa(ypdb->dom_server_addr.sin_addr), ypdb->dom_domain);
bcopy((char *)raddrp, (char *)&ypdb->dom_server_addr,
sizeof ypdb->dom_server_addr);