Package: apt
Severity: wishlist
X-Debbugs-CC: [email protected]
Hi,
APT should (eventually) warn when installing packages that are not
reproducible.
Clearly, all the bits to make this work today are not in dak, APT, the
mirrors, etc. However, I thought it was best to experiment early with
the potential user interface.
This would ensure that we know exactly what data we need and we don't
make a big mistake and miss something.
To this end, I've attached a proof of concept patch. Example output:
$ apt install python-pywt-doc
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
python-pywt-doc
0 upgraded, 1 newly installed, 0 to remove and 4 not upgraded.
Need to get 102 kB of archives.
After this operation, 978 kB of additional disk space will be used.
WARNING: The following packages are not reproducible!
python-pywt-doc
Install these packages anyway? [y/N]
$ echo $?
130
It takes an expected "--allow-unreproducible" argument, as well as an
"-o Debug::pkgAcquire::Reproducible=true" if you want to debug it. I
might play with it more at https://github.com/lamby/apt on the
reproducible-ui branch:
https://github.com/lamby/apt/tree/lamby/wip/reproducible-ui
Just to be clear, the patch is obviously an digusting hack and you
should not use it, hence the lack of a "patch" tag (!).
(We would also — later please! — need to agree on what "reproducible"
really means in terms of multiple builders.)
Regards,
--
,''`.
: :' : Chris Lamb
`. `'` [email protected] / chris-lamb.co.uk
`-
>From a381af380f642080d11048d767fe7eb3704a74ce Mon Sep 17 00:00:00 2001
From: Chris Lamb <[email protected]>
Date: Thu, 15 Dec 2016 22:58:43 +0100
Subject: [PATCH] Warn when installing packages that are not reproducible.
** This is obviously an digusting hack and you should not use it. **
It is only a proof-of-concept to experiment with the user-facing interface
of such a warning.
Signed-off-by: Chris Lamb <[email protected]>
---
apt-pkg/init.cc | 1 +
apt-private/private-cmndline.cc | 1 +
apt-private/private-download.cc | 130 +++++++++++++++++++++++++++++++++++++++-
apt-private/private-download.h | 6 ++
apt-private/private-install.cc | 3 +
completions/bash/apt | 1 +
debian/control | 3 +
7 files changed, 144 insertions(+), 1 deletion(-)
diff --git a/apt-pkg/init.cc b/apt-pkg/init.cc
index 00d991027..8142ea1d8 100644
--- a/apt-pkg/init.cc
+++ b/apt-pkg/init.cc
@@ -145,6 +145,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");
+ Cnf.CndSet("Dir::Cache::reproduciblecache","reproducible.json.bz2");
// Configuration
Cnf.CndSet("Dir::Etc", CONF_DIR + 1);
diff --git a/apt-private/private-cmndline.cc b/apt-private/private-cmndline.cc
index de3992a00..ca218d1ec 100644
--- a/apt-private/private-cmndline.cc
+++ b/apt-private/private-cmndline.cc
@@ -274,6 +274,7 @@ static bool addArgumentsAPTGet(std::vector<CommandLine::Args> &Args, char const
addArg(0,"only-source","APT::Get::Only-Source",0);
addArg(0,"allow-unauthenticated","APT::Get::AllowUnauthenticated",0);
addArg(0,"allow-insecure-repositories","Acquire::AllowInsecureRepositories",0);
+ addArg(0,"allow-unreproducible","APT::Get::AllowUnreproducible",0);
addArg(0,"allow-weak-repositories","Acquire::AllowWeakRepositories",0);
addArg(0,"install-recommends","APT::Install-Recommends",CommandLine::Boolean);
addArg(0,"install-suggests","APT::Install-Suggests",CommandLine::Boolean);
diff --git a/apt-private/private-download.cc b/apt-private/private-download.cc
index ee477f4cb..85235ad6e 100644
--- a/apt-private/private-download.cc
+++ b/apt-private/private-download.cc
@@ -85,6 +85,132 @@ bool AuthPrompt(std::vector<std::string> const &UntrustedList, bool const Prompt
return _error->Error(_("There were unauthenticated packages and -y was used without --allow-unauthenticated"));
}
+
+// GetOutput - execute CmdLine and place the first line in output /*{{{*/
+static bool GetOutput(std::string &output, std::string const CmdLine, bool const Debug)
+{
+ pid_t Child;
+ FileFd PipeFd;
+ char buf[1024];
+
+ if (Debug)
+ std::cerr << CmdLine << std::endl;
+
+ std::vector<const char *> Args = {"/bin/sh", "-c", CmdLine.c_str(), nullptr};
+ if (Popen(&Args[0], PipeFd, Child, FileFd::ReadOnly, false) == false)
+ return false;
+
+ PipeFd.ReadLine(buf, sizeof(buf));
+ buf[sizeof(buf) - 1] = '\0';
+ PipeFd.Close();
+
+ if (ExecWait(Child, "sh") == false)
+ return false;
+
+ output = _strstrip(buf);
+
+ return true;
+}
+
+// CheckReproducible - check if each download comes form a reproducible source /*{{{*/
+bool CheckReproducible(pkgAcquire& Fetcher, bool const PromptUser)
+{
+ if (_config->FindB("APT::Get::AllowUnreproducible", false))
+ return true;
+
+ std::string Output;
+ std::vector<std::string> UnreproducibleList;
+ bool const Debug = _config->FindB("Debug::pkgAcquire::Reproducible",false);
+
+ std::string const Url = _config->Find("APT::Get::ReproducibleStatusJsonUrl",
+ "https://tests.reproducible-builds.org/reproducible.json.bz2");
+ std::string const NativeArch = _config->Find("APT::Architecture");
+ std::string const CacheFileName = _config->FindFile("Dir::Cache::reproduciblecache");
+ std::string const DefaultRelease = _config->Find("APT::Default-Release","unstable");
+
+ // Update local status file
+ std::string const UpdateCommand =
+ std::string("/usr/bin/curl")
+ + ((Debug) ? "" : " --silent")
+ + " --location"
+ + " -z " + CacheFileName
+ + " -o " + CacheFileName
+ + " " + Url;
+ if (GetOutput(Output, UpdateCommand, Debug) == false)
+ return _error->Error(_("Could not update reproducible cache"));
+
+ for (pkgAcquire::ItemIterator I = Fetcher.ItemsBegin(); I < Fetcher.ItemsEnd(); ++I) {
+ std::string const BinaryPkg = (*I)->ShortDesc();
+ std::string SrcPkg = BinaryPkg;
+
+ if (Debug)
+ std::cerr << "Checking reproducibility of " << BinaryPkg << std::endl;
+
+ // Determine source package name
+ std::string const SourcePackageCommand =
+ "apt-cache show " + BinaryPkg + " | awk '/Source: / { print $2 }'";
+ if (GetOutput(Output, SourcePackageCommand, Debug) == false)
+ return _error->Error(_("Could not check source package name"));
+
+ // If we got output, update the source package name
+ if (Output.length() > 0)
+ SrcPkg = Output;
+
+ std::string const JqCommand =
+ "bunzip2 -c " + CacheFileName + " | " +
+ "jq --compact-output --raw-output '.[] | " +
+ "select(.suite==\"" + DefaultRelease + "\") | " +
+ "select(.package==\"" + SrcPkg + "\") | " +
+ "select(.status==\"reproducible\") | " +
+ "select(.architecture==\"" + NativeArch + "\")" +
+ "'";
+ if (GetOutput(Output, JqCommand, Debug) == false)
+ return _error->Error(_("Could not filter reproducible status"));
+
+ // If we got no output, we failed to match filters
+ if (Output.length() == 0)
+ UnreproducibleList.push_back(BinaryPkg);
+ }
+
+ if (UnreproducibleList.empty())
+ return true;
+
+ return ReproduciblePrompt(UnreproducibleList, PromptUser);
+}
+ /*}}}*/
+
+
+bool ReproduciblePrompt(std::vector<std::string> const &UnreproducibleList, bool const PromptUser)/*{{{*/
+{
+ ShowList(c2out,_("WARNING: The following packages are not reproducible!"), UnreproducibleList,
+ [](std::string const&) { return true; },
+ [](std::string const&str) { return str; },
+ [](std::string const&) { return ""; });
+
+ if (_config->FindB("APT::Get::AllowUnreproducible",false) == true)
+ {
+ c2out << _("Unreproducible warning overridden.\n");
+ return true;
+ }
+
+ if (PromptUser == false)
+ return _error->Error(_("Some packages are not reproducible"));
+
+ if (_config->FindI("quiet",0) < 2
+ && _config->FindB("APT::Get::Assume-Yes",false) == false)
+ {
+ if (!YnPrompt(_("Install these packages anyway?"), false))
+ return _error->Error(_("Some packages are not reproducible"));
+
+ return true;
+ }
+ else if (_config->FindB("APT::Get::Force-Yes",false) == true) {
+ _error->Warning(_("--force-yes is deprecated, use one of the options starting with --allow instead."));
+ return true;
+ }
+
+ return _error->Error(_("There were unreproducible packages and -y was used without --allow-unreproducible"));
+}
/*}}}*/
bool AcquireRun(pkgAcquire &Fetcher, int const PulseInterval, bool * const Failure, bool * const TransientNetworkFailure)/*{{{*/
{
@@ -213,7 +339,9 @@ bool DoDownload(CommandLine &CmdL)
return true;
}
- if (_error->PendingError() == true || CheckAuth(Fetcher, false) == false)
+ if (_error->PendingError() == true
+ || CheckAuth(Fetcher, false) == false
+ || CheckReproducible(Fetcher, false) == false)
return false;
bool Failed = false;
diff --git a/apt-private/private-download.h b/apt-private/private-download.h
index d829e8b24..b3fabc14d 100644
--- a/apt-private/private-download.h
+++ b/apt-private/private-download.h
@@ -16,6 +16,12 @@ bool CheckAuth(pkgAcquire& Fetcher, bool const PromptUser);
// should continue
bool AuthPrompt(std::vector<std::string> const &UntrustedList, bool const PromptUser);
+// Check if all files in the fetcher are reproducible
+bool CheckReproducible(pkgAcquire& Fetcher, bool const PromptUser);
+
+// show a warning prompt and return true if the system should continue
+bool ReproduciblePrompt(std::vector<std::string> const &UnreproducibleList, bool const PromptUser);
+
APT_PUBLIC bool AcquireRun(pkgAcquire &Fetcher, int const PulseInterval, bool * const Failure, bool * const TransientNetworkFailure);
bool CheckFreeSpaceBeforeDownload(std::string const &Dir, unsigned long long FetchBytes);
diff --git a/apt-private/private-install.cc b/apt-private/private-install.cc
index 73a03a828..d9a7ead20 100644
--- a/apt-private/private-install.cc
+++ b/apt-private/private-install.cc
@@ -310,6 +310,9 @@ bool InstallPackages(CacheFile &Cache,bool ShwKept,bool Ask, bool Safety)
if (!CheckAuth(Fetcher, true))
return false;
+ if (!CheckReproducible(Fetcher, true))
+ return false;
+
/* Unlock the dpkg lock if we are not going to be doing an install
after. */
if (_config->FindB("APT::Get::Download-Only",false) == true)
diff --git a/completions/bash/apt b/completions/bash/apt
index f7dd61f3b..293a69be1 100644
--- a/completions/bash/apt
+++ b/completions/bash/apt
@@ -27,6 +27,7 @@ _apt()
--arch-only
--allow-unauthenticated
--allow-insecure-repositories
+ --allow-unreproducible
--install-recommends
--install-suggests
--fix-policy
diff --git a/debian/control b/debian/control
index 96bbef348..e4f9e4933 100644
--- a/debian/control
+++ b/debian/control
@@ -32,6 +32,9 @@ Package: apt
Architecture: any
Depends: adduser,
gpgv | gpgv2 | gpgv1,
+ curl,
+ ca-certificates,
+ jq,
${apt:keyring},
${misc:Depends},
${shlibs:Depends}
--
2.11.0
_______________________________________________
Reproducible-builds mailing list
[email protected]
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/reproducible-builds