Serenity Operating System
at master 102 lines 3.1 kB view raw
1/* 2 * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include "Command.h" 8#include <AK/Format.h> 9#include <AK/ScopeGuard.h> 10#include <LibCore/DeprecatedFile.h> 11#include <fcntl.h> 12#include <stdio.h> 13#include <sys/wait.h> 14#include <unistd.h> 15 16namespace Core { 17 18// Only supported in serenity mode because we use `posix_spawn_file_actions_addchdir` 19#ifdef AK_OS_SERENITY 20 21ErrorOr<CommandResult> command(DeprecatedString const& command_string, Optional<LexicalPath> chdir) 22{ 23 auto parts = command_string.split(' '); 24 if (parts.is_empty()) 25 return Error::from_string_literal("empty command"); 26 auto program = parts[0]; 27 parts.remove(0); 28 return command(program, parts, chdir); 29} 30 31ErrorOr<CommandResult> command(DeprecatedString const& program, Vector<DeprecatedString> const& arguments, Optional<LexicalPath> chdir) 32{ 33 int stdout_pipe[2] = {}; 34 int stderr_pipe[2] = {}; 35 if (pipe2(stdout_pipe, O_CLOEXEC)) { 36 return Error::from_errno(errno); 37 } 38 if (pipe2(stderr_pipe, O_CLOEXEC)) { 39 perror("pipe2"); 40 return Error::from_errno(errno); 41 } 42 43 auto close_pipes = ScopeGuard([stderr_pipe, stdout_pipe] { 44 // The write-ends of these pipes are closed manually 45 close(stdout_pipe[0]); 46 close(stderr_pipe[0]); 47 }); 48 49 Vector<char const*> parts = { program.characters() }; 50 for (auto const& part : arguments) { 51 parts.append(part.characters()); 52 } 53 parts.append(nullptr); 54 55 char const** argv = parts.data(); 56 57 posix_spawn_file_actions_t action; 58 posix_spawn_file_actions_init(&action); 59 if (chdir.has_value()) { 60 posix_spawn_file_actions_addchdir(&action, chdir.value().string().characters()); 61 } 62 posix_spawn_file_actions_adddup2(&action, stdout_pipe[1], STDOUT_FILENO); 63 posix_spawn_file_actions_adddup2(&action, stderr_pipe[1], STDERR_FILENO); 64 65 pid_t pid; 66 if ((errno = posix_spawnp(&pid, program.characters(), &action, nullptr, const_cast<char**>(argv), environ))) { 67 perror("posix_spawn"); 68 VERIFY_NOT_REACHED(); 69 } 70 71 // close the write-ends so reading wouldn't block 72 close(stdout_pipe[1]); 73 close(stderr_pipe[1]); 74 75 auto read_all_from_pipe = [](int pipe[2]) { 76 auto result_file = Core::DeprecatedFile::construct(); 77 if (!result_file->open(pipe[0], Core::OpenMode::ReadOnly, Core::DeprecatedFile::ShouldCloseFileDescriptor::Yes)) { 78 perror("open"); 79 VERIFY_NOT_REACHED(); 80 } 81 return DeprecatedString::copy(result_file->read_all()); 82 }; 83 auto output = read_all_from_pipe(stdout_pipe); 84 auto error = read_all_from_pipe(stderr_pipe); 85 86 int wstatus { 0 }; 87 waitpid(pid, &wstatus, 0); 88 posix_spawn_file_actions_destroy(&action); 89 int exit_code = WEXITSTATUS(wstatus); 90 91 if (exit_code != 0) { 92# ifdef DBG_FAILED_COMMANDS 93 dbgln("command failed. stderr: {}", ); 94# endif 95 } 96 97 return CommandResult { WEXITSTATUS(wstatus), output, error }; 98} 99 100#endif 101 102}