ash: builtin: Mark more regular built-ins
[oweals/busybox.git] / shell / shell_common.c
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
22 const char defifsvar[] ALIGN1 = "IFS= \t\n";
23 const char defoptindvar[] ALIGN1 = "OPTIND=1";
24
25 /* read builtin */
26
27 /* Needs to be interruptible: shell must handle traps and shell-special signals
28  * while inside read. To implement this, be sure to not loop on EINTR
29  * and return errno == EINTR reliably.
30  */
31 //TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL"
32 //string. hush naturally has it, and ash has setvareq().
33 //Here we can simply store "VAR=" at buffer start and store read data directly
34 //after "=", then pass buffer to setvar() to consume.
35 const char* FAST_FUNC
36 shell_builtin_read(struct builtin_read_params *params)
37 {
38         struct pollfd pfd[1];
39 #define fd (pfd[0].fd) /* -u FD */
40         unsigned err;
41         unsigned end_ms; /* -t TIMEOUT */
42         int nchars; /* -n NUM */
43         char **pp;
44         char *buffer;
45         char delim;
46         struct termios tty, old_tty;
47         const char *retval;
48         int bufpos; /* need to be able to hold -1 */
49         int startword;
50         smallint backslash;
51         char **argv;
52         const char *ifs;
53         int read_flags;
54
55         errno = err = 0;
56
57         argv = params->argv;
58         pp = argv;
59         while (*pp) {
60                 if (endofname(*pp)[0] != '\0') {
61                         /* Mimic bash message */
62                         bb_error_msg("read: '%s': not a valid identifier", *pp);
63                         return (const char *)(uintptr_t)1;
64                 }
65                 pp++;
66         }
67
68         nchars = 0; /* if != 0, -n is in effect */
69         if (params->opt_n) {
70                 nchars = bb_strtou(params->opt_n, NULL, 10);
71                 if (nchars < 0 || errno)
72                         return "invalid count";
73                 /* note: "-n 0": off (bash 3.2 does this too) */
74         }
75
76         end_ms = 0;
77         if (params->opt_t && !ENABLE_FEATURE_SH_READ_FRAC) {
78                 end_ms = bb_strtou(params->opt_t, NULL, 10);
79                 if (errno)
80                         return "invalid timeout";
81                 if (end_ms > UINT_MAX / 2048) /* be safely away from overflow */
82                         end_ms = UINT_MAX / 2048;
83                 end_ms *= 1000;
84         }
85         if (params->opt_t && ENABLE_FEATURE_SH_READ_FRAC) {
86                 /* bash 4.3 (maybe earlier) supports -t N.NNNNNN */
87                 char *p;
88                 /* Eat up to three fractional digits */
89                 int frac_digits = 3 + 1;
90
91                 end_ms = bb_strtou(params->opt_t, &p, 10);
92                 if (end_ms > UINT_MAX / 2048) /* be safely away from overflow */
93                         end_ms = UINT_MAX / 2048;
94
95                 if (errno) {
96                         /* EINVAL = number is ok, but not NUL terminated */
97                         if (errno != EINVAL || *p != '.')
98                                 return "invalid timeout";
99                         /* Do not check the rest: bash allows "0.123456xyz" */
100                         while (*++p && --frac_digits) {
101                                 end_ms *= 10;
102                                 end_ms += (*p - '0');
103                                 if ((unsigned char)(*p - '0') > 9)
104                                         return "invalid timeout";
105                         }
106                 }
107                 while (--frac_digits > 0) {
108                         end_ms *= 10;
109                 }
110         }
111
112         fd = STDIN_FILENO;
113         if (params->opt_u) {
114                 fd = bb_strtou(params->opt_u, NULL, 10);
115                 if (fd < 0 || errno)
116                         return "invalid file descriptor";
117         }
118
119         if (params->opt_t && end_ms == 0) {
120                 /* "If timeout is 0, read returns immediately, without trying
121                  * to read any data. The exit status is 0 if input is available
122                  * on the specified file descriptor, non-zero otherwise."
123                  * bash seems to ignore -p PROMPT for this use case.
124                  */
125                 int r;
126                 pfd[0].events = POLLIN;
127                 r = poll(pfd, 1, /*timeout:*/ 0);
128                 /* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */
129                 return (const char *)(uintptr_t)(r <= 0);
130         }
131
132         if (params->opt_p && isatty(fd)) {
133                 fputs(params->opt_p, stderr);
134                 fflush_all();
135         }
136
137         ifs = params->ifs;
138         if (ifs == NULL)
139                 ifs = defifs;
140
141         read_flags = params->read_flags;
142         if (nchars || (read_flags & BUILTIN_READ_SILENT)) {
143                 tcgetattr(fd, &tty);
144                 old_tty = tty;
145                 if (nchars) {
146                         tty.c_lflag &= ~ICANON;
147                         // Setting it to more than 1 breaks poll():
148                         // it blocks even if there's data. !??
149                         //tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
150                         /* reads will block only if < 1 char is available */
151                         tty.c_cc[VMIN] = 1;
152                         /* no timeout (reads block forever) */
153                         tty.c_cc[VTIME] = 0;
154                 }
155                 if (read_flags & BUILTIN_READ_SILENT) {
156                         tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
157                 }
158                 /* This forces execution of "restoring" tcgetattr later */
159                 read_flags |= BUILTIN_READ_SILENT;
160                 /* if tcgetattr failed, tcsetattr will fail too.
161                  * Ignoring, it's harmless. */
162                 tcsetattr(fd, TCSANOW, &tty);
163         }
164
165         retval = (const char *)(uintptr_t)0;
166         startword = 1;
167         backslash = 0;
168         if (params->opt_t)
169                 end_ms += (unsigned)monotonic_ms();
170         buffer = NULL;
171         bufpos = 0;
172         delim = params->opt_d ? params->opt_d[0] : '\n';
173         do {
174                 char c;
175                 int timeout;
176
177                 if ((bufpos & 0xff) == 0)
178                         buffer = xrealloc(buffer, bufpos + 0x101);
179
180                 timeout = -1;
181                 if (params->opt_t) {
182                         timeout = end_ms - (unsigned)monotonic_ms();
183                         /* ^^^^^^^^^^^^^ all values are unsigned,
184                          * wrapping math is used here, good even if
185                          * 32-bit unix time wrapped (year 2038+).
186                          */
187                         if (timeout <= 0) { /* already late? */
188                                 retval = (const char *)(uintptr_t)1;
189                                 goto ret;
190                         }
191                 }
192
193                 /* We must poll even if timeout is -1:
194                  * we want to be interrupted if signal arrives,
195                  * regardless of SA_RESTART-ness of that signal!
196                  */
197                 errno = 0;
198                 pfd[0].events = POLLIN;
199                 if (poll(pfd, 1, timeout) <= 0) {
200                         /* timed out, or EINTR */
201                         err = errno;
202                         retval = (const char *)(uintptr_t)1;
203                         goto ret;
204                 }
205                 if (read(fd, &buffer[bufpos], 1) != 1) {
206                         err = errno;
207                         retval = (const char *)(uintptr_t)1;
208                         break;
209                 }
210
211                 c = buffer[bufpos];
212                 if (c == '\0')
213                         continue;
214                 if (!(read_flags & BUILTIN_READ_RAW)) {
215                         if (backslash) {
216                                 backslash = 0;
217                                 if (c != '\n')
218                                         goto put;
219                                 continue;
220                         }
221                         if (c == '\\') {
222                                 backslash = 1;
223                                 continue;
224                         }
225                 }
226                 if (c == delim) /* '\n' or -d CHAR */
227                         break;
228
229                 /* $IFS splitting. NOT done if we run "read"
230                  * without variable names (bash compat).
231                  * Thus, "read" and "read REPLY" are not the same.
232                  */
233                 if (!params->opt_d && argv[0]) {
234 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */
235                         const char *is_ifs = strchr(ifs, c);
236                         if (startword && is_ifs) {
237                                 if (isspace(c))
238                                         continue;
239                                 /* it is a non-space ifs char */
240                                 startword--;
241                                 if (startword == 1) /* first one? */
242                                         continue; /* yes, it is not next word yet */
243                         }
244                         startword = 0;
245                         if (argv[1] != NULL && is_ifs) {
246                                 buffer[bufpos] = '\0';
247                                 bufpos = 0;
248                                 params->setvar(*argv, buffer);
249                                 argv++;
250                                 /* can we skip one non-space ifs char? (2: yes) */
251                                 startword = isspace(c) ? 2 : 1;
252                                 continue;
253                         }
254                 }
255  put:
256                 bufpos++;
257         } while (--nchars);
258
259         if (argv[0]) {
260                 /* Remove trailing space $IFS chars */
261                 while (--bufpos >= 0
262                  && isspace(buffer[bufpos])
263                  && strchr(ifs, buffer[bufpos]) != NULL
264                 ) {
265                         continue;
266                 }
267                 buffer[bufpos + 1] = '\0';
268
269                 /* Last variable takes the entire remainder with delimiters
270                  * (sans trailing whitespace $IFS),
271                  * but ***only "if there are fewer vars than fields"(c)***!
272                  * The "X:Y:" case below: there are two fields,
273                  * and therefore last delimiter (:) is eaten:
274                  * IFS=": "
275                  * echo "X:Y:Z:"  | (read x y; echo "|$x|$y|") # |X|Y:Z:|
276                  * echo "X:Y:Z"   | (read x y; echo "|$x|$y|") # |X|Y:Z|
277                  * echo "X:Y:"    | (read x y; echo "|$x|$y|") # |X|Y|, not |X|Y:|
278                  * echo "X:Y  : " | (read x y; echo "|$x|$y|") # |X|Y|
279                  */
280                 if (bufpos >= 0
281                  && strchr(ifs, buffer[bufpos]) != NULL
282                 ) {
283                         /* There _is_ a non-whitespace IFS char */
284                         /* Skip whitespace IFS char before it */
285                         while (--bufpos >= 0
286                          && isspace(buffer[bufpos])
287                          && strchr(ifs, buffer[bufpos]) != NULL
288                         ) {
289                                 continue;
290                         }
291                         /* Are there $IFS chars? */
292                         if (strcspn(buffer, ifs) >= ++bufpos) {
293                                 /* No: last var takes one field, not more */
294                                 /* So, drop trailing IFS delims */
295                                 buffer[bufpos] = '\0';
296                         }
297                 }
298
299                 /* Use the remainder as a value for the next variable */
300                 params->setvar(*argv, buffer);
301                 /* Set the rest to "" */
302                 while (*++argv)
303                         params->setvar(*argv, "");
304         } else {
305                 /* Note: no $IFS removal */
306                 buffer[bufpos] = '\0';
307                 params->setvar("REPLY", buffer);
308         }
309
310  ret:
311         free(buffer);
312         if (read_flags & BUILTIN_READ_SILENT)
313                 tcsetattr(fd, TCSANOW, &old_tty);
314
315         errno = err;
316         return retval;
317 #undef fd
318 }
319
320 /* ulimit builtin */
321
322 struct limits {
323         uint8_t cmd;            /* RLIMIT_xxx fit into it */
324         uint8_t factor_shift;   /* shift by to get rlim_{cur,max} values */
325 };
326
327 static const struct limits limits_tbl[] = {
328         { RLIMIT_CORE,          9,      }, // -c
329         { RLIMIT_DATA,          10,     }, // -d
330         { RLIMIT_NICE,          0,      }, // -e
331         { RLIMIT_FSIZE,         9,      }, // -f
332 #define LIMIT_F_IDX     3
333 #ifdef RLIMIT_SIGPENDING
334         { RLIMIT_SIGPENDING,    0,      }, // -i
335 #endif
336 #ifdef RLIMIT_MEMLOCK
337         { RLIMIT_MEMLOCK,       10,     }, // -l
338 #endif
339 #ifdef RLIMIT_RSS
340         { RLIMIT_RSS,           10,     }, // -m
341 #endif
342 #ifdef RLIMIT_NOFILE
343         { RLIMIT_NOFILE,        0,      }, // -n
344 #endif
345 #ifdef RLIMIT_MSGQUEUE
346         { RLIMIT_MSGQUEUE,      0,      }, // -q
347 #endif
348 #ifdef RLIMIT_RTPRIO
349         { RLIMIT_RTPRIO,        0,      }, // -r
350 #endif
351 #ifdef RLIMIT_STACK
352         { RLIMIT_STACK,         10,     }, // -s
353 #endif
354 #ifdef RLIMIT_CPU
355         { RLIMIT_CPU,           0,      }, // -t
356 #endif
357 #ifdef RLIMIT_NPROC
358         { RLIMIT_NPROC,         0,      }, // -u
359 #endif
360 #ifdef RLIMIT_AS
361         { RLIMIT_AS,            10,     }, // -v
362 #endif
363 #ifdef RLIMIT_LOCKS
364         { RLIMIT_LOCKS,         0,      }, // -x
365 #endif
366 };
367 // bash also shows:
368 //pipe size            (512 bytes, -p) 8
369
370 static const char limits_help[] ALIGN1 =
371         "core file size (blocks)"          // -c
372         "\0""data seg size (kb)"           // -d
373         "\0""scheduling priority"          // -e
374         "\0""file size (blocks)"           // -f
375 #ifdef RLIMIT_SIGPENDING
376         "\0""pending signals"              // -i
377 #endif
378 #ifdef RLIMIT_MEMLOCK
379         "\0""max locked memory (kb)"       // -l
380 #endif
381 #ifdef RLIMIT_RSS
382         "\0""max memory size (kb)"         // -m
383 #endif
384 #ifdef RLIMIT_NOFILE
385         "\0""open files"                   // -n
386 #endif
387 #ifdef RLIMIT_MSGQUEUE
388         "\0""POSIX message queues (bytes)" // -q
389 #endif
390 #ifdef RLIMIT_RTPRIO
391         "\0""real-time priority"           // -r
392 #endif
393 #ifdef RLIMIT_STACK
394         "\0""stack size (kb)"              // -s
395 #endif
396 #ifdef RLIMIT_CPU
397         "\0""cpu time (seconds)"           // -t
398 #endif
399 #ifdef RLIMIT_NPROC
400         "\0""max user processes"           // -u
401 #endif
402 #ifdef RLIMIT_AS
403         "\0""virtual memory (kb)"          // -v
404 #endif
405 #ifdef RLIMIT_LOCKS
406         "\0""file locks"                   // -x
407 #endif
408 ;
409
410 static const char limit_chars[] ALIGN1 =
411                         "c"
412                         "d"
413                         "e"
414                         "f"
415 #ifdef RLIMIT_SIGPENDING
416                         "i"
417 #endif
418 #ifdef RLIMIT_MEMLOCK
419                         "l"
420 #endif
421 #ifdef RLIMIT_RSS
422                         "m"
423 #endif
424 #ifdef RLIMIT_NOFILE
425                         "n"
426 #endif
427 #ifdef RLIMIT_MSGQUEUE
428                         "q"
429 #endif
430 #ifdef RLIMIT_RTPRIO
431                         "r"
432 #endif
433 #ifdef RLIMIT_STACK
434                         "s"
435 #endif
436 #ifdef RLIMIT_CPU
437                         "t"
438 #endif
439 #ifdef RLIMIT_NPROC
440                         "u"
441 #endif
442 #ifdef RLIMIT_AS
443                         "v"
444 #endif
445 #ifdef RLIMIT_LOCKS
446                         "x"
447 #endif
448 ;
449
450 /* "-": treat args as parameters of option with ASCII code 1 */
451 static const char ulimit_opt_string[] ALIGN1 = "-HSa"
452                         "c::"
453                         "d::"
454                         "e::"
455                         "f::"
456 #ifdef RLIMIT_SIGPENDING
457                         "i::"
458 #endif
459 #ifdef RLIMIT_MEMLOCK
460                         "l::"
461 #endif
462 #ifdef RLIMIT_RSS
463                         "m::"
464 #endif
465 #ifdef RLIMIT_NOFILE
466                         "n::"
467 #endif
468 #ifdef RLIMIT_MSGQUEUE
469                         "q::"
470 #endif
471 #ifdef RLIMIT_RTPRIO
472                         "r::"
473 #endif
474 #ifdef RLIMIT_STACK
475                         "s::"
476 #endif
477 #ifdef RLIMIT_CPU
478                         "t::"
479 #endif
480 #ifdef RLIMIT_NPROC
481                         "u::"
482 #endif
483 #ifdef RLIMIT_AS
484                         "v::"
485 #endif
486 #ifdef RLIMIT_LOCKS
487                         "x::"
488 #endif
489 ;
490
491 enum {
492         OPT_hard = (1 << 0),
493         OPT_soft = (1 << 1),
494         OPT_all  = (1 << 2),
495 };
496
497 static void printlim(unsigned opts, const struct rlimit *limit,
498                         const struct limits *l)
499 {
500         rlim_t val;
501
502         val = limit->rlim_max;
503         if (opts & OPT_soft)
504                 val = limit->rlim_cur;
505
506         if (val == RLIM_INFINITY)
507                 puts("unlimited");
508         else {
509                 val >>= l->factor_shift;
510                 printf("%llu\n", (long long) val);
511         }
512 }
513
514 int FAST_FUNC
515 shell_builtin_ulimit(char **argv)
516 {
517         struct rlimit limit;
518         unsigned opt_cnt;
519         unsigned opts;
520         unsigned argc;
521         unsigned i;
522
523         /* We can't use getopt32: need to handle commands like
524          * ulimit 123 -c2 -l 456
525          */
526
527         /* In case getopt() was already called:
528          * reset libc getopt() internal state.
529          */
530         GETOPT_RESET();
531
532 // bash 4.4.23:
533 //
534 // -H and/or -S change meaning even of options *before* them: ulimit -f 2000 -H
535 // sets hard limit, ulimit -a -H prints hard limits.
536 //
537 // -a is equivalent for requesting all limits to be shown.
538 //
539 // If -a is specified, attempts to set limits are ignored:
540 //  ulimit -m 1000; ulimit -m 2000 -a
541 // shows 1000, not 2000. HOWEVER, *implicit* -f form "ulimit 2000 -a"
542 // DOES set -f limit [we don't implement this quirk], "ulimit -a 2000" does not.
543 // Options are still parsed: ulimit -az complains about unknown -z opt.
544 //
545 // -a is not cumulative: "ulimit -a -a" = "ulimit -a -f -m" = "ulimit -a"
546 //
547 // -HSa can be combined in one argument and with one other option (example: -Sm),
548 // but other options can't: limit value is an optional argument,
549 // thus "-mf" means "-m f", f is the parameter of -m.
550 //
551 // Limit can be set and then printed: ulimit -m 2000 -m
552 // If set more than once, they are set and printed in order:
553 // try ulimit -m -m 1000 -m -m 2000 -m -m 3000 -m
554 //
555 // Limits are shown in the order of options given:
556 // ulimit -m -f is not the same as ulimit -f -m.
557 //
558 // If both -S and -H are given, show soft limit.
559 //
560 // Short printout (limit value only) is printed only if just one option
561 // is given: ulimit -m. ulimit -f -m prints verbose lines.
562 // ulimit -f -f prints same verbose line twice.
563 // ulimit -m 10000 -f prints verbose line for -f.
564
565         argc = string_array_len(argv);
566
567         /* First pass over options: detect -H/-S/-a status,
568          * and "bare ulimit" and "only one option" cases
569          * by counting other opts.
570          */
571         opt_cnt = 0;
572         opts = 0;
573         while (1) {
574                 int opt_char = getopt(argc, argv, ulimit_opt_string);
575
576                 if (opt_char == -1)
577                         break;
578                 if (opt_char == 'H') {
579                         opts |= OPT_hard;
580                         continue;
581                 }
582                 if (opt_char == 'S') {
583                         opts |= OPT_soft;
584                         continue;
585                 }
586                 if (opt_char == 'a') {
587                         opts |= OPT_all;
588                         continue;
589                 }
590                 if (opt_char == '?') {
591                         /* bad option. getopt already complained. */
592                         return EXIT_FAILURE;
593                 }
594                 opt_cnt++;
595         } /* while (there are options) */
596
597         if (!(opts & (OPT_hard | OPT_soft)))
598                 opts |= (OPT_hard | OPT_soft);
599         if (opts & OPT_all) {
600                 const char *help = limits_help;
601                 for (i = 0; i < ARRAY_SIZE(limits_tbl); i++) {
602                         getrlimit(limits_tbl[i].cmd, &limit);
603                         printf("%-32s(-%c) ", help, limit_chars[i]);
604                         printlim(opts, &limit, &limits_tbl[i]);
605                         help += strlen(help) + 1;
606                 }
607                 return EXIT_SUCCESS;
608         }
609
610         /* Second pass: set or print limits, in order */
611         GETOPT_RESET();
612         while (1) {
613                 char *val_str;
614                 int opt_char = getopt(argc, argv, ulimit_opt_string);
615
616                 if (opt_char == -1)
617                         break;
618                 if (opt_char == 'H')
619                         continue;
620                 if (opt_char == 'S')
621                         continue;
622                 //if (opt_char == 'a') - impossible
623
624                 if (opt_char == 1) /* if "ulimit NNN", -f is assumed */
625                         opt_char = 'f';
626                 i = strchrnul(limit_chars, opt_char) - limit_chars;
627                 //if (i >= ARRAY_SIZE(limits_tbl)) - bad option, impossible
628
629                 val_str = optarg;
630                 if (!val_str && argv[optind] && argv[optind][0] != '-')
631                         val_str = argv[optind++]; /* ++ skips NN in "-c NN" case */
632
633                 getrlimit(limits_tbl[i].cmd, &limit);
634                 if (!val_str) {
635                         if (opt_cnt > 1)
636                                 printf("%-32s(-%c) ", nth_string(limits_help, i), limit_chars[i]);
637                         printlim(opts, &limit, &limits_tbl[i]);
638                 } else {
639                         rlim_t val = RLIM_INFINITY;
640                         if (strcmp(val_str, "unlimited") != 0) {
641                                 if (sizeof(val) == sizeof(int))
642                                         val = bb_strtou(val_str, NULL, 10);
643                                 else if (sizeof(val) == sizeof(long))
644                                         val = bb_strtoul(val_str, NULL, 10);
645                                 else
646                                         val = bb_strtoull(val_str, NULL, 10);
647                                 if (errno) {
648                                         bb_error_msg("invalid number '%s'", val_str);
649                                         return EXIT_FAILURE;
650                                 }
651                                 val <<= limits_tbl[i].factor_shift;
652                         }
653 //bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val);
654                         /* from man bash: "If neither -H nor -S
655                          * is specified, both the soft and hard
656                          * limits are set. */
657                         if (opts & OPT_hard)
658                                 limit.rlim_max = val;
659                         if (opts & OPT_soft)
660                                 limit.rlim_cur = val;
661 //bb_error_msg("setrlimit(%d, %lld, %lld)", limits_tbl[i].cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max);
662                         if (setrlimit(limits_tbl[i].cmd, &limit) < 0) {
663                                 bb_simple_perror_msg("error setting limit");
664                                 return EXIT_FAILURE;
665                         }
666                 }
667         } /* while (there are options) */
668
669         if (opt_cnt == 0) {
670                 /* "bare ulimit": treat it as if it was -f */
671                 getrlimit(limits_tbl[LIMIT_F_IDX].cmd, &limit);
672                 printlim(opts, &limit, &limits_tbl[LIMIT_F_IDX]);
673         }
674
675         return EXIT_SUCCESS;
676 }