1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#include "libbb.h"
20#include "shell_common.h"
21
22const char defifsvar[] ALIGN1 = "IFS= \t\n";
23
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}
36
37
38
39
40
41
42
43
44
45
46
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{
58 unsigned err;
59 unsigned end_ms;
60 int fd;
61 int nchars;
62 char **pp;
63 char *buffer;
64 struct termios tty, old_tty;
65 const char *retval;
66 int bufpos;
67 int startword;
68 smallint backslash;
69
70 errno = err = 0;
71
72 pp = argv;
73 while (*pp) {
74 if (!is_well_formed_var_name(*pp, '\0')) {
75
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;
83 if (opt_n) {
84 nchars = bb_strtou(opt_n, NULL, 10);
85 if (nchars < 0 || errno)
86 return "invalid count";
87
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
96 ts.tv_sec = bb_strtou(opt_t, &p, 10);
97 ts.tv_usec = 0;
98
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
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)) {
117 return "invalid timeout";
118 }
119#endif
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
147 read_flags |= BUILTIN_READ_SILENT;
148
149
150 tcsetattr(fd, TCSANOW, &tty);
151 }
152
153 retval = (const char *)(uintptr_t)0;
154 startword = 1;
155 backslash = 0;
156 if (end_ms)
157 end_ms = ((unsigned)monotonic_ms() + end_ms) | 1;
158 buffer = NULL;
159 bufpos = 0;
160 do {
161 char c;
162 struct pollfd pfd[1];
163 int timeout;
164
165 if ((bufpos & 0xff) == 0)
166 buffer = xrealloc(buffer, bufpos + 0x100);
167
168 timeout = -1;
169 if (end_ms) {
170 timeout = end_ms - (unsigned)monotonic_ms();
171 if (timeout <= 0) {
172 retval = (const char *)(uintptr_t)1;
173 goto ret;
174 }
175 }
176
177
178
179
180
181 errno = 0;
182 pfd[0].fd = fd;
183 pfd[0].events = POLLIN;
184 if (poll(pfd, 1, timeout) != 1) {
185
186 err = errno;
187 retval = (const char *)(uintptr_t)1;
188 goto ret;
189 }
190 if (read(fd, &buffer[bufpos], 1) != 1) {
191 err = errno;
192 retval = (const char *)(uintptr_t)1;
193 break;
194 }
195
196 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
213
214
215
216 if (argv[0]) {
217
218 const char *is_ifs = strchr(ifs, c);
219 if (startword && is_ifs) {
220 if (isspace(c))
221 continue;
222
223 startword--;
224 if (startword == 1)
225 continue;
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
234 startword = isspace(c) ? 2 : 1;
235 continue;
236 }
237 }
238 put:
239 bufpos++;
240 } while (--nchars);
241
242 if (argv[0]) {
243
244 while (--bufpos >= 0 && isspace(buffer[bufpos]) && strchr(ifs, buffer[bufpos]) != NULL)
245 continue;
246 buffer[bufpos + 1] = '\0';
247
248 setvar(*argv, buffer);
249
250 while (*++argv)
251 setvar(*argv, "");
252 } else {
253
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);
262
263 errno = err;
264 return retval;
265}
266
267
268
269struct limits {
270 uint8_t cmd;
271 uint8_t factor_shift;
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
310#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
316};
317
318enum {
319 OPT_hard = (1 << 0),
320 OPT_soft = (1 << 1),
321};
322
323
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
358#ifdef RLIMIT_NICE
359 "e::"
360#endif
361#ifdef RLIMIT_RTPRIO
362 "r::"
363#endif
364 ;
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
390
391
392
393
394
395
396#ifdef __GLIBC__
397 optind = 0;
398#else
399 optind = 1;
400
401#endif
402
403
404 argc = 1;
405 while (argv[argc])
406 argc++;
407
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++];
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) {
458 bb_error_msg("invalid number '%s'", val_str);
459 return EXIT_FAILURE;
460 }
461 val <<= l->factor_shift;
462 }
463
464
465
466
467 if (!opts)
468 opts = OPT_hard + OPT_soft;
469 if (opts & OPT_hard)
470 limit.rlim_max = val;
471 if (opts & OPT_soft)
472 limit.rlim_cur = val;
473
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 }
484
485 if (l == &limits_tbl[ARRAY_SIZE(limits_tbl)]) {
486
487 break;
488 }
489
490 }
491
492 return 0;
493}
494