/*
 *	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  JRR                     Initial version.
*       V01.20  JRR     16-Jan-1992	Added support for RS6000.
*       V01.30  JRR     20-Jan-1992	Further tweaks to above.   
*       V01.4 	JRR     02-Mar-1992	Added Cosmic V2 changes. 
*	V01.5	JRR	17-Jun-1992	Added header.
*	V01.6	JRR	   Dec-1992	Version 3.30.
*	V01.7	JRR	29-Dec-1992	If RS6000,  use setpri call.  
*	V01.8	JRR	06-Apr-1993	Added support for DECOSF.
*	V01.9	JRR	22-Oct-1993	Removed unnecessary code.
*	V01.10	JRR	28-Feb-1994	Added support for SOLARIS.
*/
/*++ nqs_enf.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.35.6/src/RCS/nqs_enf.c,v $
 *
 * DESCRIPTION:
 *
 *	This module enforces batch request resource limits.
 *
 *	Author:
 *	-------
 *	Robert W. Sandstrom, Sterling Software Incorporated.
 *	February 17, 1986.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.10 $ $Date: 1994/03/30 20:36:34 $ $State: Exp $)
 * $Log: nqs_enf.c,v $
 * Revision 1.10  1994/03/30  20:36:34  jrroma
 * Version 3.35.6
 *
 * Revision 1.9  94/02/24  21:30:51  jrroma
 * Version 3.35.3
 * 
 * Revision 1.8  93/07/13  21:33:48  jrroma
 * Version 3.34
 * 
 * Revision 1.7  93/02/05  23:16:46  jrroma
 * Version 3.31
 * 
 * Revision 1.6  92/12/22  15:39:53  jrroma
 * Version 3.30
 * 
 * Revision 1.5  92/06/18  17:31:02  jrroma
 * Added gnu header
 * 
 * Revision 1.4  92/03/02  13:50:39  jrroma
 * Added Cosmic V2 changes.
 * 
 * Revision 1.3  92/01/20  12:55:17  jrroma
 * Further tweak to RS6000 support.
 * 
 * Revision 1.2  92/01/17  10:52:32  jrroma
 * Added support for RS6000.
 * 
 * Revision 1.1  92/01/17  10:51:49  jrroma
 * Initial revision
 * 
 *
 */

#include "nqs.h"			/* NQS constants and data types */
#include <errno.h>
#include "informcc.h"			/* NQS information completion */
					/* codes and masks */
#include "requestcc.h"			/* NQS request completion codes */

#if	SYS52 
#else
#if	IBMRS
#include <sys/sched.h>
#include <sys/time.h>			/* So we can include sys/resource.h */
#include <sys/resource.h>		/* Get definition of struct rlimit */
#else
#if	SGI  | SOLARIS | BSD42 | BSD43 | ULTRIX | DECOSF | LINUX
#include <sys/time.h>			/* So we can include sys/resource.h */
#include <sys/resource.h>		/* Get definition of struct rlimit */
#else
#if	HPUX
#define RLIM_INFINITY  0x7fffffff
#define RLIMIT_CPU	0
#define RLIMIT_FSIZE	1
#define RLIMIT_DATA     2               /* data size */
#define RLIMIT_STACK    3               /* stack size */
#define RLIMIT_CORE     4               /* core file size */
#define RLIMIT_RSS      5               /* resident set size */
#include <sys/resource.h>
#include <sys/time.h>
#else
BAD SYSTEM TYPE
#endif
#endif
#endif
#endif


#if	HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
extern long ulimit();
#else
#if	BSD43 | ULTRIX | DECOSF
#else
BAD SYSTEM TYPE
#endif
#endif

#if	VALID_LIMITS & (LIM_PPCPUT | LIM_PRCPUT)
#if	HPUX | SGI | SOLARIS | BSD43 | ULTRIX | DECOSF | IBMRS | LINUX
#if	CPU_LIMIT_GRAN > 2000
REWRITE THIS FUNCTION
/* See the note about cpulimhigh() below. */
#endif
/*** enf_bsdcpu
 *
 *
 *	void enf_bsdcpu():
 *
 *	Enforce within BSD4.2 a per process or per request
 *	cpu time limit.
 */
void enf_bsdcpu (
	struct cpulimit *cpulimitp,	/* Pointer to what is to be set */
	short infinite,			/* Boolean */
	long limit_type)		/* Per_process or Per_request */
{
	int limresult;
	struct rlimit rlimit;
	
	if (limit_type == LIM_PPCPUT) {
		if (infinite) {
			/*
			 * It is impossible in NQS to have an infinite
			 * maximum and a finite warning.
			 */
			rlimit.rlim_max = RLIM_INFINITY;
			rlimit.rlim_cur = RLIM_INFINITY;
		}
		else {
			/*
			 * If seconds > 1000, ignore ms in order to avoid
			 * overflow.  (1000 is arbitrary. We know from
			 * cpulimhigh() that the limit can be enforced;
			 * we just have to avoid ever expressing the limit
			 * in units finer than those of the system call.)
			 */
			if (cpulimitp->max_seconds > 1000) {
				rlimit.rlim_max =
					cpulimitp->max_seconds * CPU_LIMIT_GRAN;
			}
			/*
			 * It is safe to temporarily express the limit
			 * in units 1000 times finer than the system call.
			 */
			else {
				rlimit.rlim_max =
					(((1000 * cpulimitp->max_seconds)
					+ cpulimitp->max_ms) *
					CPU_LIMIT_GRAN) / 1000;
			}
			/* Same reasoning as above. */
			if (cpulimitp->warn_seconds > 1000) {
				rlimit.rlim_cur =
					cpulimitp->warn_seconds *
					CPU_LIMIT_GRAN;
			}
			else {
				rlimit.rlim_cur =
					(((1000 * cpulimitp->warn_seconds)
					+ cpulimitp->warn_ms) *
					CPU_LIMIT_GRAN) / 1000;
			}
		}
		limresult = setrlimit (RLIMIT_CPU, &rlimit);
		if (limresult == -1L) {
			serexit (RCM_UNABLETOEXE | TCI_PP_CTLEXC,
				 asciierrno());
		}
	}
}
#else
#if	SYS52 
#else
BAD SYSTEM TYPE
#endif
#endif
#endif


#if	VALID_LIMITS & LIM_PPNICE
#if	BSD42 | BSD43 | ULTRIX | DECOSF
/*** enf_bsdnic
 *
 *
 *	void enf_bsdnic():
 *
 *	Enforce a nice value within BSD4.2.
 */
void enf_bsdnic (int nice_value)
{
	int limresult;
	
	limresult = setpriority (PRIO_PROCESS, getpid(), nice_value);
	if (limresult == -1) {
		serexit (RCM_UNABLETOEXE | TCI_PP_NELEXC, asciierrno());
	}
}
#else
#if	HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
/*** enf_sy5nic
 *
 *
 *	void enf_sy5nic():
 *
 *	Enforce a nice value within Silicon Graphics' Unix, System V,
 *
 */
void enf_sy5nic (int nice_value)
{
	int limresult;
	
	errno = 0;			/* nice(2) returning -1 doesn't */
					/* always mean failure */
	limresult = nice (0);		/* Get current nice value */
	if (errno != 0) {
		serexit (RCM_UNABLETOEXE | TCI_PP_NELEXC, asciierrno());
	}
	nice (nice_value - limresult);    /* Go to desired value */
	if (errno != 0) {
		serexit (RCM_UNABLETOEXE | TCI_PP_NELEXC, asciierrno());
	}
#if	IBMRS
	/*
	 * As a result of this, nice 16 becomes priority 121 and nice 19
	 * becomes priority 124.
	 */
	if (nice_value > 15) {
	    nice_value += PRIORITY_MAX - 21;
	    setpri (0, nice_value);
	}
#endif
}
#else
BAD SYSTEM TYPE
#endif
#endif
#endif


/*
 * Below, we define THESE, THEM, and THOSE for C preprocessors that
 * do not allow line continuation.
 */
#define THESE	LIM_PPCORE | LIM_PPDATA | LIM_PPPFILE | LIM_PRPFILE
#define THEM	LIM_PPMEM | LIM_PRMEM | LIM_PPQFILE | LIM_PRQFILE 
#define THOSE	LIM_PPSTACK | LIM_PPTFILE | LIM_PRTFILE | LIM_PPWORK

#if	VALID_LIMITS & (THESE | THEM | THOSE)
/*** enf_bsdquo
 *
 *
 *	void enf_bsdquo():
 *
 *	Enforce within BSD4.2 one of the limits: per process core file
 *	size, per process data segment size, per process permanent file
 *	size, per request permanent file size, per process
 *	memory size, per request memory size, per process stack
 *	segment size, per process temporary file size, per request
 *	temporary file size, or per process working set size.
 *
 */
void enf_bsdquo (
	struct quotalimit *quotalimitp,	/* Pointer to what is to be set */
	short infinite,			/* Boolean */
	long limit_type)		/* One of LIM_??? */
{
	int granularity;		/* Granularity of the sys call */
	int limresult;			/* Return value of limit setting */
	struct rlimit rlimit;		/* Holds the limit itself */

	if (infinite) {
		/*
		 * It is impossible in NQS to have an infinite maximum
		 * and a finite warning.
		 */
		rlimit.rlim_max = RLIM_INFINITY;
		rlimit.rlim_cur = RLIM_INFINITY;
	}
	else {
		if (limit_type & (LIM_PPCORE | LIM_PPPFILE)) {
			granularity = DISK_LIMIT_GRAN;
		}
		else {
			granularity = MEM_LIMIT_GRAN;
		}
		switch (quotalimitp->max_units) {
		case QLM_BYTES:
			rlimit.rlim_max = quotalimitp->max_quota / granularity;
			break;
		case QLM_WORDS:
			rlimit.rlim_max =
				(quotalimitp->max_quota * BYTES_PER_WORD) /
				granularity;
			break;
		case QLM_KBYTES:
			rlimit.rlim_max = (quotalimitp->max_quota * (1<<10)) /
				granularity;
			break;
		case QLM_KWORDS:
			rlimit.rlim_max =
				(quotalimitp->max_quota * (BYTES_PER_WORD) *
				(1<<10)) / granularity;
			break;
		case QLM_MBYTES:
			rlimit.rlim_max = (quotalimitp->max_quota * (1<<20)) /
				granularity;
			break;
		case QLM_MWORDS:
			rlimit.rlim_max =
				(quotalimitp->max_quota * (BYTES_PER_WORD) *
				(1<<20)) / granularity;
			break;
		case QLM_GBYTES:
			rlimit.rlim_max = (quotalimitp->max_quota * (1<<30)) /
				granularity;
			break;
		case QLM_GWORDS:
			rlimit.rlim_max =
				(quotalimitp->max_quota * (BYTES_PER_WORD) *
				(1<<30)) / granularity;
			break;
		}
		switch (quotalimitp->warn_units) {
		case QLM_BYTES:
			rlimit.rlim_cur = quotalimitp->warn_quota / granularity;
			break;
		case QLM_WORDS:
			rlimit.rlim_cur =
				(quotalimitp->warn_quota * BYTES_PER_WORD) /
				granularity;
			break;
		case QLM_KBYTES:
			rlimit.rlim_cur = (quotalimitp->warn_quota * (1<<10)) /
				granularity;
			break;
		case QLM_KWORDS:
			rlimit.rlim_cur =
				(quotalimitp->warn_quota * (BYTES_PER_WORD) *
				(1<<10)) / granularity;
			break;
		case QLM_MBYTES:
			rlimit.rlim_cur = (quotalimitp->warn_quota * (1<<20)) /
				granularity;
			break;
		case QLM_MWORDS:
			rlimit.rlim_cur =
				(quotalimitp->warn_quota * (BYTES_PER_WORD) *
				(1<<20)) / granularity;
			break;
		case QLM_GBYTES:
			rlimit.rlim_cur = (quotalimitp->warn_quota * (1<<30)) /
				granularity;
			break;
		case QLM_GWORDS:
			rlimit.rlim_cur =
				(quotalimitp->warn_quota * (BYTES_PER_WORD) *
				(1<<30)) / granularity;
			break;
		}
	}
	switch (limit_type) {
	case LIM_PPCORE:
		limresult = setrlimit (RLIMIT_CORE, &rlimit);
		if (limresult == -1) {
			serexit (RCM_UNABLETOEXE | TCI_PP_CFLEXC,
				 asciierrno());
		}
		break;
	case LIM_PPDATA:
		limresult = setrlimit (RLIMIT_DATA, &rlimit);
		if (limresult == -1) {
			serexit (RCM_UNABLETOEXE | TCI_PP_DSLEXC,
				 asciierrno());
		}
		break;
	case LIM_PPPFILE:
		limresult = setrlimit (RLIMIT_FSIZE, &rlimit);
		if (limresult == -1) {
			serexit (RCM_UNABLETOEXE | TCI_PP_PFLEXC,
				 asciierrno());
		}
		break;
	case LIM_PPSTACK:
		limresult = setrlimit (RLIMIT_STACK, &rlimit);
		if (limresult == -1) {
			serexit (RCM_UNABLETOEXE | TCI_PP_SSLEXC,
				 asciierrno());
		}
		break;
#if	! (SOLARIS | LINUX)
	case LIM_PPWORK:
		limresult = setrlimit (RLIMIT_RSS, &rlimit);
		if (limresult == -1) {
			serexit (RCM_UNABLETOEXE | TCI_PP_WSLEXC,
				 asciierrno());
		}
		break;
#endif
	}
}
/*** enf_sy5quo
 *
 *
 *	void enf_sy5quo():
 *
 *	Enforce within Silicon Graphics' Unix, System V.
 *	one of the limits: per process core file
 *	size, per process data segment size, per process permanent file
 *	size, per request permanent file size, per process
 *	memory size, per request memory size, per process stack
 *	segment size, per process temporary file size, per request
 *	temporary file size, or per process working set size.
 *
 */
void enf_sy5quo (
	struct quotalimit *quotalimitp,	/* Pointer to what is to be set */
	int infinite,			/* Is desired quota infinite? */
	long limit_type)		/* One of LIM_??? */
{
	int llimresult;			/* Return value of limit setting */

	switch (limit_type) {
	case LIM_PPPFILE:
		if (infinite) {
			serexit (RCM_UNABLETOEXE | TCI_PP_PFLEXC,
				"Infinite per proc. permfile not supported."
				);	
		}
		else {
			switch (quotalimitp->max_units) {
			case QLM_BYTES:
				llimresult = ulimit (2,
					quotalimitp->max_quota /
					DISK_LIMIT_GRAN);
				break;
			case QLM_WORDS:
				llimresult = ulimit (2,
					(quotalimitp->max_quota *
					BYTES_PER_WORD) / DISK_LIMIT_GRAN);
				break;
			case QLM_KBYTES:
				llimresult = ulimit (2,
					(quotalimitp->max_quota * (1<<10)) /
					DISK_LIMIT_GRAN);
				break;
			case QLM_KWORDS:
				llimresult = ulimit (2,
					(quotalimitp->max_quota *
					BYTES_PER_WORD * (1<<10)) /
					DISK_LIMIT_GRAN);
				break;
			case QLM_MBYTES:
				llimresult = ulimit (2,
					(quotalimitp->max_quota * (1<<20)) /
					DISK_LIMIT_GRAN);
				break;
			case QLM_MWORDS:
				llimresult = ulimit (2,
					(quotalimitp->max_quota *
					BYTES_PER_WORD * (1<<20)) /
						DISK_LIMIT_GRAN);
				break;
			case QLM_GBYTES:
				llimresult = ulimit (2,
					(quotalimitp->max_quota * (1<<30)) /
						DISK_LIMIT_GRAN);
				break;
			case QLM_GWORDS:
				/*
				 * For SGI, SOLARIS, SYS52, IBMRS, HPUX, 
				 * we have checked that the limit
				 * can be expressed in bytes. Therefore,
				 * rearrange to avoid overflow.
				 */
				llimresult = ulimit (2,
					((quotalimitp->max_quota * (1<<30)) /
						DISK_LIMIT_GRAN) *
						BYTES_PER_WORD);
				break;
			}
			if (llimresult == -1L) {
				serexit (RCM_UNABLETOEXE | TCI_PP_PFLEXC,
					 asciierrno());
			}
			break;
		}
	}
}
#endif
