/*
 *	Network Queueing System (NQS)
 *  This version of NQS is Copyright (C) 1992  John Roman
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 1, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/*
*  PROJECT:     Network Queueing System
*  AUTHOR:      John Roman
*
*  Modification history:
*
*       Version Who     When            Description
*       -------+-------+---------------+-------------------------
*       V01.10  JB                      Initial version.
*       V01.20  JRR     09-Apr-1992	Modified for Monsanto.
*	V01.3   JRR	26-May-1992	Fixed up for RS6000.
*	V01.4	JRR	17-Jun-1992	Added header.
*	V01.5	JRR	06-Nov-1992	Added support for HPUX.
*	V01.6	JRR	11-Nov-1992	Completed same.
*	V01.7	JRR	11-Mar-1993	Cleaned up static refs.
*	V01.8	JRR	18-Aug-1993	Miniscule cleanup of includes.
*/
/*++ ldbalsrv.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.35/lib/RCS/ldbalsrv.c,v $
 *
 * DESCRIPTION:
 *
 *      Check that there is a queue available on this machine
 *      with enough resources to run the job and a free initiator.
 *	Note: routine verify_resources is a modified version of the
 *      one in nqs_nsq.c.
 *
 *
 *	Author:
 *	-------
 *      Jean-Philippe Baud, CERN, Geneva, Switzerland.
 *      August 2, 1990.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.8 $ $Date: 1993/09/10 13:55:13 $ $State: Exp $)
 * $Log: ldbalsrv.c,v $
 * Revision 1.8  1993/09/10  13:55:13  jrroma
 * Version 3.35
 *
 * Revision 1.7  93/07/13  21:31:24  jrroma
 * Version 3.34
 * 
 * Revision 1.6  92/12/22  15:46:06  jrroma
 * Version 3.30
 * 
 * Revision 1.5  92/11/06  13:24:57  jrroma
 * Added support for HPUX.
 * 
 * Revision 1.4  92/06/18  13:23:59  jrroma
 * Added gnu header
 * 
 * Revision 1.3  92/06/18  09:41:42  jrroma
 * Version 3.21
 * 
 * Revision 1.2  92/05/06  10:12:19  jrroma
 * Version 3.20
 * 
 * Revision 1.1  92/04/09  14:57:16  jrroma
 * Initial revision
 * 
 *
 *	August 19, 1991	Verify queue run & user limits via the local NQS daemon
 *	September 25, 1991 Change the load-balancing algorithm
 *	October 29, 1991 Check first that queues are enabled and running
 *	November 11, 1991 Several load-balancing queues
 *
 */

#include "nqs.h"			/* NQS constants and data types */
#include <errno.h>
#include "nqspacket.h"			/* Packets to the local daemon */
#include "informcc.h"			/* NQS completion codes and masks */
#include "nqsxdirs.h"			/* NQS directories */
#include "transactcc.h"			/* Transaction completion codes */

#ifndef __CEXTRACT__
#if __STDC__

static int l_verify_access ( gid_t gid, uid_t uid, struct gendescr *descr );

static void l_verify_cpu_limit ( struct rawreq *rawreq, struct cpulimit *quotaval, int quotatype, long icm_code, struct gendescr *descr, unsigned long queue_seconds, short queue_ms, long *modifyres, long *exceedres );

static void l_verify_quota_limit ( struct rawreq *rawreq, struct quotalimit *quotaval, int quotatype, long icm_code, struct gendescr *descr, unsigned long queue_coefficient, short queue_units, long *modifyres, long *exceedres );

static long l_verify_resources ( struct gendescr *descr, struct rawreq *rawreq );

#else /* __STDC__ */

static int l_verify_access (/* gid_t gid, uid_t uid, struct gendescr *descr */) ; 
static void l_verify_cpu_limit (/* struct rawreq *rawreq, struct cpulimit *quotaval, int quotatype, long icm_code, struct gendescr *descr, unsigned long queue_seconds, short queue_ms, long *modifyres, long *exceedres */);

static void l_verify_quota_limit (/* struct rawreq *rawreq, struct quotalimit *quotaval, int quotatype, long icm_code, struct gendescr *descr, unsigned long queue_coefficient, short queue_units, long *modifyres, long *exceedres */);

static long l_verify_resources (/* struct gendescr *descr, struct rawreq *rawreq */);

#endif /* __STDC__ */
#endif /* __CEXTRACT__ */

/*
 *	External functions and variables:
 */
extern FILE *Logfile;
struct confd *Queuefile;		/* Queue descriptor file */

/*** ldbalsrv
 
 *      Check that there is a queue available on this machine
 *      with enough resources to run the job and a free initiator.
 *	Note: routine verify_resources is a modified version of the
 *      one in nqs_nsq.c.
 *
 */
long ldbalsrv (rawreq)
struct rawreq *rawreq;
{
	struct gendescr *descr;
	struct passwd *passwd;
	struct gendescr *qmapdescr;
	struct confd *Qmapfile;		/* Queue/device/destination mapping */
	long res;

	/*
	 *  Open the non-network queue descriptor file.
	 */
	if ((Queuefile = opendb (Nqs_queues, O_RDONLY)) == NULL) {
		fprintf (Logfile, 
			 "E$ldbalsrv(): unable to open Queuefile, errno = %d\n",
			 errno);
		fflush (Logfile);
		return (TCMP_INTERNERR);
	}
	/*
	 *  Find the database description for the pipe queue.
	 */
	descr = nextdb (Queuefile);
	while (descr != (struct gendescr *) 0 &&
	       strcmp (descr->v.que.namev.name, rawreq->quename)) {
		descr = nextdb (Queuefile);
	}
	/*
	 * Queue must exist.
	 */
	if (descr == (struct gendescr *) 0) return (TCMP_NOSUCHQUE);
	/*
	 *  If not load balanced in, take the request.
	 */
	if (!(descr->v.que.status & QUE_LDB_IN) ) return (TCML_SUBMITTED);
	/* if (strcmp (LDBALQUENAM, rawreq->quename) > 0) return (TCML_SUBMITTED); */
	/*
	 *  The request has a start time in the future.
	 */
	if (rawreq->start_time > time ((time_t *) 0)) return (TCMP_QUEBUSY);
	/*
	 *  Queue must be enabled.
	 */
	if (!(descr->v.que.status & QUE_ENABLED)) return (TCMP_QUEDISABL);
	/*
	 *  Queue must be running.
	 */
	if (!(descr->v.que.status & QUE_RUNNING)) return (TCMP_QUEBUSY);
	/*
	 *  We have located the pipe queue descriptor;
	 *  Verify access.
	 */
	passwd = fetchpwuid (getuid ());
	if (!l_verify_access (passwd->pw_gid, passwd->pw_uid, descr))
			return (TCMP_ACCESSDEN);
	/*
	 *  Open the queue destination mapping file.
	 */
	if ((Qmapfile = opendb (Nqs_qmaps, O_RDONLY)) == NULL) {
		fprintf (Logfile, "E$ldbalsrv(): unable to open Qmapfile\n");
		fflush (Logfile);
		return (TCMP_INTERNERR);
	}
	while ((qmapdescr = nextdb (Qmapfile)) != (struct gendescr *) 0) {
	    if (!qmapdescr->v.map.qtodevmap &&
		        strcmp (qmapdescr->v.map.v.qdestmap.lqueue,
			rawreq->quename) == 0) {
		/*
		 *  We have located a pipe-queue/destination mapping
		 *  for the pipe queue.
		 *  Now, locate the database description for the
		 *  pipe queue destination referenced in the mapping.
		 */
		if (telldb (Queuefile) != -1) seekdbb (Queuefile, 0L);
		descr = nextdb (Queuefile);
		while (descr != (struct gendescr *) 0 &&
			(strcmp (qmapdescr->v.map.v.qdestmap.rqueue,
				       descr->v.que.namev.name) ||
			descr->v.que.type != QUE_BATCH))
					descr = nextdb (Queuefile);
		if (descr != (struct gendescr *) 0) {
		    /*
		     *  Queue must be enabled.
		     */
		    if (!(descr->v.que.status & QUE_ENABLED)) {
			res= TCMP_QUEDISABL;
			continue;
		    }
		    /*
		     *  Queue must be running.
		     */
		    if (!(descr->v.que.status & QUE_RUNNING)) {
			res = TCMP_QUEBUSY;
			continue;
		    }
		    /*
		     *  Verify access.
		     */
		    res = l_verify_access (passwd->pw_gid, passwd->pw_uid, descr);
		    switch (res) {
			case -1:
			    fprintf (Logfile,"E$ldbalsrv: Failed to ");
			    fprintf (Logfile,"open queue access file.\n");
			    fflush (Logfile);
			    return (TCMP_INTERNERR);
			case 0:
			    res = TCMP_ACCESSDEN;
			    continue;
			case 1:
			    break;
		    }
		    /*
		     *  We have located the batch queue descriptor;
		     *  Verify request quota limits.
		     */
		    res = l_verify_resources (descr, rawreq);
		    if ((res & XCI_FULREA_MASK) != TCML_SUBMITTED) {
			
			  fprintf (Logfile,
				"I$ldbalsrv(): skipping queue ");
			  fprintf (Logfile,
				"%s because of resources\n",
				descr->v.que.namev.name);
			  fflush (Logfile);
			
			continue;
		    }
		    /*
		     *  Is there a free initiator ?
		     */
		    interclear ();
		    interw32i (passwd->pw_uid);
		    interwstr (descr->v.que.namev.name);
		    if ((res = inter (PKT_VERQUEUSR)) == TCML_SUBMITTED)
				strcpy (rawreq->quename, descr->v.que.namev.name);
		    return (res);
		}
	    }
	}
	return (res);
}


/*** l_verify_access
 *
 *
 *	int l_verify_access ():
 *
 *	Verify that the request can be placed in the specified queue.
 *	Returns: 1 if access is permitted, 0 if access is not permitted.
 *
 */
static int l_verify_access (gid, uid, descr)
gid_t gid;					/* Group id */
uid_t uid;					/* User id */
struct gendescr *descr;				/* Queue descriptor */
{
	unsigned long buffer [QAFILE_CACHESIZ];
	int bytes;				/* Did read this much */
	int cachebytes;				/* Try to read this much */
	int entries;				/* Did read this many */
	int fd;					/* File descriptor */
	int i;					/* Loop variable */
	
	if (uid == 0) return (1);		/* Root always has access */
	fd = openqacc (Queuefile, descr);
	/*
	 * Openqacc returns 0 if the queue is restricted and the file
	 * is open; -1 if the queue is unrestricted; -2 if the queue
	 * queue is restricted but the file is not open, and -3
	 * if the queue appears to have been deleted.
	 */
	if (fd < -1) return (-1);		/* Open error */
	if (fd == -1) return (1);		/* Unrestricted access */
	cachebytes = QAFILE_CACHESIZ * sizeof (unsigned long);
	while ((bytes = read (fd, (char *) buffer, cachebytes)) > 0) {
		entries = bytes / sizeof (unsigned long);
		for (i = 0; i < entries; i++) {
			/* Zero comes only at the end */
			if (buffer [i] == 0) return (0);
			if ((buffer [i] == (gid | MAKEGID))
			 || (buffer [i] == uid )) return (1);
		}
	}
	return (0);
}


/*** l_verify_resources
 *
 *
 *	long l_verify_resources():
 *	Verify resources for a request.
 *
 *	Returns:
 *		TCML_SUBMITTED if successful (information bits
 *		may be present).  Otherwise the TCMP_reason code
 *		is returned describing the condition (information
 *		bits may be present).
 */
static long l_verify_resources (descr, rawreq)
register struct gendescr *descr;	/* Target queue */
register struct rawreq *rawreq;		/* Rawreq for request */
{

	long modifyres;			/* Quota limit modified set */
	long exceedres;			/* Quota limit exceeded set */

	modifyres = 0;			/* No quotas modified or */
	exceedres = 0;			/* exceeded (yet anyway) */
	/*
	 *  Check that the request does not exceed any
	 *  of the resource limits associated with the
	 *  batch queue.
	 */
#if	(VALID_LIMITS & LIM_PPCORE)
	l_verify_quota_limit (rawreq, &rawreq->v.bat.ppcoresize,
			    LIM_PPCORE, TCI_PP_CFLEXC, descr,
			    descr->v.que.v1.batch.ppcorecoeff,
			    descr->v.que.v1.batch.ppcoreunits,
			    &modifyres, &exceedres);
#endif
#if	(VALID_LIMITS & LIM_PPDATA)
	l_verify_quota_limit (rawreq, &rawreq->v.bat.ppdatasize,
			    LIM_PPDATA, TCI_PP_DSLEXC, descr, 
			    descr->v.que.v1.batch.ppdatacoeff,
			    descr->v.que.v1.batch.ppdataunits,
			    &modifyres, &exceedres);
#endif
#if	(VALID_LIMITS & LIM_PPPFILE)
	l_verify_quota_limit (rawreq, &rawreq->v.bat.pppfilesize,
			    LIM_PPPFILE, TCI_PP_PFLEXC, descr, 
			    descr->v.que.v1.batch.pppfilecoeff,
			    descr->v.que.v1.batch.pppfileunits,
			    &modifyres, &exceedres);
#endif
#if	(VALID_LIMITS & LIM_PRPFILE)
	l_verify_quota_limit (rawreq, &rawreq->v.bat.prpfilespace,
			    LIM_PRPFILE, TCI_PR_PFLEXC, descr, 
			    descr->v.que.v1.batch.prpfilecoeff,
			    descr->v.que.v1.batch.prpfileunits,
			    &modifyres, &exceedres);
#endif
#if	(VALID_LIMITS & LIM_PPQFILE)
	l_verify_quota_limit (rawreq, &rawreq->v.bat.ppqfilesize,
			    LIM_PPQFILE, TCI_PP_QFLEXC, descr, 
			    descr->v.que.v1.batch.ppqfilecoeff,
			    descr->v.que.v1.batch.ppqfileunits,
			    &modifyres, &exceedres);
#endif
#if	(VALID_LIMITS & LIM_PRQFILE)
	l_verify_quota_limit (rawreq, &rawreq->v.bat.prqfilespace,
			    LIM_PRQFILE, TCI_PR_QFLEXC, descr, 
			    descr->v.que.v1.batch.prqfilecoeff,
			    descr->v.que.v1.batch.prqfileunits,
			    &modifyres, &exceedres);
#endif
#if	(VALID_LIMITS & LIM_PPTFILE)
	l_verify_quota_limit (rawreq, &rawreq->v.bat.pptfilesize,
			    LIM_PPTFILE, TCI_PP_TFLEXC, descr, 
			    descr->v.que.v1.batch.pptfilecoeff,
			    descr->v.que.v1.batch.pptfileunits,
			    &modifyres, &exceedres);
#endif
#if	(VALID_LIMITS & LIM_PRTFILE)
	l_verify_quota_limit (rawreq, &rawreq->v.bat.prtfilespace,
			    LIM_PRTFILE, TCI_PR_TFLEXC, descr, 
			    descr->v.que.v1.batch.prtfilecoeff,
			    descr->v.que.v1.batch.prtfileunits,
			    &modifyres, &exceedres);
#endif
#if	(VALID_LIMITS & LIM_PPMEM)
	l_verify_quota_limit (rawreq, &rawreq->v.bat.ppmemsize,
			    LIM_PPMEM, TCI_PP_MSLEXC, descr, 
			    descr->v.que.v1.batch.ppmemcoeff,
			    descr->v.que.v1.batch.ppmemunits,
			    &modifyres, &exceedres);
#endif
#if	(VALID_LIMITS & LIM_PRMEM)
	l_verify_quota_limit (rawreq, &rawreq->v.bat.prmemsize,
			    LIM_PRMEM, TCI_PR_MSLEXC, descr, 
			    descr->v.que.v1.batch.prmemcoeff,
			    descr->v.que.v1.batch.prmemunits,
			    &modifyres, &exceedres);
#endif
#if	(VALID_LIMITS & LIM_PPSTACK)
	l_verify_quota_limit (rawreq, &rawreq->v.bat.ppstacksize,
			    LIM_PPSTACK, TCI_PP_SSLEXC, descr, 
			    descr->v.que.v1.batch.ppstackcoeff,
			    descr->v.que.v1.batch.ppstackunits,
			    &modifyres, &exceedres);
#endif
#if	(VALID_LIMITS & LIM_PPWORK)
	l_verify_quota_limit (rawreq, &rawreq->v.bat.ppworkset,
			    LIM_PPWORK, TCI_PP_WSLEXC, descr, 
			    descr->v.que.v1.batch.ppworkcoeff,
			    descr->v.que.v1.batch.ppworkunits,
			    &modifyres, &exceedres);
#endif
#if	(VALID_LIMITS & LIM_PPCPUT)
	l_verify_cpu_limit (rawreq, &rawreq->v.bat.ppcputime,
			  LIM_PPCPUT, TCI_PP_CTLEXC, descr, 
			  descr->v.que.v1.batch.ppcpusecs,
			  descr->v.que.v1.batch.ppcpums,
			  &modifyres, &exceedres);
#endif
#if	(VALID_LIMITS & LIM_PRCPUT)
	l_verify_cpu_limit (rawreq, &rawreq->v.bat.prcputime,
			  LIM_PRCPUT, TCI_PR_CTLEXC, descr, 
			  descr->v.que.v1.batch.prcpusecs,
			  descr->v.que.v1.batch.prcpums,
			  &modifyres, &exceedres);
#endif
#if	(VALID_LIMITS & LIM_PPNICE)
	if (rawreq->v.bat.explicit & LIM_PPNICE) {
		if (rawreq->v.bat.infinite & LIM_PPNICE) {
			/*
			 * Infinite nice is meaningless in NQS.
			 */
			exceedres |= TCI_PP_NELEXC;
		}
		else {
			/*
			 *  The batch request specifies a
			 *  finite nice value.  Bind the
			 *  specified nice value to the
			 *  range supported by the UNIX
			 *  implementation of the local
			 *  host.
			 */
			if (rawreq->v.bat.ppnice > MAX_NICE) {
				/*
				 *  The request specifies a
				 *  nice limit that is too
				 *  high for the supporting
				 *  UNIX implementation.
				 */
				modifyres |= TCI_PP_NELEXC;
				rawreq->v.bat.ppnice = MAX_NICE;
			}
			else if (rawreq->v.bat.ppnice < MIN_NICE){
				/*
				 *  The request specifies a
				 *  nice limit that is too
				 *  low for the supporting
				 *  UNIX implementation.
				 */
				modifyres |= TCI_PP_NELEXC;
				rawreq->v.bat.ppnice = MIN_NICE;
			}
			if (rawreq->v.bat.ppnice <
			    descr->v.que.v1.batch.ppnice) {
				exceedres |= TCI_PP_NELEXC;
			}
		}
	}
#endif
#if	(VALID_LIMITS & LIM_PRDRIVES)
	/* MOREHERE */
#endif
#if	(VALID_LIMITS & LIM_PRNCPUS)
	/* MOREHERE */
#endif
	if (exceedres) return (TCMP_QUOTALIMIT | exceedres);
	return (TCML_SUBMITTED | modifyres);
}


/*** l_verify_cpu_limit
 *
 *
 *	void l_verify_cpu_limit():
 *	Verify the specified request CPU time limit.
 */
static void l_verify_cpu_limit (rawreq, quotaval, quotatype, icm_code, descr,
			      queue_seconds, queue_ms, modifyres, exceedres)
struct rawreq *rawreq;			/* Raw request structure */
struct cpulimit *quotaval;		/* CPU quota limit for quota type */
int quotatype;				/* Quota type LIM_ */
long icm_code;				/* XCI_ message bit pattern */
struct gendescr *descr;			/* Target queue */
unsigned long queue_seconds;		/* Queue CPU limit in seconds */
short queue_ms;				/* Queue CPU milliseconds limit */
long *modifyres;			/* Modified quota limits */
long *exceedres;			/* Exceeded quota limits */
{
	unsigned long new_secs;		/* cpulimhigh(), cpulimlow() return */
					/* value */
	short new_ms;			/* cpulimhigh(), cpulimlow() return */
					/* value */

	if (rawreq->v.bat.explicit & quotatype) {
		/*
		 *  The request specifies an explicit CPU time limit.
		 */
		if (cpulimhigh (quotaval->max_seconds, quotaval->max_ms,
				rawreq->v.bat.infinite & quotatype,
				&new_secs, &new_ms)) {
			*modifyres |= icm_code;
			quotaval->max_seconds = new_secs;
			quotaval->max_ms = new_ms;
			/*
			 * If the former limit was infinite, establish
			 * a sane warning.  If the former limit was
			 * was finite, don't mess with the warning
			 * unless we have to.
			 */
			if (rawreq->v.bat.infinite & quotatype) {
				quotaval->warn_seconds = new_secs;
				quotaval->warn_ms = new_ms;
			}
			else {
				if (quotaval->warn_seconds > new_secs ||
			   	(quotaval->warn_seconds == new_secs &&
			    	quotaval->warn_ms > new_ms)) {
					quotaval->warn_seconds = new_secs;
					quotaval->warn_ms = new_ms;
				}
			}
			rawreq->v.bat.infinite &= ~quotatype;
		}
		if (rawreq->v.bat.infinite & quotatype) {
			/*
			 *  The request specifies an infinite CPU
			 *  limit, and such a limit can be enforced.
			 */
			if (!(descr->v.que.v1.batch.infinite & quotatype)) {
				/*
				 *  The request specifies an infinite CPU
				 *  time limit, but the corresponding batch
				 *  queue CPU time limit is finite.
				 */
				*exceedres |= icm_code;
			}
		}
		else {
			/*
			 *  The request specifies a finite CPU time limit,
			 *  or the request specifies an infinite CPU time
			 *  limit that we will interpret as a finite CPU
			 *  time limit because an infinite limit cannot be
			 *  enforced.  Check that the finite limit
			 *  is enforceable.  Change if necessary.
			 */
			if (cpulimlow (quotaval->max_seconds, quotaval->max_ms,
				       &new_secs, &new_ms)) {
				*modifyres |= icm_code;
				quotaval->max_seconds = new_secs;
				quotaval->max_ms = new_ms;
				if (quotaval->warn_seconds > new_secs ||
				   (quotaval->warn_seconds == new_secs &&
				    quotaval->warn_ms > new_ms)) {
					/*
					 *  The warning limit must be changed
					 *  as well.
					 */
					quotaval->warn_seconds = new_secs;
					quotaval->warn_ms = new_ms;
				}
			}
			if (!(descr->v.que.v1.batch.infinite & quotatype) &&
			   ((quotaval->max_seconds > queue_seconds) ||
			    (quotaval->max_seconds == queue_seconds &&
			     quotaval->max_ms > queue_ms))) {
				/*
				 *  The resolved batch request quota limit
				 *  exceeds the corresponding queue quota
				 *  limit.
				 */
				*exceedres |= icm_code;
			}
		}
	}
}


/*** l_verify_quota_limit
 *
 *
 *	void verify_quota_limit():
 *	Verify the specified request quota limit.
 */
static void l_verify_quota_limit (rawreq, quotaval, quotatype, icm_code, descr,
				queue_coefficient, queue_units, modifyres,
				exceedres)
struct rawreq *rawreq;			/* Raw request structure */
struct quotalimit *quotaval;		/* Quota limit for quota type */
int quotatype;				/* Quota type LIM_ */
long icm_code;				/* XCI_ message bit pattern */
struct gendescr *descr;			/* Target queue */
unsigned long queue_coefficient;	/* Queue quota coefficient */
short queue_units;			/* Queue quota units */
long *modifyres;			/* Modified quota limits */
long *exceedres;			/* Exceeded quota limits */
{
	unsigned long new_quota;	/* quolimhigh(), quolimlow() return */
					/* value */
	short new_units;		/* quolimhigh(), quolimlow() return */
					/* value */

	if (rawreq->v.bat.explicit & quotatype) {
		/*
		 *  The batch request specifies an explicit quota limit
		 *  value.
		 */
		if (quolimhigh (quotaval->max_quota, quotaval->max_units,
				rawreq->v.bat.infinite & quotatype,
				quotatype, &new_quota, &new_units)) {
			*modifyres |= icm_code;
			quotaval->max_quota = new_quota;
			quotaval->max_units = new_units;
			/*
			 *  If the former limit was infinite, establish
			 *  a sane warning.  If the former limit was
			 *  was finite, don't mess with the warning
			 *  unless we have to.
			 */
			if (rawreq->v.bat.infinite & quotatype) {
				quotaval->warn_quota = new_quota;
				quotaval->warn_units = new_units;
			}
			else {
				if (secgrfir (new_quota, new_units,
				      		quotaval->warn_quota,
				      		quotaval->warn_units)) {
					quotaval->warn_quota = new_quota;
					quotaval->warn_units = new_units;
				}
			}
			rawreq->v.bat.infinite &= ~quotatype;
		}
		if (rawreq->v.bat.infinite & quotatype) {
			/*
			 *  The request specifies an infinite quota
			 *  limit, and such a limit can be enforced.
			 */
			if (!(descr->v.que.v1.batch.infinite & quotatype)) {
				/*
				 *  The batch request specifies an infinite
				 *  limit, while the corresponding queue
				 *  limit is finite.
				 */
				*exceedres |= icm_code;
			}
		}
		else {
			/*
			 *  The request specifies a finite quota limit,
			 *  or the request specifies an infinite quota
			 *  limit that we will interpret as a finite quota
			 *  limit because an infinite limit cannot be
			 *  enforced.  Check that the finite limit
			 *  is enforceable.  Change if necessary.
			 */
			if (quolimlow (quotaval->max_quota,
				       quotaval->max_units, quotatype,
				       &new_quota, &new_units)) {
				*modifyres |= icm_code;
				quotaval->max_quota = new_quota;
				quotaval->max_units = new_units;
				if (secgrfir (new_quota, new_units,
					      quotaval->warn_quota,
					      quotaval->warn_units)) {
					/*
					 *  The warning limit must be changed
					 *  as well.
					 */
					quotaval->warn_quota = new_quota;
					quotaval->warn_units = new_units;
				}
			}
			if (!(descr->v.que.v1.batch.infinite & quotatype) &&
			    secgrfir (queue_coefficient,
				      queue_units,
				      quotaval->max_quota,
				      quotaval->max_units)) {
				/*
				 *  The resolved batch request quota limit
				 *  exceeds the corresponding queue quota
				 *  limit.
				 */
				*exceedres |= icm_code;
			}
		}
	}
}
