blob: bbc22ed34b49c06b8ea3dcfba0432b6f637e542d [file] [log] [blame]
Denys Vlasenko73067272010-01-12 22:11:24 +01001/* vi: set sw=4 ts=4: */
2/*
3 * Adapted from ash applet code
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Copyright (c) 1989, 1991, 1993, 1994
9 * The Regents of the University of California. All rights reserved.
10 *
11 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
12 * was re-ported from NetBSD and debianized.
13 *
14 * Copyright (c) 2010 Denys Vlasenko
15 * Split from ash.c
16 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020017 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Denys Vlasenko73067272010-01-12 22:11:24 +010018 */
19#include "libbb.h"
20#include "shell_common.h"
21
Denys Vlasenko73067272010-01-12 22:11:24 +010022const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denys Vlasenko25d9b912010-01-13 18:22:35 +010023
24
25int FAST_FUNC is_well_formed_var_name(const char *s, char terminator)
26{
27 if (!s || !(isalpha(*s) || *s == '_'))
28 return 0;
29
30 do
31 s++;
32 while (isalnum(*s) || *s == '_');
33
34 return *s == terminator;
35}
Denys Vlasenko599ae1e2010-05-23 17:49:50 +020036
37/* read builtin */
38
Denys Vlasenko80542ba2011-05-08 21:23:43 +020039/* Needs to be interruptible: shell mush handle traps and shell-special signals
40 * while inside read. To implement this, be sure to not loop on EINTR
41 * and return errno == EINTR reliably.
42 */
Denys Vlasenko599ae1e2010-05-23 17:49:50 +020043//TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL"
44//string. hush naturally has it, and ash has setvareq().
45//Here we can simply store "VAR=" at buffer start and store read data directly
46//after "=", then pass buffer to setvar() to consume.
47const char* FAST_FUNC
48shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
49 char **argv,
50 const char *ifs,
51 int read_flags,
52 const char *opt_n,
53 const char *opt_p,
54 const char *opt_t,
55 const char *opt_u
56)
57{
Denys Vlasenko80542ba2011-05-08 21:23:43 +020058 unsigned err;
Denys Vlasenko599ae1e2010-05-23 17:49:50 +020059 unsigned end_ms; /* -t TIMEOUT */
60 int fd; /* -u FD */
61 int nchars; /* -n NUM */
62 char **pp;
63 char *buffer;
64 struct termios tty, old_tty;
65 const char *retval;
66 int bufpos; /* need to be able to hold -1 */
67 int startword;
68 smallint backslash;
69
Denys Vlasenko80542ba2011-05-08 21:23:43 +020070 errno = err = 0;
71
Denys Vlasenko599ae1e2010-05-23 17:49:50 +020072 pp = argv;
73 while (*pp) {
74 if (!is_well_formed_var_name(*pp, '\0')) {
75 /* Mimic bash message */
76 bb_error_msg("read: '%s': not a valid identifier", *pp);
77 return (const char *)(uintptr_t)1;
78 }
79 pp++;
80 }
81
82 nchars = 0; /* if != 0, -n is in effect */
83 if (opt_n) {
84 nchars = bb_strtou(opt_n, NULL, 10);
85 if (nchars < 0 || errno)
86 return "invalid count";
87 /* note: "-n 0": off (bash 3.2 does this too) */
88 }
89 end_ms = 0;
90 if (opt_t) {
91 end_ms = bb_strtou(opt_t, NULL, 10);
92 if (errno || end_ms > UINT_MAX / 2048)
93 return "invalid timeout";
94 end_ms *= 1000;
95#if 0 /* even bash has no -t N.NNN support */
96 ts.tv_sec = bb_strtou(opt_t, &p, 10);
97 ts.tv_usec = 0;
98 /* EINVAL means number is ok, but not terminated by NUL */
99 if (*p == '.' && errno == EINVAL) {
100 char *p2;
101 if (*++p) {
102 int scale;
103 ts.tv_usec = bb_strtou(p, &p2, 10);
104 if (errno)
105 return "invalid timeout";
106 scale = p2 - p;
107 /* normalize to usec */
108 if (scale > 6)
109 return "invalid timeout";
110 while (scale++ < 6)
111 ts.tv_usec *= 10;
112 }
113 } else if (ts.tv_sec < 0 || errno) {
114 return "invalid timeout";
115 }
116 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
117 return "invalid timeout";
118 }
119#endif /* if 0 */
120 }
121 fd = STDIN_FILENO;
122 if (opt_u) {
123 fd = bb_strtou(opt_u, NULL, 10);
124 if (fd < 0 || errno)
125 return "invalid file descriptor";
126 }
127
128 if (opt_p && isatty(fd)) {
129 fputs(opt_p, stderr);
130 fflush_all();
131 }
132
133 if (ifs == NULL)
134 ifs = defifs;
135
136 if (nchars || (read_flags & BUILTIN_READ_SILENT)) {
137 tcgetattr(fd, &tty);
138 old_tty = tty;
139 if (nchars) {
140 tty.c_lflag &= ~ICANON;
141 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
142 }
143 if (read_flags & BUILTIN_READ_SILENT) {
144 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
145 }
146 /* This forces execution of "restoring" tcgetattr later */
147 read_flags |= BUILTIN_READ_SILENT;
148 /* if tcgetattr failed, tcsetattr will fail too.
149 * Ignoring, it's harmless. */
150 tcsetattr(fd, TCSANOW, &tty);
151 }
152
153 retval = (const char *)(uintptr_t)0;
154 startword = 1;
155 backslash = 0;
156 if (end_ms) /* NB: end_ms stays nonzero: */
157 end_ms = ((unsigned)monotonic_ms() + end_ms) | 1;
158 buffer = NULL;
159 bufpos = 0;
160 do {
161 char c;
Denys Vlasenko10c01312011-05-11 11:49:21 +0200162 struct pollfd pfd[1];
163 int timeout;
Denys Vlasenko599ae1e2010-05-23 17:49:50 +0200164
Denys Vlasenko10c01312011-05-11 11:49:21 +0200165 if ((bufpos & 0xff) == 0)
166 buffer = xrealloc(buffer, bufpos + 0x100);
Denys Vlasenko80542ba2011-05-08 21:23:43 +0200167
Denys Vlasenko10c01312011-05-11 11:49:21 +0200168 timeout = -1;
Denys Vlasenko599ae1e2010-05-23 17:49:50 +0200169 if (end_ms) {
Denys Vlasenko599ae1e2010-05-23 17:49:50 +0200170 timeout = end_ms - (unsigned)monotonic_ms();
Denys Vlasenko10c01312011-05-11 11:49:21 +0200171 if (timeout <= 0) { /* already late? */
Denys Vlasenko599ae1e2010-05-23 17:49:50 +0200172 retval = (const char *)(uintptr_t)1;
173 goto ret;
174 }
175 }
176
Denys Vlasenko10c01312011-05-11 11:49:21 +0200177 /* We must poll even if timeout is -1:
178 * we want to be interrupted if signal arrives,
179 * regardless of SA_RESTART-ness of that signal!
180 */
181 errno = 0;
182 pfd[0].fd = fd;
183 pfd[0].events = POLLIN;
184 if (poll(pfd, 1, timeout) != 1) {
185 /* timed out, or EINTR */
186 err = errno;
187 retval = (const char *)(uintptr_t)1;
188 goto ret;
189 }
190 if (read(fd, &buffer[bufpos], 1) != 1) {
Denys Vlasenko80542ba2011-05-08 21:23:43 +0200191 err = errno;
Denys Vlasenko599ae1e2010-05-23 17:49:50 +0200192 retval = (const char *)(uintptr_t)1;
193 break;
194 }
Denys Vlasenko10c01312011-05-11 11:49:21 +0200195
Denys Vlasenko599ae1e2010-05-23 17:49:50 +0200196 c = buffer[bufpos];
197 if (c == '\0')
198 continue;
199 if (backslash) {
200 backslash = 0;
201 if (c != '\n')
202 goto put;
203 continue;
204 }
205 if (!(read_flags & BUILTIN_READ_RAW) && c == '\\') {
206 backslash = 1;
207 continue;
208 }
209 if (c == '\n')
210 break;
211
212 /* $IFS splitting. NOT done if we run "read"
213 * without variable names (bash compat).
214 * Thus, "read" and "read REPLY" are not the same.
215 */
216 if (argv[0]) {
217/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */
218 const char *is_ifs = strchr(ifs, c);
219 if (startword && is_ifs) {
220 if (isspace(c))
221 continue;
222 /* it is a non-space ifs char */
223 startword--;
224 if (startword == 1) /* first one? */
225 continue; /* yes, it is not next word yet */
226 }
227 startword = 0;
228 if (argv[1] != NULL && is_ifs) {
229 buffer[bufpos] = '\0';
230 bufpos = 0;
231 setvar(*argv, buffer);
232 argv++;
233 /* can we skip one non-space ifs char? (2: yes) */
234 startword = isspace(c) ? 2 : 1;
235 continue;
236 }
237 }
238 put:
239 bufpos++;
240 } while (--nchars);
241
242 if (argv[0]) {
243 /* Remove trailing space $IFS chars */
244 while (--bufpos >= 0 && isspace(buffer[bufpos]) && strchr(ifs, buffer[bufpos]) != NULL)
245 continue;
246 buffer[bufpos + 1] = '\0';
247 /* Use the remainder as a value for the next variable */
248 setvar(*argv, buffer);
249 /* Set the rest to "" */
250 while (*++argv)
251 setvar(*argv, "");
252 } else {
253 /* Note: no $IFS removal */
254 buffer[bufpos] = '\0';
255 setvar("REPLY", buffer);
256 }
257
258 ret:
259 free(buffer);
260 if (read_flags & BUILTIN_READ_SILENT)
261 tcsetattr(fd, TCSANOW, &old_tty);
Denys Vlasenko80542ba2011-05-08 21:23:43 +0200262
263 errno = err;
Denys Vlasenko599ae1e2010-05-23 17:49:50 +0200264 return retval;
265}
266
267/* ulimit builtin */
268
269struct limits {
270 uint8_t cmd; /* RLIMIT_xxx fit into it */
271 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
272 char option;
273 const char *name;
274};
275
276static const struct limits limits_tbl[] = {
277#ifdef RLIMIT_FSIZE
278 { RLIMIT_FSIZE, 9, 'f', "file size (blocks)" },
279#endif
280#ifdef RLIMIT_CPU
281 { RLIMIT_CPU, 0, 't', "cpu time (seconds)" },
282#endif
283#ifdef RLIMIT_DATA
284 { RLIMIT_DATA, 10, 'd', "data seg size (kb)" },
285#endif
286#ifdef RLIMIT_STACK
287 { RLIMIT_STACK, 10, 's', "stack size (kb)" },
288#endif
289#ifdef RLIMIT_CORE
290 { RLIMIT_CORE, 9, 'c', "core file size (blocks)" },
291#endif
292#ifdef RLIMIT_RSS
293 { RLIMIT_RSS, 10, 'm', "resident set size (kb)" },
294#endif
295#ifdef RLIMIT_MEMLOCK
296 { RLIMIT_MEMLOCK, 10, 'l', "locked memory (kb)" },
297#endif
298#ifdef RLIMIT_NPROC
299 { RLIMIT_NPROC, 0, 'p', "processes" },
300#endif
301#ifdef RLIMIT_NOFILE
302 { RLIMIT_NOFILE, 0, 'n', "file descriptors" },
303#endif
304#ifdef RLIMIT_AS
305 { RLIMIT_AS, 10, 'v', "address space (kb)" },
306#endif
307#ifdef RLIMIT_LOCKS
308 { RLIMIT_LOCKS, 0, 'w', "locks" },
309#endif
Denys Vlasenkoe32d05b2011-04-04 02:12:14 +0200310#ifdef RLIMIT_NICE
311 { RLIMIT_NICE, 0, 'e', "scheduling priority" },
312#endif
313#ifdef RLIMIT_RTPRIO
314 { RLIMIT_RTPRIO, 0, 'r', "real-time priority" },
315#endif
Denys Vlasenko599ae1e2010-05-23 17:49:50 +0200316};
317
318enum {
319 OPT_hard = (1 << 0),
320 OPT_soft = (1 << 1),
321};
322
323/* "-": treat args as parameters of option with ASCII code 1 */
324static const char ulimit_opt_string[] = "-HSa"
325#ifdef RLIMIT_FSIZE
326 "f::"
327#endif
328#ifdef RLIMIT_CPU
329 "t::"
330#endif
331#ifdef RLIMIT_DATA
332 "d::"
333#endif
334#ifdef RLIMIT_STACK
335 "s::"
336#endif
337#ifdef RLIMIT_CORE
338 "c::"
339#endif
340#ifdef RLIMIT_RSS
341 "m::"
342#endif
343#ifdef RLIMIT_MEMLOCK
344 "l::"
345#endif
346#ifdef RLIMIT_NPROC
347 "p::"
348#endif
349#ifdef RLIMIT_NOFILE
350 "n::"
351#endif
352#ifdef RLIMIT_AS
353 "v::"
354#endif
355#ifdef RLIMIT_LOCKS
356 "w::"
357#endif
Denys Vlasenkoe32d05b2011-04-04 02:12:14 +0200358#ifdef RLIMIT_NICE
359 "e::"
360#endif
361#ifdef RLIMIT_RTPRIO
362 "r::"
363#endif
Denys Vlasenko599ae1e2010-05-23 17:49:50 +0200364 ;
365
366static void printlim(unsigned opts, const struct rlimit *limit,
367 const struct limits *l)
368{
369 rlim_t val;
370
371 val = limit->rlim_max;
372 if (!(opts & OPT_hard))
373 val = limit->rlim_cur;
374
375 if (val == RLIM_INFINITY)
376 printf("unlimited\n");
377 else {
378 val >>= l->factor_shift;
379 printf("%llu\n", (long long) val);
380 }
381}
382
383int FAST_FUNC
384shell_builtin_ulimit(char **argv)
385{
386 unsigned opts;
387 unsigned argc;
388
389 /* We can't use getopt32: need to handle commands like
390 * ulimit 123 -c2 -l 456
391 */
392
393 /* In case getopt was already called:
394 * reset the libc getopt() function, which keeps internal state.
395 */
396#ifdef __GLIBC__
397 optind = 0;
398#else /* BSD style */
399 optind = 1;
400 /* optreset = 1; */
401#endif
402 /* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */
403
Denys Vlasenkob7c9fb22011-02-03 00:05:48 +0100404 argc = 1;
405 while (argv[argc])
406 argc++;
Denys Vlasenko599ae1e2010-05-23 17:49:50 +0200407
408 opts = 0;
409 while (1) {
410 struct rlimit limit;
411 const struct limits *l;
412 int opt_char = getopt(argc, argv, ulimit_opt_string);
413
414 if (opt_char == -1)
415 break;
416 if (opt_char == 'H') {
417 opts |= OPT_hard;
418 continue;
419 }
420 if (opt_char == 'S') {
421 opts |= OPT_soft;
422 continue;
423 }
424
425 if (opt_char == 'a') {
426 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
427 getrlimit(l->cmd, &limit);
428 printf("-%c: %-30s ", l->option, l->name);
429 printlim(opts, &limit, l);
430 }
431 continue;
432 }
433
434 if (opt_char == 1)
435 opt_char = 'f';
436 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
437 if (opt_char == l->option) {
438 char *val_str;
439
440 getrlimit(l->cmd, &limit);
441
442 val_str = optarg;
443 if (!val_str && argv[optind] && argv[optind][0] != '-')
444 val_str = argv[optind++]; /* ++ skips NN in "-c NN" case */
445 if (val_str) {
446 rlim_t val;
447
448 if (strcmp(val_str, "unlimited") == 0)
449 val = RLIM_INFINITY;
450 else {
451 if (sizeof(val) == sizeof(int))
452 val = bb_strtou(val_str, NULL, 10);
453 else if (sizeof(val) == sizeof(long))
454 val = bb_strtoul(val_str, NULL, 10);
455 else
456 val = bb_strtoull(val_str, NULL, 10);
457 if (errno) {
Denys Vlasenkob32a5432010-08-29 13:29:02 +0200458 bb_error_msg("invalid number '%s'", val_str);
Denys Vlasenko599ae1e2010-05-23 17:49:50 +0200459 return EXIT_FAILURE;
460 }
461 val <<= l->factor_shift;
462 }
463//bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val);
Alexander Shishkin17e0e432010-07-27 08:40:55 +0200464 /* from man bash: "If neither -H nor -S
465 * is specified, both the soft and hard
466 * limits are set. */
467 if (!opts)
468 opts = OPT_hard + OPT_soft;
Denys Vlasenko599ae1e2010-05-23 17:49:50 +0200469 if (opts & OPT_hard)
470 limit.rlim_max = val;
Alexander Shishkin17e0e432010-07-27 08:40:55 +0200471 if (opts & OPT_soft)
Denys Vlasenko599ae1e2010-05-23 17:49:50 +0200472 limit.rlim_cur = val;
473//bb_error_msg("setrlimit(%d, %lld, %lld)", l->cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max);
474 if (setrlimit(l->cmd, &limit) < 0) {
475 bb_perror_msg("error setting limit");
476 return EXIT_FAILURE;
477 }
478 } else {
479 printlim(opts, &limit, l);
480 }
481 break;
482 }
483 } /* for (every possible opt) */
484
485 if (l == &limits_tbl[ARRAY_SIZE(limits_tbl)]) {
486 /* bad option. getopt already complained. */
487 break;
488 }
489
490 } /* while (there are options) */
491
492 return 0;
493}