154 lines
3.8 KiB
C++
154 lines
3.8 KiB
C++
/**
|
|
* @file Command.cpp
|
|
* @author apio (cloudapio.eu)
|
|
* @brief Command parsing and execution.
|
|
*
|
|
* @copyright Copyright (c) 2024, the Luna authors.
|
|
*
|
|
*/
|
|
|
|
#include "Command.h"
|
|
#include <luna/HashMap.h>
|
|
#include <os/Process.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
extern shell_builtin_t builtin_cd;
|
|
extern shell_builtin_t builtin_exit;
|
|
extern shell_builtin_t builtin_set;
|
|
extern shell_builtin_t builtin_unset;
|
|
extern shell_builtin_t builtin_echo;
|
|
|
|
static HashMap<StringView, shell_builtin_t> s_builtins;
|
|
|
|
Result<void> init_builtins()
|
|
{
|
|
s_builtins = {};
|
|
|
|
TRY(s_builtins.try_set("cd"_sv, builtin_cd));
|
|
TRY(s_builtins.try_set("exit"_sv, builtin_exit));
|
|
TRY(s_builtins.try_set("set"_sv, builtin_set));
|
|
TRY(s_builtins.try_set("unset"_sv, builtin_unset));
|
|
TRY(s_builtins.try_set("echo"_sv, builtin_echo));
|
|
|
|
return {};
|
|
}
|
|
|
|
Result<Option<String>> read_command(SharedPtr<os::File> file)
|
|
{
|
|
auto maybe_cmd = file->read_line();
|
|
if (maybe_cmd.has_error())
|
|
{
|
|
if (maybe_cmd.error() == EINTR)
|
|
{
|
|
os::print("\n");
|
|
return Option<String> { String {} };
|
|
}
|
|
return maybe_cmd.release_error();
|
|
}
|
|
|
|
auto cmd = maybe_cmd.release_value();
|
|
if (cmd.is_empty()) return Option<String> {};
|
|
|
|
if (strspn(cmd.chars(), " \n") == cmd.length()) return Option<String> { String {} };
|
|
|
|
if (strcspn(cmd.chars(), " #") == 0) return Option<String> { String {} };
|
|
|
|
return Option<String> { move(cmd) };
|
|
}
|
|
|
|
Result<OwnedPtr<Command>> parse_command(StringView command)
|
|
{
|
|
auto vec = TRY(command.split(" \n"_sv));
|
|
|
|
auto cmd = TRY(make_owned<Command>());
|
|
cmd->args = move(vec);
|
|
|
|
return cmd;
|
|
}
|
|
|
|
shell_builtin_t* check_builtin(Command& command)
|
|
{
|
|
if (!strchr(command.args[0].chars(), '/')) { return s_builtins.try_get_ref(command.args[0].view()); }
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
[[noreturn]] void execute_command(OwnedPtr<Command> command)
|
|
{
|
|
auto err = os::Process::exec(command->args[0].view(), command->args.slice());
|
|
|
|
os::eprintln("%s: %s", command->args[0].chars(), err.error_string());
|
|
|
|
os::Process::exit(127);
|
|
}
|
|
|
|
[[noreturn]] void execute_command_or_builtin(OwnedPtr<Command> command)
|
|
{
|
|
shell_builtin_t* builtin;
|
|
if ((builtin = check_builtin(*command)))
|
|
{
|
|
auto rc = run_builtin(command->args, *builtin);
|
|
if (rc.has_error()) os::Process::exit(rc.error());
|
|
os::Process::exit(0);
|
|
}
|
|
|
|
execute_command(move(command));
|
|
}
|
|
|
|
Result<void> run_builtin(const Vector<String>& args, shell_builtin_t builtin)
|
|
{
|
|
Vector<const char*> argv;
|
|
for (const auto& arg : args) { TRY(argv.try_append(arg.chars())); }
|
|
TRY(argv.try_append(nullptr));
|
|
|
|
auto rc = builtin((int)args.size(), const_cast<char**>(argv.data()));
|
|
if (rc.has_error())
|
|
{
|
|
os::eprintln("%s: %s", args[0].chars(), rc.error_string());
|
|
return rc.release_error();
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
Result<int> execute_command_in_subprocess(OwnedPtr<Command> command, SharedPtr<os::File> input_file, bool interactive)
|
|
{
|
|
shell_builtin_t* builtin;
|
|
if ((builtin = check_builtin(*command)))
|
|
{
|
|
auto rc = run_builtin(command->args, *builtin);
|
|
if (rc.has_error()) return rc.error();
|
|
return 0;
|
|
}
|
|
|
|
pid_t child = TRY(os::Process::fork());
|
|
|
|
if (child == 0)
|
|
{
|
|
if (interactive)
|
|
{
|
|
setpgid(0, 0);
|
|
tcsetpgrp(input_file->fd(), getpid());
|
|
}
|
|
|
|
execute_command(move(command));
|
|
}
|
|
|
|
int status;
|
|
TRY(os::Process::wait(child, &status));
|
|
|
|
if (interactive) tcsetpgrp(input_file->fd(), getpgid(0));
|
|
|
|
if (WIFSIGNALED(status))
|
|
{
|
|
int sig = WTERMSIG(status);
|
|
if (sig != SIGINT && sig != SIGQUIT) os::println("[sh] Process %d exited: %s", child, strsignal(sig));
|
|
else
|
|
os::print("\n");
|
|
}
|
|
|
|
return status;
|
|
}
|