ftp.nice.ch/pub/next/unix/network/system/gated.2.1pl2.NI.bs.tar.gz#/gated-2.1/src/task.c

This is task.c in view mode; [Download] [Up]

/*
 *  $Header: /disk/d/src/devel/gated/dist/src/RCS/task.c,v 2.1 92/02/24 14:13:05 jch Exp $
 */

/*%Copyright%*/
/************************************************************************
*									*
*	GateD, Release 2						*
*									*
*	Copyright (c) 1990,1991,1992 by Cornell University		*
*	    All rights reserved.					*
*									*
*	THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY		*
*	EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT		*
*	LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY		*
*	AND FITNESS FOR A PARTICULAR PURPOSE.				*
*									*
*	Royalty-free licenses to redistribute GateD Release		*
*	2 in whole or in part may be obtained by writing to:		*
*									*
*	    GateDaemon Project						*
*	    Information Technologies/Network Resources			*
*	    143 Caldwell Hall						*
*	    Cornell University						*
*	    Ithaca, NY 14853-2602					*
*									*
*	GateD is based on Kirton's EGP, UC Berkeley's routing		*
*	daemon	 (routed), and DCN's HELLO routing Protocol.		*
*	Development of Release 2 has been supported by the		*
*	National Science Foundation.					*
*									*
*	Please forward bug fixes, enhancements and questions to the	*
*	gated mailing list: gated-people@gated.cornell.edu.		*
*									*
*	Authors:							*
*									*
*		Jeffrey C Honig <jch@gated.cornell.edu>			*
*		Scott W Brim <swb@gated.cornell.edu>			*
*									*
*************************************************************************
*									*
*      Portions of this software may fall under the following		*
*      copyrights:							*
*									*
*	Copyright (c) 1988 Regents of the University of California.	*
*	All rights reserved.						*
*									*
*	Redistribution and use in source and binary forms are		*
*	permitted provided that the above copyright notice and		*
*	this paragraph are duplicated in all such forms and that	*
*	any documentation, advertising materials, and other		*
*	materials related to such distribution and use			*
*	acknowledge that the software was developed by the		*
*	University of California, Berkeley.  The name of the		*
*	University may not be used to endorse or promote		*
*	products derived from this software without specific		*
*	prior written permission.  THIS SOFTWARE IS PROVIDED		*
*	``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,	*
*	INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF	*
*	MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.		*
*									*
************************************************************************/


#include "include.h"
#include "bgp.h"
#include "egp.h"
#include "rip.h"
#include "hello.h"
#include "icmp.h"
#include "snmp.h"
#include "parse.h"
#include <signal.h>
#if	defined(_IBMR2)
#include <time.h>
#endif				/* defined(_IBMR2) */
#include <sys/time.h>
#ifdef	SYSV
#include <sys/sioctl.h>
#include <sys/stropts.h>
#else	/* SYSV */
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#endif	/* SYSV */
#include "task_sig.h"

char *task_path_name;			/* Directory where we were started */

static timer *timer_active;		/* Pointer to the active timer */
static timer timer_queue_active =
{&timer_queue_active, &timer_queue_active, "activeTimers"};	/* Doubly linked list of active timers */
static timer timer_queue_inactive =
{&timer_queue_inactive, &timer_queue_inactive, "inactiveTimers"};	/* Doubly linked list of inactive timers */

static task task_head =
{&task_head, &task_head, "taskHead"};	/* Head of doubly linked list of timers */
static fd_set task_select_readbits;
static fd_set task_select_writebits;
static fd_set task_select_exceptbits;
static int task_max_socket = 0;
static task **task_socket_tasks = (task **) 0;

#ifndef	SYSV
static int task_signal_mask = 0;	/* Signals that are blocked */
#endif	/* SYSV */
static int task_signals[] = {
    SIGTERM,
    SIGALRM,
    SIGUSR1,
    SIGINT,
    SIGHUP,
#ifndef	NO_FORK
    SIGCHLD,
#endif				/* NO_FORK */
    0};
#define	SIGNAL_LIST(ip)	{ int *ip; for (ip = task_signals; *ip; ip++)
#define	SIGNAL_LIST_END(ip) }

static bits task_flag_bits[] =
{
    {TASKF_ACCEPT, "Accept"},
    {TASKF_CONNECT, "Connect"},
    {TASKF_IPHEADER, "IPHeader"},
    {0}
};

static bits task_socket_options[] =
{
    {TASKOPTION_RECVBUF, "RecvBuffer"},
    {TASKOPTION_SENDBUF, "SendBuffer"},
    {TASKOPTION_LINGER, "Linger"},
    {TASKOPTION_REUSEADDR, "ReUseAddress"},
    {TASKOPTION_BROADCAST, "Broadcast"},
    {TASKOPTION_DONTROUTE, "DontRoute"},
    {TASKOPTION_KEEPALIVE, "KeepAlive"},
    {TASKOPTION_DEBUG, "Debug"},
    {TASKOPTION_NONBLOCKING, "NonBlocking"},
    {TASKOPTION_USELOOPBACK, "UseLoopback"},
    {0, NULL}
};

static bits task_msg_bits[] =
{
    {MSG_OOB, "MSG_OOB"},
    {MSG_PEEK, "MSG_PEEK"},
    {MSG_DONTROUTE, "MSG_DONTROUTE"},
#ifdef	MSG_EOR
    {MSG_EOR, "MSG_EOR"},
#endif				/* MSG_EOR */
#ifdef	MSG_TRUNC
    {MSG_TRUNC, "MSG_TRUNC"},
#endif				/* MSG_TRUNC */
#ifdef	MSG_CTRUNC
    {MSG_CTRUNC, "MSG_CTRUNC"},
#endif				/* MSG_CTRUNC */
#ifdef	MSG_WAITALL
    {MSG_WAITALL, "MSG_WAITALL"},
#endif				/* MSG_WAITALL */
    {0, NULL}
};

static bits timer_flag_bits[] =
{
    {TIMERF_ABSOLUTE, "Absolute"},
    {TIMERF_DELETE, "Delete"},
    {0}
};


/*
 *	Insert a timer on one of the queues.  Inactive timers are
 *	inserted at the beginning of their queue, Active timers are
 *	inserted in order of their expiration.
 */
static void
timer_insert(tip)
timer *tip;
{
    timer *tip1;

    if (tip->timer_interval) {
	TIMER_ACTIVE(tip1) {
	    if (tip->timer_next_time < tip1->timer_next_time) {
		break;
	    }
	} TIMER_ACTIVEEND(tip1);
    } else {
	tip1 = timer_queue_inactive.timer_forw;
    }

    insque((struct qelem *) tip, (struct qelem *) tip1->timer_back);
}


/*
 *	Return a pointer to a string containing the timer name
 */
char *
timer_name(tip)
timer *tip;
{
    static char name[MAXHOSTNAMELENGTH];

    if (tip->timer_task) {
	if (tip->timer_task->task_addr.in.sin_addr.s_addr) {
	    (void) sprintf(name, "%s_%s.%A",
			   tip->timer_task->task_name,
			   tip->timer_name,
			   &tip->timer_task->task_addr);
	} else {
	    (void) sprintf(name, "%s_%s",
			   tip->timer_task->task_name,
			   tip->timer_name);
	}
    } else {
	strcpy(name, tip->timer_name);
    }
    return (name);
}


/*
 *	Create a timer - returns pointer to timer structure
 */
timer *
timer_create(tp, indx, name, flags, interval, job)
task *tp;
int indx;
const char *name;
flag_t flags;
time_t interval;
void (*job) ();
{
    timer *tip;

    tip = (timer *) calloc(1, sizeof(timer));
    if (!tip) {
	trace(TR_ALL, LOG_ERR, "timer_create: calloc: %m");
	quit(errno);
    }
    tip->timer_name = name;
    tip->timer_task = tp;
    tip->timer_index = indx;
    tip->timer_flags = flags;
    tip->timer_interval = interval;
    tip->timer_job = job;

    /* Link timer to it's task if there is one */
    if (tip->timer_task) {
	tip->timer_task->task_timer[tip->timer_index] = tip;
    }
    /* If this timer is active, set the intervals */
    if (tip->timer_interval) {
	tip->timer_next_time = tip->timer_last_time = time_sec;
	if (tip->timer_flags & TIMERF_ABSOLUTE) {
	    tip->timer_next_time += tip->timer_interval;
	}
    }
    /* Insert in the correct queue */
    timer_insert(tip);

    /* If we have changed the wakeup time cause a wakeup now to recalculate */
    if ((tip == timer_queue_active.timer_forw) &&
	(timer_queue_active.timer_next_time > tip->timer_next_time)) {
	(void) kill(my_pid, SIGALRM);
    }
    trace(TR_TIMER, 0, "timer_create: created timer %s  flags <%s>  interval %#T at %T",
	  timer_name(tip),
	  trace_bits(timer_flag_bits, tip->timer_flags),
	  tip->timer_interval,
	  tip->timer_next_time);

    return (tip);
}


/*
 *	Delete a timer
 */
void
timer_delete(tip)
timer *tip;
{
    trace(TR_TIMER, 0, "timer_delete: %s", timer_name(tip));

    /* Unlink this timer from it's task if there is one */
    if (tip->timer_task) {
	tip->timer_task->task_timer[tip->timer_index] = (timer *) 0;
    }
    if (tip == timer_active) {
	tip->timer_flags |= TIMERF_DELETE;
    } else {
	remque((struct qelem *) tip);
	(void) free((caddr_t) tip);
    }

}


/*
 *	Reset a timer - move it to the inactive queue
 */
void
timer_reset(tip)
timer *tip;
{
    if (tip->timer_interval) {
	tip->timer_next_time = tip->timer_last_time = tip->timer_interval = (time_t) 0;

	remque((struct qelem *) tip);
	timer_insert(tip);

	trace(TR_TIMER, 0, "timer_reset: reset %s",
	      timer_name(tip));
    }
}


/*
 *	Set a timer to fire in interval seconds from now
 */
void
timer_set(tip, interval)
timer *tip;
time_t interval;
{
    tip->timer_interval = interval;
    if (!tip->timer_next_time) {
	tip->timer_last_time = tip->timer_next_time = time_sec;
    }
    tip->timer_next_time = time_sec + tip->timer_interval;

    /* Re-insert this timer in the active queue in expiration order */
    remque((struct qelem *) tip);
    timer_insert(tip);

    /* If we have changed the wakeup time cause a wakeup now to recalculate */
    if ((tip == timer_queue_active.timer_forw) &&
	(tip != timer_active) &&
	(timer_queue_active.timer_next_time > tip->timer_next_time)) {
	(void) kill(my_pid, SIGALRM);
    }
    trace(TR_TIMER, 0, "timer_set: timer %s interval set to %#T at %T",
	  timer_name(tip),
	  tip->timer_interval,
	  tip->timer_next_time);

}

/*
 *	Set a timer to fire in interval seconds from the last time it fired
 */
void
timer_interval(tip, interval)
timer *tip;
time_t interval;
{
    if (tip->timer_interval != interval) {
	tip->timer_interval = interval;
	if (!tip->timer_next_time) {
	    tip->timer_last_time = tip->timer_next_time = time_sec;
	}
	tip->timer_next_time = tip->timer_last_time + tip->timer_interval;

	/* Re-insert this timer in the active queue in expiration order */
	remque((struct qelem *) tip);
	timer_insert(tip);

	/* If we have changed the wakeup time cause a wakeup now to recalculate */
	if ((tip == timer_queue_active.timer_forw) &&
	    (tip != timer_active) &&
	    (timer_queue_active.timer_next_time > tip->timer_next_time)) {
	    (void) kill(my_pid, SIGALRM);
	}
	trace(TR_TIMER, 0, "timer_interval: timer %s interval set to %#T at %T",
	      timer_name(tip),
	      tip->timer_interval,
	      tip->timer_next_time);
    }
}


/*
 *	Dump the provided timer
 */
static void
timer_dump(fd, tip)
FILE *fd;
timer *tip;
{
    (void) fprintf(fd, "\t\t%s",
		   timer_name(tip));
    if (tip->timer_interval) {
	(void) fprintf(fd, "\tlast: %T\tnext: %T\tinterval: %#T",
		       tip->timer_last_time,
		       tip->timer_next_time,
		       tip->timer_interval);
    }
    if (tip->timer_flags) {
	(void) fprintf(fd, "\t<%s>",
		       trace_bits(timer_flag_bits, tip->timer_flags));
    }
    (void) fprintf(fd, "\n");
}


/*
 * timer control for periodic route-age and interface processing.
 * timer_dispatch() is called when the periodic interrupt timer expires.
 */
static void
timer_dispatch()
{
    time_t late = 0;
    timer *tip;
    struct itimerval value;

    /* Log a message if the system dispatched us late */
    if (timer_queue_active.timer_last_time) {
	trace(TR_TIMER, 0, "timer_dispatch: requested interval: %#T actual interval: %#T",
	      timer_queue_active.timer_interval,
	      time_sec - timer_queue_active.timer_last_time);

	late = time_sec - timer_queue_active.timer_last_time - timer_queue_active.timer_interval;
	if (late < 0) {
	    late = 0;
	}
	if (late) {
	    trace(TR_INT, 0, "timer_dispatch: interval timer interrupt %d seconds late",
		  late);
	}
    } else {
	trace(TR_TIMER, 0, "timer_dispatch: initializing");
    }

    /* Run the queues until all the expired timers have been serviced.  This allows for timers that expire while we are */
    /* working on other timers */
    do {
	TIMER_ACTIVE(tip) {
	    /* Timers are in time order so we don't have to scan the whole list */
	    if (time_sec < tip->timer_next_time) {
		break;
	    }
	    /* Log the timer */
	    trace(TR_TIMER, 0, "timer_dispatch: call %s, due at %T, last at %T, interval %#T",
		  timer_name(tip),
		  tip->timer_next_time,
		  tip->timer_last_time,
		  tip->timer_next_time - tip->timer_last_time);

	    /* Update time of last firing */
	    tip->timer_last_time = time_sec;

	    /* Call the timer routine */
	    timer_active = tip;
	    tip->timer_job(tip, tip->timer_interval);
	    timer_active = (timer *) 0;

	    tracef("timer_dispatch: returned from %s, ",
		   timer_name(tip));

	    /* If the timer is a one shot, delete it now */
	    if (tip->timer_flags & TIMERF_DELETE) {
		trace(TR_TIMER, 0, "deletion requested");
		timer_delete(tip);
		continue;
	    }
	    if (tip->timer_interval) {
		/* Reschedule again at the next interval after the current time */
		while (tip->timer_next_time <= time_sec) {
		    tip->timer_next_time += tip->timer_interval;
		}

		/* Remove and re-insert to maintain order in the queue */
		remque((struct qelem *) tip);
		timer_insert(tip);

		tracef("rescheduled ");
		if (tip->timer_interval > late) {
		    tracef("in %#T ",
			   tip->timer_interval - late);
		}
		trace(TR_TIMER, 0, "at %T",
		      tip->timer_next_time);
	    } else {
		trace(TR_TIMER, 0, "now inactive");
	    }

	} TIMER_ACTIVEEND(tip);

	/* Get the current time */
	getod();

    } while (timer_queue_active.timer_forw->timer_next_time <= time_sec);

    /* Calulate when we are supposed to wake up next and how long that is from now */
    timer_queue_active.timer_next_time = timer_queue_active.timer_forw->timer_next_time;
    timer_queue_active.timer_interval = timer_queue_active.timer_next_time - time_sec;
    timer_queue_active.timer_last_time = time_sec;

    trace(TR_TIMER, 0, "timer_dispatch: end, next job: %T delta: %#T",
	  timer_queue_active.timer_next_time,
	  timer_queue_active.timer_interval);

    /* Check for invalid intervals (should not happen) */
    if (timer_queue_active.timer_interval <= 0) {
	trace(TR_INT, 0, "timer_dispatch: timer interval (%#T) invalid, using 1 second",
	      timer_queue_active.timer_interval);
	timer_queue_active.timer_interval = 1;
    }
    /* Set the interval timer */
    value.it_interval.tv_sec = 0;	/* no auto timer reload */
    value.it_interval.tv_usec = 0;
    value.it_value.tv_sec = timer_queue_active.timer_interval;
    value.it_value.tv_usec = 0;
    if (setitimer(ITIMER_REAL, &value, (struct itimerval *) 0)) {
	trace(TR_ALL, LOG_ERR, "timer_dispatch: setitimer: %m");
	quit(errno);
    }
}


/*  */
/*
 *	Return a pointer to a string containing the task name
 */
char *
task_name(tp)
task *tp;
{
    static char name[MAXHOSTNAMELENGTH];

    if (tp->task_addr.in.sin_addr.s_addr) {
	(void) sprintf(name, "%s.%A",
		       tp->task_name,
		       &tp->task_addr);
    } else {
	strcpy(name, tp->task_name);
    }

    if (tp->task_pid > 0) {
	(void) sprintf(&name[strlen(name)], "[%d]",
		       tp->task_pid);
    }
    return (name);
}


/*
 *	Receive packet and check for errors
 */
int
task_receive_packet(tp, count)
task *tp;
int *count;
{
    int iov;
    struct sockaddr_in *from;
    struct msghdr *msghdr;

    msghdr = &recv_msghdr;
    iov = tp->task_flags & TASKF_IPHEADER ? RECV_IOVEC_IP : RECV_IOVEC_DATA;
    msghdr->msg_iov = &recv_iovec[iov];
    msghdr->msg_iovlen = RECV_IOVEC_SIZE - iov;

    msghdr->msg_namelen = sizeof(recv_addr);	/* Set max size */
    memset(msghdr->msg_name, (char) 0, msghdr->msg_namelen);	/* Clean name */
    from = (struct sockaddr_in *) msghdr->msg_name;	/* Set pointer to address */

    *count = recvmsg(tp->task_socket, msghdr, 0);

    if (!*count) {
	return (-1);
    }
    if (*count < 0) {
	int do_log = LOG_ERR;

	switch (errno) {
	    case EINTR:
		break;
	    case ENETDOWN:
	    case ENETUNREACH:
	    case ENETRESET:
	    case ECONNABORTED:
	    case ECONNRESET:
	    case ENOBUFS:
	    case ETIMEDOUT:
	    case ECONNREFUSED:
	    case EHOSTDOWN:
	    case EHOSTUNREACH:
		do_log = 0;
	    default:
		trace(TR_ALL, do_log, "task_receive_packet: %s recvmsg: %m",
		      task_name(tp));
		break;
	}
	return (errno);
    }
    trace(TR_TASK, 0, "task_receive_packet: task %s from %#A socket %d length %d",
	  task_name(tp),
	  from,
	  tp->task_socket,
	  *count);

    if (msghdr->msg_namelen != socksize(from)) {
	trace(TR_INT, LOG_ERR, "task_receive_packet: %s fromlen %d invalid, expected %d",
	      task_name(tp),
	      msghdr->msg_namelen,
	      socksize(from));
	return (EINVAL);
    }
    return (0);
}


/*
 *	Send a packet
 */
int
task_send_packet(tp, msg, len, flags, addr)
task *tp;
caddr_t msg;
int len;
flag_t flags;
sockaddr_un *addr;
{
    int rc = 0;

    tracef("task_send_packet: task %s socket %d length %d",
	   task_name(tp),
	   tp->task_socket,
	   len);
    if (flags) {
	tracef(" flags %s(%X)",
	       trace_bits(task_msg_bits, flags),
	       flags);
    }
    if (addr) {
	tracef(" to %#A",
	       addr);
	rc = sendto(tp->task_socket, msg, len, (int) flags, addr, socksize(addr));
    } else {
	rc = send(tp->task_socket, msg, len, (int) flags);
    }

    if (rc < 0) {
	trace(TR_ALL, LOG_ERR, ": %m");
    } else if (rc != len) {
	trace(TR_ALL, LOG_ERR, ": %d bytes not accepted",
	      len - rc);
    } else {
	trace(TR_TASK, 0, NULL);
    }

    return rc;
}


/*
 *	Wait for incoming packets
 */
void
task_main()
{
#ifndef	SYSV
    int sigmask_save;
#endif	/* SYSV */
    int n, count, socket;
    fd_set read_bits, write_bits, except_bits;
    int forever = TRUE;
    task *tp;

    /* Allocate receive buffer know that we know it's maximum size */
    if (count = recv_iovec[RECV_IOVEC_DATA].iov_len) {
	if (!(recv_iovec[RECV_IOVEC_DATA].iov_base = (caddr_t) malloc((u_int) count))) {
	    trace(TR_ALL, LOG_ERR, "task_main: malloc: %m");
	    quit(errno);
	}
    }
    timer_dispatch();
    trace(TR_TASK, 0, NULL);

    while (forever) {
	trace(TR_TASK, 0, NULL);
	read_bits = task_select_readbits;
	write_bits = task_select_writebits;
	except_bits = task_select_exceptbits;
	n = select(task_max_socket + 1, &read_bits, &write_bits, &except_bits, (struct timeval *) 0);

	if (n < 0) {
	    if (errno == EINTR) {
		trace(TR_TASK, 0, "task_main: select: %m");
		continue;
	    } else {
		trace(TR_ALL, LOG_ERR, "task_main: select: %m");
		quit(errno);
	    }
	}
	getod();			/* current time */

#ifdef	SYSV
	SIGNAL_LIST(ip) {
	    sighold(*ip);
	} SIGNAL_LIST_END(ip) ;
#else	/* SYSV */
	sigmask_save = sigblock(task_signal_mask);
#endif	/* SYSV */

	for (socket = 0; socket < task_max_socket + 1; socket++) {
	    tp = task_socket_tasks[socket];

	    /* Check for ready for read on socket */
	    if (FD_ISSET(socket, &read_bits)) {
		if (tp) {
		    if (tp->task_flags & TASKF_ACCEPT) {
			if (tp->task_accept) {
			    trace(TR_TASK, 0, "task_main: accept ready for %s socket %d, protocol %d, port %d",
				  task_name(tp),
				  socket,
				  tp->task_proto,
				  ntohs(tp->task_addr.in.sin_port));
			    (void) tp->task_accept(tp);
			} else {
			    trace(TR_INT, 0, "task_main: no task for accept on socket %d", socket);
			}
		    } else {
			if (tp->task_recv) {
			    trace(TR_TASK, 0, "task_main: recv ready for %s socket %d, protocol %d, port %d",
				  task_name(tp),
				  socket,
				  tp->task_proto,
				  ntohs(tp->task_addr.in.sin_port));
			    (void) tp->task_recv(tp);
			} else {
			    trace(TR_INT, 0, "task_main: no task for read on socket %d", socket);
			}
		    }
		}
	    }
	    /* Check for ready for write on socket */
	    if (FD_ISSET(socket, &write_bits)) {
		if (tp) {
		    if (tp->task_flags & TASKF_CONNECT) {
			if (tp->task_connect) {
			    trace(TR_TASK, 0, "task_main: connect ready for %s socket %d, protocol %d, port %d",
				  task_name(tp),
				  socket,
				  tp->task_proto,
				  ntohs(tp->task_addr.in.sin_port));
			    (void) tp->task_connect(tp);
			} else {
			    trace(TR_TASK, 0, "task_main: no task for connect on socket %d, protocol %d, port %d",
				  socket,
				  tp->task_proto,
				  ntohs(tp->task_addr.in.sin_port));
			}
		    } else {
			if (tp->task_write) {
			    trace(TR_TASK, 0, "task_main: write ready for %s socket %d, protocol %d, port %d",
				  task_name(tp),
				  socket,
				  tp->task_proto,
				  ntohs(tp->task_addr.in.sin_port));
			    (void) tp->task_write(tp);
			} else {
			    trace(TR_TASK, 0, "task_main: no task for write on socket %d, protocol %d, port %d",
				  socket,
				  tp->task_proto,
				  ntohs(tp->task_addr.in.sin_port));
			}
		    }
		} else {
		    trace(TR_INT, 0, "task_main: no task for write/connect socket %d", socket);
		}
	    }
	    /* Check for exception on socket */
	    if (FD_ISSET(socket, &except_bits)) {
		if (tp) {
		    trace(TR_TASK, 0, "task_main: exception for %s socket %d, protocol %d, port %d",
			  task_name(tp),
			  socket,
			  tp->task_proto,
			  ntohs(tp->task_addr.in.sin_port));
		    (void) tp->task_except(tp);
		} else {
		    trace(TR_INT, 0, "task_main: no task for exception socket %d", socket);
		}
	    }
	}

#ifdef	SYSV
	SIGNAL_LIST(ip) {
	    sigrelse(*ip);
	} SIGNAL_LIST_END(ip) ;
#else	/* SYSV */
	(void) sigsetmask(sigmask_save);
#endif	/* SYSV */

    }
}


/*
 *	Call all tasks that have posted a cleanup routine
 */
static void
task_cleanup()
{
    task *tp;

    trace(TR_TASK, 0, NULL);
    TASK_TABLE(tp) {
	if (tp->task_cleanup) {
	    trace(TR_TASK, 0, "task_cleanup: Starting cleanup for task %s",
		  task_name(tp));
	    tp->task_cleanup(tp);
	    trace(TR_TASK, 0, "task_cleanup: Finished cleanup for task %s",
		  task_name(tp));
	}
    } TASK_TABLEEND(tp);
}


/*
 *	Call all task that have posted a reinit routine
 */
static void
task_reinit()
{
    task *tp;

    trace(TR_TASK, 0, NULL);
    TASK_TABLE(tp) {
	if (tp->task_reinit) {
	    trace(TR_TASK, 0, "task_reinit: Starting reinit for task %s",
		  task_name(tp));
	    tp->task_reinit(tp);
	    trace(TR_TASK, 0, "task_reinit: Finished reinit for task %s",
		  task_name(tp));
	}
    } TASK_TABLEEND(tp);
}


/*
 *	Call all tasks that have posted an ifchange routine
 */
void
task_ifchange(ifp)
if_entry *ifp;
{
    task *tp;

    trace(TR_TASK, 0, NULL);
    TASK_TABLE(tp) {
	if (tp->task_ifchange) {
	    trace(TR_TASK, 0, "task_ifchange: Starting ifchange for task %s",
		  task_name(tp));
	    tp->task_ifchange(tp, ifp);
	    trace(TR_TASK, 0, "task_ifchange: Finished ifchange for task %s",
		  task_name(tp));
	}
    } TASK_TABLEEND(tp);
}


static void	    
task_reconfigure()
{
    int i;
    u_int count = recv_iovec[RECV_IOVEC_DATA].iov_len;
#ifndef	SYSV
    int sigmask_save;
#endif	/* SYSV */

#ifdef	SYSV
    SIGNAL_LIST(ip) {
	sighold(*ip);
    } SIGNAL_LIST_END(ip) ;
#else	/* SYSV */
    sigmask_save = sigblock(task_signal_mask);
#endif	/* SYSV */

    trace(TR_ALL, 0, NULL);
    trace(TR_ALL, LOG_NOTICE, "task_receive_signal: re-initializing from %s",
	  EGPINITFILE);
    trace(TR_ALL, 0, NULL);
    i = adv_n_allocated;
    task_cleanup();
    if (adv_n_allocated) {
	trace(TR_ALL, LOG_ERR, "reinit: %d of %d adv_entry elements not freed",
	      adv_n_allocated, i);
    }
    /* Reset options */
    install = TRUE;

    if (parse_parse(EGPINITFILE)) {
	quit(0);
    }
    task_reinit();

    /* XXX - Do we need to reinit all of these? */
#if	defined(PROTO_ICMP) && !defined(RTM_ADD)
    icmp_init();
#endif				/* defined(PROTO_ICMP) && !defined(RTM_ADD) */
#ifdef	PROTO_EGP
    egp_init();
#endif				/* PROTO_EGP */
#ifdef	PROTO_BGP
    bgp_init();
#endif				/* PROTO_BGP */
#ifdef	PROTO_RIP
    rip_init();
#endif				/* PROTO_RIP */
#ifdef	PROTO_HELLO
    hello_init();
#endif				/* PROTO_HELLO */
#ifdef	AGENT_SNMP
    snmp_init();
#endif				/* AGENT_SNMP */

    /* Reallocate receive buffer if it's size has been increased */
    if (recv_iovec[RECV_IOVEC_DATA].iov_len > count) {
	count = recv_iovec[RECV_IOVEC_DATA].iov_len;

	free(recv_iovec[RECV_IOVEC_DATA]);
	
        if (!(recv_iovec[RECV_IOVEC_DATA].iov_base = (caddr_t) malloc(count))) {
            trace(TR_ALL, LOG_ERR, "task_receive_signal: malloc %m");
	    quit(errno);
        }
    }

    trace(TR_ALL, 0, NULL);
    trace(TR_ALL, LOG_NOTICE, "task_receive_signal: reinitializing done");
    trace(TR_ALL, 0, NULL);
#ifdef	SYSV
    SIGNAL_LIST(ip) {
	sigrelse(*ip);
    } SIGNAL_LIST_END(ip) ;
#else	/* SYSV */
    (void) sigsetmask(sigmask_save);
#endif	/* SYSV */
}


/*ARGSUSED*/
static SIGTYPE
task_receive_signal(sig, code, scp)
int sig, code;
struct sigcontext *scp;
{
    static int terminate = 0;
    task *tp;
    static const char *term_names[] =
    {
	"first",
	"second",
	"third"
    };

    getod();

    trace(TR_TASK, 0, NULL);
    trace(TR_TASK, 0, "task_receive_signal: received SIG%s code %d", trace_state(signal_names, sig - 1), code);
    switch (sig) {
	case SIGTERM:
	    trace(TR_INT, LOG_NOTICE, "task_receive_signal: %s terminate signal received", term_names[terminate]);

	    /* Subprocesses terminate immediately for now */
	    if (my_pid != my_mpid) {
		exit(0);
	    }
	    terminate++;
	    if (terminate > 2) {
		quit(0);
	    }
	    TASK_TABLE(tp) {
		if (tp->task_terminate) {
		    trace(TR_TASK, 0, "task_receive_signal: terminating task %s",
			  task_name(tp));
		    tp->task_terminate(tp);
		    trace(TR_TASK, 0, NULL);
		}
	    } TASK_TABLEEND(tp);
	    trace(TR_TASK, 0, "task_receive_signal: Exiting and waiting for completion");
	    break;

	case SIGALRM:
	    timer_dispatch();
	    break;

	case SIGHUP:
	    task_reconfigure();
	    break;

	case SIGINT:
	    trace_dump(FALSE);
	    break;

	case SIGUSR1:
	    if (trace_file == NULL) {
		trace(TR_ALL, LOG_ERR, "task_receive_signal: can not toggle tracing to console");
		break;
	    }
	    if (trace_flags) {
		trace_off();
	    } else {
		trace_on(trace_file, TRUE);
	    }
	    break;

#ifndef	NO_FORK
	case SIGCHLD:
	{
	    int pid;
	    WAIT_T statusp;
	
	    pid = waitpid(-1, &statusp, WNOHANG|WUNTRACED);
	    if (pid) {
		if (pid < 0) {
		    trace(TR_ALL, LOG_ERR, "task_receive_signal: waitpid() error: %m");
		} else {
		    TASK_TABLE(tp) {
			if (pid == tp->task_pid) {
			    break;
			}
		    } TASK_TABLEEND(tp);

		    if (tp) {
			int done = TRUE;
			    
			if (WIFSTOPPED(statusp)) {
			    /* Stopped by a signal */
			
			    trace(TR_ALL, LOG_ERR, "task_receive_signal: %s stopped by SIG%s",
				  task_name(tp),
				  trace_bits(signal_names, WSTOPSIG(statusp) - 1));
			    done = FALSE;
			} else if (WIFSIGNALED(statusp)) {
			    /* Terminated by a signal */
				
			    trace(TR_ALL, LOG_ERR, "task_receive_signal: %s terminated abnormally by SIG%s",
				  task_name(tp),
				  trace_bits(signal_names, WTERMSIG(statusp) - 1));
			} else if (WEXITSTATUS(statusp)) {
			    /* Non-zero exit status */
				
			    trace(TR_ALL, LOG_ERR, "task_receive_signal: %s terminated abnormally with retcode %d",
				  task_name(tp),
				  WEXITSTATUS(statusp));
			} else {
			    /* Normal termination */
				
			    trace(TR_TASK, 0, "task_receive_signal: %s terminated normally",
				  task_name(tp));
			    
			    if (tp->task_child) {
 				tp->task_child(tp);
			    }
			}
		    } else {
			trace(TR_ALL, LOG_ERR, "task_receive_signal: waitpid() returned status about unknown pid: %d",
			      pid);
		    }
		}
	    }
	}

	    break;
#endif	/* NO_FORK */

	default:
	    trace(TR_INT, LOG_ERR,
		  "task_receive_signal: Ignoring unknown signal SIG%s code %d",
		  trace_state(signal_names, sig - 1),
		  code);
    }
    trace(TR_TASK, 0, NULL);
    SIGRETURN;
}


#ifdef	notdef
/*
 *	close a task's socket and terminate
 */
void
task_close(tp)
task *tp;
{
    trace(TR_TASK, 0, "task_close: close socket %d task %s",
	  tp->task_socket,
	  task_name(tp));

    if (close(tp->task_socket)) {
	trace(TR_ALL, LOG_ERR, "task_close: close %s.%d: %m",
	      task_name(tp),
	      tp->task_socket);
    }
    task_delete(tp);
}

#endif				/* notdef */


/*
 *  Delete a task block and free allocated storage.  When the last task has been deleted, exit.
 */
void
task_delete(tp)
task *tp;
{
    int i;
    int socket;

    trace(TR_TASK, 0, "task_delete: deleting task %s",
	  task_name(tp));

    if (tp->task_socket != -1) {
	trace(TR_TASK, 0, "task_delete: closing socket %d for task %s",
	      tp->task_socket,
	      task_name(tp));
	socket = tp->task_socket;
	task_reset_socket(tp);
	if (close(socket)) {
	    trace(TR_ALL, LOG_ERR, "task_delete: close %s.%d: %m",
		  task_name(tp),
		  socket);
	}
    }
    /* Delete any timers associated with this task */
    for (i = 0; i < TASK_TIMERS; i++) {
	if (tp->task_timer[i]) {
	    timer_delete(tp->task_timer[i]);
	}
    }

    if (tp->task_forw) {
	remque((struct qelem *) tp);
	free((char *) tp);
    }
    if ((task_head.task_forw == task_head.task_back) & (task_head.task_forw == (task *) & task_head)) {
	trace(TR_TASK, 0, "task_delete: Removed last task, exiting");
	quit(0);
    }
}


void
task_flash(tp)
task *tp;
{
    task *tp1;

    trace(TR_TASK, 0, "task_flash: flash update request from %s revision is %d",
	  task_name(tp),
	  rt_revision);

    TASK_TABLE(tp1) {
	if (tp1->task_flash && (tp1->task_rtrevision < rt_revision)) {
	    trace(TR_TASK, 0, "task_flash: calling flash routine for %s revision %d",
		  task_name(tp1),
		  tp->task_rtrevision);
	    tp1->task_flash(tp1);
	    trace(TR_TASK, 0, "task_flash: return from routine for %s revision %d",
		  task_name(tp1),
		  tp->task_rtrevision);
	}
    } TASK_TABLEEND(tp1);
}


/*
 * Apply the function "f" to all non-passive
 * interfaces.  If the interface supports the
 * use of broadcasting use it, otherwise address
 * the output to the known router.
 */
void
task_toall(tp, func, point_to_point, if_flag, gw_list, flash_update)
task *tp;
void (*func) ();
int point_to_point;
int if_flag;
gw_entry *gw_list;
int flash_update;
{
    register if_entry *ifp;
    register gw_entry *gwp;
    register struct sockaddr *dst;

    trace(TR_TASK, 0, "task_toall: task %s revision %d rt_revision %d",
	  task_name(tp),
	  tp->task_rtrevision,
	  rt_revision);

    if (!point_to_point) {
	IF_LIST(ifp) {
	    if ((ifp->int_state & (if_flag | IFS_UP | IFS_LOOPBACK)) != IFS_UP) {
		continue;
	    }
	    if (ifp->int_state & IFS_BROADCAST) {
		dst = &ifp->int_broadaddr.a;
	    } else if (ifp->int_state & IFS_POINTOPOINT) {
		dst = &ifp->int_dstaddr.a;
	    } else if (ifp->int_state & IFS_NOAGE) {
		continue;
	    } else {
		dst = &ifp->int_addr.a;
	    }
	    (*func) (tp, dst, &ifp->int_addr.a, ifp, NULL, TRUE, flash_update);
	} IF_LISTEND(ifp) ;
    }
    GW_LIST(gw_list, gwp) {
	if (gwp->gw_flags & GWF_SOURCE) {
	    if ((ifp = if_withdst(&gwp->gw_addr)) <= (if_entry *) 0) {
		trace(TR_ALL, LOG_ERR, "task_toall: Source gateway %A not on same net",
		      &gwp->gw_addr);
		continue;
	    }
	    if ((ifp->int_state & (if_flag | IFS_UP)) != IFS_UP) {
		/* XXX -Should no rip out apply to source[rip|hello]gateways */
		continue;
	    }
	    (*func) (tp, &gwp->gw_addr, &ifp->int_addr.a, ifp, gwp, TRUE, flash_update);
	}
    } GW_LISTEND;

    tp->task_rtrevision = rt_revision;
}


/*
 *	Allocate a task block with the specified name
 */
task *
task_alloc(name)
const char *name;
{
    task *tp;

    if (!(tp = (task *) calloc(1, sizeof(task)))) {
	trace(TR_ALL, LOG_ERR, "task_alloc: calloc: %m");
	quit(errno);
    }
    tp->task_name = name;
    tp->task_terminate = task_delete;
    tp->task_socket = -1;

    trace(TR_TASK, 0, "task_alloc: allocated task block for %s", tp->task_name);
    return (tp);
}


/*
 *	Build a task block and add to the linked list
 */
int
task_create(tp, maxsize)
task *tp;
int maxsize;
{

    if (tp->task_flash) {
	tp->task_rtrevision = rt_revision;
    }
    if (tp->task_socket != -1) {
	task_set_socket(tp, tp->task_socket);
    }
    /*
     *	Set maximum receive buffer size
     */
    if (maxsize > recv_iovec[RECV_IOVEC_DATA].iov_len) {
	recv_iovec[RECV_IOVEC_DATA].iov_len = maxsize;
	trace(TR_TASK, 0, "task_create: receive buffer size set to %d", maxsize);
    }
    insque((struct qelem *) tp, (struct qelem *) & task_head);	/* Insert at the top of the task queue */

    tracef("task_create: %s",
	   task_name(tp));

    if (tp->task_proto) {
	tracef("  proto %d",
	       tp->task_proto);
    }
    if (tp->task_addr.in.sin_port) {
	tracef("  port %d",
	       ntohs(tp->task_addr.in.sin_port));
    }
    if (tp->task_socket != -1) {
	tracef("  socket %d",
	       tp->task_socket);
    }
    if (tp->task_rtproto) {
	tracef("  rt_proto <%s>",
	       trace_bits(rt_proto_bits, tp->task_rtproto));
    }
    trace(TR_TASK, 0, NULL);

    return (1);
}


/*
 * Terminate a subprocess
 */
static void
task_kill(tp)
task *tp;
{
    kill(tp->task_pid, SIGTERM);
}


/*
 * Spawn a process and create a task for it.
 */
int
task_fork(tp)
task *tp;
{
    int rc = 0;

    if (!(tp->task_pid = fork())) {
	tp->task_pid = my_pid = getpid();

	trace(TR_TASK, 0, "task_fork: %s forked",
	      task_name(tp));

	if (tp->task_process) {
	    tp->task_process(tp);
	}
	trace(TR_TASK, 0, "task_fork: %s exiting",
	      task_name(tp));

	exit(0);
    }
    if (tp->task_pid < 0) {
	trace(TR_ALL, LOG_ERR, "task_fork: could not fork %s: %m",
	      task_name(tp));
	task_delete(tp);
    } else {
	tp->task_terminate = task_kill;
	rc = task_create(tp, 0);
    }

    return rc;
}


/*  */

int
task_ioctl(fd, cmd, data, len)
int fd;
int cmd;
caddr_t data;
int len;
{
#if	!defined(SYSV)
    return ioctl(fd, cmd, data);
#else	/* !defined(SYSV) */
    struct strioctl si;
 
    si.ic_cmd = cmd;
    si.ic_timout = 0;
    si.ic_len = len;
    si.ic_dp = dp;
    
    return ioctl(fd, I_STR, &si);
#endif	/* !defined(SYSV) */
}

/**/

void
task_set_socket(tp, socket)
task *tp;
int socket;
{

    tp->task_socket = socket;

    trace(TR_TASK, 0, "task_set_socket: task %s socket %d",
	  task_name(tp),
	  tp->task_socket);

    /* Allocate space for socket to task index */
    if (!task_socket_tasks) {
	task_socket_tasks = (task **) calloc(1, sizeof(task *) * getdtablesize());
	if (!task_socket_tasks) {
	    trace(TR_ALL, LOG_ERR, "task_set_socket: calloc: %m");
	    quit(errno);
	}
    }
    if (tp->task_recv || tp->task_accept) {
	FD_SET(tp->task_socket, &task_select_readbits);
    }
    if (tp->task_write || tp->task_connect) {
	FD_SET(tp->task_socket, &task_select_writebits);
    }
    if (tp->task_except) {
	FD_SET(tp->task_socket, &task_select_exceptbits);
    }
    if (task_socket_tasks[tp->task_socket] && (task_socket_tasks[tp->task_socket] != tp)) {
	tracef("task_set_socket: attempt to assign socket %d to task %s ",
	       tp->task_socket,
	       task_name(tp));
	trace(TR_ALL, LOG_ERR, "socket already assigned to task %s",
	      task_name(task_socket_tasks[tp->task_socket]));
	quit(EBADF);
    }
    task_socket_tasks[tp->task_socket] = tp;
}


void
task_reset_socket(tp)
task *tp;
{

    trace(TR_TASK, 0, "task_reset_socket: task %s socket %d",
	  task_name(tp),
	  tp->task_socket);

    FD_CLR(tp->task_socket, &task_select_readbits);
    FD_CLR(tp->task_socket, &task_select_writebits);
    FD_CLR(tp->task_socket, &task_select_exceptbits);

    /* Delete from socket to task table if no routines present */
    if (!task_socket_tasks[tp->task_socket]) {
	trace(TR_ALL, LOG_ERR, "task_reset_socket: attempt to release socket %d by task %s - socket not assigned",
	      tp->task_socket,
	      task_name(tp));
	quit(EBADF);
    }
    task_socket_tasks[tp->task_socket] = (task *) 0;
    tp->task_socket = -1;
    tp->task_flags &= ~(TASKF_CONNECT | TASKF_ACCEPT);
    tp->task_recv = (void (*) ()) 0;
    tp->task_accept = (void (*) ()) 0;
    tp->task_write = (void (*) ()) 0;
    tp->task_connect = (void (*) ()) 0;
    tp->task_except = (void (*) ()) 0;
}


/*
 *	task_socket_options - Sets socket options.  Isolates protocols from system layer.
 */
int
task_set_option(tp, option, value)
task *tp;
int option;
caddr_t value;
{
    int opt;
    int rc = 0;
    int value_int;
    int len = sizeof(value_int);
    caddr_t ptr = (caddr_t) & value_int;

#ifdef	LINGER_PARAM
    struct linger linger;

#endif				/* LINGER_PARAM */
    int level = SOL_SOCKET;

    tracef("task_set_option: task %s socket %d option %s(%d)",
	   task_name(tp),
	   tp->task_socket,
	   trace_state(task_socket_options, option),
	   option);

    switch (option) {
	case TASKOPTION_RECVBUF:
#ifdef	SO_RCVBUF
	    opt = SO_RCVBUF;
	    goto int_value;
#else				/* SO_RCVBUF */
	    break;
#endif				/* SO_RCVBUF */

	case TASKOPTION_SENDBUF:
#ifdef	SO_SNDBUF
	    opt = SO_SNDBUF;
	    goto int_value;
#else				/* SO_SNDBUF */
	    break;
#endif				/* SO_SNDBUF */

	case TASKOPTION_LINGER:
	    opt = SO_LINGER;
#ifdef	LINGER_PARAM
	    linger.l_linger = (int) value;
	    linger.l_onoff = linger.l_linger ? TRUE : FALSE;
	    ptr = (caddr_t) & linger;
	    len = sizeof(struct linger);
	    tracef(" value { %d, %d }",
		   linger.l_linger,
		   linger.l_onoff);
#else				/* LINGER_PARAM */
	    ptr = 0;
	    len = 0;
#endif				/* LINGER_PARAM */
	    goto setsocketopt;

	case TASKOPTION_REUSEADDR:
	    opt = SO_REUSEADDR;
	    goto int_value;

	case TASKOPTION_BROADCAST:
#ifdef	SO_BROADCAST
	    opt = SO_BROADCAST;
	    goto int_value;
#else				/* SO_BROADCAST */
	    break;
#endif				/* SO_BROADCAST */

	case TASKOPTION_DONTROUTE:
	    opt = SO_DONTROUTE;
	    goto int_value;

	case TASKOPTION_KEEPALIVE:
	    opt = SO_KEEPALIVE;
	    goto int_value;

	case TASKOPTION_DEBUG:
	    opt = SO_DEBUG;
	    goto int_value;

	case TASKOPTION_USELOOPBACK:
	    opt = SO_USELOOPBACK;
	    goto int_value;

	int_value:
	    value_int = (int) value;
	    tracef(" value %d",
		   value);
	    /* goto setsocketopt; */

	setsocketopt:
	    if (!test_flag) {
		rc = setsockopt(tp->task_socket, level, opt, ptr, len);
	    }
	    break;

	case TASKOPTION_NONBLOCKING:
	    value_int = (int) value;
	    tracef(" value %d",
		   value);
	    if (!test_flag) {
#ifdef	SYSV
		rc = fcntl(tp->task_socket, F_SETFL, O_NDELAY);
#else	/* SYSV */
		rc = task_ioctl(tp->task_socket, FIONBIO, (caddr_t) & value_int, sizeof (value_int));
#endif	/* SYSV */
	    }
	    break;

	default:
	    rc = -1;
	    errno = EINVAL;
    }

    if (rc < 0) {
	trace(TR_ALL, LOG_ERR, ": %m");
    } else {
	trace(TR_TASK, 0, NULL);
    }

    return (rc);
}

/*
 * task_init()   set up for receiving signals and other initialization
 */
void
task_init()
{
#ifndef	SYSV
    struct sigvec vec, ovec;

    /* Set up signals to block */
    SIGNAL_LIST(ip) {
	task_signal_mask |= sigmask(*ip);
    } SIGNAL_LIST_END(ip) ;

    /* Setup signal processing */
    memset((char *) &vec, (char) 0, sizeof(struct sigvec));
    vec.sv_mask = task_signal_mask;
    vec.sv_handler = task_receive_signal;
#endif	/* SYSV */

    SIGNAL_LIST(ip) {
#ifdef	SYSV
	sigset (*ip, task_receive_signal);
#else	/* SYSV */
	if (sigvec(*ip, &vec, &ovec)) {
	    trace(TR_ALL, LOG_ERR, "task_init: sigvec SIG%s: %m", trace_state(signal_names, *ip));
	    quit(errno);
	}
#endif	/* SYSV */
    } SIGNAL_LIST_END(ip) ;
}


/*
 *	Dump task information to dump file
 */
void
task_dump(fd)
FILE *fd;
{
    int i;
    int first;
    int socket;
    task *tp;
    timer *tip;

    /* Print out task blocks */
    (void) fprintf(fd, "Task and Timers:\n\n");
    TASK_TABLE(tp) {
	(void) fprintf(fd, "\t%s",
		       task_name(tp));

	if (tp->task_proto) {
	    (void) fprintf(fd, "\tProto %3d",
			   tp->task_proto);
	}
	if (ntohs(tp->task_addr.in.sin_port)) {
	    (void) fprintf(fd, "\tPort %5u",
			   ntohs(tp->task_addr.in.sin_port));
	}
	if (tp->task_socket != -1) {
	    (void) fprintf(fd, "\tSocket %2d",
			   tp->task_socket);
	}
	if (tp->task_rtproto) {
	    (void) fprintf(fd, "\tRtProto %s",
			   trace_bits(rt_proto_bits, tp->task_rtproto));
	}
	if (tp->task_flags) {
	    (void) fprintf(fd, "\t<%s>",
			   trace_bits(task_flag_bits, tp->task_flags));
	}
	(void) fprintf(fd, "\n");
	first = TRUE;
	for (i = 0; i < TASK_TIMERS; i++) {
	    if (tp->task_timer[i]) {
		if (first) {
		    (void) fprintf(fd, "\n");
		    first = FALSE;
		}
		timer_dump(fd, tp->task_timer[i]);
	    }
	}
	(void) fprintf(fd, "\n");
    } TASK_TABLEEND(tp);
    (void) fprintf(fd, "\n");

    /* Print timers that are not associated with tasks */
    first = TRUE;
    TIMER_ACTIVE(tip) {
	if (!tip->timer_task) {
	    if (first) {
		(void) fprintf(fd, "\tTimers without tasks:\n\n");
		first = FALSE;
	    }
	    timer_dump(fd, tip);
	}
    } TIMER_ACTIVEEND(tip);

    TIMER_INACTIVE(tip) {
	if (!tip->timer_task) {
	    if (first) {
		(void) fprintf(fd, "\tTimers without tasks:\n\n");
		first = FALSE;
	    }
	    timer_dump(fd, tip);
	}
    } TIMER_INACTIVEEND(tip);

    if (!first) {
	(void) fprintf(fd, "\n");
    }
    /* Print mapping of sockets to tasks */
    (void) fprintf(fd, "Task to socket mapping:\n\n");
    for (socket = 0; socket <= task_max_socket; socket++) {
	if (tp = task_socket_tasks[socket]) {
	    (void) fprintf(fd, "\tsocket: %d\ttask: %s\n",
			   socket,
			   task_name(tp));
	}
    }
    (void) fprintf(fd, "\n");

    /* Do task-specific dumps */
    TASK_TABLE(tp) {
	if (tp->task_dump) {
	    tp->task_dump(fd);
	}
    } TASK_TABLEEND(tp);
}

static bits domains[AF_MAX + 1] =
{
    {AF_UNSPEC, "UNSPEC"},
    {AF_UNIX, "UNIX"},
    {AF_INET, "INET"},
    {AF_IMPLINK, "IMPLINK"},
    {AF_PUP, "PUP"},
    {AF_CHAOS, "CHAOS"},
    {AF_NS, "NS"},
#ifdef	AF_ISO
    {AF_ISO, "ISO"},
#else				/* AF_ISO */
    {AF_NBS, "NBS"},
#endif				/* AF_ISO */
    {AF_ECMA, "ECMA"},
    {AF_DATAKIT, "DATAKIT"},
    {AF_CCITT, "CCITT"},
    {AF_SNA, "SNA"},
#ifdef	AF_DECnet
    {AF_DECnet, "DECnet"},
    {AF_DLI, "DLI"},
    {AF_LAT, "LAT"},
    {AF_HYLINK, "HYLINK"},
    {AF_APPLETALK, "APPLETALK"},
#endif				/* AF_DECnet */
#ifdef	AF_ROUTE
    {AF_ROUTE, "Route"},
#endif				/* AF_ROUTE */
#ifdef	AF_NIT
    {AF_NIT, "NIT"},
#endif				/* AF_NIT */
};

static bits types[5] =
{
    {SOCK_STREAM, "STREAM"},
    {SOCK_DGRAM, "DGRAM"},
    {SOCK_RAW, "RAW"},
    {SOCK_RDM, "RDM"},
    {SOCK_SEQPACKET, "SEQPACKET"},
};

/*
 * task_get_socket gets a socket, retries later if no buffers at present
 */

int
task_get_socket(domain, type, protocol)
int domain, type, protocol;
{
    int retry, get_socket, error;

    if (test_flag) {
	if (task_max_socket) {
	    get_socket = ++task_max_socket;
	} else {
	    /* Skip first few that may be used for logging */
	    get_socket = (task_max_socket = 2);
	}
    } else {
	retry = 2;			/* if no buffers a retry might work */
	while ((get_socket = socket(domain, type, protocol)) < 0 && retry--) {
	    error = errno;
	    trace(TR_ALL, LOG_ERR, "task_get_socket: socket: %m");
	    if (error == ENOBUFS) {
		sleep(5);
	    } else {
		break;
	    }
	}
    }

    trace(TR_TASK, 0, "task_get_socket: domain AF_%s  type SOCK_%s  protocol %d  socket %d",
	  trace_state(domains, domain),
	  trace_state(types, type - 1),
	  protocol,
	  get_socket);

    if (get_socket >= getdtablesize()) {
	trace(TR_ALL, LOG_ERR, "task_get_socket: Too many sockets for select mask");
	quit(EMFILE);
    }
    if (get_socket > task_max_socket) {
	task_max_socket = get_socket;
    }
    return (get_socket);
}


/*
 *	Path names
 */
#ifndef	vax11c
char *
task_getwd()
{
    static char path_name[MAXPATHLEN];
    
    if (!getwd(path_name)) {
	trace(TR_ALL, LOG_ERR, "task_getwd: getwd: %s",
	      path_name);
	quit(ENOENT);
    }

    return path_name;
}


int
task_chdir(path_name)
char *path_name;
{
    int rc;
    
    if (rc = chdir(path_name)) {
	trace(TR_ALL, LOG_ERR, "task_cwd: chdir: %m");
    }

    return rc;
}


void
task_getpaths()
{
    char *path = task_getwd();
    int len = strlen(path);
    
    /* Remember directory we were started from */
    task_path_name = (caddr_t) malloc(len);
    strcpy(task_path_name, path);
}
#endif	/* vax11c */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.