diff options
-rw-r--r-- | apt-pkg/edsp.cc | 42 | ||||
-rw-r--r-- | apt-pkg/edsp/edspsystem.cc | 3 | ||||
-rw-r--r-- | apt-pkg/init.cc | 4 | ||||
-rw-r--r-- | apt-private/private-cmndline.cc | 20 | ||||
-rw-r--r-- | apt-private/private-cmndline.h | 1 | ||||
-rw-r--r-- | apt-private/private-main.cc | 1 | ||||
-rw-r--r-- | cmdline/apt-dump-solver.cc | 215 | ||||
-rw-r--r-- | cmdline/apt-internal-solver.cc | 1 | ||||
-rw-r--r-- | cmdline/makefile | 4 | ||||
-rwxr-xr-x | test/integration/test-external-dependency-solver-protocol | 19 |
10 files changed, 232 insertions, 78 deletions
diff --git a/apt-pkg/edsp.cc b/apt-pkg/edsp.cc index 94cac4eb1..58d2769f9 100644 --- a/apt-pkg/edsp.cc +++ b/apt-pkg/edsp.cc @@ -597,10 +597,12 @@ bool EDSP::WriteRequest(pkgDepCache &Cache, FileFd &output, WriteOkay(Okay, output, "Forbid-New-Install: yes\n"); if (flags & Request::FORBID_REMOVE) WriteOkay(Okay, output, "Forbid-Remove: yes\n"); + auto const solver = _config->Find("APT::Solver", "internal"); + WriteOkay(Okay, output, "Solver: ", solver, "\n"); if (_config->FindB("APT::Solver::Strict-Pinning", true) == false) WriteOkay(Okay, output, "Strict-Pinning: no\n"); string solverpref("APT::Solver::"); - solverpref.append(_config->Find("APT::Solver", "internal")).append("::Preferences"); + solverpref.append(solver).append("::Preferences"); if (_config->Exists(solverpref) == true) WriteOkay(Okay, output, "Preferences: ", _config->Find(solverpref,""), "\n"); return WriteOkay(Okay, output, "\n"); @@ -926,15 +928,23 @@ bool EDSP::WriteError(char const * const uuid, std::string const &message, FileF "\n\n"); } /*}}}*/ +static std::string findExecutable(std::vector<std::string> const &dirs, char const * const binary) {/*{{{*/ + for (auto && dir : dirs) { + std::string const file = flCombine(dir, binary); + if (RealFileExists(file) == true) + return file; + } + return ""; +} + /*}}}*/ static pid_t ExecuteExternal(char const* const type, char const * const binary, char const * const configdir, int * const solver_in, int * const solver_out) {/*{{{*/ - std::vector<std::string> const solverDirs = _config->FindVector(configdir); - std::string file; - for (std::vector<std::string>::const_iterator dir = solverDirs.begin(); - dir != solverDirs.end(); ++dir) { - file = flCombine(*dir, binary); - if (RealFileExists(file.c_str()) == true) - break; - file.clear(); + auto const solverDirs = _config->FindVector(configdir); + auto const file = findExecutable(solverDirs, binary); + std::string dumper; + { + dumper = findExecutable(solverDirs, "apt-dump-solver"); + if (dumper.empty()) + dumper = findExecutable(solverDirs, "dump"); } if (file.empty() == true) @@ -955,8 +965,18 @@ static pid_t ExecuteExternal(char const* const type, char const * const binary, if (Solver == 0) { dup2(external[0], STDIN_FILENO); dup2(external[3], STDOUT_FILENO); - const char* calling[2] = { file.c_str(), 0 }; - execv(calling[0], (char**) calling); + auto const dumpfile = _config->FindFile((std::string("Dir::Log::") + type).c_str()); + auto const dumpdir = flNotFile(dumpfile); + if (dumper.empty() || dumpfile.empty() || dumper == file || CreateAPTDirectoryIfNeeded(dumpdir, dumpdir) == false) + { + char const * const calling[] = { file.c_str(), nullptr }; + execv(calling[0], const_cast<char**>(calling)); + } + else + { + char const * const calling[] = { dumper.c_str(), dumpfile.c_str(), file.c_str(), nullptr }; + execv(calling[0], const_cast<char**>(calling)); + } std::cerr << "Failed to execute " << type << " '" << binary << "'!" << std::endl; _exit(100); } diff --git a/apt-pkg/edsp/edspsystem.cc b/apt-pkg/edsp/edspsystem.cc index 9b23dc3ca..2a78efe58 100644 --- a/apt-pkg/edsp/edspsystem.cc +++ b/apt-pkg/edsp/edspsystem.cc @@ -60,9 +60,12 @@ pkgPackageManager *edspLikeSystem::CreatePM(pkgDepCache * /*Cache*/) const // System::Initialize - Setup the configuration space.. /*{{{*/ bool edspLikeSystem::Initialize(Configuration &Cnf) { + Cnf.Set("Dir::Log", "/dev/null"); // state is included completely in the input files + Cnf.Set("Dir::Etc::preferences", "/dev/null"); Cnf.Set("Dir::Etc::preferencesparts", "/dev/null"); Cnf.Set("Dir::State::status","/dev/null"); + Cnf.Set("Dir::State::extended_states","/dev/null"); Cnf.Set("Dir::State::lists","/dev/null"); // do not store an mmap cache Cnf.Set("Dir::Cache::pkgcache", ""); diff --git a/apt-pkg/init.cc b/apt-pkg/init.cc index 0dfb10978..a41d604d3 100644 --- a/apt-pkg/init.cc +++ b/apt-pkg/init.cc @@ -56,7 +56,7 @@ bool pkgInitConfig(Configuration &Cnf) Cnf.CndSet("Dir::Cache::archives","archives/"); Cnf.CndSet("Dir::Cache::srcpkgcache","srcpkgcache.bin"); Cnf.CndSet("Dir::Cache::pkgcache","pkgcache.bin"); - + // Configuration Cnf.CndSet("Dir::Etc","etc/apt/"); Cnf.CndSet("Dir::Etc::sourcelist","sources.list"); @@ -72,7 +72,7 @@ bool pkgInitConfig(Configuration &Cnf) Cnf.CndSet("Dir::Bin::solvers::","/usr/lib/apt/solvers"); Cnf.CndSet("Dir::Media::MountPath","/media/apt"); - // State + // State Cnf.CndSet("Dir::Log","var/log/apt"); Cnf.CndSet("Dir::Log::Terminal","term.log"); Cnf.CndSet("Dir::Log::History","history.log"); diff --git a/apt-private/private-cmndline.cc b/apt-private/private-cmndline.cc index 7e50b1401..135ee3c4e 100644 --- a/apt-private/private-cmndline.cc +++ b/apt-private/private-cmndline.cc @@ -131,6 +131,11 @@ static bool addArgumentsAPTConfig(std::vector<CommandLine::Args> &Args, char con return true; } /*}}}*/ +static bool addArgumentsAPTDumpSolver(std::vector<CommandLine::Args> &, char const * const)/*{{{*/ +{ + return true; +} + /*}}}*/ static bool addArgumentsAPTExtractTemplates(std::vector<CommandLine::Args> &Args, char const * const)/*{{{*/ { addArg('t',"tempdir","APT::ExtractTemplates::TempDir",CommandLine::HasArg); @@ -347,6 +352,7 @@ std::vector<CommandLine::Args> getCommandArgs(APT_CMD const Program, char const case APT_CMD::APT_CACHE: addArgumentsAPTCache(Args, Cmd); break; case APT_CMD::APT_CDROM: addArgumentsAPTCDROM(Args, Cmd); break; case APT_CMD::APT_CONFIG: addArgumentsAPTConfig(Args, Cmd); break; + case APT_CMD::APT_DUMP_SOLVER: addArgumentsAPTDumpSolver(Args, Cmd); break; case APT_CMD::APT_EXTRACTTEMPLATES: addArgumentsAPTExtractTemplates(Args, Cmd); break; case APT_CMD::APT_FTPARCHIVE: addArgumentsAPTFTPArchive(Args, Cmd); break; case APT_CMD::APT_HELPER: addArgumentsAPTHelper(Args, Cmd); break; @@ -402,6 +408,7 @@ static bool ShowCommonHelp(APT_CMD const Binary, CommandLine &CmdL, std::vector< case APT_CMD::APT_CACHE: cmd = "apt-cache(8)"; break; case APT_CMD::APT_CDROM: cmd = "apt-cdrom(8)"; break; case APT_CMD::APT_CONFIG: cmd = "apt-config(8)"; break; + case APT_CMD::APT_DUMP_SOLVER: cmd = nullptr; break; case APT_CMD::APT_EXTRACTTEMPLATES: cmd = "apt-extracttemplates(1)"; break; case APT_CMD::APT_FTPARCHIVE: cmd = "apt-ftparchive(1)"; break; case APT_CMD::APT_GET: cmd = "apt-get(8)"; break; @@ -412,14 +419,15 @@ static bool ShowCommonHelp(APT_CMD const Binary, CommandLine &CmdL, std::vector< } if (cmd != nullptr) ioprintf(std::cout, _("See %s for more information about the available commands."), cmd); - std::cout << std::endl << - _("Configuration options and syntax is detailed in apt.conf(5).\n" - "Information about how to configure sources can be found in sources.list(5).\n" - "Package and version choices can be expressed via apt_preferences(5).\n" - "Security details are available in apt-secure(8).\n"); + if (Binary != APT_CMD::APT_DUMP_SOLVER && Binary != APT_CMD::APT_INTERNAL_SOLVER) + std::cout << std::endl << + _("Configuration options and syntax is detailed in apt.conf(5).\n" + "Information about how to configure sources can be found in sources.list(5).\n" + "Package and version choices can be expressed via apt_preferences(5).\n" + "Security details are available in apt-secure(8).\n"); if (Binary == APT_CMD::APT_GET || Binary == APT_CMD::APT) std::cout << std::right << std::setw(70) << _("This APT has Super Cow Powers.") << std::endl; - else if (Binary == APT_CMD::APT_HELPER) + else if (Binary == APT_CMD::APT_HELPER || Binary == APT_CMD::APT_DUMP_SOLVER) std::cout << std::right << std::setw(70) << _("This APT helper has Super Meep Powers.") << std::endl; return true; } diff --git a/apt-private/private-cmndline.h b/apt-private/private-cmndline.h index 6235ef9f5..c0c5a7455 100644 --- a/apt-private/private-cmndline.h +++ b/apt-private/private-cmndline.h @@ -21,6 +21,7 @@ enum class APT_CMD { APT_INTERNAL_SOLVER, APT_MARK, APT_SORTPKG, + APT_DUMP_SOLVER, }; struct aptDispatchWithHelp { diff --git a/apt-private/private-main.cc b/apt-private/private-main.cc index d6517dd2a..5a5940b7e 100644 --- a/apt-private/private-main.cc +++ b/apt-private/private-main.cc @@ -34,6 +34,7 @@ void InitLocale(APT_CMD const binary) /*{{{*/ case APT_CMD::APT_MARK: textdomain("apt"); break; + case APT_CMD::APT_DUMP_SOLVER: case APT_CMD::APT_EXTRACTTEMPLATES: case APT_CMD::APT_FTPARCHIVE: case APT_CMD::APT_INTERNAL_SOLVER: diff --git a/cmdline/apt-dump-solver.cc b/cmdline/apt-dump-solver.cc index 0de248e75..c6d98cd97 100644 --- a/cmdline/apt-dump-solver.cc +++ b/cmdline/apt-dump-solver.cc @@ -7,70 +7,177 @@ ##################################################################### */ /*}}}*/ // 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 <string.h> -#include <unistd.h> #include <cstdio> #include <iostream> +#include <memory> #include <sstream> -#include <config.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include <string.h> +#include <unistd.h> + +#include <apti18n.h> /*}}}*/ -// 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<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[]) /*{{{*/ { - // we really don't need anything - DropPrivileges(); - - 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; - } - - FileFd stdoutfd; - if (stdoutfd.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false) - return 1; - - 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", stdoutfd); - return 0; - } - - RemoveFile(argv[0], filename); - FileFd input, output; - if (input.OpenDescriptor(STDIN_FILENO, FileFd::ReadOnly) == false || - output.Open(filename, FileFd::WriteOnly | FileFd::Create | FileFd::Exclusive, FileFd::Extension, 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(), stdoutfd); - return 0; - } - - EDSP::WriteError("ERR_JUST_DUMPING", "I am too dumb, i can just dump!\nPlease use one of my friends instead!", stdoutfd); - 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) { + 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; } diff --git a/cmdline/apt-internal-solver.cc b/cmdline/apt-internal-solver.cc index 8296e8d01..aecb0eaba 100644 --- a/cmdline/apt-internal-solver.cc +++ b/cmdline/apt-internal-solver.cc @@ -123,6 +123,7 @@ int main(int argc,const char *argv[]) /*{{{*/ _config->Set("APT::System", "Debian APT solver interface"); _config->Set("APT::Solver", "internal"); _config->Set("edsp::scenario", "/nonexistent/stdin"); + _config->Clear("Dir::Log"); FileFd output; if (output.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false) DIE("stdout couldn't be opened"); diff --git a/cmdline/makefile b/cmdline/makefile index 6d21b0803..cd8ff8252 100644 --- a/cmdline/makefile +++ b/cmdline/makefile @@ -88,8 +88,8 @@ include $(PROGRAM_H) # This just dumps out the state PROGRAM=apt-dump-solver -SLIBS = -lapt-pkg $(INTLLIBS) -LIB_MAKES = apt-pkg/makefile +SLIBS = -lapt-pkg -lapt-private $(INTLLIBS) +LIB_MAKES = apt-pkg/makefile apt-private/makefile SOURCE = apt-dump-solver.cc include $(PROGRAM_H) diff --git a/test/integration/test-external-dependency-solver-protocol b/test/integration/test-external-dependency-solver-protocol index 4b13edc09..10b07e896 100755 --- a/test/integration/test-external-dependency-solver-protocol +++ b/test/integration/test-external-dependency-solver-protocol @@ -25,11 +25,17 @@ insertpackage 'experimental' 'coolstuff' 'i386,amd64' '3' 'Depends: cool, stuff' setupaptarchive +testsuccess aptget install --solver apt coolstuff -s +testempty find -name 'edsp.last.*' +echo 'Dir::Log::Solver "edsp.last.xz";' > rootdir/etc/apt/apt.conf.d/log-edsp.conf + testfailure aptget install --solver dump coolstuff -s -testsuccess grep ERR_NO_FILENAME rootdir/tmp/testfailure.output +testsuccess grep 'ERR_NO_FILENAME' rootdir/tmp/testfailure.output +testfailure test -s rootdir/var/log/apt/edsp.last.xz export APT_EDSP_DUMP_FILENAME="/nonexistent/apt/edsp.dump" testfailure aptget install --solver dump coolstuff -s -testsuccess grep ERR_WRITE_ERROR rootdir/tmp/testfailure.output +testsuccess grep 'ERR_CREATE_FILE' rootdir/tmp/testfailure.output +testfailure test -s rootdir/var/log/apt/edsp.last.xz export APT_EDSP_DUMP_FILENAME="${TMPWORKINGDIRECTORY}/downloaded/dump.edsp" testfailureequal 'Reading package lists... @@ -41,8 +47,8 @@ I am too dumb, i can just dump! Please use one of my friends instead! E: External solver failed with: I am too dumb, i can just dump!' aptget install --solver dump coolstuff -s +testfailure test -s rootdir/var/log/apt/edsp.last.xz testsuccess test -s "$APT_EDSP_DUMP_FILENAME" -rm -f "$APT_EDSP_DUMP_FILENAME" testsuccessequal 'Reading package lists... Building dependency tree... @@ -52,6 +58,12 @@ The following NEW packages will be installed: 0 upgraded, 1 newly installed, 0 to remove and 2 not upgraded. Inst coolstuff (2 unstable [amd64]) Conf coolstuff (2 unstable [amd64])' aptget install --solver apt coolstuff -s +testsuccess test -s rootdir/var/log/apt/edsp.last.xz +sed -i -e 's#^Solver: dump$#Solver: apt#' "$APT_EDSP_DUMP_FILENAME" +testequal "$(cat "$APT_EDSP_DUMP_FILENAME") +" apthelper cat-file rootdir/var/log/apt/edsp.last.xz +cp rootdir/var/log/apt/edsp.last.xz rootdir/var/log/apt/edsp.last.xz.1 +rm -f "$APT_EDSP_DUMP_FILENAME" testsuccessequal 'Reading package lists... Building dependency tree... @@ -61,6 +73,7 @@ The following NEW packages will be installed: 0 upgraded, 1 newly installed, 0 to remove and 2 not upgraded. Inst coolstuff (3 experimental [amd64]) Conf coolstuff (3 experimental [amd64])' aptget install --solver apt coolstuff -s -t experimental +testfailure cmp rootdir/var/log/apt/edsp.last.xz rootdir/var/log/apt/edsp.last.xz.1 testsuccessequal "Reading package lists... Building dependency tree... |