source: branches/3.2/mindi-busybox/shell/shell_common.c @ 3232

Last change on this file since 3232 was 3232, checked in by Bruno Cornec, 7 years ago
  • Update mindi-busybox to 1.21.1
  • 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 mush 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[] = "-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        printf("unlimited\n");
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
497    } /* while (there are options) */
498
499    return 0;
500}
Note: See TracBrowser for help on using the repository browser.