diff options
Diffstat (limited to 'cmdline/apt-dump-solver.cc')
-rw-r--r-- | cmdline/apt-dump-solver.cc | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/cmdline/apt-dump-solver.cc b/cmdline/apt-dump-solver.cc new file mode 100644 index 000000000..e94021fcf --- /dev/null +++ b/cmdline/apt-dump-solver.cc @@ -0,0 +1,185 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +/* ##################################################################### + + dummy solver to get quickly a scenario file out of APT + + ##################################################################### */ + /*}}}*/ +// Include Files /*{{{*/ +#include <config.h> + +#include <apt-pkg/cmndline.h> +#include <apt-pkg/configuration.h> +#include <apt-pkg/edsp.h> +#include <apt-pkg/fileutl.h> +#include <apt-pkg/strutl.h> + +#include <apt-private/private-cmndline.h> + +#include <cstdio> +#include <iostream> +#include <memory> +#include <sstream> + +#include <sys/types.h> +#include <sys/wait.h> + +#include <string.h> +#include <unistd.h> + +#include <apti18n.h> + /*}}}*/ + +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<aptDispatchWithHelp> 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[]) /*{{{*/ +{ + 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<char**>(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<char[]> 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; +} |