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