From 25878968814c23048bc7e55a93aa79bae794f20c Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Fri, 21 Mar 2008 12:51:20 +0000 Subject: Make ulimit able to get and set multiple limits in a single invocation like bash and zsh do. Requested by espie@, OK deraadt@ --- bin/ksh/c_ulimit.c | 224 ++++++++++++++++++++++++++++++----------------------- bin/ksh/ksh.1 | 19 ++--- bin/ksh/misc.c | 16 ++-- bin/ksh/sh.1 | 19 ++--- 4 files changed, 148 insertions(+), 130 deletions(-) diff --git a/bin/ksh/c_ulimit.c b/bin/ksh/c_ulimit.c index 8c81dc14d83..bf580772a9c 100644 --- a/bin/ksh/c_ulimit.c +++ b/bin/ksh/c_ulimit.c @@ -1,4 +1,4 @@ -/* $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 $ */ /* ulimit -- handle "ulimit" builtin @@ -24,51 +24,52 @@ #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; /* option character (-d, -f, ...) */ +}; + +static void print_ulimit(const struct limits *, int); +static int set_ulimit(const struct limits *, const char *, int); + int c_ulimit(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 */ - { "time(cpu-seconds)", RLIMIT, RLIMIT_CPU, RLIMIT_CPU, 1, 't' }, - { "file(blocks)", RLIMIT, RLIMIT_FSIZE, RLIMIT_FSIZE, 512, 'f' }, - { "coredump(blocks)", RLIMIT, RLIMIT_CORE, RLIMIT_CORE, 512, 'c' }, - { "data(kbytes)", RLIMIT, RLIMIT_DATA, RLIMIT_DATA, 1024, 'd' }, - { "stack(kbytes)", RLIMIT, RLIMIT_STACK, RLIMIT_STACK, 1024, 's' }, - { "lockedmem(kbytes)", RLIMIT, RLIMIT_MEMLOCK, RLIMIT_MEMLOCK, - 1024, 'l' }, - { "memory(kbytes)", RLIMIT, RLIMIT_RSS, RLIMIT_RSS, 1024, 'm' }, - { "nofiles(descriptors)", RLIMIT, RLIMIT_NOFILE, RLIMIT_NOFILE, - 1, 'n' }, - { "processes", RLIMIT, RLIMIT_NPROC, RLIMIT_NPROC, 1, 'p' }, - #ifdef RLIMIT_VMEM - { "vmemory(kbytes)", RLIMIT, RLIMIT_VMEM, RLIMIT_VMEM, 1024, 'v' }, - #endif /* RLIMIT_VMEM */ + static const struct limits limits[] = { + /* Do not use options -H, -S or -a or change the order. */ + { "time(cpu-seconds)", RLIMIT_CPU, 1, 't' }, + { "file(blocks)", RLIMIT_FSIZE, 512, 'f' }, + { "coredump(blocks)", RLIMIT_CORE, 512, 'c' }, + { "data(kbytes)", RLIMIT_DATA, 1024, 'd' }, + { "stack(kbytes)", RLIMIT_STACK, 1024, 's' }, + { "lockedmem(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' }, + { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' }, + { "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' }, + { "processes", RLIMIT_NPROC, 1, 'p' }, +#ifdef RLIMIT_VMEM + { "vmemory(kbytes)", RLIMIT_VMEM, 1024, 'v' }, +#endif /* RLIMIT_VMEM */ { (char *) 0 } }; - static char options[3 + NELEM(limits)]; - rlim_t val = 0; + static char options[4 + NELEM(limits) * 2]; int how = SOFT | HARD; const struct limits *l; - int set, all = 0; - int optc, what; - struct rlimit limit; + int optc, all = 0; + if (!options[0]) { /* build options string on first call - yuck */ char *p = options; *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, options)) != -1) switch (optc) { case 'H': @@ -83,92 +84,117 @@ c_ulimit(char **wp) case '?': return 1; default: - what = optc; + break; } - for (l = limits; l->name && l->option != what; l++) - ; - if (!l->name) { - internal_errorf(0, "ulimit: %c", what); + if (wp[builtin_opt.optind] != NULL) { + bi_errorf("usage: ulimit [-acdfHlmnpSst] [value]"); return 1; } - wp += builtin_opt.optind; - set = *wp ? 1 : 0; - if (set) { - if (all || wp[1]) { - bi_errorf("too many arguments"); + /* 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, options)) != -1) + switch (optc) { + case 'a': + case 'H': + case 'S': + break; + case '?': return 1; - } - if (strcmp(wp[0], "unlimited") == 0) - val = 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 && !digit(wp[0][0])) { - bi_errorf("invalid limit: %s", wp[0]); + default: + for (l = limits; l->name && l->option != optc; l++) + ; + if (!l->name) { + internal_errorf(0, "ulimit: %c", optc); return 1; } - val = (rlim_t)rval * l->factor; + if (builtin_opt.optarg) { + if (set_ulimit(l, builtin_opt.optarg, how)) + return 1; + } else + print_ulimit(l, how); + break; } - } + + wp += builtin_opt.optind; + if (all) { for (l = limits; l->name; l++) { - if (l->which == RLIMIT) { - getrlimit(l->gcmd, &limit); - if (how & SOFT) - val = limit.rlim_cur; - else if (how & HARD) - val = limit.rlim_max; - } shprintf("%-20s ", l->name); - if (val == RLIM_INFINITY) - shprintf("unlimited\n"); - else { - val /= l->factor; - shprintf("%ld\n", (long) val); - } + print_ulimit(l, how); } - 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) { - if (errno == EPERM) - bi_errorf("exceeds allowable limit"); - else - bi_errorf("bad limit: %s", - strerror(errno)); + } else if (builtin_opt.optind == 1) { + /* No limit specified, use file size */ + l = &limits[1]; + if (wp[0] != NULL) { + if (set_ulimit(l, wp[0], how)) return 1; - } + wp++; } else { - if (how & SOFT) - val = limit.rlim_cur; - else if (how & HARD) - val = limit.rlim_max; + print_ulimit(l, how); } } - if (!set) { - if (val == RLIM_INFINITY) - shprintf("unlimited\n"); - else { - val /= l->factor; - shprintf("%ld\n", (long) val); + + return 0; +} + +static int +set_ulimit(const struct limits *l, const char *v, int how) +{ + rlim_t val = 0; + struct rlimit limit; + + if (strcmp(v, "unlimited") == 0) + val = 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 && !digit(v[0])) { + bi_errorf("invalid limit: %s", v); + return 1; } + val = (rlim_t)rval * l->factor; + } + + getrlimit(l->resource, &limit); + 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("exceeds allowable limit"); + else + bi_errorf("bad limit: %s", strerror(errno)); + return 1; } return 0; } + +static void +print_ulimit(const struct limits *l, int how) +{ + rlim_t val = 0; + struct rlimit limit; + + getrlimit(l->resource, &limit); + if (how & SOFT) + val = limit.rlim_cur; + else if (how & HARD) + val = limit.rlim_max; + if (val == RLIM_INFINITY) + shprintf("unlimited\n"); + else { + val /= l->factor; + shprintf("%ld\n", (long) val); + } +} diff --git a/bin/ksh/ksh.1 b/bin/ksh/ksh.1 index 47bfc5e5185..9331becda47 100644 --- a/bin/ksh/ksh.1 +++ b/bin/ksh/ksh.1 @@ -1,8 +1,8 @@ -.\" $OpenBSD: ksh.1,v 1.120 2007/05/31 20:47:44 otto Exp $ +.\" $OpenBSD: ksh.1,v 1.121 2008/03/21 12:51:19 millert Exp $ .\" .\" Public Domain .\" -.Dd $Mdocdate: May 31 2007 $ +.Dd $Mdocdate: March 21 2008 $ .Dt KSH 1 .Os .Sh NAME @@ -4256,24 +4256,19 @@ except zero padding is used instead of space padding. .Pp .It Xo .Ic ulimit -.Op Fl acdfHlmnpSst -.Op Ar value +.Op Fl acdfHlmnpSst 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. -Note that some systems may not allow limits to be increased -once they are set. -Also note that the types of limits available are system -dependent \- some systems have only the -.Fl f -limit. +limit is imposed; limits may not be increased once they are set. .Bl -tag -width 5n .It Fl a Display all limits; unless diff --git a/bin/ksh/misc.c b/bin/ksh/misc.c index 24844c013e0..3418242da7b 100644 --- a/bin/ksh/misc.c +++ b/bin/ksh/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 $ */ /* * Miscellaneous functions @@ -891,10 +891,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. @@ -977,13 +977,15 @@ ksh_getopt(char **argv, Getopt *go, const char *options) * argument is missing. */ if (argv[go->optind - 1][go->p]) { - if (digit(argv[go->optind - 1][go->p])) { + if (digit(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 = (char *) 0; } else { - if (argv[go->optind] && digit(argv[go->optind][0])) { + if (argv[go->optind] && (digit(argv[go->optind][0]) || + !strcmp(argv[go->optind], "unlimited"))) { go->optarg = argv[go->optind++]; go->p = 0; } else diff --git a/bin/ksh/sh.1 b/bin/ksh/sh.1 index 675773490b8..f87f0b22b8f 100644 --- a/bin/ksh/sh.1 +++ b/bin/ksh/sh.1 @@ -1,8 +1,8 @@ -.\" $OpenBSD: sh.1,v 1.75 2007/05/31 20:47:44 otto Exp $ +.\" $OpenBSD: sh.1,v 1.76 2008/03/21 12:51:19 millert Exp $ .\" .\" Public Domain .\" -.Dd $Mdocdate: May 31 2007 $ +.Dd $Mdocdate: March 21 2008 $ .Dt SH 1 .Os .Sh NAME @@ -3375,24 +3375,19 @@ except zero padding is used instead of space padding. .Pp .It Xo .Ic ulimit -.Op Fl acdfHlmnpSst -.Op Ar value +.Op Fl acdfHlmnpSst 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. -Note that some systems may not allow limits to be increased -once they are set. -Also note that the types of limits available are system -dependent \- some systems have only the -.Fl f -limit. +limit is imposed; limits may not be increased once they are set. .Bl -tag -width 5n .It Fl a Display all limits; unless -- cgit v1.2.3