540 lines
13 KiB
C
540 lines
13 KiB
C
/*
|
|
* Copyright (c) 1986, 1988 Regents of the University of California.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifndef lint
|
|
static char sccsid[] = "@(#)ns_maint.c 4.39 (Berkeley) 3/2/91";
|
|
#endif /* not lint */
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#if defined(SYSV)
|
|
#include <unistd.h>
|
|
#endif SYSV
|
|
#include <netinet/in.h>
|
|
#include <stdio.h>
|
|
#include <syslog.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
#include <arpa/nameser.h>
|
|
#include <sys/wait.h>
|
|
#include "ns.h"
|
|
#include "db.h"
|
|
#include "pathnames.h"
|
|
|
|
extern int errno;
|
|
extern int maint_interval;
|
|
extern int needzoneload;
|
|
extern u_short ns_port;
|
|
extern char *ctime();
|
|
|
|
int xfers_running; /* number of xfers running */
|
|
int xfers_deferred; /* number of needed xfers not run yet */
|
|
static int alarm_pending;
|
|
|
|
|
|
/*
|
|
* Invoked at regular intervals by signal interrupt; refresh all secondary
|
|
* zones from primary name server and remove old cache entries. Also,
|
|
* ifdef'd ALLOW_UPDATES, dump database if it has changed since last
|
|
* dump/bootup.
|
|
*/
|
|
ns_maint()
|
|
{
|
|
register struct zoneinfo *zp;
|
|
struct itimerval ival;
|
|
time_t next_refresh = 0;
|
|
int zonenum;
|
|
|
|
gettime(&tt);
|
|
|
|
#ifdef DEBUG
|
|
if (debug)
|
|
fprintf(ddt,"\nns_maint(); now %s", ctime(&tt.tv_sec));
|
|
#endif
|
|
|
|
xfers_deferred = 0;
|
|
alarm_pending = 0;
|
|
for (zp = zones, zonenum = 0; zp < &zones[nzones]; zp++, zonenum++) {
|
|
#ifdef DEBUG
|
|
if (debug >= 2)
|
|
printzoneinfo(zonenum);
|
|
#endif
|
|
if (tt.tv_sec >= zp->z_time && zp->z_refresh > 0) {
|
|
/*
|
|
* Set default time for next action first,
|
|
* so that it can be changed later if necessary.
|
|
*/
|
|
zp->z_time = tt.tv_sec + zp->z_refresh;
|
|
|
|
switch (zp->z_type) {
|
|
|
|
case Z_CACHE:
|
|
doachkpt();
|
|
break;
|
|
|
|
case Z_SECONDARY:
|
|
if ((zp->z_state & Z_NEED_RELOAD) == 0) {
|
|
if (zp->z_state & Z_XFER_RUNNING)
|
|
abortxfer(zp);
|
|
else if (xfers_running < MAX_XFERS_RUNNING)
|
|
startxfer(zp);
|
|
else {
|
|
zp->z_state |= Z_NEED_XFER;
|
|
++xfers_deferred;
|
|
#ifdef DEBUG
|
|
if (debug > 1)
|
|
fprintf(ddt,
|
|
"xfer deferred for %s\n",
|
|
zp->z_origin);
|
|
#endif
|
|
}
|
|
}
|
|
break;
|
|
#ifdef ALLOW_UPDATES
|
|
case Z_PRIMARY:
|
|
/*
|
|
* Checkpoint the zone if it has changed
|
|
* since we last checkpointed
|
|
*/
|
|
if (zp->hasChanged)
|
|
zonedump(zp);
|
|
break;
|
|
#endif ALLOW_UPDATES
|
|
}
|
|
gettime(&tt);
|
|
}
|
|
}
|
|
sched_maint();
|
|
#ifdef DEBUG
|
|
if (debug)
|
|
fprintf(ddt,"exit ns_maint()\n");
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Find when the next refresh needs to be and set
|
|
* interrupt time accordingly.
|
|
*/
|
|
sched_maint()
|
|
{
|
|
register struct zoneinfo *zp;
|
|
struct itimerval ival;
|
|
time_t next_refresh = 0;
|
|
static time_t next_alarm;
|
|
|
|
for (zp = zones; zp < &zones[nzones]; zp++)
|
|
if (zp->z_time != 0 &&
|
|
(next_refresh == 0 || next_refresh > zp->z_time))
|
|
next_refresh = zp->z_time;
|
|
/*
|
|
* Schedule the next call to ns_maint.
|
|
* Don't visit any sooner than maint_interval.
|
|
*/
|
|
bzero((char *)&ival, sizeof (ival));
|
|
if (next_refresh != 0) {
|
|
if (next_refresh == next_alarm && alarm_pending) {
|
|
#ifdef DEBUG
|
|
if (debug)
|
|
fprintf(ddt,"sched_maint: no schedule change\n");
|
|
#endif
|
|
return;
|
|
}
|
|
ival.it_value.tv_sec = next_refresh - tt.tv_sec;
|
|
if (ival.it_value.tv_sec < maint_interval)
|
|
ival.it_value.tv_sec = maint_interval;
|
|
next_alarm = next_refresh;
|
|
alarm_pending = 1;
|
|
}
|
|
(void) setitimer(ITIMER_REAL, &ival, (struct itimerval *)NULL);
|
|
#ifdef DEBUG
|
|
if (debug)
|
|
fprintf(ddt,"sched_maint: Next interrupt in %d sec\n",
|
|
ival.it_value.tv_sec);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Start an asynchronous zone transfer for a zone.
|
|
* Depends on current time being in tt.
|
|
* The caller must call sched_maint after startxfer.
|
|
*/
|
|
startxfer(zp)
|
|
struct zoneinfo *zp;
|
|
{
|
|
static char *argv[NSMAX + 20], argv_ns[NSMAX][MAXDNAME];
|
|
int cnt, argc = 0, argc_ns = 0, pid, omask;
|
|
char debug_str[10];
|
|
char serial_str[10];
|
|
char port_str[10];
|
|
|
|
#ifdef DEBUG
|
|
if (debug)
|
|
fprintf(ddt,"startxfer() %s\n", zp->z_origin);
|
|
#endif
|
|
|
|
argv[argc++] = "named-xfer";
|
|
argv[argc++] = "-z";
|
|
argv[argc++] = zp->z_origin;
|
|
argv[argc++] = "-f";
|
|
argv[argc++] = zp->z_source;
|
|
argv[argc++] = "-s";
|
|
sprintf(serial_str, "%d", zp->z_serial);
|
|
argv[argc++] = serial_str;
|
|
if (zp->z_state & Z_SYSLOGGED)
|
|
argv[argc++] = "-q";
|
|
argv[argc++] = "-P";
|
|
sprintf(port_str, "%d", ns_port);
|
|
argv[argc++] = port_str;
|
|
#ifdef DEBUG
|
|
if (debug) {
|
|
argv[argc++] = "-d";
|
|
sprintf(debug_str, "%d", debug);
|
|
argv[argc++] = debug_str;
|
|
argv[argc++] = "-l";
|
|
argv[argc++] = "/usr/tmp/xfer.ddt";
|
|
if (debug > 5) {
|
|
argv[argc++] = "-t";
|
|
argv[argc++] = "/usr/tmp/xfer.trace";
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Copy the server ip addresses into argv, after converting
|
|
* to ascii and saving the static inet_ntoa result
|
|
*/
|
|
for (cnt = 0; cnt < zp->z_addrcnt; cnt++)
|
|
argv[argc++] = strcpy(argv_ns[argc_ns++],
|
|
inet_ntoa(zp->z_addr[cnt]));
|
|
|
|
argv[argc] = 0;
|
|
|
|
#ifdef DEBUG
|
|
#ifdef ECHOARGS
|
|
if (debug) {
|
|
int i;
|
|
for (i = 0; i < argc; i++)
|
|
fprintf(ddt, "Arg %d=%s\n", i, argv[i]);
|
|
}
|
|
#endif /* ECHOARGS */
|
|
#endif /* DEBUG */
|
|
|
|
#ifdef SYSV
|
|
#define vfork fork
|
|
#else
|
|
gettime(&tt);
|
|
omask = sigblock(sigmask(SIGCHLD));
|
|
#endif
|
|
if ((pid = vfork()) == -1) {
|
|
#ifdef DEBUG
|
|
if (debug)
|
|
fprintf(ddt, "xfer [v]fork: %d\n", errno);
|
|
#endif
|
|
syslog(LOG_ERR, "xfer [v]fork: %m");
|
|
#ifndef SYSV
|
|
(void) sigsetmask(omask);
|
|
#endif
|
|
zp->z_time = tt.tv_sec + 10;
|
|
return;
|
|
}
|
|
|
|
if (pid) {
|
|
#ifdef DEBUG
|
|
if (debug)
|
|
fprintf(ddt, "started xfer child %d\n", pid);
|
|
#endif
|
|
zp->z_state &= ~Z_NEED_XFER;
|
|
zp->z_state |= Z_XFER_RUNNING;
|
|
zp->z_xferpid = pid;
|
|
xfers_running++;
|
|
zp->z_time = tt.tv_sec + MAX_XFER_TIME;
|
|
#ifndef SYSV
|
|
(void) sigsetmask(omask);
|
|
#endif
|
|
} else {
|
|
execve(_PATH_XFER, argv, NULL);
|
|
syslog(LOG_ERR, "can't exec %s: %m", _PATH_XFER);
|
|
_exit(XFER_FAIL); /* avoid duplicate buffer flushes */
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printzoneinfo(zonenum)
|
|
int zonenum;
|
|
{
|
|
struct timeval tt;
|
|
struct zoneinfo *zp = &zones[zonenum];
|
|
char *ZoneType;
|
|
|
|
if (!debug)
|
|
return; /* Else fprintf to ddt will bomb */
|
|
fprintf(ddt, "printzoneinfo(%d):\n", zonenum);
|
|
|
|
gettime(&tt);
|
|
switch (zp->z_type) {
|
|
case Z_PRIMARY: ZoneType = "Primary"; break;
|
|
case Z_SECONDARY: ZoneType = "Secondary"; break;
|
|
case Z_CACHE: ZoneType = "Cache"; break;
|
|
default: ZoneType = "Unknown";
|
|
}
|
|
if (zp->z_origin[0] == '\0')
|
|
fprintf(ddt,"origin ='.'");
|
|
else
|
|
fprintf(ddt,"origin ='%s'", zp->z_origin);
|
|
fprintf(ddt,", type = %s", ZoneType);
|
|
fprintf(ddt,", source = %s\n", zp->z_source);
|
|
fprintf(ddt,"z_refresh = %ld", zp->z_refresh);
|
|
fprintf(ddt,", retry = %ld", zp->z_retry);
|
|
fprintf(ddt,", expire = %ld", zp->z_expire);
|
|
fprintf(ddt,", minimum = %ld", zp->z_minimum);
|
|
fprintf(ddt,", serial = %ld\n", zp->z_serial);
|
|
fprintf(ddt,"z_time = %d", zp->z_time);
|
|
if (zp->z_time) {
|
|
fprintf(ddt,", now time : %d sec", tt.tv_sec);
|
|
fprintf(ddt,", time left: %d sec", zp->z_time - tt.tv_sec);
|
|
}
|
|
fprintf(ddt,"; state %x\n", zp->z_state);
|
|
}
|
|
#endif DEBUG
|
|
|
|
/*
|
|
* remove_zone (htp, zone) --
|
|
* Delete all RR's in the zone "zone" under specified hash table.
|
|
*/
|
|
remove_zone(htp, zone)
|
|
register struct hashbuf *htp;
|
|
register int zone;
|
|
{
|
|
register struct databuf *dp, *pdp;
|
|
register struct namebuf *np;
|
|
struct namebuf **npp, **nppend;
|
|
|
|
nppend = htp->h_tab + htp->h_size;
|
|
for (npp = htp->h_tab; npp < nppend; npp++)
|
|
for (np = *npp; np != NULL; np = np->n_next) {
|
|
for (pdp = NULL, dp = np->n_data; dp != NULL; ) {
|
|
if (dp->d_zone == zone)
|
|
dp = rm_datum(dp, np, pdp);
|
|
else {
|
|
pdp = dp;
|
|
dp = dp->d_next;
|
|
}
|
|
}
|
|
/* Call recursively to remove subdomains. */
|
|
if (np->n_hash)
|
|
remove_zone(np->n_hash, zone);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Abort an xfer that has taken too long.
|
|
*/
|
|
abortxfer(zp)
|
|
register struct zoneinfo *zp;
|
|
{
|
|
|
|
kill(zp->z_xferpid, SIGKILL); /* don't trust it at all */
|
|
#ifdef DEBUG
|
|
if (debug)
|
|
fprintf(ddt, "Killed child %d (zone %s) due to timeout\n",
|
|
zp->z_xferpid, zp->z_origin);
|
|
#endif /* DEBUG */
|
|
zp->z_time = tt.tv_sec + zp->z_retry;
|
|
}
|
|
|
|
#ifdef SYSV
|
|
union wait {
|
|
unsigned short w_termsig:7; /* termination signal */
|
|
unsigned short w_coredump:1; /* core dump indicator */
|
|
unsigned short w_retcode:8; /* exit code if w_termsig==0 */
|
|
};
|
|
#endif
|
|
|
|
/*
|
|
* SIGCHLD signal handler: process exit of xfer's.
|
|
* (Note: also called when outgoing transfer completes.)
|
|
*/
|
|
SIG_FN
|
|
endxfer()
|
|
{
|
|
register struct zoneinfo *zp;
|
|
int pid, xfers = 0;
|
|
union wait status;
|
|
|
|
gettime(&tt);
|
|
#if defined(SYSV)
|
|
{ int stat;
|
|
pid = wait(&stat);
|
|
status.w_termsig = stat & 0x7f;
|
|
status.w_retcode = stat >> 8;
|
|
}
|
|
#else /* SYSV */
|
|
while ((pid =
|
|
wait3((int *)&status, WNOHANG, (struct rusage *)NULL)) > 0) {
|
|
#endif /* SYSV */
|
|
for (zp = zones; zp < &zones[nzones]; zp++)
|
|
if (zp->z_xferpid == pid) {
|
|
xfers++;
|
|
xfers_running--;
|
|
zp->z_xferpid = 0;
|
|
zp->z_state &= ~Z_XFER_RUNNING;
|
|
#ifdef DEBUG
|
|
if (debug)
|
|
fprintf(ddt,
|
|
"\nendxfer: child %d zone %s returned status=%d termsig=%d\n",
|
|
pid, zp->z_origin, status.w_retcode,
|
|
status.w_termsig);
|
|
#endif
|
|
if (status.w_termsig != 0) {
|
|
if (status.w_termsig != SIGKILL) {
|
|
syslog(LOG_ERR,
|
|
"named-xfer exited with signal %d\n",
|
|
status.w_termsig);
|
|
#ifdef DEBUG
|
|
if (debug)
|
|
fprintf(ddt,
|
|
"\tchild termination with signal %d\n",
|
|
status.w_termsig);
|
|
#endif
|
|
}
|
|
zp->z_time = tt.tv_sec + zp->z_retry;
|
|
} else switch (status.w_retcode) {
|
|
case XFER_UPTODATE:
|
|
zp->z_state &= ~Z_SYSLOGGED;
|
|
zp->z_lastupdate = tt.tv_sec;
|
|
zp->z_time = tt.tv_sec + zp->z_refresh;
|
|
/*
|
|
* Restore z_auth in case expired,
|
|
* but only if there were no errors
|
|
* in the zone file.
|
|
*/
|
|
if ((zp->z_state & Z_DB_BAD) == 0)
|
|
zp->z_auth = 1;
|
|
if (zp->z_source) {
|
|
#if defined(SYSV)
|
|
struct utimbuf t;
|
|
|
|
t.actime = tt.tv_sec;
|
|
t.modtime = tt.tv_sec;
|
|
(void) utime(zp->z_source, &t);
|
|
#else
|
|
struct timeval t[2];
|
|
|
|
t[0] = tt;
|
|
t[1] = tt;
|
|
(void) utimes(zp->z_source, t);
|
|
#endif /* SYSV */
|
|
}
|
|
break;
|
|
|
|
case XFER_SUCCESS:
|
|
zp->z_state |= Z_NEED_RELOAD;
|
|
zp->z_state &= ~Z_SYSLOGGED;
|
|
needzoneload++;
|
|
break;
|
|
|
|
case XFER_TIMEOUT:
|
|
#ifdef DEBUG
|
|
if (debug) fprintf(ddt,
|
|
"zoneref: Masters for secondary zone %s unreachable\n",
|
|
zp->z_origin);
|
|
#endif
|
|
if ((zp->z_state & Z_SYSLOGGED) == 0) {
|
|
zp->z_state |= Z_SYSLOGGED;
|
|
syslog(LOG_WARNING,
|
|
"zoneref: Masters for secondary zone %s unreachable",
|
|
zp->z_origin);
|
|
}
|
|
zp->z_time = tt.tv_sec + zp->z_retry;
|
|
break;
|
|
|
|
default:
|
|
if ((zp->z_state & Z_SYSLOGGED) == 0) {
|
|
zp->z_state |= Z_SYSLOGGED;
|
|
syslog(LOG_ERR,
|
|
"named-xfer exit code %d",
|
|
status.w_retcode);
|
|
}
|
|
/* FALLTHROUGH */
|
|
case XFER_FAIL:
|
|
zp->z_state |= Z_SYSLOGGED;
|
|
zp->z_time = tt.tv_sec + zp->z_retry;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
#ifndef SYSV
|
|
}
|
|
#endif /* SYSV */
|
|
if (xfers) {
|
|
for (zp = zones;
|
|
xfers_deferred != 0 && xfers_running < MAX_XFERS_RUNNING &&
|
|
zp < &zones[nzones]; zp++)
|
|
if (zp->z_state & Z_NEED_XFER) {
|
|
xfers_deferred--;
|
|
startxfer(zp);
|
|
}
|
|
sched_maint();
|
|
}
|
|
#if defined(SYSV)
|
|
(void)signal(SIGCLD, endxfer);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Reload zones whose transfers have completed.
|
|
*/
|
|
loadxfer()
|
|
{
|
|
register struct zoneinfo *zp;
|
|
|
|
gettime(&tt);
|
|
for (zp = zones; zp < &zones[nzones]; zp++)
|
|
if (zp->z_state & Z_NEED_RELOAD) {
|
|
#ifdef DEBUG
|
|
if (debug)
|
|
fprintf(ddt, "loadxfer() '%s'\n",
|
|
zp->z_origin[0] ? zp->z_origin : ".");
|
|
#endif
|
|
zp->z_state &= ~Z_NEED_RELOAD;
|
|
zp->z_auth = 0;
|
|
remove_zone(hashtab, zp - zones);
|
|
if (db_load(zp->z_source, zp->z_origin, zp, 0) == 0)
|
|
zp->z_auth = 1;
|
|
if (zp->z_state & Z_TMP_FILE)
|
|
(void) unlink(zp->z_source);
|
|
}
|
|
sched_maint();
|
|
}
|