From 374f8492e6f109e8427816a8f513e5e8feda9049 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Sun, 9 Nov 2014 15:40:19 +0100 Subject: allow uninstalled packages to be put on hold MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dpkg wants to know about a package before it can be put on hold, so we have to at least hint about its existance in the available file it "maintaince" to know about such stuff. The simple thing would probably be to just feed all Packages files into dpkg as well, but what would be the point really? Exactly, so we take a shortcut here and just create dummies in the available file if we need to which isn't going to be that common as usually you are holding packages back and not off. Who would have thought that a simple feature like setting a package on hold requires more than 200 lines of codeā€¦ at least with the testcase it is now explicitly tested code. --- cmdline/apt-mark.cc | 63 ++++++++++++++++++++++++++++++++---- test/integration/test-apt-mark | 72 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 6 deletions(-) create mode 100755 test/integration/test-apt-mark diff --git a/cmdline/apt-mark.cc b/cmdline/apt-mark.cc index 2702dbbd3..860982f9e 100644 --- a/cmdline/apt-mark.cc +++ b/cmdline/apt-mark.cc @@ -275,10 +275,64 @@ static bool DoHold(CommandLine &CmdL) } Args.erase(Args.begin() + BaseArgs, Args.end()); - Args.push_back("--set-selections"); + Args.push_back("--merge-avail"); + Args.push_back("-"); Args.push_back(NULL); int external[2] = {-1, -1}; + if (pipe(external) != 0) + return _error->WarningE("DoHold", "Can't create IPC pipe for dpkg --merge-avail"); + + pid_t dpkgMergeAvail = ExecFork(); + if (dpkgMergeAvail == 0) + { + close(external[1]); + std::string const chrootDir = _config->FindDir("DPkg::Chroot-Directory"); + if (chrootDir != "/" && chroot(chrootDir.c_str()) != 0 && chdir("/") != 0) + _error->WarningE("getArchitecture", "Couldn't chroot into %s for dpkg --merge-avail", chrootDir.c_str()); + dup2(external[0], STDIN_FILENO); + int const nullfd = open("/dev/null", O_RDONLY); + dup2(nullfd, STDOUT_FILENO); + execvp(Args[0], (char**) &Args[0]); + _error->WarningE("dpkgGo", "Can't get dpkg --merge-avail running!"); + _exit(2); + } + + FILE* dpkg = fdopen(external[1], "w"); + for (APT::PackageList::iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg) + { + if (Pkg->CurrentVer != 0) + continue; + char const * Arch; + if (Pkg->VersionList != 0) + Arch = Pkg.VersionList().Arch(); + else + Arch = Pkg.Arch(); + fprintf(dpkg, "Package: %s\nVersion: 0~\nArchitecture: %s\nMaintainer: Dummy Example \n" + "Description: dummy package record\n A record is needed to put a package on hold, so here it is.\n\n", Pkg.Name(), Arch); + } + fclose(dpkg); + + if (dpkgMergeAvail > 0) + { + int Status = 0; + while (waitpid(dpkgMergeAvail, &Status, 0) != dpkgMergeAvail) + { + if (errno == EINTR) + continue; + _error->WarningE("dpkgGo", _("Waited for %s but it wasn't there"), "dpkg --merge-avail"); + break; + } + if (WIFEXITED(Status) == false || WEXITSTATUS(Status) != 0) + return _error->Error(_("Executing dpkg failed. Are you root?")); + } + + Args.erase(Args.begin() + BaseArgs, Args.end()); + Args.push_back("--set-selections"); + Args.push_back(NULL); + + external[0] = -1; + external[1] = -1; if (pipe(external) != 0) return _error->WarningE("DoHold", "Can't create IPC pipe for dpkg --set-selections"); @@ -289,16 +343,13 @@ static bool DoHold(CommandLine &CmdL) std::string const chrootDir = _config->FindDir("DPkg::Chroot-Directory"); if (chrootDir != "/" && chroot(chrootDir.c_str()) != 0 && chdir("/") != 0) _error->WarningE("getArchitecture", "Couldn't chroot into %s for dpkg --set-selections", chrootDir.c_str()); - int const nullfd = open("/dev/null", O_RDONLY); dup2(external[0], STDIN_FILENO); - dup2(nullfd, STDOUT_FILENO); - dup2(nullfd, STDERR_FILENO); execvp(Args[0], (char**) &Args[0]); - _error->WarningE("dpkgGo", "Can't detect if dpkg supports multi-arch!"); + _error->WarningE("dpkgGo", "Can't get dpkg --set-selections running!"); _exit(2); } - FILE* dpkg = fdopen(external[1], "w"); + dpkg = fdopen(external[1], "w"); for (APT::PackageList::iterator Pkg = pkgset.begin(); Pkg != pkgset.end(); ++Pkg) { if (dpkgMultiArch == false) diff --git a/test/integration/test-apt-mark b/test/integration/test-apt-mark new file mode 100755 index 000000000..69e0f933d --- /dev/null +++ b/test/integration/test-apt-mark @@ -0,0 +1,72 @@ +#!/bin/sh +set -e + +TESTDIR=$(readlink -f $(dirname $0)) +. $TESTDIR/framework +setupenvironment +configarchitecture 'amd64' 'i386' + +insertpackage 'unstable' 'bar' 'amd64,i386' '1' +insertpackage 'unstable' 'uninstalled' 'all' '1' +insertpackage 'unstable' 'uninstalled-native' 'amd64' '1' + +insertinstalledpackage 'foo' 'all' '1' +insertinstalledpackage 'bar' 'amd64' '1' + +setupaptarchive + +# dpkg is "installed" by our test framework +testdpkginstalled dpkg + +testnoautopkg() { + testempty aptmark showauto + testequal 'bar +dpkg +foo' aptmark showmanual + testequal 'bar +foo' aptmark showmanual bar foo uninstalled +} +testmarkonpkgasauto() { + testsuccess aptmark $1 foo + testequal 'foo' aptmark showauto + testequal 'foo' aptmark showauto foo + testequal 'bar +dpkg' aptmark showmanual + testequal 'bar' aptmark showmanual bar + + testsuccess aptmark $2 foo + testnoautopkg +} + +testequal 'E: No packages found' aptmark auto +testequal 'E: No packages found' aptmark manual + +testnoautopkg +testmarkonpkgasauto 'auto' 'manual' +testmarkonpkgasauto 'markauto' 'unmarkauto' + +testnoholdpkg() { + testempty aptmark showhold + testempty aptmark showholds # typical "typo" + testempty aptmark showhold dpkg + testempty aptmark showholds dpkg +} +testmarkonepkgashold() { + testsuccess aptmark hold $1 + testequal "$1" aptmark showhold + testequal "$1" aptmark showholds + testsuccess aptmark unhold $1 + testnoholdpkg +} + +testequal 'E: No packages found' aptmark hold +testequal 'E: No packages found' aptmark unhold + +testnoholdpkg +testmarkonepkgashold 'foo' +testmarkonepkgashold 'bar' +testmarkonepkgashold 'uninstalled' +testmarkonepkgashold 'uninstalled-native' + +testequal 'uninstalled set on hold.' aptmark hold uninstalled +testequal 'uninstalled-native set on hold.' aptmark hold uninstalled-native -- cgit v1.2.3