]> git.saurik.com Git - apt.git/commitdiff
allow uninstalled packages to be put on hold
authorDavid Kalnischkies <david@kalnischkies.de>
Sun, 9 Nov 2014 14:40:19 +0000 (15:40 +0100)
committerDavid Kalnischkies <david@kalnischkies.de>
Sun, 9 Nov 2014 20:45:55 +0000 (21:45 +0100)
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
test/integration/test-apt-mark [new file with mode: 0755]

index 2702dbbd367e49fdb8de20c8c42a4a1a7863d466..860982f9e5d7b83d3e8c6ae007d1db57e8f2c2b9 100644 (file)
@@ -275,10 +275,64 @@ static bool DoHold(CommandLine &CmdL)
    }
 
    Args.erase(Args.begin() + BaseArgs, Args.end());
    }
 
    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};
    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 <dummy@example.org>\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");
 
    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());
       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(external[0], STDIN_FILENO);
-      dup2(nullfd, STDOUT_FILENO);
-      dup2(nullfd, STDERR_FILENO);
       execvp(Args[0], (char**) &Args[0]);
       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);
    }
 
       _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)
    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 (executable)
index 0000000..69e0f93
--- /dev/null
@@ -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