/*
 * nqs_generic/all-systems/quolimok.c
 * Routines to check the validity of resource limits
 */

#include <nqs_generic/license.h>
  /* NQS license information */
#include <nqs_generic/debug.h>
  /* debugging support */
#include <nqs_generic/proto.h>
  /* ANSI C prototype support */

#if	BSD43 | ULTRIX | DECOSF
#if	DECOSF
#if	RLIM_INFINITY != 0x7FFFFFFFFFFFFFFF
REWRITE THIS FILE
#endif
#else
#if	RLIM_INFINITY != 0x7FFFFFFF
REWRITE THIS FILE
#endif
#if	MEM_LIMIT_GRAN != 1	/* BSD4.2, BSD4.3, and ULTRIX code assumes */
				/* MEM_LIMIT_GRAN == 1 */
REWRITE THIS FILE
#endif
#endif
#if	DISK_LIMIT_GRAN != 1	/* BSD4.2, BSD4.3, and ULTRIX code assumes */
				/* DISK_LIMIT_GRAN == 1 */
REWRITE THIS FILE
#endif
#else
#if	SGI | SOLARIS | LINUX
#include <sys/resource.h>
#else
#if	HPUX | SYS52 | IBMRS 
#else
BAD SYSTEM TYPE
#endif
#endif
#endif

/*
 * FIXME:
 * 
 * 	We use platform-specific values (GRANUALITY values) as part of
 * 	these calculations.  This needs to be removed.
 */

/*** cpulimhigh
 *
 *	int cpulimhigh ():
 *
 *	Given a cpu time limit, report whether the implied limit
 *	could be enforced on this machine.  In determining whether
 *	the limit is too high, we must remember that some
 *	apparently finite limit values might have been usurped
 *	by the system call in question to mean "infinity".
 *
 *	If the limit is ok, return 0. If the limit is too big,
 *	return 1 and assign the largest enforceable limit 
 *	to the designated variables.
 */
int cpulimhigh (
	unsigned long secs,		/* Seconds part */
	short ms,			/* Milliseconds part */
	short infinite,			/* Boolean */
	unsigned long *new_secsp,	/* Pointer to altered seconds value */
	short *new_msp)			/* Pointer to altered milliseconds */
{
  ENTER_FUNCTION("nqs_generic/all-systems/cpulimhigh");
    /* debugging info */
  TEST_ARG(new_secsp != NULL, 4);
  TEST_ARG(new_msp   != NULL, 5);
    /* ensure we are not working with garbage */
  
  if (infinite)
  {
    EXIT_FUNCTION;
    return (0);
  }
  
  if (secs >= (RLIM_INFINITY - ((ms * CPU_LIMIT_GRAN) / 1000))
      / CPU_LIMIT_GRAN)
  {
    *new_secsp = (RLIM_INFINITY / CPU_LIMIT_GRAN) -1;
    *new_msp = 0;
    EXIT_FUNCTION;
    return (1);
  }
  EXIT_FUNCTION;
  return (0);
}

/*** cpulimlow
 *
 *
 *	int cpulimlow ():
 *
 *	Given a cpu time limit, report whether the implied limit
 *	could be enforced on this machine.  In determining whether
 *	the limit is too low, we must remember that some
 *	apparently finite limit values might have been usurped
 *	by the system call in question to mean "infinity".
 *
 *	The limit shall be deemed to be too small if it is
 *	a small number and attempting to enforce it would
 *	result in an infinite limit.
 *
 *	If the limit is ok, return 0. If the limit is too small,
 *	return 1 and assign the smallest enforceable limit
 *	to the designated variables.
 */
int cpulimlow (	       
	unsigned long secs,		/* Seconds part */
	short ms,			/* Milliseconds part */
	unsigned long *new_secsp,	/* Pointer to altered seconds value */
	short *new_msp)			/* Pointer to altered milliseconds */
{
  ENTER_FUNCTION("nqs_generic/all-systems/cpulimlow");
    /* probably overkill to include this information */
  TEST_ARG(new_secsp != NULL, 4);
  TEST_ARG(new_msp   != NULL, 5);
  EXIT_FUNCTION;
  return (0);
}


/*** quolimhigh
 *
 *
 *	int quolimhigh():
 *
 *	Given the coefficient and the units of a memory or disk
 *	resource limit, report whether the implied limit could be
 *	enforced on this machine. In determining whether the limit
 *	is too high, we must remember that some apparently finite
 *	limit values might have been usurped by the system call in
 *	question to mean "infinity". 
 *
 *	Presently, we assume that on BSD4.3, DECOSF, and ULTRIX, that
 *	MEM_LIMIT_GRAN and DISK_LIMIT_GRAN are 1.  If either is not, the
 *	code breaks.  We also pretend that for SYS52, SGI, & SOLARIS,  a limit
 *	is too high if the limit expressed in bytes doesn't fit in 31 bits.
 *	This treats some legal limits as illegal.
 *
 *	If the limit could be enforced, return 0.
 *	If the limit could not be enforced, return 1
 *	and assign the largest limit that could be enforced
 *	to the designated variables.
 *
 *	We assume that the actual parameter "coeff" < 2**31.
 *	This is in line with scanquolim().  Limit_type is kept as
 *	a formal parameter for two reasons:
 *	1) For symmetry with quolimlow().
 *	2) To remind porters to consider it.
 */
int quolimhigh (
	unsigned long coeff,		/* Coefficient of limit in question */
	short units,			/* Units of limit in question */
	short infinite,			/* Boolean */
	int limit_type,			/* One of LIM_??? */
	unsigned long *new_coeffp,	/* Pointer to altered coefficient */
	short *new_unitsp)		/* Pointer to altered units */
{
  ENTER_FUNCTION("nqs_generic/all-systems/quolimhigh");
#if	BSD42 | BSD43 | ULTRIX | DECOSF
	/*
	 * If the limit would look like infinity to the system call,
	 * or if it is bigger than the number that would look like
	 * infinity, change it and return 1.
	 */
	if (infinite) return (0);
	switch (units) {
	case QLM_BYTES:
		if (coeff >= RLIM_INFINITY) {
			*new_coeffp = RLIM_INFINITY - 1;
			*new_unitsp = (short) QLM_BYTES;
		  	EXIT_FUNCTION;
			return (1);
		}
	  	EXIT_FUNCTION;
		return (0);
	case QLM_WORDS:
		if (coeff >= RLIM_INFINITY / (BYTES_PER_WORD)) {
			*new_coeffp = RLIM_INFINITY -1;
			*new_unitsp = (short) QLM_BYTES;
		  	EXIT_FUNCTION;
			return (1);
		}
	  	EXIT_FUNCTION;
		return (0);
	case QLM_KBYTES:
		if (coeff >= RLIM_INFINITY / (1<<10)) {
			*new_coeffp = RLIM_INFINITY - 1;
			*new_unitsp = (short) QLM_BYTES;
		  	EXIT_FUNCTION;
			return (1);
		}
	  	EXIT_FUNCTION;
		return (0);
	case QLM_KWORDS:
		if (coeff >= RLIM_INFINITY / ((1<<10) * BYTES_PER_WORD)) {
			*new_coeffp = RLIM_INFINITY -1;
			*new_unitsp = (short) QLM_BYTES;
		  	EXIT_FUNCTION;
			return (1);
		}
	  	EXIT_FUNCTION;
		return (0);
	case QLM_MBYTES:
		if (coeff >= RLIM_INFINITY / (1<<20)) {
			*new_coeffp = RLIM_INFINITY - 1;
			*new_unitsp = (short) QLM_BYTES;
		  	EXIT_FUNCTION;
			return (1);
		}
	  	EXIT_FUNCTION;
		return (0);
	case QLM_MWORDS:
		if (coeff >= RLIM_INFINITY / ((1<<20) * BYTES_PER_WORD)) {
			*new_coeffp = RLIM_INFINITY -1;
			*new_unitsp = (short) QLM_BYTES;
		  	EXIT_FUNCTION;
			return (1);
		}
	  	EXIT_FUNCTION;
		return (0);
	case QLM_GBYTES:
		if (coeff >= RLIM_INFINITY / (1<<30)) {
			*new_coeffp = RLIM_INFINITY - 1;
			*new_unitsp = (short) QLM_BYTES;
		  	EXIT_FUNCTION;
			return (1);
		}
	  	EXIT_FUNCTION;
		return (0);
	case QLM_GWORDS:
		/*
		 * Zero gigawords is an exception.
		 */
		if (coeff == 0) return (0);
		/*
		 * In the case of gigawords, we have to check
		 * for overflow.
		 */
		if (BYTES_PER_WORD > 1) {
			*new_coeffp = RLIM_INFINITY -1;
			*new_unitsp = (short) QLM_BYTES;
		  	EXIT_FUNCTION;
			return (1);
		}
		if (coeff >= (RLIM_INFINITY / (1<<30)) / BYTES_PER_WORD) {
			*new_coeffp = RLIM_INFINITY -1;
			*new_unitsp = (short) QLM_BYTES;
		  	EXIT_FUNCTION;
			return (1);
		}
	  	EXIT_FUNCTION;
		return (0);
	}
#else
#if	 HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
	/*
	 *  Unlike BSD4.3, DECOSF, and ULTRIX, we do not have to watch 
	 *  for infinity here.  However, if the limit, when expressed in bytes,
	 *  would be > 2**31 -1, then change it and return 1.
	 */
	if (infinite) {
		*new_coeffp = 2147483647L;
		*new_unitsp = (short) QLM_BYTES;
	  	EXIT_FUNCTION;
		return (1);
	}
	switch (units) {
	case QLM_BYTES:
		return (0);
	case QLM_WORDS:
		if (coeff > 2147483647L / BYTES_PER_WORD) {
			*new_coeffp = 2147483647L;
			*new_unitsp = (short) QLM_BYTES;
		  	EXIT_FUNCTION;
			return (1);
		}
	  	EXIT_FUNCTION;
		return (0);
	case QLM_KBYTES:
		if (coeff > 2147483647L / (1<<10)) {
			*new_coeffp = 2147483647L;
			*new_unitsp = (short) QLM_BYTES;
		  	EXIT_FUNCTION;
			return (1);
		}
	  	EXIT_FUNCTION;
		return (0);
	case QLM_KWORDS:
		if (coeff > 2147483647L / ((1<<10) * BYTES_PER_WORD)) {
			*new_coeffp = 2147483647L;
			*new_unitsp = (short) QLM_BYTES;
		  	EXIT_FUNCTION;
			return (1);
		}
	  	EXIT_FUNCTION;
		return (0);
	case QLM_MBYTES:
		if (coeff > 2147483647L / (1<<20)) {
			*new_coeffp = 2147483647L;
			*new_unitsp = (short) QLM_BYTES;
		  	EXIT_FUNCTION;
			return (1);
		}
	  	EXIT_FUNCTION;
		return (0);
	case QLM_MWORDS:
		if (coeff > 2147483647L / ((1<<20) * BYTES_PER_WORD)) {
			*new_coeffp = 2147483647L;
			*new_unitsp = (short) QLM_BYTES;
		  	EXIT_FUNCTION;
			return (1);
		}
	  	EXIT_FUNCTION;
		return (0);
	case QLM_GBYTES:
		if (coeff > 2147483647L / (1<<30)) {
			*new_coeffp = 2147483647L;
			*new_unitsp = (short) QLM_BYTES;
		  	EXIT_FUNCTION;
			return (1);
		}
	  	EXIT_FUNCTION;
		return (0);
	case QLM_GWORDS:
		if (coeff == 0)
	        {
		  EXIT_FUNCTION;
		  return (0);
		}
	  
		/*
		 * In the case of gigawords, we have to check
		 * for overflow.
		 */
		if (BYTES_PER_WORD > 1) {
			*new_coeffp = 2147483647L;
			*new_unitsp = (short) QLM_BYTES;
		  	EXIT_FUNCTION;
			return (1);
		}
		if (coeff > (2147483647L / (1<<30)) / BYTES_PER_WORD) {
			*new_coeffp = 2147483647L;
			*new_unitsp = (short) QLM_BYTES;
		  	EXIT_FUNCTION;
			return (1);
		}
	  	EXIT_FUNCTION;
		return (0);
	}
#else
BAD SYSTEM TYPE
#endif
#endif
}


/*** quolimlow
 *
 *
 *	int quolimlow ():
 *
 *	Given the coefficient and the units of a memory or disk
 *	resource limit, report whether the implied limit could be
 *	enforced on this machine. In determining whether the limit
 *	is too low, we must remember that some apparently finite
 *	limit values might have been usurped by the system call in
 *	question to mean "infinity". 
 *
 *	The limit shall be deemed to be too small if it is
 *	a small number and attempting to enforce it would
 *	result in an infinite limit, or would cause a batch request
 *	to fail in a way inscrutable to the user.
 *
 *	If the limit is ok, return 0. If the limit is too small,
 *	return 1 and assign the smallest enforceable limit
 *	to the designated variables.
 *
 *	We assume that the actual parameter "coeff" < 2**31.
 *	This is in line with scanquolim().
 */
int quolimlow (
	unsigned long coeff,		/* Coefficient of limit in question */
	short units,			/* Units of limit in question */
	int limit_type,			/* One of LIM_??? */
	unsigned long *new_coeffp,	/* Pointer to altered coefficient */
	short *new_unitsp)		/* Pointer to altered units */
{
  ENTER_FUNCTION("nqs_generic/all-systems/quolimlow");
    /* debugging support */
  TEST_ARG(new_coeffp != NULL, 4);
  TEST_ARG(new_unitsp != NULL, 5);
    /* ensure we are not working with garbage */
  
	if (limit_type == LIM_PPPFILE) {
		/*
		 * Ulimit(2) claims to support the setting of values
		 * as low as zero bytes.  Here, however, we prevent
		 * the setting of any limit lower than 2048 bytes.
		 * The child of the shepherd process writes to the
		 * shepherd AFTER the call to ulimit().  A ulimit
		 * of less than 2048 bytes might result in the
		 * shepherd failing to get a completion message,
		 * or mail failing to get through.
		 */
		switch (units) {
		case QLM_BYTES:
			if (coeff < 2048) {
				*new_coeffp = 2048;
				*new_unitsp = QLM_BYTES;
			  	EXIT_FUNCTION;
				return (1);
			}
		  	EXIT_FUNCTION;
			return (0);
		case QLM_WORDS:
			if ((coeff * BYTES_PER_WORD) < 2048) {
				*new_coeffp = 2048;
				*new_unitsp = QLM_BYTES;
			  	EXIT_FUNCTION;
				return (1);
			}
		  	EXIT_FUNCTION;
			return (0);
		case QLM_KBYTES:
			if (coeff < 2) {
				*new_coeffp = 2048;
				*new_unitsp = QLM_BYTES;
			  	EXIT_FUNCTION;
				return (1);
			}
		  	EXIT_FUNCTION;
			return (0);
		case QLM_KWORDS:
			if ((coeff * BYTES_PER_WORD) < 2) {
				*new_coeffp = 2048;
				*new_unitsp = QLM_BYTES;
			  	EXIT_FUNCTION;
				return (1);
			}
		  	EXIT_FUNCTION;
			return (0);
		case QLM_MBYTES:
		case QLM_MWORDS:
		case QLM_GBYTES:
		case QLM_GWORDS:
			if (coeff == 0) {
				*new_coeffp = 2048;
				*new_unitsp = QLM_BYTES;
			  	EXIT_FUNCTION;
				return (1);
			}
		  	EXIT_FUNTION;
			return (0);
		}
	}
	/*
	 *  Below, we handle limits OTHER than LIM_PPPFILE.
	 */
	/*
	 *  No problem of zero meaning infinity here.
	 */
  	EXIT_FUNCTION;
	return (0);
}
