From bf214787228dc097a268ee4207d87bf3bf09d2b3 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Wed, 30 Jun 2004 11:10:35 +0000 Subject: 20118: improvements to limits builtins --- Src/Builtins/rlimits.c | 482 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 328 insertions(+), 154 deletions(-) (limited to 'Src/Builtins/rlimits.c') diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c index ff66d9594..cabf7975b 100644 --- a/Src/Builtins/rlimits.c +++ b/Src/Builtins/rlimits.c @@ -78,57 +78,108 @@ zstrtorlimt(const char *s, char **t, int base) return ret; } -/* 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) +showlimitvalue(int lim, rlim_t val) { - 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 (limtype[rt] == ZLIMTYPE_TIME) { - /* time-type resource -- display as hours, minutes and - seconds. */ - printf("%d:%02d:%02d\n", (int)(val / 3600), - (int)(val / 60) % 60, (int)(val % 60)); - } else if (limtype[rt] == ZLIMTYPE_NUMBER || limtype[rt] == ZLIMTYPE_UNKNOWN) { - /* pure numeric resource */ - printf("%d\n", (int)val); - } else if (val >= 1024L * 1024L) - /* memory resource -- display with `K' or `M' modifier */ + /* display limit for resource number lim */ + if (lim < ZSH_NLIMITS) + printf("%-16s", recs[lim]); + else + { + /* Unknown limit, hence unknown units. */ + printf("%-16d", lim); + } + if (val == RLIM_INFINITY) + printf("unlimited\n"); + else if (lim >= ZSH_NLIMITS) + { # ifdef RLIM_T_IS_QUAD_T - printf("%qdMB\n", val / (1024L * 1024L)); - else - printf("%qdkB\n", val / 1024L); + printf("%qd\n", val); +# else +# ifdef RLIM_T_IS_LONG_LONG + printf("%lld\n", val); +# else +# ifdef RLIM_T_IS_UNSIGNED + printf("%lu\n", val); +# else + printf("%ld\n", val); +# endif /* RLIM_T_IS_UNSIGNED */ +# endif /* RLIM_T_IS_LONG_LONG */ +# endif /* RLIM_T_IS_QUAD_T */ + } + else if (limtype[lim] == ZLIMTYPE_TIME) { + /* time-type resource -- display as hours, minutes and + seconds. */ + printf("%d:%02d:%02d\n", (int)(val / 3600), + (int)(val / 60) % 60, (int)(val % 60)); + } else if (limtype[lim] == ZLIMTYPE_NUMBER || + limtype[lim] == ZLIMTYPE_UNKNOWN) { + /* pure numeric resource */ + printf("%d\n", (int)val); + } 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 # ifdef RLIM_T_IS_LONG_LONG - printf("%lldMB\n", val / (1024L * 1024L)); - else - printf("%lldkB\n", val / 1024L); + printf("%lldMB\n", val / (1024L * 1024L)); + else + printf("%lldkB\n", val / 1024L); # else # ifdef RLIM_T_IS_UNSIGNED - printf("%luMB\n", val / (1024L * 1024L)); - else - printf("%lukB\n", val / 1024L); + printf("%luMB\n", val / (1024L * 1024L)); + else + printf("%lukB\n", val / 1024L); # else - printf("%ldMB\n", val / (1024L * 1024L)); - else - printf("%ldkB\n", val / 1024L); + printf("%ldMB\n", val / (1024L * 1024L)); + else + printf("%ldkB\n", val / 1024L); # endif /* RLIM_T_IS_UNSIGNED */ # endif /* RLIM_T_IS_LONG_LONG */ # endif /* RLIM_T_IS_QUAD_T */ +} + +/* 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 int +showlimits(char *nam, int hard, int lim) +{ + int rt; + + if (lim >= ZSH_NLIMITS) + { + /* + * Not configured into the shell. Ask the OS + * explicitly for this limit. + */ + struct rlimit vals; + if (getrlimit(lim, &vals) < 0) + { + zwarnnam(nam, "can't read limit: %e", NULL, errno); + return 1; } + showlimitvalue(lim, hard ? vals.rlim_max : vals.rlim_cur); + } + else if (lim != -1) + { + showlimitvalue(lim, hard ? limits[lim].rlim_max : + limits[lim].rlim_cur); + } + else + { + /* main loop over resource types */ + for (rt = 0; rt != ZSH_NLIMITS; rt++) + showlimitvalue(rt, (hard) ? limits[rt].rlim_max : + limits[rt].rlim_cur); + } + + return 0; } /* Display a resource limit, in ulimit style. lim specifies which * @@ -136,40 +187,52 @@ showlimits(int hard, int lim) * soft limit should be displayed. */ /**/ -static void -printulimit(int lim, int hard, int head) +static int +printulimit(char *nam, int lim, int hard, int head) { rlim_t limit; /* get the limit in question */ - limit = (hard) ? limits[lim].rlim_max : limits[lim].rlim_cur; + if (lim >= ZSH_NLIMITS) + { + struct rlimit vals; + + if (getrlimit(lim, &vals) < 0) + { + zwarnnam(nam, "can't read limit: %e", NULL, errno); + return 1; + } + limit = (hard) ? vals.rlim_max : vals.rlim_cur; + } + else + 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) "); + printf("-t: cpu time (seconds) "); break; case RLIMIT_FSIZE: if (head) - printf("file size (blocks) "); + printf("-f: file size (blocks) "); if (limit != RLIM_INFINITY) limit /= 512; break; case RLIMIT_DATA: if (head) - printf("data seg size (kbytes) "); + printf("-d: data seg size (kbytes) "); if (limit != RLIM_INFINITY) limit /= 1024; break; case RLIMIT_STACK: if (head) - printf("stack size (kbytes) "); + printf("-s: stack size (kbytes) "); if (limit != RLIM_INFINITY) limit /= 1024; break; case RLIMIT_CORE: if (head) - printf("core file size (blocks) "); + printf("-c: core file size (blocks) "); if (limit != RLIM_INFINITY) limit /= 512; break; @@ -178,7 +241,7 @@ printulimit(int lim, int hard, int head) # if defined(HAVE_RLIMIT_RSS) && !defined(RLIMIT_VMEM_IS_RSS) case RLIMIT_RSS: if (head) - printf("resident set size (kbytes) "); + printf("-m: resident set size (kbytes) "); if (limit != RLIM_INFINITY) limit /= 1024; break; @@ -186,7 +249,7 @@ printulimit(int lim, int hard, int head) # ifdef HAVE_RLIMIT_MEMLOCK case RLIMIT_MEMLOCK: if (head) - printf("locked-in-memory size (kb) "); + printf("-l: locked-in-memory size (kb) "); if (limit != RLIM_INFINITY) limit /= 1024; break; @@ -194,22 +257,22 @@ printulimit(int lim, int hard, int head) # ifdef HAVE_RLIMIT_NPROC case RLIMIT_NPROC: if (head) - printf("processes "); + printf("-u: processes "); break; # endif /* HAVE_RLIMIT_NPROC */ # ifdef HAVE_RLIMIT_NOFILE case RLIMIT_NOFILE: if (head) - printf("file descriptors "); + printf("-n: file descriptors "); break; # endif /* HAVE_RLIMIT_NOFILE */ # ifdef HAVE_RLIMIT_VMEM case RLIMIT_VMEM: if (head) # if defined(HAVE_RLIMIT_RSS) && defined(RLIMIT_VMEM_IS_RSS) - printf("memory size (kb) "); + printf("-m: memory size (kb) "); # else - printf("virtual memory size (kb) "); + printf("-v: virtual memory size (kb) "); # endif if (limit != RLIM_INFINITY) limit /= 1024; @@ -218,7 +281,7 @@ printulimit(int lim, int hard, int head) # if defined HAVE_RLIMIT_AS && !defined(RLIMIT_VMEM_IS_AS) case RLIMIT_AS: if (head) - printf("address space (kb) "); + printf("-v: address space (kb) "); if (limit != RLIM_INFINITY) limit /= 1024; break; @@ -226,19 +289,19 @@ printulimit(int lim, int hard, int head) # ifdef HAVE_RLIMIT_TCACHE case RLIMIT_TCACHE: if (head) - printf("cached threads "); + printf("-N %2d: cached threads ", RLIMIT_TCACHE); break; # endif /* HAVE_RLIMIT_TCACHE */ # ifdef HAVE_RLIMIT_AIO_OPS case RLIMIT_AIO_OPS: if (head) - printf("AIO operations "); + printf("-N %2d: AIO operations ", RLIMIT_AIO_OPS); break; # endif /* HAVE_RLIMIT_AIO_OPS */ # ifdef HAVE_RLIMIT_AIO_MEM case RLIMIT_AIO_MEM: if (head) - printf("AIO locked-in-memory (kb) "); + printf("-N %2d: AIO locked-in-memory (kb) ", RLIMIT_AIO_MEM); if (limit != RLIM_INFINITY) limit /= 1024; break; @@ -246,7 +309,7 @@ printulimit(int lim, int hard, int head) # ifdef HAVE_RLIMIT_SBSIZE case RLIMIT_SBSIZE: if (head) - printf("socket buffer size (kb) "); + printf("-N %2d: socket buffer size (kb) ", RLIMIT_SBSIZE); if (limit != RLIM_INFINITY) limit /= 1024; break; @@ -254,15 +317,19 @@ printulimit(int lim, int hard, int head) # ifdef HAVE_RLIMIT_PTHREAD case RLIMIT_PTHREAD: if (head) - printf("threads per process "); + printf("-N %2d: threads per process ", RLIMIT_PTHREAD); break; # endif /* HAVE_RLIMIT_PTHREAD */ # ifdef HAVE_RLIMIT_LOCKS case RLIMIT_LOCKS: if (head) - printf("file locks "); + printf("-N %2d: file locks ", RLIMIT_LOCKS); break; # endif /* HAVE_RLIMIT_LOCKS */ + default: + if (head) + printf("-N %2d: ", lim); + break; } /* display the limit */ if (limit == RLIM_INFINITY) @@ -282,6 +349,93 @@ printulimit(int lim, int hard, int head) # endif /* RLIM_T_IS_LONG_LONG */ # endif /* RLIM_T_IS_QUAD_T */ } + + return 0; +} + +/**/ +static int +do_limit(char *nam, int lim, rlim_t val, int hard, int soft, int set) +{ + if (lim >= ZSH_NLIMITS) { + struct rlimit vals; + if (getrlimit(lim, &vals) < 0) + { + /* best guess about error */ + zwarnnam(nam, "can't read limit: %e", NULL, errno); + return 1; + } + if (hard) + { + if (val > vals.rlim_max && geteuid()) { + zwarnnam(nam, "can't raise hard limits", NULL, 0); + return 1; + } + vals.rlim_max = val; + /* + * not show if all systems will do this silently, but + * best be safe... + */ + if (val < vals.rlim_cur) + vals.rlim_cur = val; + } + if (soft || !hard) { + if (val > vals.rlim_max) { + zwarnnam(nam, "limit exceeds hard limit", NULL, 0); + return 1; + } + else + vals.rlim_cur = val; + } + if (!set) + { + zwarnnam(nam, + "warning: unrecognised limit %d, use -s to set", + NULL, lim); + return 1; + } + else if (setrlimit(lim, &vals) < 0) + { + zwarnnam(nam, "setrlimit failed: %e", NULL, errno); + return 1; + } + } else { + /* 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(nam, "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; + } + } + if (soft || !hard) { + if (val > limits[lim].rlim_max) { + /* no idea about this difference, don't intend to worry */ + if (*nam == 'u') + { + /* ulimit does this */ + if (val > current_limits[lim].rlim_max && geteuid()) { + zwarnnam(nam, "value exceeds hard limit", NULL, 0); + return 1; + } + limits[lim].rlim_max = limits[lim].rlim_cur = val; + } else { + /* but limit does this */ + zwarnnam(nam, "limit exceeds hard limit", NULL, 0); + return 1; + } + } else + limits[lim].rlim_cur = val; + if (set && zsetlimit(lim, "limit")) + return 1; + } + } + return 0; } /* limit: set or show resource limits. The variable hard indicates * @@ -289,7 +443,7 @@ printulimit(int lim, int hard, int head) /**/ static int -bin_limit(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func)) +bin_limit(char *nam, char **argv, Options ops, UNUSED(int func)) { char *s; int hard, limnum, lim; @@ -300,35 +454,46 @@ bin_limit(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func)) if (OPT_ISSET(ops,'s') && !*argv) return setlimits(NULL); /* without arguments, display limits */ - if (!*argv) { - showlimits(hard, -1); - return 0; - } + if (!*argv) + return showlimits(nam, hard, -1); 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; - } + if (idigit(*s)) + { + lim = (int)zstrtol(s, NULL, 10); + } + else + 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", + zwarnnam(nam, (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 (!(s = *argv++)) + return showlimits(nam, hard, lim); + if (lim >= ZSH_NLIMITS) + { + val = zstrtorlimt(s, &s, 10); + if (*s) + { + /* unknown limit, no idea how to scale */ + zwarnnam(nam, "unknown scaling factor: %s", s, 0); + return 1; + } } - if (limtype[lim] == ZLIMTYPE_TIME) { + else if (limtype[lim] == ZLIMTYPE_TIME) { /* 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 * @@ -342,7 +507,7 @@ bin_limit(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func)) else if (*s == ':') val = val * 60 + zstrtorlimt(s + 1, &s, 10); else { - zwarnnam("limit", "unknown scaling factor: %s", s, 0); + zwarnnam(nam, "unknown scaling factor: %s", s, 0); return 1; } } @@ -352,7 +517,7 @@ bin_limit(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func)) char *t = s; val = zstrtorlimt(t, &s, 10); if (s == t) { - zwarnnam("limit", "limit must be a number", NULL, 0); + zwarnnam(nam, "limit must be a number", NULL, 0); return 1; } } else { @@ -365,31 +530,60 @@ bin_limit(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func)) } else if ((*s == 'M' || *s == 'm') && !s[1]) val *= 1024L * 1024; else { - zwarnnam("limit", "unknown scaling factor: %s", s, 0); + zwarnnam(nam, "unknown scaling factor: %s", s, 0); return 1; } } - /* new limit is valid and has been interpreted; apply it to the - specified resource */ + if (do_limit(nam, lim, val, hard, !hard, OPT_ISSET(ops, 's'))) + ret++; + } + return ret; +} + +/**/ +static int +do_unlimit(char *nam, int lim, int hard, int soft, int set, int euid) +{ + /* remove specified limit */ + if (lim >= ZSH_NLIMITS) { + struct rlimit vals; + if (getrlimit(lim, &vals) < 0) + { + zwarnnam(nam, "can't read limit: %e", NULL, errno); + return 1; + } 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); + if (euid && vals.rlim_max != RLIM_INFINITY) { + zwarnnam(nam, "can't remove 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); + } else + vals.rlim_max = RLIM_INFINITY; + } + if (!hard || soft) + vals.rlim_cur = vals.rlim_max; + if (!set) { + zwarnnam(nam, + "warning: unrecognised limit %d, use -s to set", + NULL, lim); + return 1; + } else if (setrlimit(lim, &vals) < 0) { + zwarnnam(nam, "setrlimit failed: %e", NULL, errno); + return 1; + } + } else { + if (hard) { + if (euid && current_limits[lim].rlim_max != RLIM_INFINITY) { + zwarnnam(nam, "can't remove hard limits", NULL, 0); + return 1; + } else + limits[lim].rlim_max = RLIM_INFINITY; + } + if (!hard || soft) + limits[lim].rlim_cur = limits[lim].rlim_max; + if (set && zsetlimit(lim, nam)) return 1; - } else - limits[lim].rlim_cur = val; - if (OPT_ISSET(ops,'s') && zsetlimit(lim, "limit")) - ret++; } - return ret; + return 0; } /* unlimit: remove resource limits. Much of this code is the same as * @@ -425,13 +619,17 @@ bin_unlimit(char *nam, char **argv, Options ops, UNUSED(int func)) * 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; - } + if (idigit(**argv)) { + lim = (int)zstrtol(*argv, NULL, 10); + } else { + 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) { @@ -440,16 +638,8 @@ bin_unlimit(char *nam, char **argv, Options ops, UNUSED(int func)) : "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 (OPT_ISSET(ops,'s') && zsetlimit(lim, nam)) + else if (do_unlimit(nam, lim, hard, !hard, OPT_ISSET(ops, 's'), + euid)) ret++; } } @@ -462,7 +652,7 @@ bin_unlimit(char *nam, char **argv, Options ops, UNUSED(int func)) static int bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) { - int res, resmask = 0, hard = 0, soft = 0, nres = 0, all = 0; + int res, resmask = 0, hard = 0, soft = 0, nres = 0, all = 0, ret = 0; char *options; do { @@ -485,6 +675,22 @@ bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) case 'S': soft = 1; continue; + case 'N': + if (options[1]) { + res = (int)zstrtol(options+1, NULL, 10); + } else if (*argv) { + res = (int)zstrtol(*argv++, NULL, 10); + } else { + zwarnnam(name, "number required after -N", + NULL, 0); + return 1; + } + /* + * fake it so it looks like we just finished an option... + */ + while (options[1]) + options++; + break; case 'a': if (resmask) { zwarnnam(name, "no limits allowed with -a", @@ -606,50 +812,18 @@ bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) 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; - } + if (do_limit(name, res, limit, hard, soft, 1)) + ret++; } 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 (do_unlimit(name, res, hard, soft, 1, geteuid())) + ret++; } - 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; + for (res = 0; resmask; res++, resmask >>= 1) + if ((resmask & 1) && printulimit(name, res, hard, nres > 1)) + ret++; + return ret; } #else /* !HAVE_GETRLIMIT || !RLIM_INFINITY */ -- cgit 1.4.1