From f3c742f925c31bdf1b0d52c82e0ef608a344e10f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 6 Mar 2010 20:12:00 +0100 Subject: [PATCH] hush: use ash's ulimit builtin; make it more more bash0like while at it Based on a patch by Tobias Klauser function old new delta shell_builtin_ulimit - 498 +498 limits_tbl 33 88 +55 ulimit_opt_string - 38 +38 bltins1 288 300 +12 limits_name 127 - -127 ulimitcmd 415 7 -408 ------------------------------------------------------------------------------ (add/remove: 3/1 grow/shrink: 2/1 up/down: 603/-535) Total: 68 bytes text data bss dec hex filename 839229 453 6828 846510 ceaae busybox_old 839423 453 6828 846704 ceb70 busybox_unstripped Signed-off-by: Denys Vlasenko --- shell/Kbuild | 4 +- shell/ash.c | 214 +------------------------------------- shell/builtin_ulimit.c | 227 +++++++++++++++++++++++++++++++++++++++++ shell/builtin_ulimit.h | 19 ++++ shell/hush.c | 4 +- 5 files changed, 253 insertions(+), 215 deletions(-) create mode 100644 shell/builtin_ulimit.c create mode 100644 shell/builtin_ulimit.h diff --git a/shell/Kbuild b/shell/Kbuild index d8306dc96..8b528654a 100644 --- a/shell/Kbuild +++ b/shell/Kbuild @@ -5,8 +5,8 @@ # Licensed under the GPL v2, see the file LICENSE in this tarball. lib-y:= -lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o builtin_read.o -lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o builtin_read.o +lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o builtin_read.o builtin_ulimit.o +lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o builtin_read.o builtin_ulimit.o lib-$(CONFIG_CTTYHACK) += cttyhack.o lib-$(CONFIG_SH_MATH_SUPPORT) += math.o diff --git a/shell/ash.c b/shell/ash.c index 8ffe67cca..0a8b6c0f2 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -51,6 +51,7 @@ #include "shell_common.h" #include "builtin_read.h" +#include "builtin_ulimit.h" #include "math.h" #if ENABLE_ASH_RANDOM_SUPPORT # include "random.h" @@ -12614,219 +12615,10 @@ umaskcmd(int argc UNUSED_PARAM, char **argv) return 0; } -/* - * ulimit builtin - * - * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and - * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with - * ash by J.T. Conklin. - * - * Public domain. - */ -struct limits { - uint8_t cmd; /* RLIMIT_xxx fit into it */ - uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ - char option; -}; - -static const struct limits limits_tbl[] = { -#ifdef RLIMIT_CPU - { RLIMIT_CPU, 0, 't' }, -#endif -#ifdef RLIMIT_FSIZE - { RLIMIT_FSIZE, 9, 'f' }, -#endif -#ifdef RLIMIT_DATA - { RLIMIT_DATA, 10, 'd' }, -#endif -#ifdef RLIMIT_STACK - { RLIMIT_STACK, 10, 's' }, -#endif -#ifdef RLIMIT_CORE - { RLIMIT_CORE, 9, 'c' }, -#endif -#ifdef RLIMIT_RSS - { RLIMIT_RSS, 10, 'm' }, -#endif -#ifdef RLIMIT_MEMLOCK - { RLIMIT_MEMLOCK, 10, 'l' }, -#endif -#ifdef RLIMIT_NPROC - { RLIMIT_NPROC, 0, 'p' }, -#endif -#ifdef RLIMIT_NOFILE - { RLIMIT_NOFILE, 0, 'n' }, -#endif -#ifdef RLIMIT_AS - { RLIMIT_AS, 10, 'v' }, -#endif -#ifdef RLIMIT_LOCKS - { RLIMIT_LOCKS, 0, 'w' }, -#endif -}; -static const char limits_name[] = -#ifdef RLIMIT_CPU - "time(seconds)" "\0" -#endif -#ifdef RLIMIT_FSIZE - "file(blocks)" "\0" -#endif -#ifdef RLIMIT_DATA - "data(kb)" "\0" -#endif -#ifdef RLIMIT_STACK - "stack(kb)" "\0" -#endif -#ifdef RLIMIT_CORE - "coredump(blocks)" "\0" -#endif -#ifdef RLIMIT_RSS - "memory(kb)" "\0" -#endif -#ifdef RLIMIT_MEMLOCK - "locked memory(kb)" "\0" -#endif -#ifdef RLIMIT_NPROC - "process" "\0" -#endif -#ifdef RLIMIT_NOFILE - "nofiles" "\0" -#endif -#ifdef RLIMIT_AS - "vmemory(kb)" "\0" -#endif -#ifdef RLIMIT_LOCKS - "locks" "\0" -#endif -; - -enum limtype { SOFT = 0x1, HARD = 0x2 }; - -static void -printlim(enum limtype how, const struct rlimit *limit, - const struct limits *l) -{ - rlim_t val; - - val = limit->rlim_max; - if (how & SOFT) - val = limit->rlim_cur; - - if (val == RLIM_INFINITY) - out1fmt("unlimited\n"); - else { - val >>= l->factor_shift; - out1fmt("%lld\n", (long long) val); - } -} - static int FAST_FUNC -ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +ulimitcmd(int argc UNUSED_PARAM, char **argv) { - rlim_t val; - enum limtype how = SOFT | HARD; - const struct limits *l; - int set, all = 0; - int optc, what; - struct rlimit limit; - - what = 'f'; - while ((optc = nextopt("HSa" -#ifdef RLIMIT_CPU - "t" -#endif -#ifdef RLIMIT_FSIZE - "f" -#endif -#ifdef RLIMIT_DATA - "d" -#endif -#ifdef RLIMIT_STACK - "s" -#endif -#ifdef RLIMIT_CORE - "c" -#endif -#ifdef RLIMIT_RSS - "m" -#endif -#ifdef RLIMIT_MEMLOCK - "l" -#endif -#ifdef RLIMIT_NPROC - "p" -#endif -#ifdef RLIMIT_NOFILE - "n" -#endif -#ifdef RLIMIT_AS - "v" -#endif -#ifdef RLIMIT_LOCKS - "w" -#endif - )) != '\0') - switch (optc) { - case 'H': - how = HARD; - break; - case 'S': - how = SOFT; - break; - case 'a': - all = 1; - break; - default: - what = optc; - } - - for (l = limits_tbl; l->option != what; l++) - continue; - - set = *argptr ? 1 : 0; - val = 0; - if (set) { - char *p = *argptr; - - if (all || argptr[1]) - ash_msg_and_raise_error("too many arguments"); - if (strncmp(p, "unlimited\n", 9) == 0) - val = RLIM_INFINITY; - else { - if (sizeof(val) == sizeof(int)) - val = bb_strtou(p, NULL, 10); - else if (sizeof(val) == sizeof(long)) - val = bb_strtoul(p, NULL, 10); - else - val = bb_strtoull(p, NULL, 10); - if (errno) - ash_msg_and_raise_error("bad number"); - val <<= l->factor_shift; - } - } - if (all) { - const char *lname = limits_name; - for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) { - getrlimit(l->cmd, &limit); - out1fmt("%-20s ", lname); - lname += strlen(lname) + 1; - printlim(how, &limit, l); - } - return 0; - } - - getrlimit(l->cmd, &limit); - if (set) { - if (how & HARD) - limit.rlim_max = val; - if (how & SOFT) - limit.rlim_cur = val; - if (setrlimit(l->cmd, &limit) < 0) - ash_msg_and_raise_error("error setting limit (%m)"); - } else { - printlim(how, &limit, l); - } - return 0; + return shell_builtin_ulimit(argv); } /* ============ main() and helpers */ diff --git a/shell/builtin_ulimit.c b/shell/builtin_ulimit.c new file mode 100644 index 000000000..7e8678341 --- /dev/null +++ b/shell/builtin_ulimit.c @@ -0,0 +1,227 @@ +/* vi: set sw=4 ts=4: */ +/* + * ulimit builtin + * + * Adapted from ash applet code + * + * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and + * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with + * ash by J.T. Conklin. + * + * Public domain. + * + * Copyright (c) 2010 Tobias Klauser + * Split from ash.c and slightly adapted. + * + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ +#include "libbb.h" +#include "builtin_ulimit.h" + + +struct limits { + uint8_t cmd; /* RLIMIT_xxx fit into it */ + uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ + char option; + const char *name; +}; + +static const struct limits limits_tbl[] = { +#ifdef RLIMIT_FSIZE + { RLIMIT_FSIZE, 9, 'f', "file size (blocks)" }, +#endif +#ifdef RLIMIT_CPU + { RLIMIT_CPU, 0, 't', "cpu time (seconds)" }, +#endif +#ifdef RLIMIT_DATA + { RLIMIT_DATA, 10, 'd', "data seg size (kb)" }, +#endif +#ifdef RLIMIT_STACK + { RLIMIT_STACK, 10, 's', "stack size (kb)" }, +#endif +#ifdef RLIMIT_CORE + { RLIMIT_CORE, 9, 'c', "core file size (blocks)" }, +#endif +#ifdef RLIMIT_RSS + { RLIMIT_RSS, 10, 'm', "resident set size (kb)" }, +#endif +#ifdef RLIMIT_MEMLOCK + { RLIMIT_MEMLOCK, 10, 'l', "locked memory (kb)" }, +#endif +#ifdef RLIMIT_NPROC + { RLIMIT_NPROC, 0, 'p', "processes" }, +#endif +#ifdef RLIMIT_NOFILE + { RLIMIT_NOFILE, 0, 'n', "file descriptors" }, +#endif +#ifdef RLIMIT_AS + { RLIMIT_AS, 10, 'v', "address space (kb)" }, +#endif +#ifdef RLIMIT_LOCKS + { RLIMIT_LOCKS, 0, 'w', "locks" }, +#endif +}; + +enum { + OPT_hard = (1 << 0), + OPT_soft = (1 << 1), +}; + +/* "-": treat args as parameters of option with ASCII code 1 */ +static const char ulimit_opt_string[] = "-HSa" +#ifdef RLIMIT_FSIZE + "f::" +#endif +#ifdef RLIMIT_CPU + "t::" +#endif +#ifdef RLIMIT_DATA + "d::" +#endif +#ifdef RLIMIT_STACK + "s::" +#endif +#ifdef RLIMIT_CORE + "c::" +#endif +#ifdef RLIMIT_RSS + "m::" +#endif +#ifdef RLIMIT_MEMLOCK + "l::" +#endif +#ifdef RLIMIT_NPROC + "p::" +#endif +#ifdef RLIMIT_NOFILE + "n::" +#endif +#ifdef RLIMIT_AS + "v::" +#endif +#ifdef RLIMIT_LOCKS + "w::" +#endif + ; + +static void printlim(unsigned opts, const struct rlimit *limit, + const struct limits *l) +{ + rlim_t val; + + val = limit->rlim_max; + if (!(opts & OPT_hard)) + val = limit->rlim_cur; + + if (val == RLIM_INFINITY) + printf("unlimited\n"); + else { + val >>= l->factor_shift; + printf("%llu\n", (long long) val); + } +} + +int FAST_FUNC shell_builtin_ulimit(char **argv) +{ + unsigned opts; + unsigned argc; + + /* We can't use getopt32: need to handle commands like + * ulimit 123 -c2 -l 456 + */ + + /* In case getopt was already called: + * reset the libc getopt() function, which keeps internal state. + */ +#ifdef __GLIBC__ + optind = 0; +#else /* BSD style */ + optind = 1; + /* optreset = 1; */ +#endif + /* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */ + + argc = 1; + while (argv[argc]) + argc++; + + opts = 0; + while (1) { + struct rlimit limit; + const struct limits *l; + int opt_char = getopt(argc, argv, ulimit_opt_string); + + if (opt_char == -1) + break; + if (opt_char == 'H') { + opts |= OPT_hard; + continue; + } + if (opt_char == 'S') { + opts |= OPT_soft; + continue; + } + + if (opt_char == 'a') { + for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) { + getrlimit(l->cmd, &limit); + printf("-%c: %-30s ", l->option, l->name); + printlim(opts, &limit, l); + } + continue; + } + + if (opt_char == 1) + opt_char = 'f'; + for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) { + if (opt_char == l->option) { + char *val_str = optarg ? optarg : (argv[optind] && argv[optind][0] != '-' ? argv[optind] : NULL); + + getrlimit(l->cmd, &limit); + if (val_str) { + rlim_t val; + + if (!optarg) /* -c NNN: make getopt skip NNN */ + optind++; + + if (strcmp(val_str, "unlimited") == 0) + val = RLIM_INFINITY; + else { + if (sizeof(val) == sizeof(int)) + val = bb_strtou(val_str, NULL, 10); + else if (sizeof(val) == sizeof(long)) + val = bb_strtoul(val_str, NULL, 10); + else + val = bb_strtoull(val_str, NULL, 10); + if (errno) { + bb_error_msg("bad number"); + return EXIT_FAILURE; + } + val <<= l->factor_shift; + } +//bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val); + if (opts & OPT_hard) + limit.rlim_max = val; + if ((opts & OPT_soft) || opts == 0) + limit.rlim_cur = val; +//bb_error_msg("setrlimit(%d, %lld, %lld)", l->cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max); + if (setrlimit(l->cmd, &limit) < 0) { + bb_perror_msg("error setting limit"); + return EXIT_FAILURE; + } + } else { + printlim(opts, &limit, l); + } + break; + } + } /* for (every possible opt) */ + + if (l == &limits_tbl[ARRAY_SIZE(limits_tbl)]) { + /* bad option. getopt already complained. */ + break; + } + + } /* while (there are options) */ + + return 0; +} diff --git a/shell/builtin_ulimit.h b/shell/builtin_ulimit.h new file mode 100644 index 000000000..ec1af78a6 --- /dev/null +++ b/shell/builtin_ulimit.h @@ -0,0 +1,19 @@ +/* vi: set sw=4 ts=4: */ +/* + * Adapted from ash applet code + * + * Copyright (c) 2010 Tobias Klauser + * Split from ash.c and slightly adapted. + * + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ +#ifndef SHELL_BUILTIN_ULIMIT_H +#define SHELL_BUILTIN_ULIMIT_H 1 + +PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN + +int FAST_FUNC shell_builtin_ulimit(char **argv); + +POP_SAVED_FUNCTION_VISIBILITY + +#endif diff --git a/shell/hush.c b/shell/hush.c index 0310b02ed..6f391b881 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -57,7 +57,6 @@ * * TODOs: * grep for "TODO" and fix (some of them are easy) - * builtins: ulimit * special variables (done: PWD) * follow IFS rules more precisely, including update semantics * export builtin should be special, its arguments are assignments @@ -87,6 +86,7 @@ #include "shell_common.h" #include "builtin_read.h" +#include "builtin_ulimit.h" #include "math.h" #include "match.h" #if ENABLE_HUSH_RANDOM_SUPPORT @@ -671,7 +671,7 @@ static const struct built_in_command bltins1[] = { BLTIN("shift" , builtin_shift , "Shift positional parameters"), BLTIN("trap" , builtin_trap , "Trap signals"), BLTIN("type" , builtin_type , "Write a description of command type"), -// BLTIN("ulimit" , builtin_ulimit , "Control resource limits"), + BLTIN("ulimit" , shell_builtin_ulimit , "Control resource limits"), BLTIN("umask" , builtin_umask , "Set file creation mask"), BLTIN("unset" , builtin_unset , "Unset variables"), BLTIN("wait" , builtin_wait , "Wait for process"), -- 2.25.1