source: MondoRescue/branches/3.3/mindi-busybox/shell/shell_common.c@ 3647

Last change on this file since 3647 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

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