1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 20:47:45 +00:00

Escalator: Major rework and enable forwarding of stdin/stdout

- Escalator now uses `posix_spawn` instead of `exec`
- Escalator is now able to forward stdin and stdout
- A few general changes to improve code quality
This commit is contained in:
Fabian Dellwing 2023-05-07 20:47:40 +02:00 committed by Andrew Kaster
parent 059904371f
commit 27297d2817
3 changed files with 64 additions and 22 deletions

View file

@ -1,6 +1,7 @@
/*
* Copyright (c) 2022, Ashley N. <dev-serenity@ne0ndrag0n.com>
* Copyright (c) 2022, the SerenityOS developers.
* Copyright (c) 2023, Fabian Dellwing <fabian@dellwing.net>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -8,6 +9,7 @@
#include "EscalatorWindow.h"
#include <AK/Assertions.h>
#include <Applications/Escalator/EscalatorGML.h>
#include <LibCore/File.h>
#include <LibCore/SecretString.h>
#include <LibCore/System.h>
#include <LibGUI/FileIconProvider.h>
@ -15,13 +17,16 @@
#include <LibGUI/Label.h>
#include <LibGUI/MessageBox.h>
#include <LibGUI/Widget.h>
#include <fcntl.h>
#include <unistd.h>
EscalatorWindow::EscalatorWindow(StringView executable, Vector<StringView> arguments, EscalatorWindow::Options const& options)
: m_arguments(arguments)
: m_arguments(move(arguments))
, m_executable(executable)
, m_current_user(options.current_user)
, m_preserve_env(options.preserve_env)
, m_forward_stdin(options.forward_stdin)
, m_forward_stdout(options.forward_stdout)
{
auto app_icon = GUI::FileIconProvider::icon_for_executable(m_executable);
@ -52,8 +57,8 @@ EscalatorWindow::EscalatorWindow(StringView executable, Vector<StringView> argum
auto result = check_password();
if (result.is_error()) {
GUI::MessageBox::show_error(this, DeprecatedString::formatted("Failed to execute command: {}", result.error()));
close();
}
close();
};
m_ok_button->set_default(true);
@ -83,32 +88,59 @@ ErrorOr<void> EscalatorWindow::check_password()
// Caller will close Escalator if error is returned.
TRY(execute_command());
VERIFY_NOT_REACHED();
return {};
}
ErrorOr<void> EscalatorWindow::execute_command()
{
// Translate environ to format for Core::System::exec.
Vector<StringView> exec_environment;
for (size_t i = 0; environ[i]; ++i) {
StringView env_view { environ[i], strlen(environ[i]) };
auto maybe_needle = env_view.find('=');
if (!maybe_needle.has_value())
continue;
if (!m_preserve_env && env_view.substring_view(0, maybe_needle.value()) != "TERM"sv)
continue;
exec_environment.append(env_view);
}
char const* envp[] = { nullptr };
Vector<char const*, 4> argv;
for (auto& arg : m_arguments)
argv.append(arg.characters_without_null_termination());
argv.append(nullptr);
// Escalate process privilege to root user.
TRY(Core::System::seteuid(0));
auto root_user = TRY(Core::Account::from_uid(0));
TRY(root_user.login());
TRY(Core::System::pledge("stdio sendfd rpath exec"));
TRY(Core::System::exec(m_executable, m_arguments, Core::System::SearchInPath::No, exec_environment));
VERIFY_NOT_REACHED();
if (m_forward_stdin || m_forward_stdout) {
auto in_pipefds = TRY(Core::System::pipe2(O_CLOEXEC));
auto out_pipefds = TRY(Core::System::pipe2(O_CLOEXEC));
ScopeGuard guard_fds { [&] {
::close(in_pipefds[1]);
::close(out_pipefds[0]);
} };
{
posix_spawn_file_actions_t file_actions;
posix_spawn_file_actions_init(&file_actions);
posix_spawn_file_actions_adddup2(&file_actions, in_pipefds[0], STDIN_FILENO);
posix_spawn_file_actions_adddup2(&file_actions, out_pipefds[1], STDOUT_FILENO);
ScopeGuard guard_fds_and_file_actions { [&]() {
posix_spawn_file_actions_destroy(&file_actions);
::close(in_pipefds[0]);
::close(out_pipefds[1]);
} };
TRY(Core::System::pledge("stdio sendfd rpath proc exec"));
(void)TRY(Core::System::posix_spawn(m_executable, &file_actions, nullptr, const_cast<char* const*>(argv.data()), m_preserve_env ? environ : const_cast<char**>(envp)));
if (m_forward_stdin) {
auto in_outfile = TRY(Core::File::adopt_fd(in_pipefds[1], Core::File::OpenMode::Write, Core::File::ShouldCloseFileDescriptor::No));
auto in_infile = TRY(Core::File::standard_input());
TRY(in_outfile->write_until_depleted(TRY(in_infile->read_until_eof())));
}
if (m_forward_stdout) {
auto out_outfile = TRY(Core::File::standard_output());
auto out_infile = TRY(Core::File::adopt_fd(out_pipefds[0], Core::File::OpenMode::Read, Core::File::ShouldCloseFileDescriptor::No));
TRY(out_outfile->write_until_depleted(TRY(out_infile->read_until_eof())));
}
}
} else {
TRY(Core::System::pledge("stdio sendfd rpath proc exec"));
(void)TRY(Core::System::posix_spawn(m_executable, nullptr, nullptr, const_cast<char* const*>(argv.data()), m_preserve_env ? environ : const_cast<char**>(envp)));
}
return {};
}