diff options
Diffstat (limited to 'shell/hush.c')
-rw-r--r-- | shell/hush.c | 149 |
1 files changed, 96 insertions, 53 deletions
diff --git a/shell/hush.c b/shell/hush.c index 6e64efb70..06fe0e405 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -1501,12 +1501,15 @@ static void free_strings(char **strings) | |||
1501 | free(strings); | 1501 | free(strings); |
1502 | } | 1502 | } |
1503 | 1503 | ||
1504 | static int fcntl_F_DUPFD(int fd, int avoid_fd) | 1504 | static int dup_CLOEXEC(int fd, int avoid_fd) |
1505 | { | 1505 | { |
1506 | int newfd; | 1506 | int newfd; |
1507 | repeat: | 1507 | repeat: |
1508 | newfd = fcntl(fd, F_DUPFD, avoid_fd + 1); | 1508 | newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1); |
1509 | if (newfd < 0) { | 1509 | if (newfd >= 0) { |
1510 | if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */ | ||
1511 | fcntl(newfd, F_SETFD, FD_CLOEXEC); | ||
1512 | } else { /* newfd < 0 */ | ||
1510 | if (errno == EBUSY) | 1513 | if (errno == EBUSY) |
1511 | goto repeat; | 1514 | goto repeat; |
1512 | if (errno == EINTR) | 1515 | if (errno == EINTR) |
@@ -2691,6 +2694,42 @@ static int i_peek2(struct in_str *i) | |||
2691 | return ch; | 2694 | return ch; |
2692 | } | 2695 | } |
2693 | 2696 | ||
2697 | static int i_getch_and_eat_bkslash_nl(struct in_str *input) | ||
2698 | { | ||
2699 | for (;;) { | ||
2700 | int ch, ch2; | ||
2701 | |||
2702 | ch = i_getch(input); | ||
2703 | if (ch != '\\') | ||
2704 | return ch; | ||
2705 | ch2 = i_peek(input); | ||
2706 | if (ch2 != '\n') | ||
2707 | return ch; | ||
2708 | /* backslash+newline, skip it */ | ||
2709 | i_getch(input); | ||
2710 | } | ||
2711 | } | ||
2712 | |||
2713 | /* Note: this function _eats_ \<newline> pairs, safe to use plain | ||
2714 | * i_getch() after it instead of i_getch_and_eat_bkslash_nl(). | ||
2715 | */ | ||
2716 | static int i_peek_and_eat_bkslash_nl(struct in_str *input) | ||
2717 | { | ||
2718 | for (;;) { | ||
2719 | int ch, ch2; | ||
2720 | |||
2721 | ch = i_peek(input); | ||
2722 | if (ch != '\\') | ||
2723 | return ch; | ||
2724 | ch2 = i_peek2(input); | ||
2725 | if (ch2 != '\n') | ||
2726 | return ch; | ||
2727 | /* backslash+newline, skip it */ | ||
2728 | i_getch(input); | ||
2729 | i_getch(input); | ||
2730 | } | ||
2731 | } | ||
2732 | |||
2694 | static void setup_file_in_str(struct in_str *i, FILE *f) | 2733 | static void setup_file_in_str(struct in_str *i, FILE *f) |
2695 | { | 2734 | { |
2696 | memset(i, 0, sizeof(*i)); | 2735 | memset(i, 0, sizeof(*i)); |
@@ -3119,7 +3158,7 @@ static int glob_brace(char *pattern, o_string *o, int n) | |||
3119 | return o_save_ptr_helper(o, n); | 3158 | return o_save_ptr_helper(o, n); |
3120 | } | 3159 | } |
3121 | if (gr == GLOB_NOSPACE) | 3160 | if (gr == GLOB_NOSPACE) |
3122 | bb_error_msg_and_die(bb_msg_memory_exhausted); | 3161 | bb_die_memory_exhausted(); |
3123 | /* GLOB_ABORTED? Only happens with GLOB_ERR flag, | 3162 | /* GLOB_ABORTED? Only happens with GLOB_ERR flag, |
3124 | * but we didn't specify it. Paranoia again. */ | 3163 | * but we didn't specify it. Paranoia again. */ |
3125 | bb_error_msg_and_die("glob error %d on '%s'", gr, pattern); | 3164 | bb_error_msg_and_die("glob error %d on '%s'", gr, pattern); |
@@ -3221,7 +3260,7 @@ static int perform_glob(o_string *o, int n) | |||
3221 | goto literal; | 3260 | goto literal; |
3222 | } | 3261 | } |
3223 | if (gr == GLOB_NOSPACE) | 3262 | if (gr == GLOB_NOSPACE) |
3224 | bb_error_msg_and_die(bb_msg_memory_exhausted); | 3263 | bb_die_memory_exhausted(); |
3225 | /* GLOB_ABORTED? Only happens with GLOB_ERR flag, | 3264 | /* GLOB_ABORTED? Only happens with GLOB_ERR flag, |
3226 | * but we didn't specify it. Paranoia again. */ | 3265 | * but we didn't specify it. Paranoia again. */ |
3227 | bb_error_msg_and_die("glob error %d on '%s'", gr, pattern); | 3266 | bb_error_msg_and_die("glob error %d on '%s'", gr, pattern); |
@@ -3812,16 +3851,28 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3812 | if (ctx->pending_redirect) { | 3851 | if (ctx->pending_redirect) { |
3813 | /* We do not glob in e.g. >*.tmp case. bash seems to glob here | 3852 | /* We do not glob in e.g. >*.tmp case. bash seems to glob here |
3814 | * only if run as "bash", not "sh" */ | 3853 | * only if run as "bash", not "sh" */ |
3815 | /* http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html | 3854 | /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html |
3816 | * "2.7 Redirection | 3855 | * "2.7 Redirection |
3817 | * ...the word that follows the redirection operator | 3856 | * If the redirection operator is "<<" or "<<-", the word |
3818 | * shall be subjected to tilde expansion, parameter expansion, | 3857 | * that follows the redirection operator shall be |
3819 | * command substitution, arithmetic expansion, and quote | 3858 | * subjected to quote removal; it is unspecified whether |
3820 | * removal. Pathname expansion shall not be performed | 3859 | * any of the other expansions occur. For the other |
3860 | * redirection operators, the word that follows the | ||
3861 | * redirection operator shall be subjected to tilde | ||
3862 | * expansion, parameter expansion, command substitution, | ||
3863 | * arithmetic expansion, and quote removal. | ||
3864 | * Pathname expansion shall not be performed | ||
3821 | * on the word by a non-interactive shell; an interactive | 3865 | * on the word by a non-interactive shell; an interactive |
3822 | * shell may perform it, but shall do so only when | 3866 | * shell may perform it, but shall do so only when |
3823 | * the expansion would result in one word." | 3867 | * the expansion would result in one word." |
3824 | */ | 3868 | */ |
3869 | //bash does not do parameter/command substitution or arithmetic expansion | ||
3870 | //for _heredoc_ redirection word: these constructs look for exact eof marker | ||
3871 | // as written: | ||
3872 | // <<EOF$t | ||
3873 | // <<EOF$((1)) | ||
3874 | // <<EOF`true` [this case also makes heredoc "quoted", a-la <<"EOF". Probably bash-4.3.43 bug] | ||
3875 | |||
3825 | ctx->pending_redirect->rd_filename = xstrdup(word->data); | 3876 | ctx->pending_redirect->rd_filename = xstrdup(word->data); |
3826 | /* Cater for >\file case: | 3877 | /* Cater for >\file case: |
3827 | * >\a creates file a; >\\a, >"\a", >"\\a" create file \a | 3878 | * >\a creates file a; >\\a, >"\a", >"\\a" create file \a |
@@ -4011,7 +4062,7 @@ static int parse_redirect(struct parse_context *ctx, | |||
4011 | if (dup_num == REDIRFD_SYNTAX_ERR) | 4062 | if (dup_num == REDIRFD_SYNTAX_ERR) |
4012 | return 1; | 4063 | return 1; |
4013 | } else { | 4064 | } else { |
4014 | int ch = i_peek(input); | 4065 | int ch = i_peek_and_eat_bkslash_nl(input); |
4015 | dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */ | 4066 | dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */ |
4016 | if (dup_num) { /* <<-... */ | 4067 | if (dup_num) { /* <<-... */ |
4017 | ch = i_getch(input); | 4068 | ch = i_getch(input); |
@@ -4021,7 +4072,7 @@ static int parse_redirect(struct parse_context *ctx, | |||
4021 | } | 4072 | } |
4022 | 4073 | ||
4023 | if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) { | 4074 | if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) { |
4024 | int ch = i_peek(input); | 4075 | int ch = i_peek_and_eat_bkslash_nl(input); |
4025 | if (ch == '|') { | 4076 | if (ch == '|') { |
4026 | /* >|FILE redirect ("clobbering" >). | 4077 | /* >|FILE redirect ("clobbering" >). |
4027 | * Since we do not support "set -o noclobber" yet, | 4078 | * Since we do not support "set -o noclobber" yet, |
@@ -4189,6 +4240,7 @@ static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_ | |||
4189 | 4240 | ||
4190 | redir->rd_type = REDIRECT_HEREDOC2; | 4241 | redir->rd_type = REDIRECT_HEREDOC2; |
4191 | /* redir->rd_dup is (ab)used to indicate <<- */ | 4242 | /* redir->rd_dup is (ab)used to indicate <<- */ |
4243 | bb_error_msg("redir->rd_filename:'%s'", redir->rd_filename); | ||
4192 | p = fetch_till_str(&ctx->as_string, input, | 4244 | p = fetch_till_str(&ctx->as_string, input, |
4193 | redir->rd_filename, redir->rd_dup); | 4245 | redir->rd_filename, redir->rd_dup); |
4194 | if (!p) { | 4246 | if (!p) { |
@@ -4340,39 +4392,6 @@ static int parse_group(o_string *dest, struct parse_context *ctx, | |||
4340 | /* command remains "open", available for possible redirects */ | 4392 | /* command remains "open", available for possible redirects */ |
4341 | } | 4393 | } |
4342 | 4394 | ||
4343 | static int i_getch_and_eat_bkslash_nl(struct in_str *input) | ||
4344 | { | ||
4345 | for (;;) { | ||
4346 | int ch, ch2; | ||
4347 | |||
4348 | ch = i_getch(input); | ||
4349 | if (ch != '\\') | ||
4350 | return ch; | ||
4351 | ch2 = i_peek(input); | ||
4352 | if (ch2 != '\n') | ||
4353 | return ch; | ||
4354 | /* backslash+newline, skip it */ | ||
4355 | i_getch(input); | ||
4356 | } | ||
4357 | } | ||
4358 | |||
4359 | static int i_peek_and_eat_bkslash_nl(struct in_str *input) | ||
4360 | { | ||
4361 | for (;;) { | ||
4362 | int ch, ch2; | ||
4363 | |||
4364 | ch = i_peek(input); | ||
4365 | if (ch != '\\') | ||
4366 | return ch; | ||
4367 | ch2 = i_peek2(input); | ||
4368 | if (ch2 != '\n') | ||
4369 | return ch; | ||
4370 | /* backslash+newline, skip it */ | ||
4371 | i_getch(input); | ||
4372 | i_getch(input); | ||
4373 | } | ||
4374 | } | ||
4375 | |||
4376 | #if ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS | 4395 | #if ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS |
4377 | /* Subroutines for copying $(...) and `...` things */ | 4396 | /* Subroutines for copying $(...) and `...` things */ |
4378 | static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote); | 4397 | static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote); |
@@ -4974,8 +4993,14 @@ static struct pipe *parse_stream(char **pstring, | |||
4974 | nommu_addchr(&ctx.as_string, ch); | 4993 | nommu_addchr(&ctx.as_string, ch); |
4975 | 4994 | ||
4976 | next = '\0'; | 4995 | next = '\0'; |
4977 | if (ch != '\n') | 4996 | if (ch != '\n') { |
4978 | next = i_peek(input); | 4997 | next = i_peek(input); |
4998 | /* Can't use i_peek_and_eat_bkslash_nl(input) here: | ||
4999 | * echo '\ | ||
5000 | * ' | ||
5001 | * will break. | ||
5002 | */ | ||
5003 | } | ||
4979 | 5004 | ||
4980 | is_special = "{}<>;&|()#'" /* special outside of "str" */ | 5005 | is_special = "{}<>;&|()#'" /* special outside of "str" */ |
4981 | "\\$\"" IF_HUSH_TICK("`") /* always special */ | 5006 | "\\$\"" IF_HUSH_TICK("`") /* always special */ |
@@ -5178,6 +5203,8 @@ static struct pipe *parse_stream(char **pstring, | |||
5178 | goto parse_error; | 5203 | goto parse_error; |
5179 | } | 5204 | } |
5180 | redir_style = REDIRECT_OVERWRITE; | 5205 | redir_style = REDIRECT_OVERWRITE; |
5206 | if (next == '\\') | ||
5207 | next = i_peek_and_eat_bkslash_nl(input); | ||
5181 | if (next == '>') { | 5208 | if (next == '>') { |
5182 | redir_style = REDIRECT_APPEND; | 5209 | redir_style = REDIRECT_APPEND; |
5183 | ch = i_getch(input); | 5210 | ch = i_getch(input); |
@@ -5198,6 +5225,8 @@ static struct pipe *parse_stream(char **pstring, | |||
5198 | goto parse_error; | 5225 | goto parse_error; |
5199 | } | 5226 | } |
5200 | redir_style = REDIRECT_INPUT; | 5227 | redir_style = REDIRECT_INPUT; |
5228 | if (next == '\\') | ||
5229 | next = i_peek_and_eat_bkslash_nl(input); | ||
5201 | if (next == '<') { | 5230 | if (next == '<') { |
5202 | redir_style = REDIRECT_HEREDOC; | 5231 | redir_style = REDIRECT_HEREDOC; |
5203 | heredoc_cnt++; | 5232 | heredoc_cnt++; |
@@ -5365,7 +5394,7 @@ static struct pipe *parse_stream(char **pstring, | |||
5365 | /* Eat multiple semicolons, detect | 5394 | /* Eat multiple semicolons, detect |
5366 | * whether it means something special */ | 5395 | * whether it means something special */ |
5367 | while (1) { | 5396 | while (1) { |
5368 | ch = i_peek(input); | 5397 | ch = i_peek_and_eat_bkslash_nl(input); |
5369 | if (ch != ';') | 5398 | if (ch != ';') |
5370 | break; | 5399 | break; |
5371 | ch = i_getch(input); | 5400 | ch = i_getch(input); |
@@ -5387,6 +5416,8 @@ static struct pipe *parse_stream(char **pstring, | |||
5387 | if (done_word(&dest, &ctx)) { | 5416 | if (done_word(&dest, &ctx)) { |
5388 | goto parse_error; | 5417 | goto parse_error; |
5389 | } | 5418 | } |
5419 | if (next == '\\') | ||
5420 | next = i_peek_and_eat_bkslash_nl(input); | ||
5390 | if (next == '&') { | 5421 | if (next == '&') { |
5391 | ch = i_getch(input); | 5422 | ch = i_getch(input); |
5392 | nommu_addchr(&ctx.as_string, ch); | 5423 | nommu_addchr(&ctx.as_string, ch); |
@@ -5403,6 +5434,8 @@ static struct pipe *parse_stream(char **pstring, | |||
5403 | if (ctx.ctx_res_w == RES_MATCH) | 5434 | if (ctx.ctx_res_w == RES_MATCH) |
5404 | break; /* we are in case's "word | word)" */ | 5435 | break; /* we are in case's "word | word)" */ |
5405 | #endif | 5436 | #endif |
5437 | if (next == '\\') | ||
5438 | next = i_peek_and_eat_bkslash_nl(input); | ||
5406 | if (next == '|') { /* || */ | 5439 | if (next == '|') { /* || */ |
5407 | ch = i_getch(input); | 5440 | ch = i_getch(input); |
5408 | nommu_addchr(&ctx.as_string, ch); | 5441 | nommu_addchr(&ctx.as_string, ch); |
@@ -6123,7 +6156,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
6123 | } else | 6156 | } else |
6124 | /* If EXP_FLAG_SINGLEWORD, we handle assignment 'a=....$@.....' | 6157 | /* If EXP_FLAG_SINGLEWORD, we handle assignment 'a=....$@.....' |
6125 | * and in this case should treat it like '$*' - see 'else...' below */ | 6158 | * and in this case should treat it like '$*' - see 'else...' below */ |
6126 | if (first_ch == ('@'|0x80) /* quoted $@ */ | 6159 | if (first_ch == (char)('@'|0x80) /* quoted $@ */ |
6127 | && !(output->o_expflags & EXP_FLAG_SINGLEWORD) /* not v="$@" case */ | 6160 | && !(output->o_expflags & EXP_FLAG_SINGLEWORD) /* not v="$@" case */ |
6128 | ) { | 6161 | ) { |
6129 | while (1) { | 6162 | while (1) { |
@@ -6890,7 +6923,7 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) | |||
6890 | if (sq) for (; sq[i].orig_fd >= 0; i++) { | 6923 | if (sq) for (; sq[i].orig_fd >= 0; i++) { |
6891 | /* If we collide with an already moved fd... */ | 6924 | /* If we collide with an already moved fd... */ |
6892 | if (fd == sq[i].moved_to) { | 6925 | if (fd == sq[i].moved_to) { |
6893 | sq[i].moved_to = fcntl_F_DUPFD(sq[i].moved_to, avoid_fd); | 6926 | sq[i].moved_to = dup_CLOEXEC(sq[i].moved_to, avoid_fd); |
6894 | debug_printf_redir("redirect_fd %d: already busy, moving to %d\n", fd, sq[i].moved_to); | 6927 | debug_printf_redir("redirect_fd %d: already busy, moving to %d\n", fd, sq[i].moved_to); |
6895 | if (sq[i].moved_to < 0) /* what? */ | 6928 | if (sq[i].moved_to < 0) /* what? */ |
6896 | xfunc_die(); | 6929 | xfunc_die(); |
@@ -6904,7 +6937,7 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) | |||
6904 | } | 6937 | } |
6905 | 6938 | ||
6906 | /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */ | 6939 | /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */ |
6907 | moved_to = fcntl_F_DUPFD(fd, avoid_fd); | 6940 | moved_to = dup_CLOEXEC(fd, avoid_fd); |
6908 | debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, moved_to); | 6941 | debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, moved_to); |
6909 | if (moved_to < 0 && errno != EBADF) | 6942 | if (moved_to < 0 && errno != EBADF) |
6910 | xfunc_die(); | 6943 | xfunc_die(); |
@@ -7622,6 +7655,10 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7622 | */ | 7655 | */ |
7623 | close_saved_fds_and_FILE_fds(); | 7656 | close_saved_fds_and_FILE_fds(); |
7624 | //FIXME: should also close saved redir fds | 7657 | //FIXME: should also close saved redir fds |
7658 | //This casuses test failures in | ||
7659 | //redir_children_should_not_see_saved_fd_2.tests | ||
7660 | //redir_children_should_not_see_saved_fd_3.tests | ||
7661 | //if you replace "busybox find" with just "find" in them | ||
7625 | /* Without this, "rm -i FILE" can't be ^C'ed: */ | 7662 | /* Without this, "rm -i FILE" can't be ^C'ed: */ |
7626 | switch_off_special_sigs(G.special_sig_mask); | 7663 | switch_off_special_sigs(G.special_sig_mask); |
7627 | debug_printf_exec("running applet '%s'\n", argv[0]); | 7664 | debug_printf_exec("running applet '%s'\n", argv[0]); |
@@ -8166,6 +8203,12 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
8166 | rcode = 1; /* exitcode if redir failed */ | 8203 | rcode = 1; /* exitcode if redir failed */ |
8167 | if (setup_redirects(command, &squirrel) == 0) { | 8204 | if (setup_redirects(command, &squirrel) == 0) { |
8168 | debug_printf_exec(": run_list\n"); | 8205 | debug_printf_exec(": run_list\n"); |
8206 | //FIXME: we need to pass squirrel down into run_list() | ||
8207 | //for SH_STANDALONE case, or else this construct: | ||
8208 | // { find /proc/self/fd; true; } >FILE; cmd2 | ||
8209 | //has no way of closing saved fd#1 for "find", | ||
8210 | //and in SH_STANDALONE mode, "find" is not execed, | ||
8211 | //therefore CLOEXEC on saved fd does not help. | ||
8169 | rcode = run_list(command->group) & 0xff; | 8212 | rcode = run_list(command->group) & 0xff; |
8170 | } | 8213 | } |
8171 | restore_redirects(squirrel); | 8214 | restore_redirects(squirrel); |
@@ -9347,7 +9390,7 @@ int hush_main(int argc, char **argv) | |||
9347 | G_saved_tty_pgrp = 0; | 9390 | G_saved_tty_pgrp = 0; |
9348 | 9391 | ||
9349 | /* try to dup stdin to high fd#, >= 255 */ | 9392 | /* try to dup stdin to high fd#, >= 255 */ |
9350 | G_interactive_fd = fcntl_F_DUPFD(STDIN_FILENO, 254); | 9393 | G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); |
9351 | if (G_interactive_fd < 0) { | 9394 | if (G_interactive_fd < 0) { |
9352 | /* try to dup to any fd */ | 9395 | /* try to dup to any fd */ |
9353 | G_interactive_fd = dup(STDIN_FILENO); | 9396 | G_interactive_fd = dup(STDIN_FILENO); |
@@ -9420,10 +9463,10 @@ int hush_main(int argc, char **argv) | |||
9420 | #elif ENABLE_HUSH_INTERACTIVE | 9463 | #elif ENABLE_HUSH_INTERACTIVE |
9421 | /* No job control compiled in, only prompt/line editing */ | 9464 | /* No job control compiled in, only prompt/line editing */ |
9422 | if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { | 9465 | if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { |
9423 | G_interactive_fd = fcntl_F_DUPFD(STDIN_FILENO, 254); | 9466 | G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); |
9424 | if (G_interactive_fd < 0) { | 9467 | if (G_interactive_fd < 0) { |
9425 | /* try to dup to any fd */ | 9468 | /* try to dup to any fd */ |
9426 | G_interactive_fd = dup(STDIN_FILENO); | 9469 | G_interactive_fd = dup_CLOEXEC(STDIN_FILENO); |
9427 | if (G_interactive_fd < 0) | 9470 | if (G_interactive_fd < 0) |
9428 | /* give up */ | 9471 | /* give up */ |
9429 | G_interactive_fd = 0; | 9472 | G_interactive_fd = 0; |