/*
 * libsal/syslimit/rlimit.c
 * Support for the get/setrlimit interface
 */

/*
 * Author(s) :
 * 
 * Stuart Herbert (S.Herbert@sheffied.ac.uk)
 * Academic Computing Services
 * University of Sheffield
 */

#include <libsal/license.h>
#include <libsal/syslimit/internal.h>
#include <libsal/proto.h>
#include <libsal/defines.h>

static int sal_syslimit__getrlimit(int, struct rlimit *);
static int sal_syslimit__setrlimit(int, struct rlimit *);

/*
 * !! FUNCTION:	sal_set_rlimit
 * !! PURPOSE:	Call this function to specify a new value for a supported
 * !! PURPOSE:	kernel-based limit which uses the get/setrlimits interface.
 * !! PURPOSE:	You should really call 'sal_set_syslimit', which knows
 * !! PURPOSE:	which limits are supported by which interfaces, rather than
 * !! PURPOSE:	call this directly from inside third-party code.
 * 
 * !! A_NAME:	rlimit
 * !! A_TYPE:	int
 * !! A_DESC:	the limit to be set (iID from the syslimits table)
 * 
 * !! A_NAME:	new_limit
 * !! A_TYPE:	int
 * !! A_DESC:	the new value for the limit
 * 
 * !! R_TYPE:	int
 * !! R_DESC:	Returns TRUE (1) if successful, 0 on error, and errno
 * !! R_DESC:	contains the error value (for now - full error handling
 * !! R_DESC:	will be added before this code is used in NQS).
 * 
 */

int sal_syslimit_setrlimit(sal_syslimit *stLimitArg)
{
#if	HAS_BSD_LIMITS | IS_BSD
  struct rlimit stLimit;
  TEST_ARG(stLimitArg != NULL, 1);
  
  if (stLimitArg->stLimitValue.boUnlimited)
    stLimitArg->stLimitValue.iValue = RLIM_INFINITY;
  
  stLimit.rlim_cur = (rlim_t) stLimitArg->stLimitValue.iValue;
  stLimit.rlim_max = (rlim_t) stLimitArg->stLimitValue.iValue;
  
  if (sal_syslimit__setrlimit(stLimitArg->stLimitInfo.iID2, &stLimit) != 0)
  {
    if (stLimitArg->stLimitValue.boUnlimited)
      sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_DEBUGLOW, "Unable to enforce limit %s with value '%lu'\n",stLimitArg->stLimitInfo.szName, (unsigned long) stLimitArg->stLimitValue.iValue);
    else
      sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_DEBUGLOW, "Unable to enforce limit %s with value 'unlimited'\n",stLimitArg->stLimitInfo.szName);
    return 0;
  }
  else
  {
    sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_DEBUGLOW, "sal_syslimit_setrlimit() exited okay.\n");
    return 1;
  }
  
#else
  TEST_ARG(stLimitArg != NULL, 1);
  /* not supported - always return FALSE */
  return 0;
#endif /* HAS_BSD_LIMITS | IS_BSD */
}

int sal_syslimit_getrlimit(sal_syslimit *stLimitArg)
{
#if	HAS_BSD_LIMITS | IS_BSD
  struct rlimit stLimit;
  TEST_ARG(stLimitArg != NULL, 1);
  
  if (!sal_syslimit__getrlimit(stLimitArg->stLimitInfo.iID2, &stLimit))
  {
    stLimitArg->stLimitValue.iValue = (sal_syslimit_t) stLimit.rlim_cur;
    stLimitArg->stLimitValue.boUnlimited = (stLimit.rlim_cur == RLIM_INFINITY);
    return 1;
  }
  else
    return 0;
  
#else
  TEST_ARG(stLimitArg != NULL, 1);
  /* not supported - always return FALSE */
  return 0;
#endif /* HAS_BSD_LIMITS | IS_BSD */
}

static int sal_syslimit__getrlimit(int iID, struct rlimit *pstLimit)
{
  TEST_ARG(pstLimit != NULL, 2);
  
#if	HAS_BSD_LIMITS | IS_BSD
#if	HAS_RLIMIT64
  return getrlimit64(iID, pstLimit);
#else
  return getrlimit(iID, pstLimit);
#endif /* HAS_RLIMIT64 */
#else
  sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, "sal_syslimit__getrlimit() called, but this platform does not support BSD resource limits!!\n");
  return 1;
#endif /* HAS_BSD_LIMITS | IS_BSD */
}

static int sal_syslimit__setrlimit(int iID, struct rlimit *pstLimit)
{
  TEST_ARG(pstLimit != NULL, 2);
#if	HAS_BSD_LIMITS | IS_BSD  
#if	HAS_RLIMIT64
  return setrlimit64(iID, pstLimit);
#else
  return setrlimit(iID, pstLimit);
#endif /* HAS_RLIMIT64 */
#else
  sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, "sal_syslimit__setrlimit() called, but this platform does not support BSD resource limits!!\n");
  return 1;
#endif /* HAS_BSD_LIMITS | IS_BSD */
}
