* Copyright (c) 2010 Denys Vlasenko
* Split from ash.c
*
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
#include "libbb.h"
#include "shell_common.h"
+#include <sys/resource.h> /* getrlimit */
const char defifsvar[] ALIGN1 = "IFS= \t\n";
+const char defoptindvar[] ALIGN1 = "OPTIND=1";
int FAST_FUNC is_well_formed_var_name(const char *s, char terminator)
/* read builtin */
+/* Needs to be interruptible: shell must handle traps and shell-special signals
+ * while inside read. To implement this, be sure to not loop on EINTR
+ * and return errno == EINTR reliably.
+ */
//TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL"
//string. hush naturally has it, and ash has setvareq().
//Here we can simply store "VAR=" at buffer start and store read data directly
const char *opt_u
)
{
+ unsigned err;
unsigned end_ms; /* -t TIMEOUT */
int fd; /* -u FD */
int nchars; /* -n NUM */
int startword;
smallint backslash;
+ errno = err = 0;
+
pp = argv;
while (*pp) {
if (!is_well_formed_var_name(*pp, '\0')) {
old_tty = tty;
if (nchars) {
tty.c_lflag &= ~ICANON;
- tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
+ // Setting it to more than 1 breaks poll():
+ // it blocks even if there's data. !??
+ //tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
+ /* reads will block only if < 1 char is available */
+ tty.c_cc[VMIN] = 1;
+ /* no timeout (reads block forever) */
+ tty.c_cc[VTIME] = 0;
}
if (read_flags & BUILTIN_READ_SILENT) {
tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
bufpos = 0;
do {
char c;
+ struct pollfd pfd[1];
+ int timeout;
- if (end_ms) {
- int timeout;
- struct pollfd pfd[1];
+ if ((bufpos & 0xff) == 0)
+ buffer = xrealloc(buffer, bufpos + 0x101);
- pfd[0].fd = fd;
- pfd[0].events = POLLIN;
+ timeout = -1;
+ if (end_ms) {
timeout = end_ms - (unsigned)monotonic_ms();
- if (timeout <= 0 /* already late? */
- || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
- ) { /* timed out! */
+ if (timeout <= 0) { /* already late? */
retval = (const char *)(uintptr_t)1;
goto ret;
}
}
- if ((bufpos & 0xff) == 0)
- buffer = xrealloc(buffer, bufpos + 0x100);
- if (nonblock_safe_read(fd, &buffer[bufpos], 1) != 1) {
+ /* We must poll even if timeout is -1:
+ * we want to be interrupted if signal arrives,
+ * regardless of SA_RESTART-ness of that signal!
+ */
+ errno = 0;
+ pfd[0].fd = fd;
+ pfd[0].events = POLLIN;
+ if (poll(pfd, 1, timeout) != 1) {
+ /* timed out, or EINTR */
+ err = errno;
+ retval = (const char *)(uintptr_t)1;
+ goto ret;
+ }
+ if (read(fd, &buffer[bufpos], 1) != 1) {
+ err = errno;
retval = (const char *)(uintptr_t)1;
break;
}
+
c = buffer[bufpos];
if (c == '\0')
continue;
- if (backslash) {
- backslash = 0;
- if (c != '\n')
- goto put;
- continue;
- }
- if (!(read_flags & BUILTIN_READ_RAW) && c == '\\') {
- backslash = 1;
- continue;
+ if (!(read_flags & BUILTIN_READ_RAW)) {
+ if (backslash) {
+ backslash = 0;
+ if (c != '\n')
+ goto put;
+ continue;
+ }
+ if (c == '\\') {
+ backslash = 1;
+ continue;
+ }
}
if (c == '\n')
break;
free(buffer);
if (read_flags & BUILTIN_READ_SILENT)
tcsetattr(fd, TCSANOW, &old_tty);
+
+ errno = err;
return retval;
}
#ifdef RLIMIT_LOCKS
{ RLIMIT_LOCKS, 0, 'w', "locks" },
#endif
+#ifdef RLIMIT_NICE
+ { RLIMIT_NICE, 0, 'e', "scheduling priority" },
+#endif
+#ifdef RLIMIT_RTPRIO
+ { RLIMIT_RTPRIO, 0, 'r', "real-time priority" },
+#endif
};
enum {
};
/* "-": treat args as parameters of option with ASCII code 1 */
-static const char ulimit_opt_string[] = "-HSa"
+static const char ulimit_opt_string[] ALIGN1 = "-HSa"
#ifdef RLIMIT_FSIZE
"f::"
#endif
#endif
#ifdef RLIMIT_LOCKS
"w::"
+#endif
+#ifdef RLIMIT_NICE
+ "e::"
+#endif
+#ifdef RLIMIT_RTPRIO
+ "r::"
#endif
;
val = limit->rlim_cur;
if (val == RLIM_INFINITY)
- printf("unlimited\n");
+ puts("unlimited");
else {
val >>= l->factor_shift;
printf("%llu\n", (long long) val);
/* In case getopt was already called:
* reset the libc getopt() function, which keeps internal state.
*/
-#ifdef __GLIBC__
- optind = 0;
-#else /* BSD style */
- optind = 1;
- /* optreset = 1; */
-#endif
- /* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */
+ GETOPT_RESET();
- argc = 1;
- while (argv[argc])
- argc++;
+ argc = string_array_len(argv);
opts = 0;
while (1) {
else
val = bb_strtoull(val_str, NULL, 10);
if (errno) {
- bb_error_msg("bad number");
+ bb_error_msg("invalid number '%s'", val_str);
return EXIT_FAILURE;
}
val <<= l->factor_shift;
}
//bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val);
+ /* from man bash: "If neither -H nor -S
+ * is specified, both the soft and hard
+ * limits are set. */
+ if (!opts)
+ opts = OPT_hard + OPT_soft;
if (opts & OPT_hard)
limit.rlim_max = val;
- if ((opts & OPT_soft) || opts == 0)
+ if (opts & OPT_soft)
limit.rlim_cur = val;
//bb_error_msg("setrlimit(%d, %lld, %lld)", l->cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max);
if (setrlimit(l->cmd, &limit) < 0) {
/* bad option. getopt already complained. */
break;
}
-
} /* while (there are options) */
return 0;