ladybird/Userland/Libraries/LibCore/Command.cpp
Brian Gianforcaro 4674577d80 Everywhere: Rename CommandResult stdout, stderr members to output, error
The names stdout / stderr are bound to conflict with existing
declarations when compiling against other LibC's. The build on OpenBSD
is broken for this reason at the moment.

Lets rename the members to more generic names to resolve the situation.
2022-03-27 16:41:39 -07:00

102 lines
2.9 KiB
C++

/*
* Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "Command.h"
#include <AK/Format.h>
#include <AK/ScopeGuard.h>
#include <LibCore/File.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
namespace Core {
// Only supported in serenity mode because we use `posix_spawn_file_actions_addchdir`
#ifdef __serenity__
ErrorOr<CommandResult> command(String const& command_string, Optional<LexicalPath> chdir)
{
auto parts = command_string.split(' ');
if (parts.is_empty())
return Error::from_string_literal("empty command"sv);
auto program = parts[0];
parts.remove(0);
return command(program, parts, chdir);
}
ErrorOr<CommandResult> command(String const& program, Vector<String> const& arguments, Optional<LexicalPath> chdir)
{
int stdout_pipe[2] = {};
int stderr_pipe[2] = {};
if (pipe2(stdout_pipe, O_CLOEXEC)) {
return Error::from_errno(errno);
}
if (pipe2(stderr_pipe, O_CLOEXEC)) {
perror("pipe2");
return Error::from_errno(errno);
}
auto close_pipes = ScopeGuard([stderr_pipe, stdout_pipe] {
// The write-ends of these pipes are closed manually
close(stdout_pipe[0]);
close(stderr_pipe[0]);
});
Vector<const char*> parts = { program.characters() };
for (const auto& part : arguments) {
parts.append(part.characters());
}
parts.append(nullptr);
const char** argv = parts.data();
posix_spawn_file_actions_t action;
posix_spawn_file_actions_init(&action);
if (chdir.has_value()) {
posix_spawn_file_actions_addchdir(&action, chdir.value().string().characters());
}
posix_spawn_file_actions_adddup2(&action, stdout_pipe[1], STDOUT_FILENO);
posix_spawn_file_actions_adddup2(&action, stderr_pipe[1], STDERR_FILENO);
pid_t pid;
if ((errno = posix_spawnp(&pid, program.characters(), &action, nullptr, const_cast<char**>(argv), environ))) {
perror("posix_spawn");
VERIFY_NOT_REACHED();
}
// close the write-ends so reading wouldn't block
close(stdout_pipe[1]);
close(stderr_pipe[1]);
auto read_all_from_pipe = [](int pipe[2]) {
auto result_file = Core::File::construct();
if (!result_file->open(pipe[0], Core::OpenMode::ReadOnly, Core::File::ShouldCloseFileDescriptor::Yes)) {
perror("open");
VERIFY_NOT_REACHED();
}
return String::copy(result_file->read_all());
};
auto output = read_all_from_pipe(stdout_pipe);
auto error = read_all_from_pipe(stderr_pipe);
int wstatus { 0 };
waitpid(pid, &wstatus, 0);
posix_spawn_file_actions_destroy(&action);
int exit_code = WEXITSTATUS(wstatus);
if (exit_code != 0) {
# ifdef DBG_FAILED_COMMANDS
dbgln("command failed. stderr: {}", );
# endif
}
return CommandResult { WEXITSTATUS(wstatus), output, error };
}
#endif
}