blob: 03cbe8a0bcf469eb3158bd012faaf03afaabbf14 [file] [log] [blame]
This file is ulimit.def, from which is created ulimit.c.
It implements the builtin "ulimit" in Bash.
Copyright (C) 1987-2010 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>.
$PRODUCES ulimit.c
$BUILTIN ulimit
$FUNCTION ulimit_builtin
$DEPENDS_ON !_MINIX
$SHORT_DOC ulimit [-SHacdefilmnpqrstuvx] [limit]
Modify shell resource limits.
Provides control over the resources available to the shell and processes
it creates, on systems that allow such control.
Options:
-S use the `soft' resource limit
-H use the `hard' resource limit
-a all current limits are reported
-b the socket buffer size
-c the maximum size of core files created
-d the maximum size of a process's data segment
-e the maximum scheduling priority (`nice')
-f the maximum size of files written by the shell and its children
-i the maximum number of pending signals
-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
-q the maximum number of bytes in POSIX message queues
-r the maximum real-time scheduling priority
-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
-x the maximum number of file locks
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.
Exit Status:
Returns success unless an invalid option is supplied or an error occurs.
$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 "../bashintl.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
#elif defined (HAVE_SYS_TIMES_H)
# 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 (HAVE_RESOURCE) && defined (HAVE_ULIMIT_H)
# include <ulimit.h>
#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
/* "Blocks" are defined as 512 bytes when in Posix mode and 1024 bytes
otherwise. */
#define POSIXBLK -2
#define BLOCKSIZE(x) (((x) == POSIXBLK) ? (posixly_correct ? 512 : 1024) : (x))
extern int posixly_correct;
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. */
const char * const description; /* Descriptive string to output. */
const char * const units; /* scale */
} RESOURCE_LIMITS;
static RESOURCE_LIMITS limits[] = {
#ifdef RLIMIT_PTHREAD
{ 'T', RLIMIT_PTHREAD, 1, "number of threads", (char *)NULL },
#endif
#ifdef RLIMIT_SBSIZE
{ 'b', RLIMIT_SBSIZE, 1, "socket buffer size", "bytes" },
#endif
#ifdef RLIMIT_CORE
{ 'c', RLIMIT_CORE, POSIXBLK, "core file size", "blocks" },
#endif
#ifdef RLIMIT_DATA
{ 'd', RLIMIT_DATA, 1024, "data seg size", "kbytes" },
#endif
#ifdef RLIMIT_NICE
{ 'e', RLIMIT_NICE, 1, "scheduling priority", (char *)NULL },
#endif
{ 'f', RLIMIT_FILESIZE, POSIXBLK, "file size", "blocks" },
#ifdef RLIMIT_SIGPENDING
{ 'i', RLIMIT_SIGPENDING, 1, "pending signals", (char *)NULL },
#endif
#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_MSGQUEUE
{ 'q', RLIMIT_MSGQUEUE, 1, "POSIX message queues", "bytes" },
#endif
#ifdef RLIMIT_RTPRIO
{ 'r', RLIMIT_RTPRIO, 1, "real-time priority", (char *)NULL },
#endif
#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
#ifdef RLIMIT_LOCKS
{ 'x', RLIMIT_LOCKS, 1, "file locks", (char *)NULL },
#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 (sh_chkwrite (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 = BLOCKSIZE(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 (_POSIX_PIPE_BUF)
*valuep = (RLIMTYPE) _POSIX_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 /* _POSIX_PIPE_BUF */
#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)
printone (i, (mode & LIMIT_SOFT) ? softlim : hardlim, 1);
else if (errno != EINVAL)
builtin_error ("%s: cannot get limit: %s", limits[i].description,
strerror (errno));
}
}
static void
printone (limind, curlim, pdesc)
int limind;
RLIMTYPE curlim;
int pdesc;
{
char unitstr[64];
int factor;
factor = BLOCKSIZE(limits[limind].block_factor);
if (pdesc)
{
if (limits[limind].units)
sprintf (unitstr, "(%s, -%c) ", limits[limind].units, limits[limind].option);
else
sprintf (unitstr, "(-%c) ", limits[limind].option);
printf ("%-20s %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 / 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 */