This file is ulimit.def, from which is created ulimit.c.
It implements the builtin "ulimit" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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 2, or (at your option) any later
version.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES ulimit.c

$BUILTIN ulimit
$FUNCTION ulimit_builtin
$DEPENDS_ON !_MINIX
$SHORT_DOC ulimit [-SHacdflmnpstuv] [limit]
Ulimit provides control over the resources available to processes
started by the shell, on systems that allow such control.  If an
option is given, it is interpreted as follows:

    -S	use the `soft' resource limit
    -H	use the `hard' resource limit
    -a	all current limits are reported
    -c	the maximum size of core files created
    -d	the maximum size of a process's data segment
    -f	the maximum size of files created by the shell
    -l	the maximum size a process may lock into memory
    -m	the maximum resident set size
    -n	the maximum number of open file descriptors
    -p	the pipe buffer size
    -s	the maximum stack size
    -t	the maximum amount of cpu time in seconds
    -u	the maximum number of user processes
    -v	the size of virtual memory 

If LIMIT is given, it is the new value of the specified resource;
the special LIMIT values `soft', `hard', and `unlimited' stand for
the current soft limit, the current hard limit, and no limit, respectively.
Otherwise, the current value of the specified resource is printed.
If no option is given, then -f is assumed.  Values are in 1024-byte
increments, except for -t, which is in seconds, -p, which is in
increments of 512 bytes, and -u, which is an unscaled number of
processes.
$END

#if !defined (_MINIX)

#include <config.h>

#include "../bashtypes.h"
#ifndef _MINIX
#  include <sys/param.h>
#endif

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <stdio.h>
#include <errno.h>

#include "../shell.h"
#include "common.h"
#include "bashgetopt.h"
#include "pipesize.h"

#if !defined (errno)
extern int errno;
#endif

/* For some reason, HPUX chose to make these definitions visible only if
   _KERNEL is defined, so we define _KERNEL before including <sys/resource.h>
   and #undef it afterward. */
#if defined (HAVE_RESOURCE)
#  include <sys/time.h>
#  if defined (HPUX) && defined (RLIMIT_NEEDS_KERNEL)
#    define _KERNEL
#  endif
#  include <sys/resource.h>
#  if defined (HPUX) && defined (RLIMIT_NEEDS_KERNEL)
#    undef _KERNEL
#  endif
#else
#  include <sys/times.h>
#endif

#if defined (HAVE_LIMITS_H)
#  include <limits.h>
#endif

/* Check for the most basic symbols.  If they aren't present, this
   system's <sys/resource.h> isn't very useful to us. */
#if !defined (RLIMIT_FSIZE) || !defined (HAVE_GETRLIMIT)
#  undef HAVE_RESOURCE
#endif

#if !defined (RLIMTYPE)
#  define RLIMTYPE long
#  define string_to_rlimtype(s) strtol(s, (char **)NULL, 10)
#  define print_rlimtype(num, nl) printf ("%ld%s", num, nl ? "\n" : "")
#endif

/* Some systems use RLIMIT_NOFILE, others use RLIMIT_OFILE */
#if defined (HAVE_RESOURCE) && defined (RLIMIT_OFILE) && !defined (RLIMIT_NOFILE)
#  define RLIMIT_NOFILE RLIMIT_OFILE
#endif /* HAVE_RESOURCE && RLIMIT_OFILE && !RLIMIT_NOFILE */

/* Some systems have these, some do not. */
#ifdef RLIMIT_FSIZE
#  define RLIMIT_FILESIZE	RLIMIT_FSIZE
#else
#  define RLIMIT_FILESIZE	256
#endif

#define RLIMIT_PIPESIZE	257

#ifdef RLIMIT_NOFILE
#  define RLIMIT_OPENFILES	RLIMIT_NOFILE
#else
#  define RLIMIT_OPENFILES	258
#endif

#ifdef RLIMIT_VMEM
#  define RLIMIT_VIRTMEM	RLIMIT_VMEM
#  define RLIMIT_VMBLKSZ	1024
#else
#  ifdef RLIMIT_AS
#    define RLIMIT_VIRTMEM	RLIMIT_AS
#    define RLIMIT_VMBLKSZ	1024
#  else
#    define RLIMIT_VIRTMEM	259
#    define RLIMIT_VMBLKSZ	1
#  endif
#endif

#ifdef RLIMIT_NPROC
#  define RLIMIT_MAXUPROC	RLIMIT_NPROC
#else
#  define RLIMIT_MAXUPROC	260
#endif

#if !defined (RLIM_INFINITY)
#  define RLIM_INFINITY 0x7fffffff
#endif

#if !defined (RLIM_SAVED_CUR)
#  define RLIM_SAVED_CUR RLIM_INFINITY
#endif

#if !defined (RLIM_SAVED_MAX)
#  define RLIM_SAVED_MAX RLIM_INFINITY
#endif

#define LIMIT_HARD 0x01
#define LIMIT_SOFT 0x02

static int _findlim __P((int));

static int ulimit_internal __P((int, char *, int, int));

static int get_limit __P((int, RLIMTYPE *, RLIMTYPE *));
static int set_limit __P((int, RLIMTYPE, int));

static void printone __P((int, RLIMTYPE, int));
static void print_all_limits __P((int));

static int set_all_limits __P((int, RLIMTYPE));

static int filesize __P((RLIMTYPE *));
static int pipesize __P((RLIMTYPE *));
static int getmaxuprc __P((RLIMTYPE *));
static int getmaxvm __P((RLIMTYPE *, RLIMTYPE *));

typedef struct {
  int  option;			/* The ulimit option for this limit. */
  int  parameter;		/* Parameter to pass to get_limit (). */
  int  block_factor;		/* Blocking factor for specific limit. */
  char *description;		/* Descriptive string to output. */
  char *units;			/* scale */
} RESOURCE_LIMITS;

static RESOURCE_LIMITS limits[] = {
#ifdef RLIMIT_CORE
  { 'c',	RLIMIT_CORE,  1024,	"core file size",	"blocks" },
#endif
#ifdef RLIMIT_DATA
  { 'd',	RLIMIT_DATA,  1024,	"data seg size",	"kbytes" },
#endif
  { 'f',	RLIMIT_FILESIZE, 1024,	"file size",		"blocks" },
#ifdef RLIMIT_MEMLOCK
  { 'l',	RLIMIT_MEMLOCK, 1024,	"max locked memory",	"kbytes" },
#endif
#ifdef RLIMIT_RSS
  { 'm',	RLIMIT_RSS,   1024,	"max memory size",	"kbytes" },
#endif /* RLIMIT_RSS */
  { 'n',	RLIMIT_OPENFILES, 1,	"open files",		(char *)NULL},
  { 'p',	RLIMIT_PIPESIZE, 512,	"pipe size", 		"512 bytes" },
#ifdef RLIMIT_STACK
  { 's',	RLIMIT_STACK, 1024,	"stack size",		"kbytes" },
#endif
#ifdef RLIMIT_CPU
  { 't',	RLIMIT_CPU,      1,	"cpu time",		"seconds" },
#endif /* RLIMIT_CPU */
  { 'u',	RLIMIT_MAXUPROC, 1,	"max user processes",	(char *)NULL },
#if defined (HAVE_RESOURCE)
  { 'v',	RLIMIT_VIRTMEM, RLIMIT_VMBLKSZ, "virtual memory", "kbytes" },
#endif
#ifdef RLIMIT_SWAP
  { 'w',	RLIMIT_SWAP,	1024,	"swap size",		"kbytes" },
#endif
  { -1, -1, -1, (char *)NULL, (char *)NULL }
};
#define NCMDS	(sizeof(limits) / sizeof(limits[0]))

typedef struct _cmd {
  int cmd;
  char *arg;
} ULCMD;

static ULCMD *cmdlist;
static int ncmd;
static int cmdlistsz;

#if !defined (HAVE_RESOURCE) && !defined (HAVE_ULIMIT)
long
ulimit (cmd, newlim)
     int cmd;
     long newlim;
{
  errno = EINVAL;
  return -1;
}
#endif /* !HAVE_RESOURCE && !HAVE_ULIMIT */

static int
_findlim (opt)
     int opt;
{
  register int i;

  for (i = 0; limits[i].option > 0; i++)
    if (limits[i].option == opt)
      return i;
  return -1;
}

static char optstring[4 + 2 * NCMDS];

/* Report or set limits associated with certain per-process resources.
   See the help documentation in builtins.c for a full description. */
int
ulimit_builtin (list)
     register WORD_LIST *list;
{
  register char *s;
  int c, limind, mode, opt, all_limits;

  mode = 0;

  all_limits = 0;

  /* Idea stolen from pdksh -- build option string the first time called. */
  if (optstring[0] == 0)
    {
      s = optstring;
      *s++ = 'a'; *s++ = 'S'; *s++ = 'H';
      for (c = 0; limits[c].option > 0; c++)
	{
	  *s++ = limits[c].option;
	  *s++ = ';';
	}
      *s = '\0';
    }

  /* Initialize the command list. */
  if (cmdlistsz == 0)
    cmdlist = (ULCMD *)xmalloc ((cmdlistsz = 16) * sizeof (ULCMD));
  ncmd = 0;

  reset_internal_getopt ();
  while ((opt = internal_getopt (list, optstring)) != -1)
    {
      switch (opt)
	{
	case 'a':
	  all_limits++;
	  break;

	/* -S and -H are modifiers, not real options.  */
	case 'S':
	  mode |= LIMIT_SOFT;
	  break;

	case 'H':
	  mode |= LIMIT_HARD;
	  break;

	case '?':
	  builtin_usage ();
	  return (EX_USAGE);

	default:
	  if (ncmd >= cmdlistsz)
	    cmdlist = (ULCMD *)xrealloc (cmdlist, (cmdlistsz *= 2) * sizeof (ULCMD));
	  cmdlist[ncmd].cmd = opt;
	  cmdlist[ncmd++].arg = list_optarg;
	  break;
	}
    }
  list = loptend;

  if (all_limits)
    {
#ifdef NOTYET
      if (list)		/* setting */
        {
          if (STREQ (list->word->word, "unlimited") == 0)
            {
              builtin_error ("%s: invalid limit argument", list->word->word);
              return (EXECUTION_FAILURE);
            }
          return (set_all_limits (mode == 0 ? LIMIT_SOFT|LIMIT_HARD : mode, RLIM_INFINITY));
        }
#endif
      print_all_limits (mode == 0 ? LIMIT_SOFT : mode);
      return (EXECUTION_SUCCESS);
    }

  /* default is `ulimit -f' */
  if (ncmd == 0)
    {
      cmdlist[ncmd].cmd = 'f';
      /* `ulimit something' is same as `ulimit -f something' */
      cmdlist[ncmd++].arg = list ? list->word->word : (char *)NULL;
      if (list)
	list = list->next;
    }

  /* verify each command in the list. */
  for (c = 0; c < ncmd; c++)
    {
      limind = _findlim (cmdlist[c].cmd);
      if (limind == -1)
	{
	  builtin_error ("`%c': bad command", cmdlist[c].cmd);
	  return (EX_USAGE);
	}
    }

  for (c = 0; c < ncmd; c++)
    if (ulimit_internal (cmdlist[c].cmd, cmdlist[c].arg, mode, ncmd > 1) == EXECUTION_FAILURE)
      return (EXECUTION_FAILURE);

  return (EXECUTION_SUCCESS);
}

static int
ulimit_internal (cmd, cmdarg, mode, multiple)
     int cmd;
     char *cmdarg;
     int mode, multiple;
{
  int opt, limind, setting;
  int block_factor;
  RLIMTYPE soft_limit, hard_limit, real_limit, limit;

  setting = cmdarg != 0;
  limind = _findlim (cmd);
  if (mode == 0)
    mode = setting ? (LIMIT_HARD|LIMIT_SOFT) : LIMIT_SOFT;
  opt = get_limit (limind, &soft_limit, &hard_limit);
  if (opt < 0)
    {
      builtin_error ("%s: cannot get limit: %s", limits[limind].description,
						 strerror (errno));
      return (EXECUTION_FAILURE);
    }

  if (setting == 0)	/* print the value of the specified limit */
    {
      printone (limind, (mode & LIMIT_SOFT) ? soft_limit : hard_limit, multiple);
      return (EXECUTION_SUCCESS);
    }
 
  /* Setting the limit. */
  if (STREQ (cmdarg, "hard"))
    real_limit = hard_limit;
  else if (STREQ (cmdarg, "soft"))
    real_limit = soft_limit;
  else if (STREQ (cmdarg, "unlimited"))
    real_limit = RLIM_INFINITY;
  else if (all_digits (cmdarg))
    {
      limit = string_to_rlimtype (cmdarg);
      block_factor = limits[limind].block_factor;
      real_limit = limit * block_factor;

      if ((real_limit / block_factor) != limit)
	{
	  sh_erange (cmdarg, "limit");
	  return (EXECUTION_FAILURE);
	}
    }
  else
    {
      sh_invalidnum (cmdarg);
      return (EXECUTION_FAILURE);
    }

  if (set_limit (limind, real_limit, mode) < 0)
    {
      builtin_error ("%s: cannot modify limit: %s", limits[limind].description,
						    strerror (errno));
      return (EXECUTION_FAILURE);
    }

  return (EXECUTION_SUCCESS);
}

static int
get_limit (ind, softlim, hardlim)
     int ind;
     RLIMTYPE *softlim, *hardlim;
{
  RLIMTYPE value;
#if defined (HAVE_RESOURCE)
  struct rlimit limit;
#endif

  if (limits[ind].parameter >= 256)
    {
      switch (limits[ind].parameter)
	{
	case RLIMIT_FILESIZE:
	  if (filesize (&value) < 0)
	    return -1;
	  break;
	case RLIMIT_PIPESIZE:
	  if (pipesize (&value) < 0)
	    return -1;
	  break;
	case RLIMIT_OPENFILES:
	  value = (RLIMTYPE)getdtablesize ();
	  break;
	case RLIMIT_VIRTMEM:
	  return (getmaxvm (softlim, hardlim));
	case RLIMIT_MAXUPROC:
	  if (getmaxuprc (&value) < 0)
	    return -1;
	  break;
	default:
	  errno = EINVAL;
	  return -1;
	}
      *softlim = *hardlim = value;
      return (0);
    }
  else
    {
#if defined (HAVE_RESOURCE)
      if (getrlimit (limits[ind].parameter, &limit) < 0)
	return -1;
      *softlim = limit.rlim_cur;
      *hardlim = limit.rlim_max;
#  if defined (HPUX9)
      if (limits[ind].parameter == RLIMIT_FILESIZE)
	{
	  *softlim *= 512;
	  *hardlim *= 512;			/* Ugh. */
	}
      else
#  endif /* HPUX9 */
      return 0;
#else
      errno = EINVAL;
      return -1;
#endif
    }
}

static int
set_limit (ind, newlim, mode)
     int ind;
     RLIMTYPE newlim;
     int mode;
{
#if defined (HAVE_RESOURCE)
   struct rlimit limit;
   RLIMTYPE val;
#endif

  if (limits[ind].parameter >= 256)
    switch (limits[ind].parameter)
      {
      case RLIMIT_FILESIZE:
#if !defined (HAVE_RESOURCE)
	return (ulimit (2, newlim / 512L));
#else
	errno = EINVAL;
	return -1;
#endif

      case RLIMIT_OPENFILES:
#if defined (HAVE_SETDTABLESIZE)
#  if defined (__CYGWIN__)
	/* Grrr... Cygwin declares setdtablesize as void. */
	setdtablesize (newlim);
	return 0;
#  else
	return (setdtablesize (newlim));
#  endif
#endif
      case RLIMIT_PIPESIZE:
      case RLIMIT_VIRTMEM:
      case RLIMIT_MAXUPROC:
      default:
	errno = EINVAL;
	return -1;
      }
  else
    {
#if defined (HAVE_RESOURCE)
      if (getrlimit (limits[ind].parameter, &limit) < 0)
	return -1;
#  if defined (HPUX9)
      if (limits[ind].parameter == RLIMIT_FILESIZE)
	newlim /= 512;				/* Ugh. */
#  endif /* HPUX9 */
      val = (current_user.euid != 0 && newlim == RLIM_INFINITY &&
	       (mode & LIMIT_HARD) == 0 &&		/* XXX -- test */
	       (limit.rlim_cur <= limit.rlim_max))
		 ? limit.rlim_max : newlim;
      if (mode & LIMIT_SOFT)
	limit.rlim_cur = val;
      if (mode & LIMIT_HARD)
	limit.rlim_max = val;
	  
      return (setrlimit (limits[ind].parameter, &limit));
#else
      errno = EINVAL;
      return -1;
#endif
    }
}

static int
getmaxvm (softlim, hardlim)
     RLIMTYPE *softlim, *hardlim;
{
#if defined (HAVE_RESOURCE)
  struct rlimit datalim, stacklim;

  if (getrlimit (RLIMIT_DATA, &datalim) < 0)
    return -1;

  if (getrlimit (RLIMIT_STACK, &stacklim) < 0)
    return -1;

  /* Protect against overflow. */
  *softlim = (datalim.rlim_cur / 1024L) + (stacklim.rlim_cur / 1024L);
  *hardlim = (datalim.rlim_max / 1024L) + (stacklim.rlim_max / 1024L);
  return 0;
#else
  errno = EINVAL;
  return -1;
#endif /* HAVE_RESOURCE */
}

static int
filesize(valuep)
     RLIMTYPE *valuep;
{
#if !defined (HAVE_RESOURCE)
  long result;
  if ((result = ulimit (1, 0L)) < 0)
    return -1;
  else
    *valuep = (RLIMTYPE) result * 512;
  return 0;
#else
  errno = EINVAL;
  return -1;
#endif
}

static int
pipesize (valuep)
     RLIMTYPE *valuep;
{
#if defined (PIPE_BUF)
  /* This is defined on Posix systems. */
  *valuep = (RLIMTYPE) PIPE_BUF;
  return 0;
#else
#  if defined (PIPESIZE)
  /* This is defined by running a program from the Makefile. */
  *valuep = (RLIMTYPE) PIPESIZE;
  return 0;
#  else
  errno = EINVAL;
  return -1;  
#  endif /* PIPESIZE */
#endif /* PIPE_BUF */
}

static int
getmaxuprc (valuep)
     RLIMTYPE *valuep;
{
  long maxchild;

  maxchild = getmaxchild ();
  if (maxchild < 0)
    {
      errno = EINVAL;
      return -1;
    }
  else
    {
      *valuep = (RLIMTYPE) maxchild;
      return 0;
    }
}

static void
print_all_limits (mode)
     int mode;
{
  register int i;
  RLIMTYPE softlim, hardlim;

  if (mode == 0)
    mode |= LIMIT_SOFT;

  for (i = 0; limits[i].option > 0; i++)
    {
      if (get_limit (i, &softlim, &hardlim) < 0)
	builtin_error ("%s: cannot get limit: %s", limits[i].description,
						   strerror (errno));
      else
	printone (i, (mode & LIMIT_SOFT) ? softlim : hardlim, 1);
    }
}

static void
printone (limind, curlim, pdesc)
     int limind;
     RLIMTYPE curlim;
     int pdesc;
{
  char unitstr[64];

  if (pdesc)
    {
      if (limits[limind].units)
	sprintf (unitstr, "(%s, -%c) ", limits[limind].units, limits[limind].option);
      else
        sprintf (unitstr, "(-%c) ", limits[limind].option);

      printf ("%-18s %16s", limits[limind].description, unitstr);
    }
  if (curlim == RLIM_INFINITY)
    puts ("unlimited");
  else if (curlim == RLIM_SAVED_MAX)
    puts ("hard");
  else if (curlim == RLIM_SAVED_CUR)
    puts ("soft");
  else
    print_rlimtype ((curlim / limits[limind].block_factor), 1);
}

/* Set all limits to NEWLIM.  NEWLIM currently must be RLIM_INFINITY, which
   causes all limits to be set as high as possible depending on mode (like
   csh `unlimit').  Returns -1 if NEWLIM is invalid, 0 if all limits
   were set successfully, and 1 if at least one limit could not be set.

   To raise all soft limits to their corresponding hard limits, use
	ulimit -S -a unlimited
   To attempt to raise all hard limits to infinity (superuser-only), use
	ulimit -H -a unlimited
   To attempt to raise all soft and hard limits to infinity, use
	ulimit -a unlimited
*/

static int
set_all_limits (mode, newlim)
     int mode;
     RLIMTYPE newlim;
{
  register int i;
  int retval = 0;

  if (newlim != RLIM_INFINITY)
    {
      errno = EINVAL;
      return -1;
    }
  
  if (mode == 0)
    mode = LIMIT_SOFT|LIMIT_HARD;

  for (retval = i = 0; limits[i].option > 0; i++)
    if (set_limit (i, newlim, mode) < 0)
      {
	builtin_error ("%s: cannot modify limit: %s", limits[i].description,
						      strerror (errno));
	retval = 1;
      }
  return retval;
}

#endif /* !_MINIX */
