This file is ulimit.def, from which is created ulimit.c. It implements the builtin "ulimit" in Bash. Copyright (C) 1987-2005 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 [-SHacdfilmnpqstuvx] [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 -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. $END #if !defined (_MINIX) #include #include "../bashtypes.h" #ifndef _MINIX # include #endif #if defined (HAVE_UNISTD_H) # include #endif #include #include #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 and #undef it afterward. */ #if defined (HAVE_RESOURCE) # include # if defined (HPUX) && defined (RLIMIT_NEEDS_KERNEL) # define _KERNEL # endif # include # if defined (HPUX) && defined (RLIMIT_NEEDS_KERNEL) # undef _KERNEL # endif #else # include #endif #if defined (HAVE_LIMITS_H) # include #endif /* Check for the most basic symbols. If they aren't present, this system's 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 #ifdef RLIMIT_NICE { 'e', RLIMIT_NICE, 1, "scheduling priority", (char *)NULL }, #endif { 'f', RLIMIT_FILESIZE, 1024, "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 (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 (_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]; 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 / 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 */