From 96cd04f2ba487c257d80a1d412ca43bc83d9d075 Mon Sep 17 00:00:00 2001 From: William Marlow Date: Tue, 29 Dec 2020 19:42:34 +0000 Subject: [PATCH] Shell: Implement a very basic exec builtin Other shells also support a number of other options with exec and some have special behaviour when calling exec with no arguments except redirections. This PR only supports the basic case of replacing the Shell process (or LibShell host process) with the provided command. --- Shell/Builtin.cpp | 14 +++++++++ Shell/Shell.cpp | 78 +++++++++++++++++++++++++---------------------- Shell/Shell.h | 3 ++ 3 files changed, 59 insertions(+), 36 deletions(-) diff --git a/Shell/Builtin.cpp b/Shell/Builtin.cpp index e19e8cfce3..3a7e6c884a 100644 --- a/Shell/Builtin.cpp +++ b/Shell/Builtin.cpp @@ -256,6 +256,20 @@ int Shell::builtin_dirs(int argc, const char** argv) return 0; } +int Shell::builtin_exec(int argc, const char** argv) +{ + if (argc < 2) { + fprintf(stderr, "Shell: No command given to exec\n"); + return 1; + } + + Vector argv_vector; + argv_vector.append(argv + 1, argc - 1); + argv_vector.append(nullptr); + + execute_process(move(argv_vector)); +} + int Shell::builtin_exit(int argc, const char** argv) { int exit_code = 0; diff --git a/Shell/Shell.cpp b/Shell/Shell.cpp index 6ef5a6efa7..2366a7488a 100644 --- a/Shell/Shell.cpp +++ b/Shell/Shell.cpp @@ -789,42 +789,7 @@ RefPtr Shell::run_command(const AST::Command& command) // We no longer need the jobs here. jobs.clear(); - int rc = execvp(argv[0], const_cast(argv.data())); - if (rc < 0) { - int saved_errno = errno; - struct stat st; - if (stat(argv[0], &st)) { - fprintf(stderr, "stat(%s): %s\n", argv[0], strerror(errno)); - _exit(126); - } - if (!(st.st_mode & S_IXUSR)) { - fprintf(stderr, "%s: Not executable\n", argv[0]); - _exit(126); - } - if (saved_errno == ENOENT) { - int shebang_fd = open(argv[0], O_RDONLY); - auto close_argv = ScopeGuard([shebang_fd]() { if (shebang_fd >= 0) close(shebang_fd); }); - char shebang[256] {}; - ssize_t num_read = -1; - if ((shebang_fd >= 0) && ((num_read = read(shebang_fd, shebang, sizeof(shebang))) >= 2) && (StringView(shebang).starts_with("#!"))) { - StringView shebang_path_view(&shebang[2], num_read - 2); - Optional newline_pos = shebang_path_view.find_first_of("\n\r"); - shebang[newline_pos.has_value() ? (newline_pos.value() + 2) : num_read] = '\0'; - argv[0] = shebang; - int rc = execvp(argv[0], const_cast(argv.data())); - if (rc < 0) - fprintf(stderr, "%s: Invalid interpreter \"%s\": %s\n", argv[0], &shebang[2], strerror(errno)); - } else - fprintf(stderr, "%s: Command not found.\n", argv[0]); - } else { - if (S_ISDIR(st.st_mode)) { - fprintf(stderr, "Shell: %s: Is a directory\n", argv[0]); - _exit(126); - } - fprintf(stderr, "execvp(%s): %s\n", argv[0], strerror(saved_errno)); - } - _exit(126); - } + execute_process(move(argv)); ASSERT_NOT_REACHED(); } @@ -893,6 +858,47 @@ RefPtr Shell::run_command(const AST::Command& command) return *job; } +void Shell::execute_process(Vector&& argv) +{ + int rc = execvp(argv[0], const_cast(argv.data())); + if (rc < 0) { + int saved_errno = errno; + struct stat st; + if (stat(argv[0], &st)) { + fprintf(stderr, "stat(%s): %s\n", argv[0], strerror(errno)); + _exit(126); + } + if (!(st.st_mode & S_IXUSR)) { + fprintf(stderr, "%s: Not executable\n", argv[0]); + _exit(126); + } + if (saved_errno == ENOENT) { + int shebang_fd = open(argv[0], O_RDONLY); + auto close_argv = ScopeGuard([shebang_fd]() { if (shebang_fd >= 0) close(shebang_fd); }); + char shebang[256] {}; + ssize_t num_read = -1; + if ((shebang_fd >= 0) && ((num_read = read(shebang_fd, shebang, sizeof(shebang))) >= 2) && (StringView(shebang).starts_with("#!"))) { + StringView shebang_path_view(&shebang[2], num_read - 2); + Optional newline_pos = shebang_path_view.find_first_of("\n\r"); + shebang[newline_pos.has_value() ? (newline_pos.value() + 2) : num_read] = '\0'; + argv[0] = shebang; + int rc = execvp(argv[0], const_cast(argv.data())); + if (rc < 0) + fprintf(stderr, "%s: Invalid interpreter \"%s\": %s\n", argv[0], &shebang[2], strerror(errno)); + } else + fprintf(stderr, "%s: Command not found.\n", argv[0]); + } else { + if (S_ISDIR(st.st_mode)) { + fprintf(stderr, "Shell: %s: Is a directory\n", argv[0]); + _exit(126); + } + fprintf(stderr, "execvp(%s): %s\n", argv[0], strerror(saved_errno)); + } + _exit(126); + } + ASSERT_NOT_REACHED(); +} + void Shell::run_tail(const AST::Command& invoking_command, const AST::NodeWithAction& next_in_chain, int head_exit_code) { if (m_error != ShellError::None) { diff --git a/Shell/Shell.h b/Shell/Shell.h index 05b8797138..1932afe3af 100644 --- a/Shell/Shell.h +++ b/Shell/Shell.h @@ -45,6 +45,7 @@ __ENUMERATE_SHELL_BUILTIN(cd) \ __ENUMERATE_SHELL_BUILTIN(cdh) \ __ENUMERATE_SHELL_BUILTIN(pwd) \ + __ENUMERATE_SHELL_BUILTIN(exec) \ __ENUMERATE_SHELL_BUILTIN(exit) \ __ENUMERATE_SHELL_BUILTIN(export) \ __ENUMERATE_SHELL_BUILTIN(glob) \ @@ -253,6 +254,8 @@ private: void run_tail(RefPtr); void run_tail(const AST::Command&, const AST::NodeWithAction&, int head_exit_code); + [[noreturn]] void execute_process(Vector&& argv); + virtual void custom_event(Core::CustomEvent&) override; #define __ENUMERATE_SHELL_BUILTIN(builtin) \