diff options
author | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2010-09-14 12:19:25 +1000 |
---|---|---|
committer | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2010-09-14 12:19:25 +1000 |
commit | 29ab9e37d0dc05dc6e302437106800bf62748b05 (patch) | |
tree | 9a790b0af6db8e474c83f15748d770cc320eb449 /shell/shell_common.c | |
parent | 38a1e6da34ffe9f0c6cdb8661b263eb80d72981f (diff) | |
parent | 599ae1eb9f20b4731735e14f9bac6371ad89b6d7 (diff) | |
download | busybox-w32-29ab9e37d0dc05dc6e302437106800bf62748b05.tar.gz busybox-w32-29ab9e37d0dc05dc6e302437106800bf62748b05.tar.bz2 busybox-w32-29ab9e37d0dc05dc6e302437106800bf62748b05.zip |
Merge branch 'origin/master' (early part)
Conflicts:
shell/builtin_ulimit.c
Diffstat (limited to 'shell/shell_common.c')
-rw-r--r-- | shell/shell_common.c | 428 |
1 files changed, 428 insertions, 0 deletions
diff --git a/shell/shell_common.c b/shell/shell_common.c index 669a18dfd..840e03bff 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c | |||
@@ -33,3 +33,431 @@ int FAST_FUNC is_well_formed_var_name(const char *s, char terminator) | |||
33 | 33 | ||
34 | return *s == terminator; | 34 | return *s == terminator; |
35 | } | 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 | #if ENABLE_PLATFORM_MINGW32 | ||
334 | int FAST_FUNC | ||
335 | shell_builtin_ulimit(char **argv) | ||
336 | { | ||
337 | return 1; | ||
338 | } | ||
339 | #else | ||
340 | static void printlim(unsigned opts, const struct rlimit *limit, | ||
341 | const struct limits *l) | ||
342 | { | ||
343 | rlim_t val; | ||
344 | |||
345 | val = limit->rlim_max; | ||
346 | if (!(opts & OPT_hard)) | ||
347 | val = limit->rlim_cur; | ||
348 | |||
349 | if (val == RLIM_INFINITY) | ||
350 | printf("unlimited\n"); | ||
351 | else { | ||
352 | val >>= l->factor_shift; | ||
353 | printf("%llu\n", (long long) val); | ||
354 | } | ||
355 | } | ||
356 | |||
357 | int FAST_FUNC | ||
358 | shell_builtin_ulimit(char **argv) | ||
359 | { | ||
360 | unsigned opts; | ||
361 | unsigned argc; | ||
362 | |||
363 | /* We can't use getopt32: need to handle commands like | ||
364 | * ulimit 123 -c2 -l 456 | ||
365 | */ | ||
366 | |||
367 | /* In case getopt was already called: | ||
368 | * reset the libc getopt() function, which keeps internal state. | ||
369 | */ | ||
370 | #ifdef __GLIBC__ | ||
371 | optind = 0; | ||
372 | #else /* BSD style */ | ||
373 | optind = 1; | ||
374 | /* optreset = 1; */ | ||
375 | #endif | ||
376 | /* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */ | ||
377 | |||
378 | argc = 1; | ||
379 | while (argv[argc]) | ||
380 | argc++; | ||
381 | |||
382 | opts = 0; | ||
383 | while (1) { | ||
384 | struct rlimit limit; | ||
385 | const struct limits *l; | ||
386 | int opt_char = getopt(argc, argv, ulimit_opt_string); | ||
387 | |||
388 | if (opt_char == -1) | ||
389 | break; | ||
390 | if (opt_char == 'H') { | ||
391 | opts |= OPT_hard; | ||
392 | continue; | ||
393 | } | ||
394 | if (opt_char == 'S') { | ||
395 | opts |= OPT_soft; | ||
396 | continue; | ||
397 | } | ||
398 | |||
399 | if (opt_char == 'a') { | ||
400 | for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) { | ||
401 | getrlimit(l->cmd, &limit); | ||
402 | printf("-%c: %-30s ", l->option, l->name); | ||
403 | printlim(opts, &limit, l); | ||
404 | } | ||
405 | continue; | ||
406 | } | ||
407 | |||
408 | if (opt_char == 1) | ||
409 | opt_char = 'f'; | ||
410 | for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) { | ||
411 | if (opt_char == l->option) { | ||
412 | char *val_str; | ||
413 | |||
414 | getrlimit(l->cmd, &limit); | ||
415 | |||
416 | val_str = optarg; | ||
417 | if (!val_str && argv[optind] && argv[optind][0] != '-') | ||
418 | val_str = argv[optind++]; /* ++ skips NN in "-c NN" case */ | ||
419 | if (val_str) { | ||
420 | rlim_t val; | ||
421 | |||
422 | if (strcmp(val_str, "unlimited") == 0) | ||
423 | val = RLIM_INFINITY; | ||
424 | else { | ||
425 | if (sizeof(val) == sizeof(int)) | ||
426 | val = bb_strtou(val_str, NULL, 10); | ||
427 | else if (sizeof(val) == sizeof(long)) | ||
428 | val = bb_strtoul(val_str, NULL, 10); | ||
429 | else | ||
430 | val = bb_strtoull(val_str, NULL, 10); | ||
431 | if (errno) { | ||
432 | bb_error_msg("bad number"); | ||
433 | return EXIT_FAILURE; | ||
434 | } | ||
435 | val <<= l->factor_shift; | ||
436 | } | ||
437 | //bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val); | ||
438 | if (opts & OPT_hard) | ||
439 | limit.rlim_max = val; | ||
440 | if ((opts & OPT_soft) || opts == 0) | ||
441 | limit.rlim_cur = val; | ||
442 | //bb_error_msg("setrlimit(%d, %lld, %lld)", l->cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max); | ||
443 | if (setrlimit(l->cmd, &limit) < 0) { | ||
444 | bb_perror_msg("error setting limit"); | ||
445 | return EXIT_FAILURE; | ||
446 | } | ||
447 | } else { | ||
448 | printlim(opts, &limit, l); | ||
449 | } | ||
450 | break; | ||
451 | } | ||
452 | } /* for (every possible opt) */ | ||
453 | |||
454 | if (l == &limits_tbl[ARRAY_SIZE(limits_tbl)]) { | ||
455 | /* bad option. getopt already complained. */ | ||
456 | break; | ||
457 | } | ||
458 | |||
459 | } /* while (there are options) */ | ||
460 | |||
461 | return 0; | ||
462 | } | ||
463 | #endif | ||