diff options
Diffstat (limited to 'shell/hush.c')
-rw-r--r-- | shell/hush.c | 278 |
1 files changed, 200 insertions, 78 deletions
diff --git a/shell/hush.c b/shell/hush.c index d0225edb9..9f946d82f 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -1454,11 +1454,11 @@ static int fcntl_F_DUPFD(int fd, int avoid_fd) | |||
1454 | return newfd; | 1454 | return newfd; |
1455 | } | 1455 | } |
1456 | 1456 | ||
1457 | static int xdup_and_close(int fd, int F_DUPFD_maybe_CLOEXEC, int avoid_fd) | 1457 | static int xdup_CLOEXEC_and_close(int fd, int avoid_fd) |
1458 | { | 1458 | { |
1459 | int newfd; | 1459 | int newfd; |
1460 | repeat: | 1460 | repeat: |
1461 | newfd = fcntl(fd, F_DUPFD_maybe_CLOEXEC, avoid_fd + 1); | 1461 | newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1); |
1462 | if (newfd < 0) { | 1462 | if (newfd < 0) { |
1463 | if (errno == EBUSY) | 1463 | if (errno == EBUSY) |
1464 | goto repeat; | 1464 | goto repeat; |
@@ -1469,6 +1469,8 @@ static int xdup_and_close(int fd, int F_DUPFD_maybe_CLOEXEC, int avoid_fd) | |||
1469 | return fd; | 1469 | return fd; |
1470 | xfunc_die(); | 1470 | xfunc_die(); |
1471 | } | 1471 | } |
1472 | if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */ | ||
1473 | fcntl(newfd, F_SETFD, FD_CLOEXEC); | ||
1472 | close(fd); | 1474 | close(fd); |
1473 | return newfd; | 1475 | return newfd; |
1474 | } | 1476 | } |
@@ -1507,7 +1509,7 @@ static int save_FILEs_on_redirect(int fd, int avoid_fd) | |||
1507 | while (fl) { | 1509 | while (fl) { |
1508 | if (fd == fl->fd) { | 1510 | if (fd == fl->fd) { |
1509 | /* We use it only on script files, they are all CLOEXEC */ | 1511 | /* We use it only on script files, they are all CLOEXEC */ |
1510 | fl->fd = xdup_and_close(fd, F_DUPFD_CLOEXEC, avoid_fd); | 1512 | fl->fd = xdup_CLOEXEC_and_close(fd, avoid_fd); |
1511 | debug_printf_redir("redirect_fd %d: matches a script fd, moving it to %d\n", fd, fl->fd); | 1513 | debug_printf_redir("redirect_fd %d: matches a script fd, moving it to %d\n", fd, fl->fd); |
1512 | return 1; | 1514 | return 1; |
1513 | } | 1515 | } |
@@ -1544,6 +1546,16 @@ static void close_all_FILE_list(void) | |||
1544 | } | 1546 | } |
1545 | } | 1547 | } |
1546 | #endif | 1548 | #endif |
1549 | static int fd_in_FILEs(int fd) | ||
1550 | { | ||
1551 | struct FILE_list *fl = G.FILE_list; | ||
1552 | while (fl) { | ||
1553 | if (fl->fd == fd) | ||
1554 | return 1; | ||
1555 | fl = fl->next; | ||
1556 | } | ||
1557 | return 0; | ||
1558 | } | ||
1547 | 1559 | ||
1548 | 1560 | ||
1549 | /* Helpers for setting new $n and restoring them back | 1561 | /* Helpers for setting new $n and restoring them back |
@@ -4001,24 +4013,34 @@ static char *fetch_till_str(o_string *as_string, | |||
4001 | ch = i_getch(input); | 4013 | ch = i_getch(input); |
4002 | if (ch != EOF) | 4014 | if (ch != EOF) |
4003 | nommu_addchr(as_string, ch); | 4015 | nommu_addchr(as_string, ch); |
4004 | if ((ch == '\n' || ch == EOF) | 4016 | if (ch == '\n' || ch == EOF) { |
4005 | && ((heredoc_flags & HEREDOC_QUOTED) || prev != '\\') | 4017 | check_heredoc_end: |
4006 | ) { | 4018 | if ((heredoc_flags & HEREDOC_QUOTED) || prev != '\\') { |
4007 | if (strcmp(heredoc.data + past_EOL, word) == 0) { | 4019 | if (strcmp(heredoc.data + past_EOL, word) == 0) { |
4008 | heredoc.data[past_EOL] = '\0'; | 4020 | heredoc.data[past_EOL] = '\0'; |
4009 | debug_printf_parse("parsed heredoc '%s'\n", heredoc.data); | 4021 | debug_printf_parse("parsed heredoc '%s'\n", heredoc.data); |
4010 | return heredoc.data; | 4022 | return heredoc.data; |
4011 | } | 4023 | } |
4012 | while (ch == '\n') { | 4024 | if (ch == '\n') { |
4013 | o_addchr(&heredoc, ch); | 4025 | /* This is a new line. |
4014 | prev = ch; | 4026 | * Remember position and backslash-escaping status. |
4027 | */ | ||
4028 | o_addchr(&heredoc, ch); | ||
4029 | prev = ch; | ||
4015 | jump_in: | 4030 | jump_in: |
4016 | past_EOL = heredoc.length; | 4031 | past_EOL = heredoc.length; |
4017 | do { | 4032 | /* Get 1st char of next line, possibly skipping leading tabs */ |
4018 | ch = i_getch(input); | 4033 | do { |
4019 | if (ch != EOF) | 4034 | ch = i_getch(input); |
4020 | nommu_addchr(as_string, ch); | 4035 | if (ch != EOF) |
4021 | } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t'); | 4036 | nommu_addchr(as_string, ch); |
4037 | } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t'); | ||
4038 | /* If this immediately ended the line, | ||
4039 | * go back to end-of-line checks. | ||
4040 | */ | ||
4041 | if (ch == '\n') | ||
4042 | goto check_heredoc_end; | ||
4043 | } | ||
4022 | } | 4044 | } |
4023 | } | 4045 | } |
4024 | if (ch == EOF) { | 4046 | if (ch == EOF) { |
@@ -6674,9 +6696,9 @@ static struct squirrel *append_squirrel(struct squirrel *sq, int i, int orig, in | |||
6674 | static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) | 6696 | static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) |
6675 | { | 6697 | { |
6676 | int moved_to; | 6698 | int moved_to; |
6677 | int i = 0; | 6699 | int i; |
6678 | 6700 | ||
6679 | if (sq) while (sq[i].orig_fd >= 0) { | 6701 | if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) { |
6680 | /* If we collide with an already moved fd... */ | 6702 | /* If we collide with an already moved fd... */ |
6681 | if (fd == sq[i].moved_to) { | 6703 | if (fd == sq[i].moved_to) { |
6682 | sq[i].moved_to = fcntl_F_DUPFD(sq[i].moved_to, avoid_fd); | 6704 | sq[i].moved_to = fcntl_F_DUPFD(sq[i].moved_to, avoid_fd); |
@@ -6690,7 +6712,6 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) | |||
6690 | debug_printf_redir("redirect_fd %d: already moved\n", fd); | 6712 | debug_printf_redir("redirect_fd %d: already moved\n", fd); |
6691 | return sq; | 6713 | return sq; |
6692 | } | 6714 | } |
6693 | i++; | ||
6694 | } | 6715 | } |
6695 | 6716 | ||
6696 | /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */ | 6717 | /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */ |
@@ -6701,18 +6722,41 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) | |||
6701 | return append_squirrel(sq, i, fd, moved_to); | 6722 | return append_squirrel(sq, i, fd, moved_to); |
6702 | } | 6723 | } |
6703 | 6724 | ||
6725 | static struct squirrel *add_squirrel_closed(struct squirrel *sq, int fd) | ||
6726 | { | ||
6727 | int i; | ||
6728 | |||
6729 | if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) { | ||
6730 | /* If we collide with an already moved fd... */ | ||
6731 | if (fd == sq[i].orig_fd) { | ||
6732 | /* Examples: | ||
6733 | * "echo 3>FILE 3>&- 3>FILE" | ||
6734 | * "echo 3>&- 3>FILE" | ||
6735 | * No need for last redirect to insert | ||
6736 | * another "need to close 3" indicator. | ||
6737 | */ | ||
6738 | debug_printf_redir("redirect_fd %d: already moved or closed\n", fd); | ||
6739 | return sq; | ||
6740 | } | ||
6741 | } | ||
6742 | |||
6743 | debug_printf_redir("redirect_fd %d: previous fd was closed\n", fd); | ||
6744 | return append_squirrel(sq, i, fd, -1); | ||
6745 | } | ||
6746 | |||
6704 | /* fd: redirect wants this fd to be used (e.g. 3>file). | 6747 | /* fd: redirect wants this fd to be used (e.g. 3>file). |
6705 | * Move all conflicting internally used fds, | 6748 | * Move all conflicting internally used fds, |
6706 | * and remember them so that we can restore them later. | 6749 | * and remember them so that we can restore them later. |
6707 | */ | 6750 | */ |
6708 | static int save_fds_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) | 6751 | static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) |
6709 | { | 6752 | { |
6710 | if (avoid_fd < 9) /* the important case here is that it can be -1 */ | 6753 | if (avoid_fd < 9) /* the important case here is that it can be -1 */ |
6711 | avoid_fd = 9; | 6754 | avoid_fd = 9; |
6712 | 6755 | ||
6713 | #if ENABLE_HUSH_INTERACTIVE | 6756 | #if ENABLE_HUSH_INTERACTIVE |
6714 | if (fd != 0 && fd == G.interactive_fd) { | 6757 | if (fd == G.interactive_fd) { |
6715 | G.interactive_fd = xdup_and_close(G.interactive_fd, F_DUPFD_CLOEXEC, avoid_fd); | 6758 | /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */ |
6759 | G.interactive_fd = xdup_CLOEXEC_and_close(G.interactive_fd, avoid_fd); | ||
6716 | debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G.interactive_fd); | 6760 | debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G.interactive_fd); |
6717 | return 1; /* "we closed fd" */ | 6761 | return 1; /* "we closed fd" */ |
6718 | } | 6762 | } |
@@ -6738,10 +6782,9 @@ static int save_fds_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) | |||
6738 | 6782 | ||
6739 | static void restore_redirects(struct squirrel *sq) | 6783 | static void restore_redirects(struct squirrel *sq) |
6740 | { | 6784 | { |
6741 | |||
6742 | if (sq) { | 6785 | if (sq) { |
6743 | int i = 0; | 6786 | int i; |
6744 | while (sq[i].orig_fd >= 0) { | 6787 | for (i = 0; sq[i].orig_fd >= 0; i++) { |
6745 | if (sq[i].moved_to >= 0) { | 6788 | if (sq[i].moved_to >= 0) { |
6746 | /* We simply die on error */ | 6789 | /* We simply die on error */ |
6747 | debug_printf_redir("restoring redirected fd from %d to %d\n", sq[i].moved_to, sq[i].orig_fd); | 6790 | debug_printf_redir("restoring redirected fd from %d to %d\n", sq[i].moved_to, sq[i].orig_fd); |
@@ -6751,7 +6794,6 @@ static void restore_redirects(struct squirrel *sq) | |||
6751 | debug_printf_redir("restoring redirected fd %d: closing it\n", sq[i].orig_fd); | 6794 | debug_printf_redir("restoring redirected fd %d: closing it\n", sq[i].orig_fd); |
6752 | close(sq[i].orig_fd); | 6795 | close(sq[i].orig_fd); |
6753 | } | 6796 | } |
6754 | i++; | ||
6755 | } | 6797 | } |
6756 | free(sq); | 6798 | free(sq); |
6757 | } | 6799 | } |
@@ -6761,17 +6803,47 @@ static void restore_redirects(struct squirrel *sq) | |||
6761 | restore_redirected_FILEs(); | 6803 | restore_redirected_FILEs(); |
6762 | } | 6804 | } |
6763 | 6805 | ||
6806 | #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU | ||
6807 | static void close_saved_fds_and_FILE_fds(void) | ||
6808 | { | ||
6809 | if (G_interactive_fd) | ||
6810 | close(G_interactive_fd); | ||
6811 | close_all_FILE_list(); | ||
6812 | } | ||
6813 | #endif | ||
6814 | |||
6815 | static int internally_opened_fd(int fd, struct squirrel *sq) | ||
6816 | { | ||
6817 | int i; | ||
6818 | |||
6819 | #if ENABLE_HUSH_INTERACTIVE | ||
6820 | if (fd == G.interactive_fd) | ||
6821 | return 1; | ||
6822 | #endif | ||
6823 | /* If this one of script's fds? */ | ||
6824 | if (fd_in_FILEs(fd)) | ||
6825 | return 1; | ||
6826 | |||
6827 | if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) { | ||
6828 | if (fd == sq[i].moved_to) | ||
6829 | return 1; | ||
6830 | } | ||
6831 | return 0; | ||
6832 | } | ||
6833 | |||
6764 | /* squirrel != NULL means we squirrel away copies of stdin, stdout, | 6834 | /* squirrel != NULL means we squirrel away copies of stdin, stdout, |
6765 | * and stderr if they are redirected. */ | 6835 | * and stderr if they are redirected. */ |
6766 | static int setup_redirects(struct command *prog, struct squirrel **sqp) | 6836 | static int setup_redirects(struct command *prog, struct squirrel **sqp) |
6767 | { | 6837 | { |
6768 | int openfd, mode; | ||
6769 | struct redir_struct *redir; | 6838 | struct redir_struct *redir; |
6770 | 6839 | ||
6771 | for (redir = prog->redirects; redir; redir = redir->next) { | 6840 | for (redir = prog->redirects; redir; redir = redir->next) { |
6841 | int newfd; | ||
6842 | int closed; | ||
6843 | |||
6772 | if (redir->rd_type == REDIRECT_HEREDOC2) { | 6844 | if (redir->rd_type == REDIRECT_HEREDOC2) { |
6773 | /* "rd_fd<<HERE" case */ | 6845 | /* "rd_fd<<HERE" case */ |
6774 | save_fds_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp); | 6846 | save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp); |
6775 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ | 6847 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ |
6776 | * of the heredoc */ | 6848 | * of the heredoc */ |
6777 | debug_printf_parse("set heredoc '%s'\n", | 6849 | debug_printf_parse("set heredoc '%s'\n", |
@@ -6783,6 +6855,8 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
6783 | if (redir->rd_dup == REDIRFD_TO_FILE) { | 6855 | if (redir->rd_dup == REDIRFD_TO_FILE) { |
6784 | /* "rd_fd<*>file" case (<*> is <,>,>>,<>) */ | 6856 | /* "rd_fd<*>file" case (<*> is <,>,>>,<>) */ |
6785 | char *p; | 6857 | char *p; |
6858 | int mode; | ||
6859 | |||
6786 | if (redir->rd_filename == NULL) { | 6860 | if (redir->rd_filename == NULL) { |
6787 | /* | 6861 | /* |
6788 | * Examples: | 6862 | * Examples: |
@@ -6794,9 +6868,9 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
6794 | } | 6868 | } |
6795 | mode = redir_table[redir->rd_type].mode; | 6869 | mode = redir_table[redir->rd_type].mode; |
6796 | p = expand_string_to_string(redir->rd_filename, /*unbackslash:*/ 1); | 6870 | p = expand_string_to_string(redir->rd_filename, /*unbackslash:*/ 1); |
6797 | openfd = open_or_warn(p, mode); | 6871 | newfd = open_or_warn(p, mode); |
6798 | free(p); | 6872 | free(p); |
6799 | if (openfd < 0) { | 6873 | if (newfd < 0) { |
6800 | /* Error message from open_or_warn can be lost | 6874 | /* Error message from open_or_warn can be lost |
6801 | * if stderr has been redirected, but bash | 6875 | * if stderr has been redirected, but bash |
6802 | * and ash both lose it as well | 6876 | * and ash both lose it as well |
@@ -6804,40 +6878,52 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
6804 | */ | 6878 | */ |
6805 | return 1; | 6879 | return 1; |
6806 | } | 6880 | } |
6807 | if (openfd == redir->rd_fd && sqp) { | 6881 | if (newfd == redir->rd_fd && sqp) { |
6808 | /* open() gave us precisely the fd we wanted. | 6882 | /* open() gave us precisely the fd we wanted. |
6809 | * This means that this fd was not busy | 6883 | * This means that this fd was not busy |
6810 | * (not opened to anywhere). | 6884 | * (not opened to anywhere). |
6811 | * Remember to close it on restore: | 6885 | * Remember to close it on restore: |
6812 | */ | 6886 | */ |
6813 | struct squirrel *sq = *sqp; | 6887 | *sqp = add_squirrel_closed(*sqp, newfd); |
6814 | int i = 0; | 6888 | debug_printf_redir("redir to previously closed fd %d\n", newfd); |
6815 | if (sq) while (sq[i].orig_fd >= 0) | ||
6816 | i++; | ||
6817 | *sqp = append_squirrel(sq, i, openfd, -1); /* -1 = "it was closed" */ | ||
6818 | debug_printf_redir("redir to previously closed fd %d\n", openfd); | ||
6819 | } | 6889 | } |
6820 | } else { | 6890 | } else { |
6821 | /* "rd_fd<*>rd_dup" or "rd_fd<*>-" cases */ | 6891 | /* "rd_fd>&rd_dup" or "rd_fd>&-" case */ |
6822 | openfd = redir->rd_dup; | 6892 | newfd = redir->rd_dup; |
6823 | } | 6893 | } |
6824 | 6894 | ||
6825 | if (openfd != redir->rd_fd) { | 6895 | if (newfd == redir->rd_fd) |
6826 | int closed = save_fds_on_redirect(redir->rd_fd, /*avoid:*/ openfd, sqp); | 6896 | continue; |
6827 | if (openfd == REDIRFD_CLOSE) { | 6897 | |
6828 | /* "rd_fd >&-" means "close me" */ | 6898 | /* if "N>FILE": move newfd to redir->rd_fd */ |
6829 | if (!closed) { | 6899 | /* if "N>&M": dup newfd to redir->rd_fd */ |
6830 | /* ^^^ optimization: saving may already | 6900 | /* if "N>&-": close redir->rd_fd (newfd is REDIRFD_CLOSE) */ |
6831 | * have closed it. If not... */ | 6901 | |
6832 | close(redir->rd_fd); | 6902 | closed = save_fd_on_redirect(redir->rd_fd, /*avoid:*/ newfd, sqp); |
6833 | } | 6903 | if (newfd == REDIRFD_CLOSE) { |
6834 | } else { | 6904 | /* "N>&-" means "close me" */ |
6835 | xdup2(openfd, redir->rd_fd); | 6905 | if (!closed) { |
6836 | if (redir->rd_dup == REDIRFD_TO_FILE) | 6906 | /* ^^^ optimization: saving may already |
6837 | /* "rd_fd > FILE" */ | 6907 | * have closed it. If not... */ |
6838 | close(openfd); | 6908 | close(redir->rd_fd); |
6839 | /* else: "rd_fd > rd_dup" */ | 6909 | } |
6910 | /* Sometimes we do another close on restore, getting EBADF. | ||
6911 | * Consider "echo 3>FILE 3>&-" | ||
6912 | * first redirect remembers "need to close 3", | ||
6913 | * and second redirect closes 3! Restore code then closes 3 again. | ||
6914 | */ | ||
6915 | } else { | ||
6916 | /* if newfd is a script fd or saved fd, simulate EBADF */ | ||
6917 | if (internally_opened_fd(newfd, sqp ? *sqp : NULL)) { | ||
6918 | //errno = EBADF; | ||
6919 | //bb_perror_msg_and_die("can't duplicate file descriptor"); | ||
6920 | newfd = -1; /* same effect as code above */ | ||
6840 | } | 6921 | } |
6922 | xdup2(newfd, redir->rd_fd); | ||
6923 | if (redir->rd_dup == REDIRFD_TO_FILE) | ||
6924 | /* "rd_fd > FILE" */ | ||
6925 | close(newfd); | ||
6926 | /* else: "rd_fd > rd_dup" */ | ||
6841 | } | 6927 | } |
6842 | } | 6928 | } |
6843 | return 0; | 6929 | return 0; |
@@ -7004,11 +7090,34 @@ static void exec_function(char ***to_free, | |||
7004 | argv[0] = G.global_argv[0]; | 7090 | argv[0] = G.global_argv[0]; |
7005 | G.global_argv = argv; | 7091 | G.global_argv = argv; |
7006 | G.global_argc = n = 1 + string_array_len(argv + 1); | 7092 | G.global_argc = n = 1 + string_array_len(argv + 1); |
7093 | |||
7094 | // Example when we are here: "cmd | func" | ||
7095 | // func will run with saved-redirect fds open. | ||
7096 | // $ f() { echo /proc/self/fd/*; } | ||
7097 | // $ true | f | ||
7098 | // /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/255 /proc/self/fd/3 | ||
7099 | // stdio^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ G_interactive_fd^ DIR fd for glob | ||
7100 | // Same in script: | ||
7101 | // $ . ./SCRIPT | ||
7102 | // /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/255 /proc/self/fd/3 /proc/self/fd/4 | ||
7103 | // stdio^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ G_interactive_fd^ opened ./SCRIPT DIR fd for glob | ||
7104 | // They are CLOEXEC so external programs won't see them, but | ||
7105 | // for "more correctness" we might want to close those extra fds here: | ||
7106 | //? close_saved_fds_and_FILE_fds(); | ||
7107 | |||
7108 | /* "we are in function, ok to use return" */ | ||
7109 | G_flag_return_in_progress = -1; | ||
7110 | IF_HUSH_LOCAL(G.func_nest_level++;) | ||
7111 | |||
7007 | /* On MMU, funcp->body is always non-NULL */ | 7112 | /* On MMU, funcp->body is always non-NULL */ |
7008 | n = run_list(funcp->body); | 7113 | n = run_list(funcp->body); |
7009 | fflush_all(); | 7114 | fflush_all(); |
7010 | _exit(n); | 7115 | _exit(n); |
7011 | # else | 7116 | # else |
7117 | //? close_saved_fds_and_FILE_fds(); | ||
7118 | |||
7119 | //TODO: check whether "true | func_with_return" works | ||
7120 | |||
7012 | re_execute_shell(to_free, | 7121 | re_execute_shell(to_free, |
7013 | funcp->body_as_string, | 7122 | funcp->body_as_string, |
7014 | G.global_argv[0], | 7123 | G.global_argv[0], |
@@ -7028,9 +7137,7 @@ static int run_function(const struct function *funcp, char **argv) | |||
7028 | /* "we are in function, ok to use return" */ | 7137 | /* "we are in function, ok to use return" */ |
7029 | sv_flg = G_flag_return_in_progress; | 7138 | sv_flg = G_flag_return_in_progress; |
7030 | G_flag_return_in_progress = -1; | 7139 | G_flag_return_in_progress = -1; |
7031 | # if ENABLE_HUSH_LOCAL | 7140 | IF_HUSH_LOCAL(G.func_nest_level++;) |
7032 | G.func_nest_level++; | ||
7033 | # endif | ||
7034 | 7141 | ||
7035 | /* On MMU, funcp->body is always non-NULL */ | 7142 | /* On MMU, funcp->body is always non-NULL */ |
7036 | # if !BB_MMU | 7143 | # if !BB_MMU |
@@ -7094,6 +7201,7 @@ static void exec_builtin(char ***to_free, | |||
7094 | #if BB_MMU | 7201 | #if BB_MMU |
7095 | int rcode; | 7202 | int rcode; |
7096 | fflush_all(); | 7203 | fflush_all(); |
7204 | //? close_saved_fds_and_FILE_fds(); | ||
7097 | rcode = x->b_function(argv); | 7205 | rcode = x->b_function(argv); |
7098 | fflush_all(); | 7206 | fflush_all(); |
7099 | _exit(rcode); | 7207 | _exit(rcode); |
@@ -7215,6 +7323,16 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7215 | goto skip; | 7323 | goto skip; |
7216 | #endif | 7324 | #endif |
7217 | 7325 | ||
7326 | #if ENABLE_HUSH_FUNCTIONS | ||
7327 | /* Check if the command matches any functions (this goes before bltins) */ | ||
7328 | { | ||
7329 | const struct function *funcp = find_function(argv[0]); | ||
7330 | if (funcp) { | ||
7331 | exec_function(&nommu_save->argv_from_re_execing, funcp, argv); | ||
7332 | } | ||
7333 | } | ||
7334 | #endif | ||
7335 | |||
7218 | /* Check if the command matches any of the builtins. | 7336 | /* Check if the command matches any of the builtins. |
7219 | * Depending on context, this might be redundant. But it's | 7337 | * Depending on context, this might be redundant. But it's |
7220 | * easier to waste a few CPU cycles than it is to figure out | 7338 | * easier to waste a few CPU cycles than it is to figure out |
@@ -7231,15 +7349,6 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7231 | exec_builtin(&nommu_save->argv_from_re_execing, x, argv); | 7349 | exec_builtin(&nommu_save->argv_from_re_execing, x, argv); |
7232 | } | 7350 | } |
7233 | } | 7351 | } |
7234 | #if ENABLE_HUSH_FUNCTIONS | ||
7235 | /* Check if the command matches any functions */ | ||
7236 | { | ||
7237 | const struct function *funcp = find_function(argv[0]); | ||
7238 | if (funcp) { | ||
7239 | exec_function(&nommu_save->argv_from_re_execing, funcp, argv); | ||
7240 | } | ||
7241 | } | ||
7242 | #endif | ||
7243 | 7352 | ||
7244 | #if ENABLE_FEATURE_SH_STANDALONE | 7353 | #if ENABLE_FEATURE_SH_STANDALONE |
7245 | /* Check if the command matches any busybox applets */ | 7354 | /* Check if the command matches any busybox applets */ |
@@ -7248,8 +7357,12 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7248 | if (a >= 0) { | 7357 | if (a >= 0) { |
7249 | # if BB_MMU /* see above why on NOMMU it is not allowed */ | 7358 | # if BB_MMU /* see above why on NOMMU it is not allowed */ |
7250 | if (APPLET_IS_NOEXEC(a)) { | 7359 | if (APPLET_IS_NOEXEC(a)) { |
7251 | /* Do not leak open fds from opened script files etc */ | 7360 | /* Do not leak open fds from opened script files etc. |
7252 | close_all_FILE_list(); | 7361 | * Testcase: interactive "ls -l /proc/self/fd" |
7362 | * should not show tty fd open. | ||
7363 | */ | ||
7364 | close_saved_fds_and_FILE_fds(); | ||
7365 | //FIXME: should also close saved redir fds | ||
7253 | debug_printf_exec("running applet '%s'\n", argv[0]); | 7366 | debug_printf_exec("running applet '%s'\n", argv[0]); |
7254 | run_applet_no_and_exit(a, argv[0], argv); | 7367 | run_applet_no_and_exit(a, argv[0], argv); |
7255 | } | 7368 | } |
@@ -7886,12 +7999,13 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
7886 | return G.last_exitcode; | 7999 | return G.last_exitcode; |
7887 | } | 8000 | } |
7888 | 8001 | ||
7889 | x = find_builtin(argv_expanded[0]); | ||
7890 | #if ENABLE_HUSH_FUNCTIONS | 8002 | #if ENABLE_HUSH_FUNCTIONS |
7891 | funcp = NULL; | 8003 | /* Check if argv[0] matches any functions (this goes before bltins) */ |
7892 | if (!x) | 8004 | funcp = find_function(argv_expanded[0]); |
7893 | funcp = find_function(argv_expanded[0]); | ||
7894 | #endif | 8005 | #endif |
8006 | x = NULL; | ||
8007 | if (!funcp) | ||
8008 | x = find_builtin(argv_expanded[0]); | ||
7895 | if (x || funcp) { | 8009 | if (x || funcp) { |
7896 | if (!funcp) { | 8010 | if (!funcp) { |
7897 | if (x->b_function == builtin_exec && argv_expanded[1] == NULL) { | 8011 | if (x->b_function == builtin_exec && argv_expanded[1] == NULL) { |
@@ -9158,6 +9272,14 @@ static int FAST_FUNC builtin_exec(char **argv) | |||
9158 | if (G_saved_tty_pgrp && getpid() == G.root_pid) | 9272 | if (G_saved_tty_pgrp && getpid() == G.root_pid) |
9159 | tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp); | 9273 | tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp); |
9160 | 9274 | ||
9275 | /* Saved-redirect fds, script fds and G_interactive_fd are still | ||
9276 | * open here. However, they are all CLOEXEC, and execv below | ||
9277 | * closes them. Try interactive "exec ls -l /proc/self/fd", | ||
9278 | * it should show no extra open fds in the "ls" process. | ||
9279 | * If we'd try to run builtins/NOEXECs, this would need improving. | ||
9280 | */ | ||
9281 | //close_saved_fds_and_FILE_fds(); | ||
9282 | |||
9161 | /* TODO: if exec fails, bash does NOT exit! We do. | 9283 | /* TODO: if exec fails, bash does NOT exit! We do. |
9162 | * We'll need to undo trap cleanup (it's inside execvp_or_die) | 9284 | * We'll need to undo trap cleanup (it's inside execvp_or_die) |
9163 | * and tcsetpgrp, and this is inherently racy. | 9285 | * and tcsetpgrp, and this is inherently racy. |