





























#include <stdio.h>
#include <netdb.h>
#include <errno.h>
#include <sys/time.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <rpc/types.h>
#include <rpc/xdr.h>
#include <netinet/in.h>
#include "debug.h"
#include "except.h"
#include "trace.h"
#include "expr.h"
#include "sched.h"
#include "manager.h"
#include "condor_types.h"
#include "clib.h"

static char *_FileName_ = __FILE__;		/* Used by EXCEPT (see except.h)     */

char		*param(), *strdup(), *index();
XDR			*xdr_Init(), *xdr_Udp_Init();
CONTEXT		*create_context();
MACH_REC	*create_mach_rec(), *find_rec(), *find_server();
EXPR		*build_expr();

#define NEG_INFINITY	(1<<31)	/* ASSUMES 32 bit 2's COMPLIMENT ARITHMETIC! */

extern int	Terse;
extern int	Silent;

char	*Log;
char	*NegotiatorLog;
char	*CollectorHost;
int		MaxNegotiatorLog;
int		NegotiatorInterval;
int		MachineUpdateInterval;
int		SchedD_Port;
int		ClientTimeout;
int		Foreground;
int		Termlog;
int		ClientSock = -1;
int		CommandSock = -1;
int		ConnectionSock;
int		UdpSock;
struct hostent		*Collector_Hostentp;

MACH_REC	*MachineList;
CONTEXT		*NegotiatorContext;
PRIO_REC	 DummyPrioRec, *Endmarker = &DummyPrioRec;

char *MyName;

usage( name )
char	*name;
{
	dprintf( D_ALWAYS, "Usage: %s [-f] [-t]\n", name );
	exit( 1 );
}


main( argc, argv )
int		argc;
char	*argv[];
{
	int		alarm_handler(), sigpipe_handler();
	int		sigint_handler(), sighup_handler();
	int		count;
	char	**ptr;
	fd_set	readfds;
	struct timeval timer;

	MyName = *argv;
	NegotiatorContext = create_context();
	config( argv[0], NegotiatorContext );

	init_params();
	Terse = TRUE;
	Silent = TRUE;

	if( argc > 3 ) {
		usage( argv[0] );
	}
	for( ptr=argv+1; *ptr; ptr++ ) {
		if( ptr[0][0] != '-' ) {
			usage( argv[0] );
		}
		switch( ptr[0][1] ) {
			case 'f':
				Foreground++;
				break;
			case 't':
				Termlog++;
				break;
			default:
				usage( argv[0] );
		}
	}

		/* This is so if we dump core it'll go in the log directory */
	if( chdir(Log) < 0 ) {
		EXCEPT( "chdir to log directory <%s>", Log );
	}

		/* Arrange to run in background */
	if( !Foreground ) {
		if( fork() )
			exit( 0 );
	}

		/* Set up logging */
	dprintf_config( "NEGOTIATOR", 2 );

	dprintf( D_ALWAYS, "*************************************************\n" );
	dprintf( D_ALWAYS, "***       CONDOR_NEGOTIATOR STARTING UP       ***\n" );
	dprintf( D_ALWAYS, "*************************************************\n" );
	dprintf( D_ALWAYS, "\n" );

	if( signal(SIGINT,sigint_handler) == BADSIG ) {
		EXCEPT( "signal(SIGINT,0x%x)", sigint_handler );
	}
	if( signal(SIGHUP,sighup_handler) == BADSIG ) {
		EXCEPT( "signal(SIGHUP,0x%x)", sighup_handler );
	}

	ConnectionSock = init_tcp_sock( "condor_negotiator", NEGOTIATOR_PORT );
	UdpSock = udp_connect( CollectorHost, COLLECTOR_UDP_PORT );

	if( signal(SIGPIPE,sigpipe_handler) == BADSIG ) {
		EXCEPT( "signal(SIGPIPE,0x%x)", sigpipe_handler );
	}

	if( signal(SIGALRM,alarm_handler) == BADSIG ) {
		EXCEPT( "signal(SIGALRM,0x%x)", alarm_handler );
	}

	MachineList = create_mach_rec( (MACH_REC *)NULL );

	timer.tv_sec = NegotiatorInterval;
	timer.tv_usec = 0;
	FD_ZERO( &readfds );

	for(;;) {

		FD_SET( ConnectionSock, &readfds );

		/*
		** dprintf( D_FULLDEBUG, "Selecting\n" );
		*/
#ifdef AIX31
		errno = EINTR;	/* Shouldn't have to do this... */
#endif AIX31
		count = select(FD_SETSIZE, (int *)&readfds, (int *)0, (int *)0,
											(struct timeval *)&timer );
		if( count < 0 ) {
			if( errno == EINTR ) {
				dprintf( D_ALWAYS, "select() interrupted, restarting...\n" );
				continue;
			} else {
				EXCEPT( "select() returns %d, errno = %d", count, errno );
			}
		}
		/*
		** dprintf( D_FULLDEBUG, "Select returned %d\n", NFDS(count) );
		*/

		if( NFDS(count) == 0 ) {
			reschedule();
		} else if( FD_ISSET(ConnectionSock,&readfds) ) {
			accept_tcp_connection();
		} else {
			EXCEPT( "select: unknown connection, readfds = 0x%x, count = %d",
														readfds, NFDS(count) );
		}
	}
}


init_tcp_sock( service, port )
char	*service;
int		port;
{
	struct sockaddr_in	sin;
	struct servent *servp;
	caddr_t		on = (caddr_t)1;
	int		sock;

	bzero( (char *)&sin, sizeof sin );
	servp = getservbyname(service, "tcp");
	if( servp ) {
		sin.sin_port = htons( (u_short)servp->s_port );
	} else {
		sin.sin_port = htons( (u_short)port );
	}

	if( (sock=socket(AF_INET,SOCK_STREAM,0)) < 0 ) {
		EXCEPT( "socket" );
	}

	if( setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) < 0) {
		EXCEPT( "setsockopt" );
	}

	if( bind(sock,(struct sockaddr *)&sin,sizeof(sin)) < 0 ) {
		if( errno == EADDRINUSE ) {
			EXCEPT( "CONDOR_COLLECTOR ALREADY RUNNING" );
		} else {
			EXCEPT( "bind" );
		}
	}

	if( listen(sock,5) < 0 ) {
		EXCEPT( "listen" );
	}

	return sock;
}

accept_tcp_connection()
{
	struct sockaddr_in	from;
	int		len;
	XDR		xdr, *xdrs, *xdr_Init();

	len = sizeof from;
	bzero( (char *)&from, sizeof from );
	CommandSock = accept( ConnectionSock, (struct sockaddr *)&from, &len );
	dprintf( D_FULLDEBUG, "Opened %d at %d\n", CommandSock, __LINE__ );

	if( CommandSock < 0 && errno != EINTR ) {
		EXCEPT( "accept" );
	}

	xdrs = xdr_Init( &CommandSock, &xdr );

	(void)alarm( (unsigned)ClientTimeout );	/* don't hang here forever */
	do_command( xdrs );
	(void)alarm( 0 );				/* cancel alarm */

	xdr_destroy( xdrs );
	if( CommandSock >= 0 ) {
		(void)close( CommandSock );
		dprintf( D_FULLDEBUG, "Closed %d at %d\n", CommandSock, __LINE__ );
		CommandSock = -1;
	}
}

/*
** Somebody has connected to our socket with a request.  Read the request
** and handle it.
*/
do_command( xdrs )
XDR		*xdrs;
{
	int		cmd;

		/* Read the request */
	xdrs->x_op = XDR_DECODE;
	if( !xdr_int(xdrs,&cmd) ) {
		dprintf( D_ALWAYS, "Can't receive command from client\n" );
		return;
	}

	switch( cmd ) {
		case RESCHEDULE:	/* Please do your scheduling early (now) */
			dprintf( D_ALWAYS, "Got RESCHEDULE request\n" );
			(void)alarm( 0 );	/* cancel alarm, this could take awhile */
			reschedule();
			break;
		default:
			EXCEPT( "Got unknown command (%d)\n", cmd );
	}
}

#define CLOSE_XDR_STREAM \
	xdr_destroy( xdrs ); \
	(void)close( sock ); \
	dprintf( D_FULLDEBUG, "Closed %d at %d\n", sock, __LINE__ );

reschedule()
{
	int			sock = -1;
	int			cmd;
	XDR			xdr, *xdrs = NULL;
	MACH_REC	record, *rec = &record;


		/* Connect to the collector */
	if( (sock = connect_to_collector()) < 0 ) {
		dprintf( D_ALWAYS, "Can't connect to CONDOR Collector\n" );
		return;
	}
	xdrs = xdr_Init( &sock, &xdr );
	xdrs->x_op = XDR_ENCODE;

	cmd = GIVE_STATUS;
	if( !xdr_int(xdrs, &cmd) ) {
		dprintf( D_ALWAYS, "1. Can't send GIVE_STATUS command\n" );
		CLOSE_XDR_STREAM;
		return;
	}
	if( !xdrrec_endofrecord(xdrs,TRUE) ) {
		dprintf( D_ALWAYS, "2. Can't send GIVE_STATUS command\n" );
		CLOSE_XDR_STREAM;
		return;
	}

	xdrs->x_op = XDR_DECODE;
	for(;;) {
		bzero( (char *)rec, sizeof(MACH_REC) );
		rec->machine_context = create_context();
		if( !xdr_mach_rec(xdrs,rec) ) {
			dprintf( D_ALWAYS, "Can't get machine record from collector\n" );
			free_context( rec->machine_context );
			CLOSE_XDR_STREAM;
			return;
		}
		if( !rec->name ) {
			free_context( rec->machine_context );
			break;
		}
		if( !rec->name[0] ) {
			FREE( rec->name );
			free_context( rec->machine_context );
			break;
		}
		update_machine_info( rec );
		free_context( rec->machine_context );
		FREE( rec->name );
	}
	/*
	** display_mach_list();
	*/

	CLOSE_XDR_STREAM;
	do_negotiations();
	update_collector();
}
#undef CLOSE_XDR_STREAM

update_collector()
{
	int			cmd;
	XDR			xdr, *xdrs;
	MACH_REC	*rec;

	dprintf( D_FULLDEBUG, "Called update_collector()\n" );

	xdrs = xdr_Udp_Init( &UdpSock, &xdr );
	xdrs->x_op = XDR_ENCODE;

		/* Send the command */
	cmd = NEGOTIATOR_INFO;
	if( !xdr_int(xdrs, &cmd) ) {
		xdr_destroy( xdrs );
		return;
	}

	for( rec = MachineList->next; rec->name; rec = rec->next ) {
		if( !xdr_mywrapstring(xdrs,&rec->name) ) {
			dprintf( D_ALWAYS, "Can't send machine name to collector\n" );
			xdr_destroy( xdrs );
			return;
		}
		if( !xdr_int(xdrs,&rec->prio) ) {
			dprintf( D_ALWAYS, "Can't send machine prio to collector\n" );
			xdr_destroy( xdrs );
			return;
		}
	}
	if( !xdr_prio_rec(xdrs,Endmarker) ) {
		dprintf( D_ALWAYS, "Can't send ENDMARKER to collector\n" );
		xdr_destroy( xdrs );
		return;
	}
	if( !xdrrec_endofrecord(xdrs,TRUE) ) {
		xdr_destroy( xdrs );
		return;
	}

	xdr_destroy( xdrs );
}

init_params()
{
	struct sockaddr_in	sin;
	struct servent 		*servp;
	char				*tmp;


	if( param("NEGOTIATOR_DEBUG") == NULL ) {
		EXCEPT( "NEGOTIATOR_DEBUG not specified in config file\n" );
	}
	Foreground = boolean( "NEGOTIATOR_DEBUG", "Foreground" );

	Log = param( "LOG" );
	if( Log == NULL )  {
		EXCEPT( "No log directory specified in config file\n" );
	}

	NegotiatorLog = param( "NEGOTIATOR_LOG" );
	if( NegotiatorLog == NULL ) {
		EXCEPT( "No log file specified in config file\n" );
	}

	tmp = param( "MAX_NEGOTIATOR_LOG" );
	if( tmp == NULL ) {
		MaxNegotiatorLog = 64000;
	} else {
		MaxNegotiatorLog = atoi( tmp );
	}

	tmp = param( "NEGOTIATOR_INTERVAL" );
	if( tmp == NULL ) {
		NegotiatorInterval = 120;
	} else {
		NegotiatorInterval = atoi( tmp );
	}

	tmp = param( "MACHINE_UPDATE_INTERVAL" );
	if( tmp == NULL ) {
		MachineUpdateInterval = 300;
	} else {
		MachineUpdateInterval = atoi( tmp );
	}

	tmp = param( "CLIENT_TIMEOUT" );
	if( tmp == NULL ) {
		ClientTimeout = 30;
	} else {
		ClientTimeout = atoi( tmp );
	}

	bzero( (char *)&sin, sizeof sin );
	servp = getservbyname("condor_schedd", "tcp");
	if( servp ) {
		SchedD_Port = servp->s_port;
	} else {
		SchedD_Port = htons(SCHED_PORT);
	}

	if( (CollectorHost = param("COLLECTOR_HOST")) == NULL ) {
		EXCEPT( "COLLECTOR_HOST not specified in config file\n" );
	}
	Collector_Hostentp = gethostbyname( CollectorHost );
	if( Collector_Hostentp == NULL ) {
#if defined(vax) && !defined(ultrix)
		herror( "gethostbyname" );
#endif defined(vax) && !defined(ultrix)
		EXCEPT( "Can't find host \"%s\" (Nameserver down?)\n", CollectorHost );
	}
}

SetSyscalls(){}

update_machine_info( template )
MACH_REC	*template;
{
	MACH_REC	*rec;

	if( (rec = find_rec(template)) == NULL ) {
		rec = create_mach_rec( template );
		insque( (struct qelem *)rec, (struct qelem *)MachineList );
		/*
		** dprintf(D_FULLDEBUG,"Createing new record for \"%s\"\n",rec->name);
		*/
	} else {
		/*
		** dprintf( D_FULLDEBUG, "Updating record for \"%s\"\n", rec->name );
		*/
	}

	update_context( template->machine_context, rec->machine_context );
	rec->time_stamp = template->time_stamp;
	rec->busy = template->busy;
}

MACH_REC	*
create_mach_rec( template )
MACH_REC	*template;
{
	MACH_REC	*new;

	new = (MACH_REC *)CALLOC( 1, sizeof(MACH_REC) );

	new->next = new;
	new->prev = new;

	if( template == NULL ) {
		return new;
	}

	new->name = strdup( template->name );
	new->net_addr = template->net_addr;
	new->net_addr_type = template->net_addr_type;
	new->machine_context = create_context();
	return new;
}

MACH_REC	*
find_rec( new )
MACH_REC	*new;
{
	MACH_REC	*ptr;

	for( ptr = MachineList->next; ptr->name; ptr = ptr->next ) {
		if( ptr->net_addr.s_addr == new->net_addr.s_addr ) {
			return ptr;
		}
	}
	return NULL;
}

update_context( src, dst )
CONTEXT	*src;
CONTEXT	*dst;
{
	int		i;

	for( i=0; i<src->len; i++ ) {
		store_stmt( src->data[i], dst );
		src->data[i] = NULL;
	}
	src->len = 0;
}

do_negotiations()
{
	MACH_REC	*ptr, *find_next();
	int			idle;

	update_priorities();
	sort_machine_list();

	for( ptr = MachineList->next; ptr->name; ptr = ptr->next ) {

		if( (int)time( (time_t *)0 ) - ptr->time_stamp >
													MachineUpdateInterval ) {
			/*
			** dprintf( D_FULLDEBUG,
			** "Not negotiating with %s - stale record\n", ptr->name );
			*/
			continue;
		}

		if( evaluate_int("Idle",&idle,ptr->machine_context,(CONTEXT *)0) < 0 ) {
			dprintf( D_ALWAYS,
			"Not negotiating with %s - can't evaluate \"Idle\"\n", ptr->name );
			continue;
		}
		if( idle <= 0 ) {
			/*
			** dprintf( D_FULLDEBUG,
			** "Not negotiating with %s - no idle jobs\n", ptr->name );
			*/
			continue;
		}

		dprintf( D_FULLDEBUG, "Negotiating with %s\n", ptr->name );
		negotiate( ptr );
	}
}

#define SUCCESS 1
#define FAIL 0
#undef TRACE
#define TRACE dprintf(D_ALWAYS,"%s:%d\n",__FILE__,__LINE__)

negotiate( rec )
MACH_REC	*rec;
{

	MACH_REC	*next, *find_next();
	XDR			xdr, *xdrs = NULL, *xdr_Init();
	int		next_prio;
	int		status;

	if( next = find_next(rec) ) {
		next_prio = next->prio;
	} else {
		next_prio = NEG_INFINITY;
	}

	if( (ClientSock = call_schedd(rec,10)) < 0 ) {
		dprintf( D_ALWAYS, "Can't connect to SchedD on %s\n", rec->name );
		return;
	}

	(void)alarm( (unsigned)ClientTimeout );	/* don't hang here forever */
	xdrs = xdr_Init( &ClientSock, &xdr );
	status = snd_int( xdrs, NEGOTIATE, FALSE );
	(void)alarm( 0 );				/* cancel alarm */

	if( !status ) {
		dprintf( D_ALWAYS, "1.Error negotiating with %s\n", rec->name );
		xdr_destroy( xdrs );
		return;
	}

	while( rec->prio > next_prio ) {

		(void)alarm( (unsigned)ClientTimeout );	/* don't hang here forever */
		status = simple_negotiate( rec, xdrs );
		(void)alarm( (unsigned)0 );

		if( status != SUCCESS ) {
			break;
		}
	}

	(void)alarm( (unsigned)ClientTimeout );	/* don't hang here forever */
	status = snd_int( xdrs, END_NEGOTIATE, TRUE );
	(void)alarm( 0 );				/* cancel alarm */

	if( !status ) {
		dprintf( D_ALWAYS, "2.Error negotiating with %s\n", rec->name );
		xdr_destroy( xdrs );
		return;
	}

	xdr_destroy( xdrs );
	(void)close( ClientSock );
	ClientSock = -1;
}

simple_negotiate( rec, xdrs )
MACH_REC	*rec;
XDR			*xdrs;
{
	int			op;
	MACH_REC	*server;
	CONTEXT		*job_context = NULL;

	for(;;) {
		(void)alarm( (unsigned)ClientTimeout );	/* reset the alarm every time */
		if( !snd_int(xdrs,SEND_JOB_INFO,TRUE) ) {
			dprintf( D_ALWAYS, "3.Error negotiating with %s\n", rec->name );
			return ERROR;
		}

		errno = 0;
		if( !rcv_int(xdrs,&op,FALSE) ) {
			dprintf( D_ALWAYS, "4.Error negotiating with %s\n", rec->name );
			dprintf( D_ALWAYS, "errno = %d\n", errno );
			return ERROR;
		}

		switch( op ) {
			case JOB_INFO:
				job_context = create_context();
				if( !rcv_context(xdrs,job_context) ) {
					dprintf(D_ALWAYS,
							"5.Error negotiating with %s\n", rec->name );
					free_context( job_context );
					return ERROR;
				}


				if( server = find_server(job_context) ) {
					dprintf( D_ALWAYS,
						"Allowing %s to run on %s\n",
						rec->name, server->name
					);
					if( !snd_int(xdrs,PERMISSION,TRUE) ) {
						dprintf( D_ALWAYS,
						"6.Error negotiating with %s\n", rec->name );
						free_context( job_context );
						return ERROR;
					}
					if( !snd_string(xdrs,server->name,TRUE) ) {
						dprintf( D_ALWAYS,
						"7.Error negotiating with %s\n", rec->name );
						free_context( job_context );
						return ERROR;
					}
					decrement_idle( rec );
					server->busy = TRUE;
					rec->prio -= 1;
					free_context( job_context );
					return SUCCESS;
				} else {	/* Can't find a server for it */
					if( !snd_int(xdrs,REJECTED,TRUE) ) {
						dprintf( D_ALWAYS,
						"8.Error negotiating with %s\n", rec->name );
						free_context( job_context );
						return ERROR;
					}
				}
				free_context( job_context );
				break;
			case NO_MORE_JOBS:
				xdrrec_skiprecord( xdrs );
				dprintf( D_FULLDEBUG, "Done negotiating with %s\n", rec->name );
				return FAIL;
			default:
				dprintf( D_ALWAYS, "%s sent unknown op (%d)\n", rec->name, op );
				return ERROR;
		}
	}
}

update_priorities()
{
	MACH_REC	*ptr;

	dprintf( D_FULLDEBUG, "Entered update_priorities()\n" );
	for( ptr=MachineList->next; ptr->name; ptr = ptr->next ) {
		/*
		dprintf( D_FULLDEBUG,
			"Updating priority for \"%s\" ", ptr->name );
		*/
		ptr->prio = update_prio( NegotiatorContext, ptr );
	}
}


update_prio( nego, rec )
CONTEXT		*nego;
MACH_REC	*rec;
{
	int		new_prio;
	int		inactive;
	ELEM	tmp;

	tmp.type = INT;
	tmp.i_val = rec->prio;
	store_stmt( build_expr("Prio",&tmp), rec->machine_context );

	if( evaluate_bool("INACTIVE",&inactive,nego,rec->machine_context) < 0 ) {
		EXCEPT( "Can't evaluate \"INACTIVE\"" );
	}
	if( inactive ) {
		if( rec->prio > 0 ) {
			new_prio = rec->prio - 1;
		} else if( rec->prio < 0 ) {
			new_prio = rec->prio + 1;
		} else {
			new_prio = 0;
		}
	} else {
		if(evaluate_int("UPDATE_PRIO",&new_prio,nego,rec->machine_context)< 0) {
			EXCEPT( "Can't evaluate \"UPDATE_PRIO\"" );
		}
	}
	/*
	dprintf( D_FULLDEBUG | D_NOHEADER, "from %d to %d\n", rec->prio, new_prio );
	*/

	tmp.i_val = new_prio;
	store_stmt( build_expr("Prio",&tmp), rec->machine_context );
	return new_prio;
}

sort_machine_list()
{
	MACH_REC	*sorted_list;
	MACH_REC	*tmp;
	MACH_REC	*ptr;

	sorted_list = create_mach_rec( (MACH_REC *)0 );

	for( ptr=MachineList->next; ptr->name; ptr = tmp ) {
		tmp = ptr->next;
		remque( (struct qelem *)ptr );
		prio_insert( ptr, sorted_list );
	}
	tmp = MachineList;
	MachineList = sorted_list;
	free_mach_rec( tmp );
}

prio_insert( elem, list )
MACH_REC	*elem;
MACH_REC	*list;
{
	MACH_REC	*ptr;

	for( ptr=list->prev; ptr->name; ptr = ptr->prev ) {
		if( ptr->prio > elem->prio ) {
			insque( (struct qelem *)elem, (struct qelem *)ptr );
			return;
		}
	}
	insque( (struct qelem *)elem, (struct qelem *)list );
}

MACH_REC *
find_next( ptr )
MACH_REC	*ptr;
{
	int		idle;

	for( ptr=ptr->next; ptr->name; ptr = ptr->next ) {
		if( time((time_t *)0) - ptr->time_stamp > MachineUpdateInterval ) {
			continue;
		}
		if( evaluate_int("Idle",&idle,ptr->machine_context,(CONTEXT *)0) < 0 ) {
			continue;
		}
		if( idle <= 0 ) {
			continue;
		}
		return ptr;
	}
	return NULL;
}

call_schedd( host, timeout )
MACH_REC *host;
int		timeout; /* seconds */
{
	struct sockaddr_in	sin;
	int					scheduler;
	int					on = 1, off = 0;
	struct timeval		timer;
	fd_set				writefds;
	int					nfound;
	int					nfds;
	int					tmp_errno;

	if( (scheduler=socket(AF_INET,SOCK_STREAM,0)) < 0 ) {
		EXCEPT( "socket" );
	}

	if( ioctl(scheduler,FIONBIO,(char *)&on) < 0 ) {
		EXCEPT( "ioctl" );
	}

	bzero( (char *)&sin, sizeof sin );
	bcopy( (char *)&host->net_addr, (char *)&sin.sin_addr, sizeof sin.sin_addr);
	sin.sin_family = host->net_addr_type;
	sin.sin_port = SchedD_Port;

	if( connect(scheduler,(struct sockaddr *)&sin,sizeof(sin)) < 0 ) {
		switch( errno ) {
			case EINPROGRESS:
				break;
			case ECONNREFUSED:
				(void)close( scheduler );
				return -1;
			default:
				tmp_errno = errno;
				dprintf( D_ALWAYS, "Can't connect to host \"%s\"\n",
																host->name );
				errno = tmp_errno;
				EXCEPT( "connect" );
		}
	}

	timer.tv_sec = timeout;
	timer.tv_usec = 0;
	nfds = scheduler + 1;
	FD_ZERO( &writefds );
	FD_SET( scheduler, &writefds );
	nfound = select( nfds, (int *)0, (int *)&writefds, (int *)0,
													(struct timeval *)&timer );
	switch( nfound ) {
	case 0:
		(void)close( scheduler );
		return -1;
	case 1:
		if( ioctl(scheduler,FIONBIO,(char *)&off) < 0 ) {
			EXCEPT( "ioctl" );
		}
		return scheduler;
	default:
		EXCEPT( "Select returns %d", nfound );
		return -1;
	}
}

MACH_REC	*
find_server( job_context )
CONTEXT		*job_context;
{
	MACH_REC	*ptr;

	
	dprintf( D_JOB, "\n" );
	dprintf( D_JOB, "Looking for server for job:\n" );
	if( DebugFlags & D_JOB ) {
		display_context( job_context );
	}
	dprintf( D_JOB, "\n" );


		/* Look for a server which meets both requirements and preferences */
	for( ptr=MachineList->next; ptr->name; ptr = ptr->next ) {

		dprintf( D_MACHINE, "\n" );
		dprintf( D_MACHINE, "Checking %s\n", ptr->name );
		if( DebugFlags & D_MACHINE ) {
			display_context( ptr->machine_context );
		}
		dprintf( D_MACHINE, "\n" );

		if( ptr->busy ) {
			/*
			** dprintf( D_FULLDEBUG, "Busy\n" );
			*/
			continue;
		}

		if( !check_bool("START",ptr->machine_context,job_context) ) {
			/*
			** dprintf( D_FULLDEBUG, "START is FALSE\n" );
			*/
			continue;
		}
		/*
		** dprintf( D_FULLDEBUG, "START is TRUE\n" );
		*/

		if( !check_bool("JOB_REQUIREMENTS",ptr->machine_context,job_context) ) {
			/*
			** dprintf( D_FULLDEBUG, "JOB_REQUIREMENTS is FALSE\n" );
			*/
			continue;
		}
		/*
		** dprintf( D_FULLDEBUG, "JOB_REQUIREMENTS is TRUE\n" );
		*/

		if( !check_bool("JOB_PREFERENCES",ptr->machine_context,job_context) ) {
			/*
			** dprintf( D_FULLDEBUG, "JOB_PREFERENCES is FALSE\n" );
			*/
			continue;
		}
		/*
		** dprintf( D_FULLDEBUG, "JOB_PREFERENCES is TRUE\n" );
		*/

		if( !check_string("State","NoJob",ptr->machine_context,(CONTEXT *)0) ) {
			/*
			** dprintf( D_FULLDEBUG, "State != NoJob\n" );
			*/
			continue;
		}

		dprintf( D_FULLDEBUG, "Returning 0x%x\n", ptr );

		return ptr;
	}

		/* Look for a server which just meets the requirements */
	for( ptr=MachineList->next; ptr->name; ptr = ptr->next ) {
		/*
		** dprintf( D_FULLDEBUG, "\n" );
		** dprintf( D_FULLDEBUG, "Checking %s\n", ptr->name );
		** display_context( ptr->machine_context );
		** dprintf( D_FULLDEBUG, "\n" );
		*/
		if( ptr->busy ) {
			continue;
		}
		if( !check_bool("START",ptr->machine_context,job_context) ) {
			continue;
		}
		if( !check_bool("JOB_REQUIREMENTS",ptr->machine_context,job_context) ) {
			continue;
		}
		if( !check_string("State","NoJob",ptr->machine_context,(CONTEXT *)0) ) {
			/*
			** dprintf( D_FULLDEBUG, "State != NoJob\n" );
			*/
			continue;
		}

		/*
		** dprintf( D_FULLDEBUG, "Returning 0x%x\n", ptr );
		*/
		return ptr;
	}

	/*
	** dprintf( D_FULLDEBUG, "Returning NULL\n" );
	*/
	return NULL;
}

check_bool( name, context_1, context_2 )
char	*name;
CONTEXT	*context_1;
CONTEXT	*context_2;
{
	int		answer;

	if( evaluate_bool(name,&answer,context_1,context_2) < 0 ) {
		/*
		** dprintf( D_FULLDEBUG, "Can't evaluate \"%s\"\n", name );
		** dprintf( D_FULLDEBUG, "Context1:\n" );
		** display_context( context_1 );
		** dprintf( D_FULLDEBUG, "\nContext2:\n" );
		** display_context( context_2 );
		** dprintf( D_FULLDEBUG, "\n" );
		*/
		return FALSE;
	}
	return answer;
}

check_string( name, pattern, context_1, context_2 )
char	*name;
char	*pattern;
CONTEXT	*context_1;
CONTEXT	*context_2;
{
	char	*tmp;
	int		answer;

	if( evaluate_string(name,&tmp,context_1,context_2) < 0 ) {
		dprintf( D_ALWAYS, "Can't evaluate \"%s\"\n", name );
		return FALSE;
	}
	if( strcmp(pattern,tmp) == MATCH ) {
		answer = TRUE;
	} else {
		answer = FALSE;
	}
	FREE( tmp );
	return answer;
}

decrement_idle( rec )
MACH_REC	*rec;
{
	int		idle;
	ELEM	tmp;

	if( evaluate_int("Idle",&idle,rec->machine_context,(CONTEXT *)0) < 0 ) {
		dprintf( D_ALWAYS, "Can't evaluate \"Idle\" for %s\n", rec->name );
		return;
	}

	tmp.type = INT;
	tmp.i_val = idle - 1;;
	store_stmt( build_expr("Idle",&tmp), rec->machine_context );
}

free_mach_rec( rec )
MACH_REC	*rec;
{
	if( rec->name ) {
		FREE( rec->name );
	}

	if( rec->machine_context ) {
		free_context( rec->machine_context );
	}
	FREE( (char *)rec );
}

connect_to_collector()
{
	struct sockaddr_in	sin;
	int					status;
	int					fd;

	if( (fd=socket(AF_INET,SOCK_STREAM,0)) < 0 ) {
		EXCEPT( "socket" );
	}
	dprintf( D_FULLDEBUG, "Opened %d at %d\n", fd, __LINE__ );

	bzero( (char *)&sin, sizeof sin );
	bcopy( Collector_Hostentp->h_addr, (char *)&sin.sin_addr,
										(unsigned)Collector_Hostentp->h_length);
	sin.sin_family = Collector_Hostentp->h_addrtype;
	sin.sin_port = htons(COLLECTOR_PORT);

	if( (status = connect(fd,(struct sockaddr *)&sin,sizeof(sin))) == 0 ) {
		return fd;
	} else {
		dprintf( D_ALWAYS, "connect returns %d, errno = %d\n", status, errno );
		(void)close( fd );
		return -1;
	}
}

sigint_handler()
{
	dprintf( D_ALWAYS, "Killed by SIGINT\n" );
	exit( 0 );
}

/*
** The other end of our connection to the client has gone away.  Close our
** end.  Then the read()/write() will fail, and whichever client routine
** is involved will clean up.
*/
sigpipe_handler()
{
	dprintf( D_ALWAYS, "Got SIGPIPE\n" );

	if( CommandSock >= 0 ) {
		(void)close( CommandSock );
		dprintf( D_FULLDEBUG, "Closed %d at %d\n", CommandSock, __LINE__ );
	}
	CommandSock = -1;

	if( ClientSock >= 0 ) {
		(void)close( ClientSock );
		dprintf( D_FULLDEBUG, "Closed %d at %d\n", ClientSock, __LINE__ );
	}
	ClientSock = -1;
}

/*
** The machine we are talking to has stopped responding, close our
** end.  Then the read()/write() will fail, and whichever client routine
** is involved will clean up.
*/
alarm_handler()
{
	dprintf( D_ALWAYS,
		"No response in %d seconds, breaking connection\n", ClientTimeout
	);

	if( CommandSock >= 0 ) {
		(void)close( CommandSock );
		dprintf( D_FULLDEBUG, "Closed %d at %d\n", CommandSock, __LINE__ );
	}
	CommandSock = -1;

	if( ClientSock >= 0 ) {
		(void)close( ClientSock );
		dprintf( D_FULLDEBUG, "Closed %d at %d\n", ClientSock, __LINE__ );
	}
	ClientSock = -1;
}

sighup_handler()
{
	dprintf( D_ALWAYS, "Re reading config file\n" );

	config( MyName, (CONTEXT *)0 );
	init_params();
}

