blob: 7e867834135e802da06d3911d5d38861c31fb67c [file] [log] [blame]
Denys Vlasenkof3c742f2010-03-06 20:12:00 +01001/* 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
22struct 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
29static 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
65enum {
66 OPT_hard = (1 << 0),
67 OPT_soft = (1 << 1),
68};
69
70/* "-": treat args as parameters of option with ASCII code 1 */
71static 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
107static 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
124int 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 = optarg ? optarg : (argv[optind] && argv[optind][0] != '-' ? argv[optind] : NULL);
179
180 getrlimit(l->cmd, &limit);
181 if (val_str) {
182 rlim_t val;
183
184 if (!optarg) /* -c NNN: make getopt skip NNN */
185 optind++;
186
187 if (strcmp(val_str, "unlimited") == 0)
188 val = RLIM_INFINITY;
189 else {
190 if (sizeof(val) == sizeof(int))
191 val = bb_strtou(val_str, NULL, 10);
192 else if (sizeof(val) == sizeof(long))
193 val = bb_strtoul(val_str, NULL, 10);
194 else
195 val = bb_strtoull(val_str, NULL, 10);
196 if (errno) {
197 bb_error_msg("bad number");
198 return EXIT_FAILURE;
199 }
200 val <<= l->factor_shift;
201 }
202//bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val);
203 if (opts & OPT_hard)
204 limit.rlim_max = val;
205 if ((opts & OPT_soft) || opts == 0)
206 limit.rlim_cur = val;
207//bb_error_msg("setrlimit(%d, %lld, %lld)", l->cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max);
208 if (setrlimit(l->cmd, &limit) < 0) {
209 bb_perror_msg("error setting limit");
210 return EXIT_FAILURE;
211 }
212 } else {
213 printlim(opts, &limit, l);
214 }
215 break;
216 }
217 } /* for (every possible opt) */
218
219 if (l == &limits_tbl[ARRAY_SIZE(limits_tbl)]) {
220 /* bad option. getopt already complained. */
221 break;
222 }
223
224 } /* while (there are options) */
225
226 return 0;
227}