From 6c6be2a87e04da123a20ce95ce52ab0e3cf3d12c Mon Sep 17 00:00:00 2001 From: tg Date: Wed, 16 Apr 2008 21:56:03 +0000 Subject: [PATCH] _careful_ (feature) sync with oksh: (3 weeks, 5 days ago) by millert Make ulimit able to get and set multiple limits in a single invocation like bash and zsh do. Requested by espie@, OK deraadt@ --- check.t | 4 +- funcs.c | 307 ++++++++++++++++++++++++++++++-------------------------- misc.c | 19 ++-- mksh.1 | 15 +-- sh.h | 4 +- 5 files changed, 185 insertions(+), 164 deletions(-) diff --git a/check.t b/check.t index 323f156..f45506f 100644 --- a/check.t +++ b/check.t @@ -1,4 +1,4 @@ -# $MirOS: src/bin/mksh/check.t,v 1.174 2008/04/11 19:55:23 tg Exp $ +# $MirOS: src/bin/mksh/check.t,v 1.175 2008/04/16 21:56:00 tg Exp $ # $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $ # $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $ # $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $ @@ -7,7 +7,7 @@ # http://www.research.att.com/~gsf/public/ifs.sh expected-stdout: - @(#)MIRBSD KSH R33 2008/04/11 + @(#)MIRBSD KSH R33 2008/04/16 description: Check version of shell. category: pdksh diff --git a/funcs.c b/funcs.c index acb5592..6793b5a 100644 --- a/funcs.c +++ b/funcs.c @@ -1,11 +1,11 @@ /* $OpenBSD: c_ksh.c,v 1.30 2007/08/02 10:50:25 fgsch Exp $ */ /* $OpenBSD: c_sh.c,v 1.37 2007/09/03 13:54:23 otto Exp $ */ /* $OpenBSD: c_test.c,v 1.17 2005/03/30 17:16:37 deraadt Exp $ */ -/* $OpenBSD: c_ulimit.c,v 1.16 2006/11/20 21:53:39 miod Exp $ */ +/* $OpenBSD: c_ulimit.c,v 1.17 2008/03/21 12:51:19 millert Exp $ */ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.75 2008/04/01 22:20:19 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.76 2008/04/16 21:56:01 tg Exp $"); /* A leading = means assignments before command are kept; * a leading * means a POSIX special builtin; @@ -2853,89 +2853,79 @@ ptest_error(Test_env *te, int ofs, const char *msg) #define SOFT 0x1 #define HARD 0x2 +struct limits { + const char *name; + int resource; /* resource to get/set */ + int factor; /* multiply by to get rlim_{cur,max} values */ + char option; +}; + +static void print_ulimit(const struct limits *, int); +static int set_ulimit(const struct limits *, const char *, int); + int c_ulimit(const char **wp) { - static const struct limits { - const char *name; - enum { RLIMIT, ULIMIT } which; - int gcmd; /* get command */ - int scmd; /* set command (or -1, if no set command) */ - int factor; /* multiply by to get rlim_{cur,max} values */ - char option; - } limits[] = { - /* Do not use options -H, -S or -a */ -#ifdef RLIMIT_CORE - { "coredump(blocks)", RLIMIT, RLIMIT_CORE, RLIMIT_CORE, - 512, 'c' }, -#endif -#ifdef RLIMIT_DATA - { "data(KiB)", RLIMIT, RLIMIT_DATA, RLIMIT_DATA, - 1024, 'd' }, + static const struct limits limits[] = { + /* do not use options -H, -S or -a or change the order */ +#ifdef RLIMIT_CPU + { "time(cpu-seconds)", RLIMIT_CPU, 1, 't' }, #endif #ifdef RLIMIT_FSIZE - { "file(blocks)", RLIMIT, RLIMIT_FSIZE, RLIMIT_FSIZE, - 512, 'f' }, + { "file(blocks)", RLIMIT_FSIZE, 512, 'f' }, #endif -#ifdef RLIMIT_LOCKS - { "flocks", RLIMIT, RLIMIT_LOCKS, RLIMIT_LOCKS, - -1, 'L' }, +#ifdef RLIMIT_CORE + { "coredump(blocks)", RLIMIT_CORE, 512, 'c' }, #endif -#ifdef RLIMIT_MEMLOCK - { "lockedmem(KiB)", RLIMIT, RLIMIT_MEMLOCK, RLIMIT_MEMLOCK, - 1024, 'l' }, -#endif -#ifdef RLIMIT_RSS - { "memory(KiB)", RLIMIT, RLIMIT_RSS, RLIMIT_RSS, - 1024, 'm' }, -#endif -#ifdef RLIMIT_NOFILE - { "nofiles(descriptors)", RLIMIT, RLIMIT_NOFILE, RLIMIT_NOFILE, - 1, 'n' }, -#endif -#ifdef RLIMIT_NPROC - { "processes", RLIMIT, RLIMIT_NPROC, RLIMIT_NPROC, - 1, 'p' }, +#ifdef RLIMIT_DATA + { "data(KiB)", RLIMIT_DATA, 1024, 'd' }, #endif #ifdef RLIMIT_STACK - { "stack(KiB)", RLIMIT, RLIMIT_STACK, RLIMIT_STACK, - 1024, 's' }, + { "stack(KiB)", RLIMIT_STACK, 1024, 's' }, #endif -#ifdef RLIMIT_TIME - { "humantime(seconds)", RLIMIT, RLIMIT_TIME, RLIMIT_TIME, - 1, 'T' }, +#ifdef RLIMIT_MEMLOCK + { "lockedmem(KiB)", RLIMIT_MEMLOCK, 1024, 'l' }, #endif -#ifdef RLIMIT_CPU - { "time(cpu-seconds)", RLIMIT, RLIMIT_CPU, RLIMIT_CPU, - 1, 't' }, +#ifdef RLIMIT_RSS + { "memory(KiB)", RLIMIT_RSS, 1024, 'm' }, +#endif +#ifdef RLIMIT_NOFILE + { "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' }, +#endif +#ifdef RLIMIT_NPROC + { "processes", RLIMIT_NPROC, 1, 'p' }, #endif #ifdef RLIMIT_VMEM - { "vmemory(KiB)", RLIMIT, RLIMIT_VMEM, RLIMIT_VMEM, - 1024, 'v' }, + { "vmemory(KiB)", RLIMIT_VMEM, 1024, 'v' }, #endif #ifdef RLIMIT_SWAP - { "swap(KiB)", RLIMIT, RLIMIT_SWAP, RLIMIT_SWAP, - 1024, 'w' }, + { "swap(KiB)", RLIMIT_SWAP, 1024, 'w' }, #endif - { NULL, RLIMIT, 0, 0, 0, 0 } +#ifdef RLIMIT_LOCKS + { "flocks", RLIMIT_LOCKS, -1, 'L' }, +#endif +#ifdef RLIMIT_TIME + { "humantime(seconds)", RLIMIT_TIME, 1, 'T' }, +#endif + { NULL, 0, 0, 0 } }; - static char opts[3 + NELEM(limits)]; - rlim_t val = (rlim_t)0; - int how = SOFT | HARD, optc, what; - bool all = false, set; + static char opts[4 + NELEM(limits) * 2]; + int how = SOFT | HARD, optc; + bool all = false; const struct limits *l; - struct rlimit limit; if (!opts[0]) { /* build options string on first call - yuck */ char *p = opts; *p++ = 'H'; *p++ = 'S'; *p++ = 'a'; - for (l = limits; l->name; l++) + for (l = limits; l->name; l++) { *p++ = l->option; + *p++ = '#'; + } *p = '\0'; } - what = 'f'; + /* first check for -a, -H and -S */ while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1) switch (optc) { case 'H': @@ -2948,107 +2938,134 @@ c_ulimit(const char **wp) all = true; break; case '?': - return 1; + return (1); default: - what = optc; + break; } - for (l = limits; l->name && l->option != what; l++) - ; - if (!l->name) { - internal_warningf("ulimit: %c", what); - return 1; + if (wp[builtin_opt.optind] != NULL) { + bi_errorf("usage: ulimit [-acdfHLlmnpSsTtvw] [value]"); + return (1); } + /* then parse and act on the actual limits, one at a time */ + ksh_getopt_reset(&builtin_opt, GF_ERROR); + while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1) + switch (optc) { + case 'a': + case 'H': + case 'S': + break; + case '?': + return (1); + default: + for (l = limits; l->name && l->option != optc; l++) + ; + if (!l->name) { + internal_warningf("ulimit: %c", optc); + return (1); + } + if (!builtin_opt.optarg) + print_ulimit(l, how); + else if (set_ulimit(l, builtin_opt.optarg, how)) + return (1); + break; + } + wp += builtin_opt.optind; - if ((set = *wp ? true : false)) { - if (all || wp[1]) { - bi_errorf("too many arguments"); - return 1; - } - if (strcmp(wp[0], "unlimited") == 0) - val = (rlim_t)RLIM_INFINITY; - else { - long rval; - if (!evaluate(wp[0], &rval, KSH_RETURN_ERROR, false)) - return 1; - /* Avoid problems caused by typos that - * evaluate misses due to evaluating unset - * parameters to 0... - * If this causes problems, will have to - * add parameter to evaluate() to control - * if unset params are 0 or an error. - */ - if (!rval && !ksh_isdigit(wp[0][0])) { - bi_errorf("invalid limit: %s", wp[0]); - return 1; - } - val = (rlim_t)((rlim_t)rval * l->factor); - } - } - if (all) { + if (all) for (l = limits; l->name; l++) { - if (l->which == RLIMIT) { -#ifdef RLIMIT_LOCKS - if (getrlimit(l->gcmd, &limit) < 0) - if ((errno == EINVAL) && - (l->gcmd == RLIMIT_LOCKS)) { - limit.rlim_cur = RLIM_INFINITY; - limit.rlim_max = RLIM_INFINITY; - } -#else - getrlimit(l->gcmd, &limit); -#endif - if (how & SOFT) - val = limit.rlim_cur; - else if (how & HARD) - val = limit.rlim_max; - } shprintf("%-20s ", l->name); - if (val == (rlim_t)RLIM_INFINITY) - shf_puts("unlimited\n", shl_stdout); - else { - val = (rlim_t)(val / l->factor); - shprintf("%ld\n", (long)val); - } - } - return 0; - } - if (l->which == RLIMIT) { - getrlimit(l->gcmd, &limit); - if (set) { - if (how & SOFT) - limit.rlim_cur = val; - if (how & HARD) - limit.rlim_max = val; - if (setrlimit(l->scmd, &limit) < 0) { - how = errno; - if (how == EPERM) - bi_errorf("exceeds allowable limit"); - else - bi_errorf("bad limit: %s", - strerror(how)); - return 1; - } - } else { - if (how & SOFT) - val = limit.rlim_cur; - else if (how & HARD) - val = limit.rlim_max; - } - } - if (!set) { - if (val == (rlim_t)RLIM_INFINITY) - shf_puts("unlimited\n", shl_stdout); - else { - val = (rlim_t)(val / l->factor); - shprintf("%ld\n", (long)val); + print_ulimit(l, how); } + else if (builtin_opt.optind == 1) { + /* no limit specified, use file size as default */ +#ifndef RLIMIT_FSIZE + internal_warningf("ulimit: need argument"); + return (1); +#else +#ifdef RLIMIT_CPU + l = &limits[1]; +#else + l = &limits[0]; +#endif + if (!wp[0]) + print_ulimit(l, how); + else if (set_ulimit(l, wp[0], how)) + return(1); +#endif } return (0); } +static int +set_ulimit(const struct limits *l, const char *v, int how) +{ + rlim_t val = (rlim_t)0; + struct rlimit limit; + + if (strcmp(v, "unlimited") == 0) + val = (rlim_t)RLIM_INFINITY; + else { + long rval; + + if (!evaluate(v, &rval, KSH_RETURN_ERROR, false)) + return (1); + /* + * Avoid problems caused by typos that evaluate misses due + * to evaluating unset parameters to 0... + * If this causes problems, will have to add parameter to + * evaluate() to control if unset params are 0 or an error. + */ + if (!rval && !ksh_isdigit(v[0])) { + bi_errorf("invalid limit: %s", v); + return (1); + } + val = (rlim_t)rval * l->factor; + } + + if (getrlimit(l->resource, &limit) < 0) { + /* some cannot be read, e.g. Linux RLIMIT_LOCKS */ + limit.rlim_cur = RLIM_INFINITY; + limit.rlim_max = RLIM_INFINITY; + } + if (how & SOFT) + limit.rlim_cur = val; + if (how & HARD) + limit.rlim_max = val; + if (setrlimit(l->resource, &limit) < 0) { + if (errno == EPERM) + bi_errorf("%s exceeds allowable limit", l->name); + else + bi_errorf("bad %s limit: %s", l->name, strerror(errno)); + return (1); + } + return (0); +} + +static void +print_ulimit(const struct limits *l, int how) +{ + rlim_t val = (rlim_t)0; + struct rlimit limit; + + if (getrlimit(l->resource, &limit)) { + shf_puts("unknown\n", shl_stdout); + return; + } + if (how & SOFT) + val = limit.rlim_cur; + else if (how & HARD) + val = limit.rlim_max; + if (val == RLIM_INFINITY) + shf_puts("unlimited\n", shl_stdout); + else { + val /= l->factor; + shprintf("%ld\n", (long)val); + } +} + int c_rename(const char **wp) { diff --git a/misc.c b/misc.c index 80e0de8..5c2d69d 100644 --- a/misc.c +++ b/misc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.c,v 1.32 2007/08/02 11:05:54 fgsch Exp $ */ +/* $OpenBSD: misc.c,v 1.33 2008/03/21 12:51:19 millert Exp $ */ /* $OpenBSD: path.c,v 1.12 2005/03/30 17:16:37 deraadt Exp $ */ #include "sh.h" @@ -6,7 +6,7 @@ #include #endif -__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.72 2008/04/11 19:55:23 tg Exp $\t" +__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.73 2008/04/16 21:56:02 tg Exp $\t" MKSH_SH_H_ID); #undef USE_CHVT @@ -810,10 +810,10 @@ ksh_getopt_reset(Getopt *go, int flags) * the option is missing). * Used for 'read -u2', 'print -u2' and fc -40. * - '#' is like ':' in options, expect that the argument is optional - * and must start with a digit. If the argument doesn't start with a - * digit, it is assumed to be missing and normal option processing - * continues (optarg is set to 0 if the option is missing). - * Used for 'typeset -LZ4'. + * and must start with a digit or be the string "unlimited". If the + * argument doesn't match, it is assumed to be missing and normal option + * processing continues (optarg is set to 0 if the option is missing). + * Used for 'typeset -LZ4' and 'ulimit -adunlimited'. * - accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an * option starting with + is accepted, the GI_PLUS flag will be set * in go->info. @@ -896,13 +896,16 @@ ksh_getopt(const char **argv, Getopt *go, const char *optionsp) * argument is missing. */ if (argv[go->optind - 1][go->p]) { - if (ksh_isdigit(argv[go->optind - 1][go->p])) { + if (ksh_isdigit(argv[go->optind - 1][go->p]) || + !strcmp(&argv[go->optind - 1][go->p], "unlimited")) { go->optarg = argv[go->optind - 1] + go->p; go->p = 0; } else go->optarg = NULL; } else { - if (argv[go->optind] && ksh_isdigit(argv[go->optind][0])) { + if (argv[go->optind] && + (ksh_isdigit(argv[go->optind][0]) || + !strcmp(argv[go->optind], "unlimited"))) { go->optarg = argv[go->optind++]; go->p = 0; } else diff --git a/mksh.1 b/mksh.1 index db48237..2281224 100644 --- a/mksh.1 +++ b/mksh.1 @@ -1,5 +1,5 @@ -.\" $MirOS: src/bin/mksh/mksh.1,v 1.115 2008/04/01 21:07:20 tg Exp $ -.\" $OpenBSD: ksh.1,v 1.120 2007/05/31 20:47:44 otto Exp $ +.\" $MirOS: src/bin/mksh/mksh.1,v 1.116 2008/04/16 21:56:02 tg Exp $ +.\" $OpenBSD: ksh.1,v 1.121 2008/03/21 12:51:19 millert Exp $ .\"- .\" Try to make GNU groff and AT&T nroff more compatible .\" * ` generates ‘ in groff, so use \` @@ -30,7 +30,7 @@ .el .xD \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 .. .\"- -.Dd $Mdocdate: April 1 2008 $ +.Dd $Mdocdate: April 16 2008 $ .Dt MKSH 1 .Os MirBSD .Sh NAME @@ -4077,15 +4077,16 @@ unless they are also given on the same command line. .Pp .It Xo .Ic ulimit -.Op Fl acdfHLlmnpSsTtvw -.Op Ar value +.Op Fl acdfHLlmnpSsTtvw Op Ar value +.Ar ... .Xc Display or set process limits. If no options are used, the file size limit .Pq Fl f is assumed. .Ar value , -if specified, may be either an arithmetic expression or the word +if specified, may be either an arithmetic expression starting with a +number or the word .Dq unlimited . The limits affect the shell and any processes created by the shell after a limit is imposed. @@ -5454,7 +5455,7 @@ and many other persons, and is currently maintained by .An Thorsten Glaser Aq tg@mirbsd.de . .Sh BUGS This document attempts to describe -.Nm mksh\ R33c +.Nm mksh\ R34 and up, compiled without any options impacting functionality, such as .Dv MKSH_SMALL , diff --git a/sh.h b/sh.h index 3a90cc6..603ab28 100644 --- a/sh.h +++ b/sh.h @@ -8,8 +8,8 @@ /* $OpenBSD: c_test.h,v 1.4 2004/12/20 11:34:26 otto Exp $ */ /* $OpenBSD: tty.h,v 1.5 2004/12/20 11:34:26 otto Exp $ */ -#define MKSH_SH_H_ID "$MirOS: src/bin/mksh/sh.h,v 1.204 2008/04/11 19:55:24 tg Exp $" -#define MKSH_VERSION "R33 2008/04/11" +#define MKSH_SH_H_ID "$MirOS: src/bin/mksh/sh.h,v 1.205 2008/04/16 21:56:03 tg Exp $" +#define MKSH_VERSION "R33 2008/04/16" #if HAVE_SYS_PARAM_H #include