X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/92b2e38dd1334d7f7a30358124c4fad766ca4666..HEAD:/cmdline/apt-dump-solver.cc diff --git a/cmdline/apt-dump-solver.cc b/cmdline/apt-dump-solver.cc index 2e352931f..e94021fcf 100644 --- a/cmdline/apt-dump-solver.cc +++ b/cmdline/apt-dump-solver.cc @@ -7,65 +7,179 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ +#include + +#include +#include #include +#include +#include + +#include -#include -#include #include #include +#include #include -#include +#include +#include + +#include +#include + +#include /*}}}*/ -// ShowHelp - Show a help screen /*{{{*/ -// --------------------------------------------------------------------- -/* */ -static bool ShowHelp() { - ioprintf(std::cout, "%s %s (%s)\n", PACKAGE, PACKAGE_VERSION, COMMON_ARCH); - std::cout << - "Usage: apt-dump-solver\n" - "\n" - "apt-dump-solver is a dummy solver who just dumps its input to the\n" - "file specified in the environment variable APT_EDSP_DUMP_FILENAME and\n" - "exists with a proper EDSP error.\n" - "\n" - " This dump has lost Super Cow Powers.\n"; - return true; +static bool ShowHelp(CommandLine &) /*{{{*/ +{ + std::cout << + _("Usage: apt-dump-solver\n" + "\n" + "apt-dump-solver is an interface to store an EDSP scenario in\n" + "a file and optionally forwards it to another solver.\n"); + return true; +} + /*}}}*/ +static std::vector GetCommands() /*{{{*/ +{ + return {}; +} + /*}}}*/ +static int WriteError(char const * const uid, std::ostringstream &out, FileFd &stdoutfd, pid_t const &Solver)/*{{{*/ +{ + _error->DumpErrors(out); + // ensure the solver isn't printing into "our" error message, too + if (Solver != 0) + ExecWait(Solver, "dump", true); + EDSP::WriteError(uid, out.str(), stdoutfd); + return 0; } /*}}}*/ int main(int argc,const char *argv[]) /*{{{*/ { - if (argc > 1 && (strcmp(argv[1], "--help") == 0 || strcmp(argv[1],"-h") == 0 || - strcmp(argv[1],"-v") == 0 || strcmp(argv[1],"--version") == 0)) { - ShowHelp(); - return 0; - } - - // we really don't need anything - DropPrivileges(); - char const * const filename = getenv("APT_EDSP_DUMP_FILENAME"); - if (filename == NULL || strlen(filename) == 0) - { - EDSP::WriteError("ERR_NO_FILENAME", "You have to set the environment variable APT_EDSP_DUMP_FILENAME\n" - "to a valid filename to store the dump of EDSP solver input in.\n" - "For example with: export APT_EDSP_DUMP_FILENAME=/tmp/dump.edsp", stdout); - return 0; - } - - unlink(filename); - FileFd input, output; - if (input.OpenDescriptor(STDIN_FILENO, FileFd::ReadOnly) == false || - output.Open(filename, FileFd::WriteOnly | FileFd::Create | FileFd::Exclusive, 0600) == false || - CopyFile(input, output) == false || input.Close() == false || output.Close() == false) - { - std::ostringstream out; - out << "Writing EDSP solver input to file '" << filename << "' failed!\n"; - _error->DumpErrors(out); - EDSP::WriteError("ERR_WRITE_ERROR", out.str(), stdout); - return 0; - } - - EDSP::WriteError("ERR_JUST_DUMPING", "I am too dumb, i can just dump!\nPlease use one of my friends instead!", stdout); - return 0; + CommandLine CmdL; + ParseCommandLine(CmdL, APT_CMD::APT_DUMP_SOLVER, &_config, nullptr, argc, argv, &ShowHelp, &GetCommands); + _config->Clear("Dir::Log"); + + bool const is_forwarding_dumper = (CmdL.FileSize() != 0); + + FileFd stdoutfd; + if (stdoutfd.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false) + return 252; + + FileFd dump; + char const * const filename = is_forwarding_dumper ? CmdL.FileList[0] : getenv("APT_EDSP_DUMP_FILENAME"); + if (filename == nullptr || strlen(filename) == 0) + { + if (is_forwarding_dumper == false) + { + EDSP::WriteError("ERR_NO_FILENAME", "You have to set the environment variable APT_EDSP_DUMP_FILENAME\n" + "to a valid filename to store the dump of EDSP solver input in.\n" + "For example with: export APT_EDSP_DUMP_FILENAME=/tmp/dump.edsp", stdoutfd); + return 0; + } + } + else + { + // ignore errors here as logging isn't really critical + _error->PushToStack(); + if (dump.Open(filename, FileFd::WriteOnly | FileFd::Exclusive | FileFd::Create, FileFd::Extension, 0644) == false && + is_forwarding_dumper == false) + { + _error->MergeWithStack(); + std::ostringstream out; + out << "Writing EDSP solver input to file '" << filename << "' failed as it couldn't be created!\n"; + return WriteError("ERR_CREATE_FILE", out, stdoutfd, 0); + } + _error->RevertToStack(); + } + + pid_t Solver = 0; + FileFd forward; + if (is_forwarding_dumper) + { + int external[] = {-1, -1}; + if (pipe(external) != 0) + return 250; + for (int i = 0; i < 2; ++i) + SetCloseExec(external[i], true); + + Solver = ExecFork(); + if (Solver == 0) { + _config->Set("APT::Sandbox::User", _config->Find("APT::Solver::RunAsUser", _config->Find("APT::Sandbox::User"))); + DropPrivileges(); + dup2(external[0], STDIN_FILENO); + execv(CmdL.FileList[1], const_cast(CmdL.FileList + 1)); + std::cerr << "Failed to execute '" << CmdL.FileList[1] << "'!" << std::endl; + _exit(100); + } + close(external[0]); + + if (WaitFd(external[1], true, 5) == false) + return 251; + + if (forward.OpenDescriptor(external[1], FileFd::WriteOnly | FileFd::BufferedWrite, true) == false) + return 252; + } + + DropPrivileges(); + + FileFd input; + if (input.OpenDescriptor(STDIN_FILENO, FileFd::ReadOnly) == false) + { + std::ostringstream out; + out << "Writing EDSP solver input to file '" << filename << "' failed as stdin couldn't be opened!\n"; + return WriteError("ERR_READ_ERROR", out, stdoutfd, Solver); + } + + constexpr size_t BufSize = 64 * 1024; + std::unique_ptr Buf(new char[BufSize]); + unsigned long long ToRead = 0; + do { + if (input.Read(Buf.get(),BufSize, &ToRead) == false) + { + std::ostringstream out; + out << "Writing EDSP solver input to file '" << filename << "' failed as reading from stdin failed!\n"; + return WriteError("ERR_READ_ERROR", out, stdoutfd, Solver); + } + if (ToRead == 0) + break; + if (forward.IsOpen() && forward.Failed() == false && forward.Write(Buf.get(),ToRead) == false) + forward.Close(); + if (dump.IsOpen() && dump.Failed() == false && dump.Write(Buf.get(),ToRead) == false) + dump.Close(); + } while (true); + input.Close(); + forward.Close(); + dump.Close(); + + if (_error->PendingError()) + { + std::ostringstream out; + out << "Writing EDSP solver input to file '" << filename << "' failed due to write errors!\n"; + return WriteError("ERR_WRITE_ERROR", out, stdoutfd, Solver); + } + + if (is_forwarding_dumper) + { + // Wait and collect the error code + int Status; + while (waitpid(Solver, &Status, 0) != Solver) + { + if (errno == EINTR) + continue; + + std::ostringstream out; + ioprintf(out, _("Waited for %s but it wasn't there"), CmdL.FileList[1]); + return WriteError("ERR_FORWARD", out, stdoutfd, 0); + } + if (WIFEXITED(Status)) + return WEXITSTATUS(Status); + else + return 255; + } + else + EDSP::WriteError("ERR_JUST_DUMPING", "I am too dumb, i can just dump!\nPlease use one of my friends instead!", stdoutfd); + return 0; }