summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apt-pkg/edsp.cc42
-rw-r--r--apt-pkg/edsp/edspsystem.cc3
-rw-r--r--apt-pkg/init.cc4
-rw-r--r--apt-private/private-cmndline.cc20
-rw-r--r--apt-private/private-cmndline.h1
-rw-r--r--apt-private/private-main.cc1
-rw-r--r--cmdline/apt-dump-solver.cc215
-rw-r--r--cmdline/apt-internal-solver.cc1
-rw-r--r--cmdline/makefile4
-rwxr-xr-xtest/integration/test-external-dependency-solver-protocol19
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...