hush: support "cd -- DIR" and such
[oweals/busybox.git] / shell / builtin_ulimit.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * ulimit builtin
4  *
5  * Adapted from ash applet code
6  *
7  * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
8  * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
9  * ash by J.T. Conklin.
10  *
11  * Public domain.
12  *
13  * Copyright (c) 2010 Tobias Klauser
14  * Split from ash.c and slightly adapted.
15  *
16  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
17  */
18 #include "libbb.h"
19 #include "builtin_ulimit.h"
20
21
22 struct limits {
23         uint8_t cmd;            /* RLIMIT_xxx fit into it */
24         uint8_t factor_shift;   /* shift by to get rlim_{cur,max} values */
25         char option;
26         const char *name;
27 };
28
29 static const struct limits limits_tbl[] = {
30 #ifdef RLIMIT_FSIZE
31         { RLIMIT_FSIZE,         9,      'f',    "file size (blocks)" },
32 #endif
33 #ifdef RLIMIT_CPU
34         { RLIMIT_CPU,           0,      't',    "cpu time (seconds)" },
35 #endif
36 #ifdef RLIMIT_DATA
37         { RLIMIT_DATA,          10,     'd',    "data seg size (kb)" },
38 #endif
39 #ifdef RLIMIT_STACK
40         { RLIMIT_STACK,         10,     's',    "stack size (kb)" },
41 #endif
42 #ifdef RLIMIT_CORE
43         { RLIMIT_CORE,          9,      'c',    "core file size (blocks)" },
44 #endif
45 #ifdef RLIMIT_RSS
46         { RLIMIT_RSS,           10,     'm',    "resident set size (kb)" },
47 #endif
48 #ifdef RLIMIT_MEMLOCK
49         { RLIMIT_MEMLOCK,       10,     'l',    "locked memory (kb)" },
50 #endif
51 #ifdef RLIMIT_NPROC
52         { RLIMIT_NPROC,         0,      'p',    "processes" },
53 #endif
54 #ifdef RLIMIT_NOFILE
55         { RLIMIT_NOFILE,        0,      'n',    "file descriptors" },
56 #endif
57 #ifdef RLIMIT_AS
58         { RLIMIT_AS,            10,     'v',    "address space (kb)" },
59 #endif
60 #ifdef RLIMIT_LOCKS
61         { RLIMIT_LOCKS,         0,      'w',    "locks" },
62 #endif
63 };
64
65 enum {
66         OPT_hard = (1 << 0),
67         OPT_soft = (1 << 1),
68 };
69
70 /* "-": treat args as parameters of option with ASCII code 1 */
71 static const char ulimit_opt_string[] = "-HSa"
72 #ifdef RLIMIT_FSIZE
73                         "f::"
74 #endif
75 #ifdef RLIMIT_CPU
76                         "t::"
77 #endif
78 #ifdef RLIMIT_DATA
79                         "d::"
80 #endif
81 #ifdef RLIMIT_STACK
82                         "s::"
83 #endif
84 #ifdef RLIMIT_CORE
85                         "c::"
86 #endif
87 #ifdef RLIMIT_RSS
88                         "m::"
89 #endif
90 #ifdef RLIMIT_MEMLOCK
91                         "l::"
92 #endif
93 #ifdef RLIMIT_NPROC
94                         "p::"
95 #endif
96 #ifdef RLIMIT_NOFILE
97                         "n::"
98 #endif
99 #ifdef RLIMIT_AS
100                         "v::"
101 #endif
102 #ifdef RLIMIT_LOCKS
103                         "w::"
104 #endif
105                         ;
106
107 static void printlim(unsigned opts, const struct rlimit *limit,
108                         const struct limits *l)
109 {
110         rlim_t val;
111
112         val = limit->rlim_max;
113         if (!(opts & OPT_hard))
114                 val = limit->rlim_cur;
115
116         if (val == RLIM_INFINITY)
117                 printf("unlimited\n");
118         else {
119                 val >>= l->factor_shift;
120                 printf("%llu\n", (long long) val);
121         }
122 }
123
124 int FAST_FUNC shell_builtin_ulimit(char **argv)
125 {
126         unsigned opts;
127         unsigned argc;
128
129         /* We can't use getopt32: need to handle commands like
130          * ulimit 123 -c2 -l 456
131          */
132
133         /* In case getopt was already called:
134          * reset the libc getopt() function, which keeps internal state.
135          */
136 #ifdef __GLIBC__
137         optind = 0;
138 #else /* BSD style */
139         optind = 1;
140         /* optreset = 1; */
141 #endif
142         /* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */
143
144         argc = 1;
145         while (argv[argc])
146                 argc++;
147
148         opts = 0;
149         while (1) {
150                 struct rlimit limit;
151                 const struct limits *l;
152                 int opt_char = getopt(argc, argv, ulimit_opt_string);
153
154                 if (opt_char == -1)
155                         break;
156                 if (opt_char == 'H') {
157                         opts |= OPT_hard;
158                         continue;
159                 }
160                 if (opt_char == 'S') {
161                         opts |= OPT_soft;
162                         continue;
163                 }
164
165                 if (opt_char == 'a') {
166                         for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
167                                 getrlimit(l->cmd, &limit);
168                                 printf("-%c: %-30s ", l->option, l->name);
169                                 printlim(opts, &limit, l);
170                         }
171                         continue;
172                 }
173
174                 if (opt_char == 1)
175                         opt_char = 'f';
176                 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
177                         if (opt_char == l->option) {
178                                 char *val_str;
179
180                                 getrlimit(l->cmd, &limit);
181
182                                 val_str = optarg;
183                                 if (!val_str && argv[optind] && argv[optind][0] != '-')
184                                         val_str = argv[optind++]; /* ++ skips NN in "-c NN" case */
185                                 if (val_str) {
186                                         rlim_t val;
187
188                                         if (strcmp(val_str, "unlimited") == 0)
189                                                 val = RLIM_INFINITY;
190                                         else {
191                                                 if (sizeof(val) == sizeof(int))
192                                                         val = bb_strtou(val_str, NULL, 10);
193                                                 else if (sizeof(val) == sizeof(long))
194                                                         val = bb_strtoul(val_str, NULL, 10);
195                                                 else
196                                                         val = bb_strtoull(val_str, NULL, 10);
197                                                 if (errno) {
198                                                         bb_error_msg("bad number");
199                                                         return EXIT_FAILURE;
200                                                 }
201                                                 val <<= l->factor_shift;
202                                         }
203 //bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val);
204                                         if (opts & OPT_hard)
205                                                 limit.rlim_max = val;
206                                         if ((opts & OPT_soft) || opts == 0)
207                                                 limit.rlim_cur = val;
208 //bb_error_msg("setrlimit(%d, %lld, %lld)", l->cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max);
209                                         if (setrlimit(l->cmd, &limit) < 0) {
210                                                 bb_perror_msg("error setting limit");
211                                                 return EXIT_FAILURE;
212                                         }
213                                 } else {
214                                         printlim(opts, &limit, l);
215                                 }
216                                 break;
217                         }
218                 } /* for (every possible opt) */
219
220                 if (l == &limits_tbl[ARRAY_SIZE(limits_tbl)]) {
221                         /* bad option. getopt already complained. */
222                         break;
223                 }
224
225         } /* while (there are options) */
226
227         return 0;
228 }