#if !defined(lint) && !defined(SABER)
static char sccsid[] = "@(#)ns_maint.c	4.39 (Berkeley) 3/2/91";
static char rcsid[] = "$Id: ns_maint.c,v 8.18 1996/09/22 00:13:10 vixie Exp $";
#endif /* not lint */

/*
 * ++Copyright++ 1986, 1988
 * -
 * Copyright (c) 1986, 1988
 *    The 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.
 * -
 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
 * 
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies, and that
 * the name of Digital Equipment Corporation not be used in advertising or
 * publicity pertaining to distribution of the document or software without
 * specific, written prior permission.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 * -
 * --Copyright--
 */

#ifndef WINNT
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#endif
#include <arpa/inet.h>
#include <arpa/nameser.h>
#ifndef WINNT
#include <sys/wait.h>
#endif
#include <stdio.h>
#ifndef WINNT
#include <syslog.h>
#endif
#include <signal.h>
#include <errno.h>
#include <sys/stat.h>

#include "named.h"

#ifdef USE_UTIME
#ifndef WINNT
# include <utime.h>
#else
# include <sys/utime.h>
#endif
#endif

static int		xfers_running,	/* # of xfers running */
			xfers_deferred,	/* # of needed xfers not run yet */
			qserials_running,
			alarm_pending,	/* flag */
			nxfers __P((struct zoneinfo *, int));

static void		startxfer __P((struct zoneinfo *)),
			abortxfer __P((struct zoneinfo *)),
			addxfer __P((struct zoneinfo *)),
			tryxfer __P((void));

#define	qserial_qfull()	(qserials_running == MAXQSERIAL)

#ifdef CLEANCACHE
static time_t cache_time = 0;
#endif
#ifdef XSTATS
static time_t stats_time = 0;
#endif

#ifdef WINNT
/* lgk since timer resolution on nt (at least my box) only goes to 1000 seconds
 * our method using mmedia times is no good therefore use a thread that
 * we create that sleeps the timeout period and then when done calls maint_alarm
 * and exits...
 * lgk dec. 95 change again to use a waitforsingleobject on an event for the specified
 * timeout instead of sleep so it can be signaled with a reschedule request..
 */
    
DWORD pending_delay = 0;
volatile HANDLE rescheduleevent = 0x00;
DWORD threadid;
HANDLE threadhandle = 0x00;
HANDLE threadhandle2 = 0x00;
static void named_xfer __P((HANDLE *));
static void CALLBACK maint_alarm __P((UINT,UINT,DWORD,DWORD,DWORD));
extern CRITICAL_SECTION protected_zp;
extern CRITICAL_SECTION maint_section;
volatile BOOLEAN maint_thread_running = FALSE;

DWORD thread_timer_fx(DWORD timeout)
  {

    DWORD rvalue;

    /* now wait on the handle */
    rvalue = WaitForSingleObject(rescheduleevent,timeout);

    /* now check reason we woke up */
    switch (rvalue)
     {

      case WAIT_FAILED:
        dprintf(1, (ddt, "Wait for thread timer event object failed!\n")); 
        syslog(LOG_ERR, "Wait for thread timer event object failed!");
        EnterCriticalSection(&maint_section);
        maint_thread_running = FALSE;
        LeaveCriticalSection(&maint_section);
        ExitThread(0);
        break;
      
      case WAIT_OBJECT_0:
        dprintf(1, (ddt, "In Maintenance thread... current thread is exiting for reschedule request!\n"));
        /* don't set thread running to false here as by the time this gets hit and the
           thread terminates we may overwrite someone else setting it to true */
        ExitThread(0);
        break;

      case WAIT_TIMEOUT:
        dprintf(1, (ddt, "In Maintenance thread... timer has expired!\n"));
        break;
      
      default:
        dprintf(1, (ddt, "Unknown return type from WaitForSingleObject in thread_timer_fx (%d)!\n",rvalue));
        syslog(LOG_ERR, "Unknown return type from WaitForSingleObject in thread_timer_fx (%d)!\n",rvalue);
        EnterCriticalSection(&maint_section);
        maint_thread_running = FALSE;
        LeaveCriticalSection(&maint_section);
        ExitThread(0);
        break;
      
     }

    EnterCriticalSection(&maint_section);
    maint_thread_running = FALSE;
    LeaveCriticalSection(&maint_section);
    maint_alarm(0,0,0,0,0);
    return 0;
}


#endif /* WINNT */

/*
 * Invoked at regular intervals by signal interrupt; refresh all secondary
 * zones from primary name server and remove old cache entries.
 */
void
ns_maint()
{
	register struct zoneinfo *zp;
	int zonenum;

	gettime(&tt);
	dprintf(1, (ddt, "\nns_maint(); now %s", ctimel(tt.tv_sec)));
	alarm_pending = 0;
	for (zp = zones, zonenum = 0; zp < &zones[nzones]; zp++, zonenum++) {
#ifdef DEBUG
		if (debug >= 2)
			printzoneinfo(zonenum);
#endif
                dprintf(1, (ddt, "debug tt.tv_sec = %d , zp->z_time = %d, zp->z_refresh = %d \n",
                                 tt.tv_sec,zp->z_time, zp->z_refresh));
                dprintf(1, (ddt, "want tv_sec > = z_time and z_refresh > 0 \n"));
		if (tt.tv_sec >= zp->z_time && zp->z_refresh > 0) {
			switch (zp->z_type) {

			case Z_CACHE:
				doachkpt();
				ns_refreshtime(zp, tt.tv_sec);
				break;

			case Z_SECONDARY:
#ifdef STUBS
			case Z_STUB:
#endif
				if (zp->z_serial != 0 &&
				    ((zp->z_lastupdate + zp->z_expire) <
				     tt.tv_sec)
				    ) {
					zp->z_serial = 0;
				}
				if (zp->z_flags &
				    (Z_NEED_RELOAD|Z_NEED_XFER|Z_QSERIAL)) {
					ns_refreshtime(zp, tt.tv_sec);
					break;
				}
				if (zp->z_flags & Z_XFER_RUNNING) {
				        dprintf(1, (ddt, "calling abort\n"));
					abortxfer(zp);
					break;
				}
				qserial_query(zp);
				break;
			}
			gettime(&tt);
		}
	}
#ifdef CLEANCACHE
	if ((cache_time + cache_interval) <= tt.tv_sec) {
		if (cache_time && (!NoRecurse || !NoFetchGlue))
			remove_zone(hashtab, 0, 0);
		cache_time = tt.tv_sec;
	}
#endif
#ifdef XSTATS
	if (stats_time + stats_interval <= tt.tv_sec) {
		if (stats_time)
			ns_logstats();
		stats_time = tt.tv_sec;
	}
#endif
	if (!needmaint)
		sched_maint();
	dprintf(1, (ddt, "exit ns_maint()\n"));
}

/*
 * Find when the next refresh needs to be and set
 * interrupt time accordingly.
 */
void
sched_maint()
{
	register struct zoneinfo *zp;
#ifndef WINNT
	struct itimerval ival;
#else
	DWORD wDelay;
#endif
#ifdef	CLEANCACHE
	time_t next_refresh = cache_time + cache_interval;
#else
	time_t next_refresh = 0;
#endif
	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.
	 */
#ifndef WINNT
	bzero((char *)&ival, sizeof (ival));
#endif
	if (next_refresh != 0) {
		if (next_refresh == next_alarm && alarm_pending) {
			dprintf(1, (ddt, "sched_maint: no schedule change\n"));
			return;
		}
		/*
		 *  tv_sec can be an unsigned long, so we can't let
		 *  it go negative.
		 */
		if (next_refresh < tt.tv_sec)
			next_refresh = tt.tv_sec;
#ifndef WINNT
		ival.it_value.tv_sec = next_refresh - tt.tv_sec;
		if ((long) ival.it_value.tv_sec < maint_interval)
			ival.it_value.tv_sec = maint_interval;
#else
		wDelay = (next_refresh - tt.tv_sec) * 1000;
		if ((long) wDelay < (maint_interval * 1000))
			wDelay = maint_interval * 1000;
#endif
		next_alarm = next_refresh;
		alarm_pending = 1;
	}
#ifndef WINNT
	(void) setitimer(ITIMER_REAL, &ival, (struct itimerval *)NULL);
	dprintf(1, (ddt, "sched_maint: Next interrupt in %lu sec\n",
		    (u_long)ival.it_value.tv_sec));
#else
      /* lgk under my h/w for nt maximum timer values are 1000 seconds... the above values are not for resolution
       * as the original author supposed but for the minimum and maximum timer values that mm timers supports..
       * and since they don't supprt 1 hour... the create of the timer object was failing... therefore the only
       * way I figured out to do this was create a thread that sleeps for the timeout and then waits up and calls
       * maint alarm... seems to work ok now... 
       * lgk dec. 95 change again to use a waitforsingleobject on an event for the specified
       * timeout instead of sleep so it can be signaled with a reschedule request..
       */

       /* lgk 12/95 there is a problem here were it tries to reschedule
          when a timer thread is already running... under nt now see comment above */

      if (rescheduleevent == 0x00)
        {
          if ((rescheduleevent = CreateEvent(NULL,TRUE,FALSE,NULL)) == 0x00)
            {
             syslog(LOG_ERR,"Error: Couldn't create the maint. reschedule event object!");
             dprintf(1, (ddt, "Error: Couldn't create the maint. reschedule event object!"));
             return;
            }
        }

      /* first check if already one running */
      EnterCriticalSection(&maint_section);
      
      if (maint_thread_running)
        { /* ok now check if we have a smaller time */
          dprintf(1,(ddt,"sched_maint: Rescheduling maint. from %d sec to %d sec.\n",pending_delay/1000,wDelay/1000));
          /* signal other waiting thread to exit */
          PulseEvent(rescheduleevent);
        }

       maint_thread_running = TRUE;

       if (threadhandle != 0x00)
         CloseHandle(threadhandle );
       /* reset the event */
       ResetEvent(rescheduleevent);   

       threadhandle = NULL;
       threadhandle = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)thread_timer_fx,(LPVOID)wDelay,0,&threadid);

       if (threadhandle == NULL)
	     {
           syslog(LOG_ERR, "CreateThread() for maintenance: %m");
	     }

       
       pending_delay = wDelay;
 	   dprintf(1, (ddt, "sched_maint: Next interrupt in %d sec\n",wDelay/1000));
       LeaveCriticalSection(&maint_section);

#endif
}

/*
 * Mark a zone "up to date" after named-xfer tells us this or we
 * discover it through the qserial_*() logic.
 */
static void
markUpToDate(zp)
	struct zoneinfo *zp;
{
	struct stat f_time;

	zp->z_flags &= ~Z_SYSLOGGED;
	zp->z_lastupdate = tt.tv_sec;
	ns_refreshtime(zp, tt.tv_sec);
	/*
	 * Restore Z_AUTH in case expired,
	 * but only if there were no errors
	 * in the zone file.
	 */
	if ((zp->z_flags & Z_DB_BAD) == 0)
		zp->z_flags |= Z_AUTH;
	if (zp->z_source) {
#if defined(USE_UTIME)
		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 /* USE_UTIME */
	}
	/* we use "stat" to set zp->z_ftime instead of just
	   setting it to tt.tv_sec in order to avoid any
	   possible rounding problems in utimes(). */
	if (stat(zp->z_source, &f_time) != -1)
		zp->z_ftime = f_time.st_mtime;
	/* XXX log if stat fails? */
}

/*
 * Query for the serial number of a zone, so that
 * we can check to see if we need to transfer it.
 */
void
qserial_query(zp)
	struct zoneinfo *zp;
{
	struct qinfo *qp;

	dprintf(1, (ddt, "qserial_query(%s)\n", zp->z_origin));

	if (qserial_qfull())
		return;

	qp = sysquery(zp->z_origin, zp->z_class, T_SOA,
		      zp->z_addr, zp->z_addrcnt, QUERY);
	if (!qp) {
		syslog(LOG_WARNING, "qserial_query(%s): sysquery FAILED",
		       zp->z_origin);
		return;		/* XXX - this is bad, we should do something */
	}
	qp->q_flags |= Q_ZSERIAL;
	qp->q_zquery = zp;
	zp->z_flags |= Z_QSERIAL;
	ns_refreshtime(zp, tt.tv_sec);
	qserials_running++;
	dprintf(1, (ddt, "qserial_query(%s) QUEUED\n", zp->z_origin));
}

void
qserial_answer(qp, serial)
	struct qinfo *qp;
	u_int32_t serial;
{
	struct zoneinfo *zp = qp->q_zquery;
	int was_qfull = qserial_qfull();

	dprintf(1, (ddt, "qserial_answer(%s, %lu)\n",
		    zp->z_origin, (u_long)serial));
	zp->z_flags &= ~Z_QSERIAL;
	qp->q_flags &= ~Q_ZSERIAL;	/* keeps us from being called twice */
	qserials_running--;
	if (serial == 0) {
		/* an error occurred, or the query timed out.
		 */
#ifdef GETSER_LOGGING
		syslog(GETSER_LOGGING, "Err/TO getting serial# for \"%s\"",
		       zp->z_origin);
#endif /* GETSER_LOGGING */
		addxfer(zp);
	} else if (SEQ_GT(serial, zp->z_serial) || !zp->z_serial) {
		dprintf(1, (ddt, "qserial_answer: zone is out of date\n"));
		zp->z_xaddr = from_addr.sin_addr; /* don't use qp->q_from */
		addxfer(zp);
	} else if (SEQ_GT(zp->z_serial, serial)) {
		if (!haveComplained((char*)zp, "went backward")) {
			syslog(LOG_NOTICE,
   "Zone \"%s\" (class %d) SOA serial# (%lu) rcvd from [%s] is < ours (%lu)\n",
			       zp->z_origin, zp->z_class, (u_long)serial,
			       inet_ntoa(from_addr.sin_addr),
			       (u_long)zp->z_serial);
		}
	} else {
		dprintf(1, (ddt, "qserial_answer: zone serial is still OK\n"));
		markUpToDate(zp);
	}
	if (was_qfull)
		needmaint = 1;
}

/*
 * Hold and release SIGCHLD
 */
#ifdef POSIX_SIGNALS
static sigset_t sset;
#else
#ifndef SYSV
static int omask;
#endif
#endif /* POSIX_SIGNALS */

#ifndef WINNT
void holdsigchld()
{
#ifdef POSIX_SIGNALS
	sigemptyset(&sset);
	sigaddset(&sset,SIGCHLD);
	sigprocmask(SIG_BLOCK,&sset,NULL);
#else  /* POSIX_SIGNALS */
#ifndef SYSV
	omask = sigblock(sigmask(SIGCHLD));
#else  /* SYSV */
	/* XXX - out of luck? */
#endif /* SYSV */
#endif /* POSIX_SIGNALS */
}
	
void releasesigchld()
{
#ifdef POSIX_SIGNALS
	sigprocmask(SIG_UNBLOCK,&sset,NULL);
#else
#ifndef SYSV
	(void) sigsetmask(omask);
#endif
#endif /* POSIX_SIGNALS */
}

#endif
	/* State of all running zone transfers */
static struct {
	pid_t 	xfer_pid;
	int	xfer_state; /* see below */
#ifdef sequent
	union wait xfer_status;
#else
	int	xfer_status;
#endif
} xferstatus[MAX_XFERS_RUNNING];
#define XFER_IDLE	0
#define XFER_RUNNING	1
#define XFER_DONE	2

/*
 * Start an asynchronous zone transfer for a zone.
 * Depends on current time being in tt.
 * The caller must call sched_maint after startxfer.
 */
static void
startxfer(zp)
	struct zoneinfo *zp;
{
	char *argv[NSMAX + 20], argv_ns[NSMAX][MAXDNAME];
	int argc = 0, argc_ns = 0, i;
#ifdef WINNT
	extern char *pathxferddt, *pathxfertrace;
#endif
	unsigned int cnt;
	char debug_str[10];
	char serial_str[10];
	char port_str[10];
#ifdef GEN_AXFR
	char class_str[10];
#endif
#ifdef WINNT
	char *pos;
	DWORD xfer_thread_id;
	STARTUPINFO sui;
	static char CommandLine[NSMAX*(MAXDNAME+1)+20*10];
	PROCESS_INFORMATION *proc_info_copy;
    

#else
	int pid;
#endif

	dprintf(1, (ddt, "startxfer() %s\n", zp->z_origin));

#ifndef WINNT
	argv[argc++] = "_PATH_XFER";
#else
#ifndef WIN95
	argv[argc++] = "xfer";
#else
	argv[argc++] = "xfer95";
#endif
#endif
	argv[argc++] = "-z";
#ifndef WINNT
  	argv[argc++] = zp->z_origin;
#else
 	if (strlen(zp->z_origin) == 0)
 		argv[argc++] = ".";
 	else
 		argv[argc++] = zp->z_origin;
 #endif
	argv[argc++] = "-f";
	argv[argc++] = zp->z_source;
	argv[argc++] = "-s";
	sprintf(serial_str, "%lu", (u_long)zp->z_serial);
	argv[argc++] = serial_str;
#ifdef GEN_AXFR
	argv[argc++] = "-C";
	sprintf(class_str, "%d", zp->z_class);
	argv[argc++] = class_str;
#endif
 	if (zp->z_flags & Z_SYSLOGGED)
		argv[argc++] = "-q";
	argv[argc++] = "-P";
	sprintf(port_str, "%d", ns_port);
	argv[argc++] = port_str;
#ifdef STUBS
	if (zp->z_type == Z_STUB)
		argv[argc++] = "-S";
#endif
#ifdef DEBUG
	if (debug) {
		argv[argc++] = "-d";
		sprintf(debug_str, "%d", debug);
		argv[argc++] = debug_str;
		argv[argc++] = "-l";
#ifndef WINNT
		argv[argc++] = _PATH_XFERDDT;
#else
		argv[argc++] = pathxferddt;
#endif
		if (debug > 5) {
			argv[argc++] = "-t";
#ifndef WINNT
			argv[argc++] = _PATH_XFERTRACE;
#else
			argv[argc++] = pathxfertrace;
#endif
		}
	}
#endif
	
	if (zp->z_xaddr.s_addr != 0) {
		/* Address was specified by the qserial logic, use it. */
		argv[argc++] = strcpy(argv_ns[argc_ns++],
				      inet_ntoa(zp->z_xaddr));
	} else {
		/*
		 * 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++) {
			struct in_addr a;

			a = zp->z_addr[cnt];
			if (aIsUs(a) &&
			    !haveComplained(zp->z_origin, (char*)startxfer)) {
				syslog(LOG_NOTICE,
				   "attempted to fetch zone %s from self (%s)",
				       zp->z_origin, inet_ntoa(a));
				continue;
			}
			argv[argc++] = strcpy(argv_ns[argc_ns++],
					      inet_ntoa(a));
		}
	}

	argv[argc] = 0;

#ifdef WINNT
	/* need the arguments in the form of a string not in argv format */
	pos = CommandLine;
	for (i = 0; i < argc; i++) {
		sprintf(pos, argv[i]);
		pos += strlen(pos);
		*pos++ = ' ';
	}
	*pos = '\0';
#endif 

#ifdef DEBUG
	if (debug) {
		for (i = 0; i < argc; i++) 
			fprintf(ddt, " %s", argv[i]);
		fprintf(ddt, "\n");
	}
#endif /* DEBUG */

	gettime(&tt);
#ifndef WINNT
   	holdsigchld();
#else
    /* make sure we don't have two accessing the same structures */
    /* lgk use critical section here for this and in endxfer where this is read to make sure that not more
       than one is trying to start a xfer process at a time */
    EnterCriticalSection(&protected_zp);
    proc_info_copy = (PROCESS_INFORMATION *)malloc(sizeof(PROCESS_INFORMATION));
 
#endif
  	for (i = 0; i < MAX_XFERS_RUNNING; i++) {
  		if (xferstatus[i].xfer_pid == 0) {
  			xferstatus[i].xfer_state = XFER_RUNNING;
  			break;
  		}
  	}
#ifndef WINNT
	if ((pid = vfork()) == -1) {
		syslog(LOG_ERR, "xfer vfork: %m");
  		releasesigchld();
		zp->z_time = tt.tv_sec + 10;
		return;
	}

	if (pid == 0) {
		/* Child. */
		execv(_PATH_XFER, argv);
		syslog(LOG_ERR, "can't exec %s: %m", _PATH_XFER);
		_exit(XFER_FAIL);       /* Avoid duplicate buffer flushes. */
	}
#else /* WINNT */
	bzero((char *)&sui, sizeof(STARTUPINFO));
	sui.cb = sizeof(STARTUPINFO);
	if (!CreateProcess(NULL, CommandLine, NULL, NULL, FALSE, DETACHED_PROCESS, NULL,
					   NULL, &sui, (LPPROCESS_INFORMATION)proc_info_copy))
      {
	     syslog(LOG_ERR, "CreateProcess() for xfer: %m");
             LeaveCriticalSection(&protected_zp);
	     return;
	  }

	threadhandle2 = NULL;
	threadhandle2 = CreateThread(
	         NULL,                                /* no security attributes  */
	         0,                                   /* use default stack size  */
	         (LPTHREAD_START_ROUTINE) named_xfer, /* thread function         */
	         (LPVOID) proc_info_copy,             /* argument to thread function   */
	         0,                                   /* use default creation flags    */
	         (LPDWORD) &xfer_thread_id);

    if (threadhandle2 == NULL)
	  {
        syslog(LOG_ERR, "CreateThread() for xfer status: %m");
        LeaveCriticalSection(&protected_zp);
	return;
	  }
	CloseHandle(threadhandle2); /* no handle to this needed */
	
#endif /* WINNT */
	/* parent */
#ifdef WINNT
	dprintf(1, (ddt, "started xfer child %d\n", proc_info_copy->hProcess));
    zp->z_xferpid = proc_info_copy->hProcess;
  	xferstatus[i].xfer_pid = (long)proc_info_copy->hProcess;
#else
	zp->z_xferpid = pid;
  	xferstatus[i].xfer_pid = pid;  /* XXX - small race condition here if we
  					* can't hold signals */
	dprintf(1, (ddt, "started xfer child %d\n", pid));
#endif
	zp->z_flags &= ~Z_NEED_XFER;
	zp->z_flags |= Z_XFER_RUNNING;

	xfers_running++;
	zp->z_time = tt.tv_sec + MAX_XFER_TIME;

#ifdef WINNT
    LeaveCriticalSection(&protected_zp);
#else
  	releasesigchld();
#endif
    
}

const char *
zoneTypeString(zp)
	const struct zoneinfo *zp;
{
	static char ret[sizeof "(4294967296?)"];	/* 2^32 */

	switch (zp->z_type) {
	case Z_PRIMARY:		return ("primary");
	case Z_SECONDARY:	return ("secondary");
#ifdef STUBS
	case Z_STUB:		return ("stub");
#endif
	case Z_CACHE:		return ("cache");
	default:
		sprintf(ret, "(%lu?)", (u_long)zp->z_type);
		return (ret);
	}
}

#ifdef DEBUG
void
printzoneinfo(zonenum)
	int zonenum;
{
	struct timeval  tt;
	struct zoneinfo *zp = &zones[zonenum];

	if (!debug)
		return;

	if (!zp->z_origin)
		return;

	fprintf(ddt, "printzoneinfo(%d):\n", zonenum);

	gettime(&tt);
	fprintf(ddt, "origin ='%s'", zp->z_origin[0] ? zp->z_origin : ".");
#ifdef GEN_AXFR
	fprintf(ddt, ", class = %d", zp->z_class);
#endif
 	fprintf(ddt, ", type = %s", zoneTypeString(zp));
	if (zp->z_source)
		fprintf(ddt,", source = %s\n", zp->z_source);
	fprintf(ddt, "z_refresh = %lu", (u_long)zp->z_refresh);
	fprintf(ddt, ", retry = %lu", (u_long)zp->z_retry);
	fprintf(ddt, ", expire = %lu", (u_long)zp->z_expire);
	fprintf(ddt, ", minimum = %lu", (u_long)zp->z_minimum);
	fprintf(ddt, ", serial = %lu\n", (u_long)zp->z_serial);
	fprintf(ddt, "z_time = %lu", (u_long)zp->z_time);
	if (zp->z_time) {
		fprintf(ddt, ", now time : %lu sec", (u_long)tt.tv_sec);
		fprintf(ddt, ", time left: %lu sec",
			(u_long)(zp->z_time - tt.tv_sec));
	}
	fprintf(ddt, "; flags %lx\n", (u_long)zp->z_flags);
}
#endif /* DEBUG */

/*
 * remove_zone (htp, zone) --
 *     Delete all RR's in the zone "zone" under specified hash table.
 */
void
#ifdef CLEANCACHE
remove_zone(htp, zone, all)
#else
remove_zone(htp, zone)
#endif
	register struct hashbuf *htp;
	register int zone;
#ifdef	CLEANCACHE
	register int all;
#endif
{
	register struct databuf *dp, *pdp;
	register struct namebuf *np, *pnp, *npn;
	struct namebuf **npp, **nppend;

	nppend = htp->h_tab + htp->h_size;
	for (npp = htp->h_tab; npp < nppend; npp++) {
		for (pnp = NULL, np = *npp; np != NULL; np = npn) {
			for (pdp = NULL, dp = np->n_data; dp != NULL; NULL) {
				if (dp->d_zone == zone
#ifdef CLEANCACHE
				    && (all || stale(dp))
#endif
				    ) {
					dp = rm_datum(dp, np, pdp);
				} else {
					pdp = dp;
					dp = dp->d_next;
				}
			} /*for(pdp)*/

			if (np->n_hash) {
				/* call recursively to remove subdomains. */
				remove_zone(np->n_hash, zone
#ifdef CLEANCACHE
					    , all
#endif
					    );

				/* if now empty, free it */
				if (np->n_hash->h_cnt == 0) {
					free((char*)np->n_hash);
					np->n_hash = NULL;
				}
			}

			if ((np->n_hash == NULL) && (np->n_data == NULL)) {
				npn = rm_name(np, npp, pnp);
				htp->h_cnt--;
			} else {
				npn = np->n_next;
				pnp = np;
			}
		} /*for(pnp)*/
	} /*for(npp)*/
}

#ifdef PURGE_ZONE
static void purge_z_2 __P((struct hashbuf *, int));
static bottom_of_zone __P((struct databuf *, int));

void
purge_zone(dname, htp, class)
	const char *dname;
	register struct hashbuf *htp;
	int class;
{
	const char *fname;
	struct databuf *dp, *pdp;
	struct namebuf *np;
	struct hashbuf *phtp = htp;

	dprintf(1, (ddt, "purge_zone(%s,%d)\n", dname, class));
	if ((np = nlookup(dname, &phtp, &fname, 0)) && dname == fname &&
	    !ns_wildcard(NAME(*np))) {
		for (pdp = NULL, dp = np->n_data; dp != NULL; ) {
			if (dp->d_class == class)
					dp = rm_datum(dp, np, pdp);
			else {
				pdp = dp;
				dp = dp->d_next;
			}
		}

		if (np->n_hash) {
			purge_z_2(np->n_hash, class);
			if (np->n_hash->h_cnt == 0) {
				free((char*)np->n_hash);
				np->n_hash = NULL;
			}
		}
		
		/* remove entry from cache, if required */
		if ((np->n_hash == NULL) && (np->n_data == NULL)) {
			struct namebuf **npp, **nppend;
			struct namebuf *npn, *pnp, *nnp;

			dprintf(3,(ddt, "purge_zone: cleaning cache\n"));

			/* walk parent hashtable looking for ourself */
			if (np->n_parent)
				phtp = np->n_parent->n_hash;
			else 
				phtp = htp; /* top / root zone */

			if (phtp) {
				nppend = phtp->h_tab + phtp->h_size;
				for (npp = phtp->h_tab; npp < nppend; npp++) {
				    for (pnp = NULL, nnp = *npp;
					 nnp != NULL;
					 nnp = npn) {
					    if (nnp == np) {
						    dprintf(3, (ddt,
					       "purge_zone: found our selves\n"
								));
						    npn = rm_name(nnp,npp,pnp);
						    phtp->h_cnt--;
					    } else {
						    npn = nnp->n_next;
						    pnp = nnp;
					    }
				    }
				}
			}
		}
	}
}

static void
purge_z_2(htp, class)
	register struct hashbuf *htp;
	register int class;
{
	register struct databuf *dp, *pdp;
	register struct namebuf *np, *pnp, *npn;
	struct namebuf **npp, **nppend;

	nppend = htp->h_tab + htp->h_size;
	for (npp = htp->h_tab; npp < nppend; npp++) {
		for (pnp = NULL, np = *npp; np != NULL; np = npn) {
			if (!bottom_of_zone(np->n_data, class)) {
				for (pdp = NULL, dp = np->n_data; dp != NULL; ) {
					if (dp->d_class == class)
						dp = rm_datum(dp, np, pdp);
					else {
						pdp = dp;
						dp = dp->d_next;
					}
				}
				if (np->n_hash) {
					/* call recursively to rm subdomains */
					purge_z_2(np->n_hash, class);

					/* if now empty, free it */
					if (np->n_hash->h_cnt == 0) {
						free((char*)np->n_hash);
						np->n_hash = NULL;
					}
				}
			}

			if ((np->n_hash == NULL) && (np->n_data == NULL)) {
				npn = rm_name(np, npp, pnp);
				htp->h_cnt--;
			} else {
				npn = np->n_next;
				pnp = np;
			}
		}
	}
}

static int
bottom_of_zone(dp, class)
	struct databuf *dp;
	int class;
{
	for ( ; dp ; dp = dp->d_next) {
		if (dp->d_class != class)
			continue;
		if (dp->d_zone == 0)
			continue;
#ifdef NCACHE
		if (dp->d_rcode)	/* this should not occur */
			continue;
#endif
		if (dp->d_type == T_SOA)
			return (1);
	}
	dprintf(3, (ddt, "bottom_of_zone() == 0\n"));
	return (0);
}
#endif
   
/*
 * Handle XFER limit for a nameserver.
 */
static int
nxfers(zp, delta)
	struct zoneinfo *zp;
	int delta;
{
	struct in_addr nsa;
	struct nameser *nsp;
	int ret;

	if (zp->z_xaddr.s_addr)
		nsa = zp->z_xaddr;	/* qserial overrode address */
	else if (!zp->z_addrcnt)
		return (-1);
	else
		nsa = zp->z_addr[0];	/* first ns holds zone's xfer limit */

	if (!(nsp = nameserFind(nsa, NS_F_INSERT)))
		return (-1);		/* probably ENOMEM */

	ret = nsp->xfers;
	if (delta < 0 && -delta > ret)
		return (-1);		/* taking more than we have */

	nsp->xfers += delta;
	return (ret);
}

/*
 * Abort an xfer that has taken too long.
 */

static void
abortxfer(zp)
	struct zoneinfo *zp;
   {

#ifdef WINNT
	    TerminateProcess(zp->z_xferpid, (UINT)XFER_KILLED);

	    syslog(LOG_WARNING,
		"zone transfer timeout for \"%s\"; pid %lu killed",
		zp->z_origin, (u_long)zp->z_xferpid);
             dprintf(1, (ddt, "zone transfer timeout for %s pid %lu killed\n",
		zp->z_origin, (u_long)zp->z_xferpid));

		zp->z_flags |= Z_XFER_ABORTED;
	     ns_retrytime(zp, tt.tv_sec);
 	     (void) nxfers(zp, -1);        

#else

	if (zp->z_flags & (Z_XFER_GONE|Z_XFER_ABORTED)) {
		int i;

		for (i = 0; i < MAX_XFERS_RUNNING; i++) {
			if (xferstatus[i].xfer_pid == (long)zp->z_xferpid) {
			    xferstatus[i].xfer_pid = 0;
				xferstatus[i].xfer_state = XFER_IDLE;
				break;
			}
		}

		if (zp->z_flags & Z_XFER_GONE)
			syslog(LOG_WARNING,
			   "zone transfer timeout for \"%s\"; pid %lu missing",
			       zp->z_origin, (u_long)zp->z_xferpid);

		else if (kill(zp->z_xferpid, SIGKILL) == -1)
			syslog(LOG_WARNING,
			  "zone transfer timeout for \"%s\"; kill pid %lu: %m",
			       zp->z_origin, (u_long)zp->z_xferpid);
		else
			syslog(LOG_WARNING,
                            "zone transfer timeout for \"%s\"; second kill\
                             pid %lu - forgetting, processes may accumulate",
			     zp->z_origin, (u_long)zp->z_xferpid);

		zp->z_xferpid = 0;
		xfers_running--;
		(void)nxfers(zp, -1);
		zp->z_flags &= ~(Z_XFER_RUNNING|Z_XFER_ABORTED|Z_XFER_GONE);
	}

        else if (kill(zp->z_xferpid, SIGKILL) == -1) {
		if (errno == ESRCH)
			/* No warning on first time, it may have just exited */
			zp->z_flags |= Z_XFER_GONE;
		else {
			syslog(LOG_WARNING,
		    "zone transfer timeout for \"%s\"; pid %lu kill failed %m",
			       zp->z_origin, (u_long)zp->z_xferpid);
			zp->z_flags |= Z_XFER_ABORTED;
		}
	} else {
		syslog(LOG_NOTICE,
		       "zone transfer timeout for \"%s\"; pid %lu killed",
		       zp->z_origin, (u_long)zp->z_xferpid);
		zp->z_flags |= Z_XFER_ABORTED;
	}
	
#endif
   }


#ifndef WINNT
/*
 * SIGCHLD signal handler: process exit of xfer's.
 * (Note: also called when outgoing transfer completes.)
 */
SIG_FN
reapchild()
{
	int pid, i, save_errno;
#if defined(sequent)
	union wait status;
#else
	int status;
#endif /* sequent */

#if defined(MUST_REARM_SIGS)
	(void)signal(SIGCLD, (SIG_FN (*)()) reapchild);
#endif
	save_errno = errno;
	gettime(&tt);
#if defined(USE_WAITPID)
	while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
#else /* USE_WAITPID */
	{
		pid = wait(&status);
#endif /* USE_WAITPID */
		for (i = 0; i < MAX_XFERS_RUNNING; i++) {
			if (xferstatus[i].xfer_pid == pid) {
				xferstatus[i].xfer_status = status;
				xferstatus[i].xfer_state = XFER_DONE;
				needendxfer++;
				break;
			}
		}
	}
	errno = save_errno;
}

}
#endif

/*
 * SIGCHLD signal handler: process exit of xfer's.
 * (Note: also called when outgoing transfer completes.)
 */
/* the semantics here have changed now we call reapchild when a transfer ends which sets the
   variable needendxfer ... however under nt since there are no signals endxfer is called
   directly when a transfer ends... therefore we need to clear out the entry here under 
   nt, also under nt we determine the pid to clear because it is passed in ... lgk */

#ifndef WINNT
void
endxfer()
#else
void
endxfer(pid)
	HANDLE pid;
#endif
{
    register struct zoneinfo *zp;
#ifndef WINNT   
 	int exitstatus, pid;
#if defined(sequent)
	union wait status;
#else
	int status;
#endif /* sequent */
#else
	DWORD exitstatus;
#endif
    int i;

gettime(&tt);

/* under Win NT this should not get called unless a transfer has completed 
 * or aborted
 */
#ifndef WINNT
  	for (i = 0; i < MAX_XFERS_RUNNING; i++) {
  		if (xferstatus[i].xfer_state != XFER_DONE)
  			continue;
  		pid = xferstatus[i].xfer_pid;
  		status = xferstatus[i].xfer_status;

    		exitstatus = WIFEXITED(status) ?WEXITSTATUS(status) :0;
#else
 
	if (!GetExitCodeProcess(pid, &exitstatus))
          {
	    syslog(LOG_ERR, "endxfer: cannot determine return code of the xfer (%d) process: %m",pid);
	    dprintf(1, (ddt, "endxfer: cannot determine return code of the xfer (%d) process: %d",pid,GetLastError()));
            /* lgk new code here if we cannot figure this out dummy it up so it gets rescheduled anyway */
            /* dont return as previously or the zone does not get rescheduled */
            exitstatus = XFER_FAIL;
	  }
#endif /* WINNT */

		for (zp = zones; zp < &zones[nzones]; zp++)
                   {
			if (zp->z_xferpid != pid)
				continue;
            /* ok found our zone */
#ifdef WINNT
            /* regardless of how it terminated if under NT clear it out here
             * first find the entry 
             */
             
  	         for (i = 0; i < MAX_XFERS_RUNNING; i++) {
			    if (xferstatus[i].xfer_pid == (long)pid) {
				    xferstatus[i].xfer_status = exitstatus;
                    xferstatus[i].xfer_state = XFER_IDLE;
			    	xferstatus[i].xfer_pid = 0;
			    	
			   } 
		      }
		     /* lgk problem is here we cannot close the handle here as it will reuse it and then close it again later
		        don't think we need to close this handle anyway as it is the same handle that is closed before the free
		        in named_xfer  the problem was that critical sections allow the same thread
		        to go into the same critical section more than once and since this handle was
		        already closed here it was re-allocated and closed again in named-xfer after the call to endxfer  
		     CloseHandle(zp->z_xferpid); */
#endif
			xfers_running--;
			(void) nxfers(zp, -1);
			zp->z_xferpid = 0;
  			zp->z_flags &=
  				~(Z_XFER_RUNNING|Z_XFER_ABORTED|Z_XFER_GONE);
#ifndef WINNT
			dprintf(1, (ddt,
		            "\nendxfer: child %d zone %s returned status=%d termsig=%d\n", 
				    pid, zp->z_origin, exitstatus,
				    WIFSIGNALED(status) ?WTERMSIG(status) :-1
				    )
				);
			if (WIFSIGNALED(status)) {
				if (WTERMSIG(status) != SIGKILL) {
					syslog(LOG_NOTICE,
				  "named-xfer \"%s\" exited with signal %d\n",
					  zp->z_origin[0]?zp->z_origin:".",
					  WTERMSIG(status));
				}
				ns_retrytime(zp, tt.tv_sec);
			} else {
#else /* WINNT */
			dprintf(1, (ddt,
		            "\nendxfer: child %d zone %s returned status=%d \n", 
				    pid, zp->z_origin, exitstatus));
#endif /* WINNT */
				switch (exitstatus) {
				case XFER_UPTODATE:
					markUpToDate(zp);
					break;

				case XFER_SUCCESS:
					/* XXX should incorporate loadxfer() */
					zp->z_flags |= Z_NEED_RELOAD;
					zp->z_flags &= ~Z_SYSLOGGED;
					needzoneload++;
                    dprintf(1, (ddt, "named-xfer for %s ended successfully (child = %d)\n",zp->z_origin,pid));
					break;

				case XFER_TIMEOUT:
					if (!(zp->z_flags & Z_SYSLOGGED)) {
						zp->z_flags |= Z_SYSLOGGED;
						syslog(LOG_WARNING,
	 	                                        "zoneref: Masters for secondary zone \"%s\" unreachable",
						       zp->z_origin);
					}
                    dprintf(1, (ddt, "Masters for secondary zone %s unreachable (xfer timed out) (child = %d)\n",zp->z_origin,pid));
					ns_retrytime(zp, tt.tv_sec);
					break;
#ifdef WINNT
				case XFER_KILLED:
					ns_retrytime(zp, tt.tv_sec);
					break;
#endif
				default:
					if (!(zp->z_flags & Z_SYSLOGGED)) {
						zp->z_flags |= Z_SYSLOGGED;
						syslog(LOG_NOTICE,
					         "named-xfer for \"%s\" exited %d",
						    zp->z_origin, exitstatus);
					}
					/* FALLTHROUGH */
				case XFER_FAIL:
					zp->z_flags |= Z_SYSLOGGED;
                    dprintf(1, (ddt, "(xfer for %s failed in endxfer) (child = %d)\n",zp->z_origin,pid));
                    syslog(LOG_WARNING, "(xfer for %s failed) (child = %d) - rescheduling!\n",zp->z_origin,pid);
					ns_retrytime(zp, tt.tv_sec);
					break;
				} /*switch*/
				break;
#ifndef WINNT
			} /*if/else*/
#endif
		   } /* inner for */

#ifndef WINNT
 	 xferstatus[i].xfer_state = XFER_IDLE;
	 xferstatus[i].xfer_pid = 0;
	} /* outter for */

	releasesigchld();
#endif

	tryxfer();

}

/*
 * Try to start some xfers - new "fair scheduler" by Bob Heiney @DEC (1995)
 */
static void
tryxfer() {
	static struct zoneinfo *zp = NULL;
	static struct zoneinfo *lastzones = NULL;
	static int lastnzones = 0;
	struct zoneinfo *startzp, *stopzp;

	/* initialize, and watch out for changes in zones! */
	if (lastzones != zones) {
		if (lastzones != NULL)
			syslog(LOG_INFO, "zones changed: %p != %p",
			       lastzones, zones);
		lastzones = zones;
		zp = zones;
	}

	/* did zones shrink? */
	if (lastnzones > nzones) {
		syslog(LOG_INFO, "zones shrunk");
		zp = zones;
	}
	lastnzones = nzones;
    
	if (zp == zones)
		stopzp = &zones[nzones-1];
	else
		stopzp = zp - 1;

	dprintf(3, (ddt, "tryxfer start zp=%p stopzp=%p def=%d running=%d\n",
		    zp, stopzp, xfers_deferred, xfers_running));

	startzp = zp;
	for (;;) {
		int xfers;

		if (!xfers_deferred || xfers_running >= max_xfers_running)
			break;
		
		if ((xfers = nxfers(zp, 0)) != -1 &&
		    xfers < max_xfers_per_ns &&
		    (zp->z_flags & Z_NEED_XFER)) {
			nxfers(zp, 1);
			xfers_deferred--;
			startxfer(zp);
		}

		if (zp == stopzp) {
			dprintf(3, (ddt, "tryxfer stop mark\n"));
			zp = startzp;
			break;
		}

		zp++;
		/* wrap around? */
		if (zp == &zones[nzones])
			zp = zones;
	}
	dprintf(3, (ddt, "tryxfer stop zp=%p\n", zp));

	if (!needmaint)
		sched_maint();
}

/*
 * Reload zones whose transfers have completed.
 */
void
loadxfer() {
	register struct zoneinfo *zp;   

	gettime(&tt);
	for (zp = zones; zp < &zones[nzones]; zp++) {
		if (zp->z_flags & Z_NEED_RELOAD) {
			dprintf(1, (ddt, "loadxfer() \"%s\"\n",
				    zp->z_origin[0] ? zp->z_origin : "."));
			zp->z_flags &= ~(Z_NEED_RELOAD|Z_AUTH);
			remove_zone(hashtab, zp - zones
#ifdef CLEANCACHE
				    , 1
#endif
				    );
#ifdef PURGE_ZONE
			purge_zone(zp->z_origin, hashtab, zp->z_class);
#endif
			if (!db_load(zp->z_source, zp->z_origin, zp, NULL))
				zp->z_flags |= Z_AUTH;
			if (zp->z_flags & Z_TMP_FILE)
				(void) unlink(zp->z_source);
		}
	}
	if (!needmaint)
		sched_maint();
}

/*
 * Add this zone to the set of those needing transfers.
 */
static void
addxfer(zp)
	struct zoneinfo *zp;
{
	if (!(zp->z_flags & Z_NEED_XFER)) {
		zp->z_flags |= Z_NEED_XFER;
		xfers_deferred++;
		tryxfer();
	}
}


#ifdef WINNT
void CALLBACK maint_alarm (UINT wTimerID, UINT msg,
    DWORD dwUser, DWORD dw1, DWORD dw2)
{
	int len;
	char loopbuf[] = "";
	    struct sockaddr_in loopback; 
	    needmaint = 1;
	dprintf(1, (ddt, "in maint_alarm ...\n"));
	printf("in maint_alarm ...\n");

#ifdef NOWAIT
	loopback.sin_family = AF_INET;
	loopback.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	loopback.sin_port = htons(NAMESERVER_PORT);
	if(len = sendto(datagramq->dq_dfd, loopbuf, 1, 0, (struct sockaddr *)&loopback,
		  sizeof(struct sockaddr_in)) == SOCKET_ERROR)
		{
		syslog(LOG_ERR, "failed to break select for maintenance: %m, maintenance delayed");
		syslog(LOG_ERR, "WSA ERRORCODE WAS: %d", WSAGetLastError());
		}
#endif


    UNREFERENCED_PARAMETER(dw1);
    UNREFERENCED_PARAMETER(dw2);
    UNREFERENCED_PARAMETER(dwUser);
    UNREFERENCED_PARAMETER(msg);
    UNREFERENCED_PARAMETER(wTimerID);
}

void
named_xfer(PROCESS_INFORMATION *proc_info)
{
	DWORD dwWait;

	dwWait = WaitForSingleObject((*proc_info).hProcess, (DWORD) MAX_XFER_TIME * 1000);


    /* now check reason we woke up */
    /* remove the return now so let it go into endxfer even if the wait fails so we can resched. the xfer */
    switch (dwWait)
     {

      case WAIT_TIMEOUT:
           syslog(LOG_ERR, "WaitForSingleObject() on named-xfer process (%d) status timed out!",(*proc_info).hProcess);
           dprintf(1,(ddt, "WaitForSingleObject() on named-xfer process (%d) status timed out!\n",(*proc_info).hProcess));
	       break;

      case WAIT_FAILED:
           syslog(LOG_ERR, "WaitForSingleObject() on named-xfer process (%d) status failed: %m",(*proc_info).hProcess);
           dprintf(1,(ddt, "WaitForSingleObject() on named-xfer process (%d) status failed: %d\n",(*proc_info).hProcess,GetLastError()));
	       break;

      case WAIT_OBJECT_0:
           /* normal case */
           break;
       
      default:
        dprintf(1, (ddt, "Unknown return type from WaitForSingleObject on named-xfer (%d) status! (%d)\n",(*proc_info).hProcess,dwWait));
        syslog(LOG_ERR, "Unknown return type from WaitForSingleObject on named-xfer (%d) status %d:",(*proc_info).hProcess,dwWait);
        break;
      
     }
    
    /* lgk new code using critical section to synchronize */
    EnterCriticalSection(&protected_zp);
    endxfer((*proc_info).hProcess);
    /* dont try the close if the wait failed for some reason */
    /* if we have an extra handle around so be it but if the handle is bad ie wait failed not sure what close will do */
    if (dwWait == WAIT_OBJECT_0)
      {
       CloseHandle((*proc_info).hProcess);
       CloseHandle((*proc_info).hThread);
      }
    free((char *)proc_info);
    LeaveCriticalSection(&protected_zp);
}
#endif /* WINNT */
