diff options
Diffstat (limited to 'Src/Builtins/rlimits.c')
-rw-r--r-- | Src/Builtins/rlimits.c | 593 |
1 files changed, 593 insertions, 0 deletions
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c new file mode 100644 index 000000000..20b8d663d --- /dev/null +++ b/Src/Builtins/rlimits.c @@ -0,0 +1,593 @@ +/* + * rlimits.c - resource limit builtins + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "rlimits.mdh" +#include "rlimits.pro" + +#if defined(HAVE_GETRLIMIT) && defined(RLIM_INFINITY) + +/* Generated rec array containing limits required for the limit builtin. * + * They must appear in this array in numerical order of the RLIMIT_* macros. */ + +# include "rlimits.h" + +# if defined(RLIM_T_IS_QUAD_T) || defined(RLIM_T_IS_UNSIGNED) +static rlim_t +zstrtorlimt(const char *s, char **t, int base) +{ + rlim_t ret = 0; + + if (!base) + if (*s != '0') + base = 10; + else if (*++s == 'x' || *s == 'X') + base = 16, s++; + else + base = 8; + + if (base <= 10) + for (; *s >= '0' && *s < ('0' + base); s++) + ret = ret * base + *s - '0'; + else + for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10)) + || (*s >= 'A' && *s < ('A' + base - 10)); s++) + ret = ret * base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9); + if (t) + *t = (char *)s; + return ret; +} +# else /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_UNSIGNED */ +# define zstrtorlimt(a, b, c) zstrtol((a), (b), (c)) +# endif /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_UNSIGNED */ + +/* Display resource limits. hard indicates whether `hard' or `soft' * + * limits should be displayed. lim specifies the limit, or may be -1 * + * to show all. */ + +/**/ +static void +showlimits(int hard, int lim) +{ + int rt; + rlim_t val; + + /* main loop over resource types */ + for (rt = 0; rt != ZSH_NLIMITS; rt++) + if (rt == lim || lim == -1) { + /* display limit for resource number rt */ + printf("%-16s", recs[rt]); + val = (hard) ? limits[rt].rlim_max : limits[rt].rlim_cur; + if (val == RLIM_INFINITY) + printf("unlimited\n"); + else if (rt==RLIMIT_CPU) + /* time-type resource -- display as hours, minutes and + seconds. */ + printf("%d:%02d:%02d\n", (int)(val / 3600), + (int)(val / 60) % 60, (int)(val % 60)); +# ifdef RLIMIT_NPROC + else if (rt == RLIMIT_NPROC) + /* pure numeric resource */ + printf("%d\n", (int)val); +# endif /* RLIMIT_NPROC */ +# ifdef RLIMIT_NOFILE + else if (rt == RLIMIT_NOFILE) + /* pure numeric resource */ + printf("%d\n", (int)val); +# endif /* RLIMIT_NOFILE */ + else if (val >= 1024L * 1024L) + /* memory resource -- display with `K' or `M' modifier */ +# ifdef RLIM_T_IS_QUAD_T + printf("%qdMB\n", val / (1024L * 1024L)); + else + printf("%qdkB\n", val / 1024L); +# else + printf("%ldMB\n", val / (1024L * 1024L)); + else + printf("%ldkB\n", val / 1024L); +# endif /* RLIM_T_IS_QUAD_T */ + } +} + +/* Display a resource limit, in ulimit style. lim specifies which * + * limit should be displayed, and hard indicates whether the hard or * + * soft limit should be displayed. */ + +/**/ +static void +printulimit(int lim, int hard, int head) +{ + rlim_t limit; + + /* get the limit in question */ + limit = (hard) ? limits[lim].rlim_max : limits[lim].rlim_cur; + /* display the appropriate heading */ + switch (lim) { + case RLIMIT_CPU: + if (head) + printf("cpu time (seconds) "); + break; + case RLIMIT_FSIZE: + if (head) + printf("file size (blocks) "); + if (limit != RLIM_INFINITY) + limit /= 512; + break; + case RLIMIT_DATA: + if (head) + printf("data seg size (kbytes) "); + if (limit != RLIM_INFINITY) + limit /= 1024; + break; + case RLIMIT_STACK: + if (head) + printf("stack size (kbytes) "); + if (limit != RLIM_INFINITY) + limit /= 1024; + break; + case RLIMIT_CORE: + if (head) + printf("core file size (blocks) "); + if (limit != RLIM_INFINITY) + limit /= 512; + break; +# ifdef RLIMIT_RSS + case RLIMIT_RSS: + if (head) + printf("resident set size (kbytes) "); + if (limit != RLIM_INFINITY) + limit /= 1024; + break; +# endif /* RLIMIT_RSS */ +# ifdef RLIMIT_MEMLOCK + case RLIMIT_MEMLOCK: + if (head) + printf("locked-in-memory size (kb) "); + if (limit != RLIM_INFINITY) + limit /= 1024; + break; +# endif /* RLIMIT_MEMLOCK */ +# ifdef RLIMIT_NPROC + case RLIMIT_NPROC: + if (head) + printf("processes "); + break; +# endif /* RLIMIT_NPROC */ +# ifdef RLIMIT_NOFILE + case RLIMIT_NOFILE: + if (head) + printf("file descriptors "); + break; +# endif /* RLIMIT_NOFILE */ +# ifdef RLIMIT_VMEM + case RLIMIT_VMEM: + if (head) + printf("virtual memory size (kb) "); + if (limit != RLIM_INFINITY) + limit /= 1024; + break; +# endif /* RLIMIT_VMEM */ +# if defined RLIMIT_AS && RLIMIT_AS != RLIMIT_VMEM + case RLIMIT_AS: + if (head) + printf("address space (kb) "); + if (limit != RLIM_INFINITY) + limit /= 1024; + break; +# endif /* RLIMIT_AS */ +# ifdef RLIMIT_TCACHE + case RLIMIT_TCACHE: + if (head) + printf("cached threads "); + break; +# endif /* RLIMIT_TCACHE */ + } + /* display the limit */ + if (limit == RLIM_INFINITY) + printf("unlimited\n"); + else + printf("%ld\n", (long)limit); +} + +/* limit: set or show resource limits. The variable hard indicates * + * whether `hard' or `soft' resource limits are being set/shown. */ + +/**/ +static int +bin_limit(char *nam, char **argv, char *ops, int func) +{ + char *s; + int hard, limnum, lim; + rlim_t val; + int ret = 0; + + hard = ops['h']; + if (ops['s'] && !*argv) + return setlimits(NULL); + /* without arguments, display limits */ + if (!*argv) { + showlimits(hard, -1); + return 0; + } + while ((s = *argv++)) { + /* Search for the appropriate resource name. When a name matches (i.e. * + * starts with) the argument, the lim variable changes from -1 to the * + * number of the resource. If another match is found, lim goes to -2. */ + for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++) + if (!strncmp(recs[limnum], s, strlen(s))) { + if (lim != -1) + lim = -2; + else + lim = limnum; + } + /* lim==-1 indicates that no matches were found. * + * lim==-2 indicates that multiple matches were found. */ + if (lim < 0) { + zwarnnam("limit", + (lim == -2) ? "ambiguous resource specification: %s" + : "no such resource: %s", s, 0); + return 1; + } + /* without value for limit, display the current limit */ + if (!(s = *argv++)) { + showlimits(hard, lim); + return 0; + } + if (lim==RLIMIT_CPU) { + /* time-type resource -- may be specified as seconds, or minutes or * + * hours with the `m' and `h' modifiers, and `:' may be used to add * + * together more than one of these. It's easier to understand from * + * the code: */ + val = zstrtorlimt(s, &s, 10); + if (*s) + if ((*s == 'h' || *s == 'H') && !s[1]) + val *= 3600L; + else if ((*s == 'm' || *s == 'M') && !s[1]) + val *= 60L; + else if (*s == ':') + val = val * 60 + zstrtorlimt(s + 1, &s, 10); + else { + zwarnnam("limit", "unknown scaling factor: %s", s, 0); + return 1; + } + } +# ifdef RLIMIT_NPROC + else if (lim == RLIMIT_NPROC) + /* pure numeric resource -- only a straight decimal number is + permitted. */ + val = zstrtorlimt(s, &s, 10); +# endif /* RLIMIT_NPROC */ +# ifdef RLIMIT_NOFILE + else if (lim == RLIMIT_NOFILE) + /* pure numeric resource -- only a straight decimal number is + permitted. */ + val = zstrtorlimt(s, &s, 10); +# endif /* RLIMIT_NOFILE */ + else { + /* memory-type resource -- `k' and `M' modifiers are permitted, + meaning (respectively) 2^10 and 2^20. */ + val = zstrtorlimt(s, &s, 10); + if (!*s || ((*s == 'k' || *s == 'K') && !s[1])) + val *= 1024L; + else if ((*s == 'M' || *s == 'm') && !s[1]) + val *= 1024L * 1024; + else { + zwarnnam("limit", "unknown scaling factor: %s", s, 0); + return 1; + } + } + /* new limit is valid and has been interpreted; apply it to the + specified resource */ + if (hard) { + /* can only raise hard limits if running as root */ + if (val > current_limits[lim].rlim_max && geteuid()) { + zwarnnam("limit", "can't raise hard limits", NULL, 0); + return 1; + } else { + limits[lim].rlim_max = val; + if (val < limits[lim].rlim_cur) + limits[lim].rlim_cur = val; + } + } else if (val > limits[lim].rlim_max) { + zwarnnam("limit", "limit exceeds hard limit", NULL, 0); + return 1; + } else + limits[lim].rlim_cur = val; + if (ops['s'] && zsetlimit(lim, "limit")) + ret++; + } + return ret; +} + +/* unlimit: remove resource limits. Much of this code is the same as * + * that in bin_limit(). */ + +/**/ +static int +bin_unlimit(char *nam, char **argv, char *ops, int func) +{ + int hard, limnum, lim; + int ret = 0; + uid_t euid = geteuid(); + + hard = ops['h']; + /* Without arguments, remove all limits. */ + if (!*argv) { + for (limnum = 0; limnum != RLIM_NLIMITS; limnum++) { + if (hard) + if (euid && current_limits[limnum].rlim_max != RLIM_INFINITY) + ret++; + else + limits[limnum].rlim_max = RLIM_INFINITY; + else + limits[limnum].rlim_cur = limits[limnum].rlim_max; + } + if (ops['s']) + ret += setlimits(nam); + if (ret) + zwarnnam(nam, "can't remove hard limits", NULL, 0); + } else { + for (; *argv; argv++) { + /* Search for the appropriate resource name. When a name * + * matches (i.e. starts with) the argument, the lim variable * + * changes from -1 to the number of the resource. If another * + * match is found, lim goes to -2. */ + for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++) + if (!strncmp(recs[limnum], *argv, strlen(*argv))) { + if (lim != -1) + lim = -2; + else + lim = limnum; + } + /* lim==-1 indicates that no matches were found. * + * lim==-2 indicates that multiple matches were found. */ + if (lim < 0) { + zwarnnam(nam, + (lim == -2) ? "ambiguous resource specification: %s" + : "no such resource: %s", *argv, 0); + return 1; + } + /* remove specified limit */ + if (hard) + if (euid && current_limits[lim].rlim_max != RLIM_INFINITY) { + zwarnnam(nam, "can't remove hard limits", NULL, 0); + ret++; + } else + limits[lim].rlim_max = RLIM_INFINITY; + else + limits[lim].rlim_cur = limits[lim].rlim_max; + if (ops['s'] && zsetlimit(lim, nam)) + ret++; + } + } + return ret; +} + +/* ulimit: set or display resource limits */ + +/**/ +static int +bin_ulimit(char *name, char **argv, char *ops, int func) +{ + int res, resmask = 0, hard = 0, soft = 0, nres = 0; + char *options; + + do { + options = *argv; + if (options && *options == '-' && !options[1]) { + zwarnnam(name, "missing option letter", NULL, 0); + return 1; + } + res = -1; + if (options && *options == '-') { + argv++; + while (*++options) { + if(*options == Meta) + *++options ^= 32; + res = -1; + switch (*options) { + case 'H': + hard = 1; + continue; + case 'S': + soft = 1; + continue; + case 'a': + if (*argv || options[1] || resmask) { + zwarnnam(name, "no arguments required after -a", + NULL, 0); + return 1; + } + resmask = (1 << RLIM_NLIMITS) - 1; + nres = RLIM_NLIMITS; + continue; + case 't': + res = RLIMIT_CPU; + break; + case 'f': + res = RLIMIT_FSIZE; + break; + case 'd': + res = RLIMIT_DATA; + break; + case 's': + res = RLIMIT_STACK; + break; + case 'c': + res = RLIMIT_CORE; + break; +# ifdef RLIMIT_RSS + case 'm': + res = RLIMIT_RSS; + break; +# endif /* RLIMIT_RSS */ +# ifdef RLIMIT_MEMLOCK + case 'l': + res = RLIMIT_MEMLOCK; + break; +# endif /* RLIMIT_MEMLOCK */ +# ifdef RLIMIT_NOFILE + case 'n': + res = RLIMIT_NOFILE; + break; +# endif /* RLIMIT_NOFILE */ +# ifdef RLIMIT_NPROC + case 'u': + res = RLIMIT_NPROC; + break; +# endif /* RLIMIT_NPROC */ +# ifdef RLIMIT_VMEM + case 'v': + res = RLIMIT_VMEM; + break; +# endif /* RLIMIT_VMEM */ + default: + /* unrecognised limit */ + zwarnnam(name, "bad option: -%c", NULL, *options); + return 1; + } + if (options[1]) { + resmask |= 1 << res; + nres++; + } + } + } + if (!*argv || **argv == '-') { + if (res < 0) + if (*argv || nres) + continue; + else + res = RLIMIT_FSIZE; + resmask |= 1 << res; + nres++; + continue; + } + if (res < 0) + res = RLIMIT_FSIZE; + if (strcmp(*argv, "unlimited")) { + /* set limit to specified value */ + rlim_t limit; + + limit = zstrtorlimt(*argv, NULL, 10); + /* scale appropriately */ + switch (res) { + case RLIMIT_FSIZE: + case RLIMIT_CORE: + limit *= 512; + break; + case RLIMIT_DATA: + case RLIMIT_STACK: +# ifdef RLIMIT_RSS + case RLIMIT_RSS: +# endif /* RLIMIT_RSS */ +# ifdef RLIMIT_MEMLOCK + case RLIMIT_MEMLOCK: +# endif /* RLIMIT_MEMLOCK */ +# ifdef RLIMIT_VMEM + case RLIMIT_VMEM: +# endif /* RLIMIT_VMEM */ + limit *= 1024; + break; + } + if (hard) { + /* can't raise hard limit unless running as root */ + if (limit > current_limits[res].rlim_max && geteuid()) { + zwarnnam(name, "can't raise hard limits", NULL, 0); + return 1; + } + limits[res].rlim_max = limit; + if (limit < limits[res].rlim_cur) + limits[res].rlim_cur = limit; + } + if (!hard || soft) { + /* can't raise soft limit above hard limit */ + if (limit > limits[res].rlim_max) { + if (limit > current_limits[res].rlim_max && geteuid()) { + zwarnnam(name, "value exceeds hard limit", NULL, 0); + return 1; + } + limits[res].rlim_max = limits[res].rlim_cur = limit; + } else + limits[res].rlim_cur = limit; + } + } else { + /* remove specified limit */ + if (hard) { + /* can't remove hard limit unless running as root */ + if (current_limits[res].rlim_max != RLIM_INFINITY && geteuid()) { + zwarnnam(name, "can't remove hard limits", NULL, 0); + return 1; + } + limits[res].rlim_max = RLIM_INFINITY; + } + if (!hard || soft) + /* `removal' of soft limit means setting it equal to the + corresponding hard limit */ + limits[res].rlim_cur = limits[res].rlim_max; + } + if (zsetlimit(res, name)) + return 1; + argv++; + } while (*argv); + for (res = 0; res < RLIM_NLIMITS; res++, resmask >>= 1) + if (resmask & 1) + printulimit(res, hard, nres > 1); + return 0; +} + +#else /* !HAVE_GETRLIMIT || !RLIM_INFINITY */ + +# define bin_limit bin_notavail +# define bin_ulimit bin_notavail +# define bin_unlimit bin_notavail + +#endif /* !HAVE_GETRLIMIT || !RLIM_INFINITY */ + +static struct builtin bintab[] = { + BUILTIN("limit", 0, bin_limit, 0, -1, 0, "sh", NULL), + BUILTIN("ulimit", 0, bin_ulimit, 0, -1, 0, NULL, NULL), + BUILTIN("unlimit", 0, bin_unlimit, 0, -1, 0, "hs", NULL), +}; + +/**/ +int +boot_rlimits(Module m) +{ + return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); +} + +#ifdef MODULE + +/**/ +int +cleanup_rlimits(Module m) +{ + deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); + return 0; +} +#endif |