mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-20 17:58:18 +03:00
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.
This commit is contained in:
parent
50d24e4f98
commit
96cd04f2ba
Notes:
sideshowbarker
2024-07-19 17:30:42 +09:00
Author: https://github.com/lux01 Commit: https://github.com/SerenityOS/serenity/commit/96cd04f2ba4 Pull-request: https://github.com/SerenityOS/serenity/pull/4638
@ -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<const char*> 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;
|
||||
|
@ -789,42 +789,7 @@ RefPtr<Job> Shell::run_command(const AST::Command& command)
|
||||
// We no longer need the jobs here.
|
||||
jobs.clear();
|
||||
|
||||
int rc = execvp(argv[0], const_cast<char* const*>(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<size_t> 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<char* const*>(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<Job> Shell::run_command(const AST::Command& command)
|
||||
return *job;
|
||||
}
|
||||
|
||||
void Shell::execute_process(Vector<const char*>&& argv)
|
||||
{
|
||||
int rc = execvp(argv[0], const_cast<char* const*>(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<size_t> 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<char* const*>(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) {
|
||||
|
@ -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<Job>);
|
||||
void run_tail(const AST::Command&, const AST::NodeWithAction&, int head_exit_code);
|
||||
|
||||
[[noreturn]] void execute_process(Vector<const char*>&& argv);
|
||||
|
||||
virtual void custom_event(Core::CustomEvent&) override;
|
||||
|
||||
#define __ENUMERATE_SHELL_BUILTIN(builtin) \
|
||||
|
Loading…
Reference in New Issue
Block a user