hush: use ash's ulimit builtin; make it more more bash0like while at it
authorDenys Vlasenko <vda.linux@googlemail.com>
Sat, 6 Mar 2010 19:12:00 +0000 (20:12 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sat, 6 Mar 2010 19:12:00 +0000 (20:12 +0100)
Based on a patch by Tobias Klauser <tklauser@distanz.ch>

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 <vda.linux@googlemail.com>
shell/Kbuild
shell/ash.c
shell/builtin_ulimit.c [new file with mode: 0644]
shell/builtin_ulimit.h [new file with mode: 0644]
shell/hush.c

index d8306dc96a4aa5efef7343abe8e8e608032044ea..8b528654a8adce62b3e16694662b0f288029fc18 100644 (file)
@@ -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
index 8ffe67ccabcddfc36479fdedcf3693e387fac8bf..0a8b6c0f2e9d3bf34321caebfd5aa2e7de308496 100644 (file)
@@ -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 (file)
index 0000000..7e86783
--- /dev/null
@@ -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 (file)
index 0000000..ec1af78
--- /dev/null
@@ -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
index 0310b02ed8f8a4f31648dfacdacfa5b7210cab83..6f391b8819204b098528259bf5f7c23601a23c87 100644 (file)
@@ -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"),