/*
 * libnqs/unwelcome/ldbalsrv.c
 * 
 * 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.
 *
 *	Original Author:
 *	-------
 *      Jean-Philippe Baud, CERN, Geneva, Switzerland.
 *      August 2, 1990.
 */

#include <libnqs/license.h>
#include <libnqs/proto.h>		/* NQS constants and data types */
#include <errno.h>
#include <libnqs/nqspacket.h>  		/* Packets to the local daemon */
#include <libnqs/informcc.h>   		/* NQS completion codes and masks */
#include <libnqs/nqsxdirs.h>   		/* NQS directories */
#include <libnqs/nqsxvars.h>		/* NQS global variables */
#include <libnqs/transactcc.h> 		/* Transaction completion codes */
#include <unistd.h>
#include <libsal/license.h>
#include <libsal/proto.h>
#include <libsal/defines.h>

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

static void l_verify_cpu_limit ( struct rawreq *rawreq, struct cpulimit *quotaval, int quotatype, long icm_code, struct quedescr *queue, unsigned long queue_seconds, unsigned 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 quedescr *queue, unsigned long queue_coefficient, short queue_units, long *modifyres, long *exceedres );

/*
 *	External functions and variables:
 */
extern FILE *NetLogfile;

/*** 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 (struct rawreq *rawreq, int debug)
{
	struct gendescr *descr;
	struct passwd *passwd;
	struct gendescr *qmapdescr;
	struct confd *Qmapfile;		/* Queue/device/destination mapping */
	struct confd *Queuefile;	/* Queue descriptor file */
	long res;

	/*
	 *  Open the non-network queue descriptor file.
	 */
	if ((Queuefile = opendb (Nqs_queues, O_RDONLY)) == NULL) {
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, "ldbalsrv(): unable to open Queuefile, errno = %d\n", errno);
		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 = sal_fetchpwuid (getuid ());
	if (!l_verify_access (passwd->pw_gid, passwd->pw_uid, descr, 
				Queuefile))
			return (TCMP_ACCESSDEN);
	/*
	 *  Open the queue destination mapping file.
	 */
	if ((Qmapfile = opendb (Nqs_qmaps, O_RDONLY)) == NULL) {
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, "ldbalsrv(): unable to open Qmapfile\n");
		return (TCMP_INTERNERR);
	}
	sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_DEBUGMEDIUM,"ldbalsrv(): Rawreq queuename is %s\n", rawreq->quename);
  
	/*
	 * Default return code
	 */
	res = TCMP_NETDBERR;
	while ((qmapdescr = nextdb (Qmapfile)) != (struct gendescr *) 0) {
	    sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_DEBUGMEDIUM, "ldbalsrv(): Local queue %s\n", qmapdescr->v.map.v.qdestmap.lqueue);
	    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, Queuefile);
		    switch (res) {
			case -1:
			    sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING ,"E$ldbalsrv: Failed to open queue access file.\n");
			    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->v.que), rawreq);
		    if ((res & XCI_FULREA_MASK) != TCML_SUBMITTED) {
		        sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_DEBUGHIGH, "ldbalsrv(): skipping queue %s because of resources\n", descr->v.que.namev.name);
			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);
		} else {
		    sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_DEBUGHIGH, "ldbalsrv(): No remote queue for %s\n", qmapdescr->v.map.v.qdestmap.rqueue);
		}
	    }
	}
	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_t gid, uid_t uid, struct gendescr *descr, 
		struct confd *queuefile)
{
	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).
 */
long l_verify_resources (register struct quedescr *queue,
		         register struct rawreq *rawreq)
{

	long modifyres;			/* Quota limit modified set */
	long exceedres;			/* Quota limit exceeded set */
  	sal_syslimit stSyslimit;	/* system limit data type   */

	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 (rawreq->type == RTYPE_BATCH) 
  	{
    
  	  if (nqs_getsyslimit(LIM_PPCORE, &stSyslimit) && sal_syslimit_supported(&stSyslimit))
  	  {
	    l_verify_quota_limit (rawreq, &rawreq->v.bat.ppcoresize,
			    LIM_PPCORE, TCI_PP_CFLEXC, queue,
			    queue->v1.batch.ppcorecoeff,
			    queue->v1.batch.ppcoreunits,
			    &modifyres, &exceedres);
	  }
  
  	  if (nqs_getsyslimit(LIM_PPDATA, &stSyslimit) && sal_syslimit_supported(&stSyslimit))
  	  {
	    l_verify_quota_limit (rawreq, &rawreq->v.bat.ppdatasize,
			    LIM_PPDATA, TCI_PP_DSLEXC, queue,
			    queue->v1.batch.ppdatacoeff,
			    queue->v1.batch.ppdataunits,
			    &modifyres, &exceedres);
	  }

  	  if (nqs_getsyslimit(LIM_PPPFILE, &stSyslimit) && sal_syslimit_supported(&stSyslimit))
  	  {
	    l_verify_quota_limit (rawreq, &rawreq->v.bat.pppfilesize,
			    LIM_PPPFILE, TCI_PP_PFLEXC, queue,
			    queue->v1.batch.pppfilecoeff,
			    queue->v1.batch.pppfileunits,
			    &modifyres, &exceedres);
	  }

  	  if (nqs_getsyslimit(LIM_PRPFILE, &stSyslimit) && sal_syslimit_supported(&stSyslimit))
    	  {
	    l_verify_quota_limit (rawreq, &rawreq->v.bat.prpfilespace,
			    LIM_PRPFILE, TCI_PR_PFLEXC, queue,
			    queue->v1.batch.prpfilecoeff,
			    queue->v1.batch.prpfileunits,
			    &modifyres, &exceedres);
	  } 
  
  	  if (nqs_getsyslimit(LIM_PPQFILE, &stSyslimit) && sal_syslimit_supported(&stSyslimit))
  	  {
	    l_verify_quota_limit (rawreq, &rawreq->v.bat.ppqfilesize,
			    LIM_PPQFILE, TCI_PP_QFLEXC, queue,
			    queue->v1.batch.ppqfilecoeff,
			    queue->v1.batch.ppqfileunits,
			    &modifyres, &exceedres);
	  }
  
  	  if (nqs_getsyslimit(LIM_PRQFILE, &stSyslimit) && sal_syslimit_supported(&stSyslimit))
  	  {
	    l_verify_quota_limit (rawreq, &rawreq->v.bat.prqfilespace,
			    LIM_PRQFILE, TCI_PR_QFLEXC, queue,
			    queue->v1.batch.prqfilecoeff,
			    queue->v1.batch.prqfileunits,
			    &modifyres, &exceedres);
	  }

  	  if (nqs_getsyslimit(LIM_PPTFILE, &stSyslimit) && sal_syslimit_supported(&stSyslimit))
  	  {	
	    l_verify_quota_limit (rawreq, &rawreq->v.bat.pptfilesize,
			    LIM_PPTFILE, TCI_PP_TFLEXC, queue,
			    queue->v1.batch.pptfilecoeff,
			    queue->v1.batch.pptfileunits,
			    &modifyres, &exceedres);
	  }
  	  if (nqs_getsyslimit(LIM_PRTFILE, &stSyslimit) && sal_syslimit_supported(&stSyslimit))
    	  {
	    l_verify_quota_limit (rawreq, &rawreq->v.bat.prtfilespace,
			    LIM_PRTFILE, TCI_PR_TFLEXC, queue,
			    queue->v1.batch.prtfilecoeff,
			    queue->v1.batch.prtfileunits,
			    &modifyres, &exceedres);
	  }
  
  	  if (nqs_getsyslimit(LIM_PPMEM, &stSyslimit) && sal_syslimit_supported(&stSyslimit))
  	  {
	    l_verify_quota_limit (rawreq, &rawreq->v.bat.ppmemsize,
			    LIM_PPMEM, TCI_PP_MSLEXC, queue,
			    queue->v1.batch.ppmemcoeff,
			    queue->v1.batch.ppmemunits,
			    &modifyres, &exceedres);
	  }
  
  	  if (nqs_getsyslimit(LIM_PRMEM, &stSyslimit) && sal_syslimit_supported(&stSyslimit))
  	  {
	    l_verify_quota_limit (rawreq, &rawreq->v.bat.prmemsize,
			    LIM_PRMEM, TCI_PR_MSLEXC, queue,
			    queue->v1.batch.prmemcoeff,
			    queue->v1.batch.prmemunits,
			    &modifyres, &exceedres);
	  }
  
  	  if (nqs_getsyslimit(LIM_PPSTACK, &stSyslimit) && sal_syslimit_supported(&stSyslimit))
  	  {
	    l_verify_quota_limit (rawreq, &rawreq->v.bat.ppstacksize,
			    LIM_PPSTACK, TCI_PP_SSLEXC, queue,
			    queue->v1.batch.ppstackcoeff,
			    queue->v1.batch.ppstackunits,
			    &modifyres, &exceedres);
	  }
  	  if (nqs_getsyslimit(LIM_PPWORK, &stSyslimit) && sal_syslimit_supported(&stSyslimit))
  	  {
	    l_verify_quota_limit (rawreq, &rawreq->v.bat.ppworkset,
			    LIM_PPWORK, TCI_PP_WSLEXC, queue,
			    queue->v1.batch.ppworkcoeff,
			    queue->v1.batch.ppworkunits,
			    &modifyres, &exceedres);
	  }
  	  if (nqs_getsyslimit(LIM_PPCPUT, &stSyslimit) && sal_syslimit_supported(&stSyslimit))
  	  {
	    l_verify_cpu_limit (rawreq, &rawreq->v.bat.ppcputime,
			  LIM_PPCPUT, TCI_PP_CTLEXC, queue,
			  queue->v1.batch.ppcpusecs,
			  queue->v1.batch.ppcpums,
			  &modifyres, &exceedres);
	  }
  
  	  if (nqs_getsyslimit(LIM_PRCPUT, &stSyslimit) && sal_syslimit_supported(&stSyslimit))
	  {
	    l_verify_cpu_limit (rawreq, &rawreq->v.bat.prcputime,
			  LIM_PRCPUT, TCI_PR_CTLEXC, queue, 
			  queue->v1.batch.prcpusecs,
			  queue->v1.batch.prcpums,
			  &modifyres, &exceedres);
	  }

  	  if (nqs_getsyslimit(LIM_PPNICE, &stSyslimit) && sal_syslimit_supported(&stSyslimit))
  	  {
	    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 <
			    queue->v1.batch.ppnice) {
				exceedres |= TCI_PP_NELEXC;
			}
		}
	    }
	  }
	}
#if 0
  	else {
	  if (rawreq->v.dev.forms [0] != '\0') {
			/*
			 *  Forms were specified for the device
			 *  request.  Make sure that the required
			 *  forms exist.
			 */
			seekdbb (Formsfile, 0L);
			forms = nextdb (Formsfile);
			while (forms != (struct gendescr *) 0 &&
			       strcmp (forms->v.form.forms,
				       rawreq->v.dev.forms)) {
				forms = nextdb (Formsfile);
			}
			if (forms == (struct gendescr *) 0) {
				/*
				 *  The specified forms do not exist.
				 */
				return (TCML_NOSUCHFORM);
			}
		}
 		if (Maxcopies < rawreq->v.dev.copies) {
			exceedres |= TCI_COPLIMEXC;
		}
		if (Maxprint < rawreq->v.dev.size) {
			exceedres |= TCI_PRILIMEXC;
		}
	}
#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(
	struct rawreq *rawreq,
	struct cpulimit *quotaval,
	int quotatype,
	long icm_code,
	struct quedescr *queue,
	unsigned long queue_seconds,
	unsigned short queue_ms,	
	long *modifyres,
	long *exceedres)
{
	unsigned long new_secs;		/* cpulimhigh(), cpulimlow() return */
					/* value */
	unsigned 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 (!(queue->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 (!(queue->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;
			}
		}
	}
  	else {
		/*
		 *  The batch request did not specify a CPU quota limit
		 *  for the CPU resource limit type.  By default, the batch
		 *  request inherits the corresponding queue CPU limit value.
		 */
		if (queue->v1.batch.infinite & quotatype) {
			rawreq->v.bat.infinite |= quotatype;
		}
		else rawreq->v.bat.infinite &= ~quotatype;
		quotaval->max_seconds = queue_seconds;
		quotaval->max_ms = queue_ms;
		quotaval->warn_seconds = queue_seconds;
		quotaval->warn_ms = queue_ms;
	}
}


/*** l_verify_quota_limit
 *
 *
 *	void verify_quota_limit():
 *	Verify the specified request quota limit.
 */
static void l_verify_quota_limit (
	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 quedescr *queue,		/* 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 (!(queue->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 (!(queue->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;
			}
		}
	}
  	else {
		/*
		 *  The batch request did not specify a quota limit
		 *  for the resource limit type.  By default, the batch
		 *  request inherits the corresponding queue limit value.
		 */
		if (queue->v1.batch.infinite & quotatype) {
			rawreq->v.bat.infinite |= quotatype;
		}
		else rawreq->v.bat.infinite &= ~quotatype;
		quotaval->max_quota = queue_coefficient;
		quotaval->max_units = queue_units;
		quotaval->warn_quota = queue_coefficient;
		quotaval->warn_units = queue_units;
	}
}
