statechanges.cc 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. #include <apt-pkg/pkgcache.h>
  2. #include <apt-pkg/cacheset.h>
  3. #include <apt-pkg/debsystem.h>
  4. #include <apt-pkg/fileutl.h>
  5. #include <apt-pkg/statechanges.h>
  6. #include <algorithm>
  7. #include <memory>
  8. namespace APT
  9. {
  10. class StateChanges::Private
  11. {
  12. public:
  13. APT::VersionVector hold;
  14. APT::VersionVector unhold;
  15. APT::VersionVector install;
  16. APT::VersionVector deinstall;
  17. APT::VersionVector purge;
  18. APT::VersionVector error;
  19. };
  20. #define APT_GETTERSETTER(Name, Container) \
  21. void StateChanges::Name(pkgCache::VerIterator const &Ver) \
  22. { \
  23. Container.push_back(Ver); \
  24. }\
  25. APT::VersionVector& StateChanges::Name() \
  26. { \
  27. return Container; \
  28. }
  29. APT_GETTERSETTER(Hold, d->hold)
  30. APT_GETTERSETTER(Unhold, d->unhold)
  31. APT_GETTERSETTER(Install, d->install)
  32. APT_GETTERSETTER(Remove, d->deinstall)
  33. APT_GETTERSETTER(Purge, d->purge)
  34. #undef APT_GETTERSETTER
  35. APT::VersionVector& StateChanges::Error()
  36. {
  37. return d->error;
  38. }
  39. void StateChanges::clear()
  40. {
  41. d->hold.clear();
  42. d->unhold.clear();
  43. d->install.clear();
  44. d->deinstall.clear();
  45. d->purge.clear();
  46. d->error.clear();
  47. }
  48. bool StateChanges::empty() const
  49. {
  50. return d->hold.empty() &&
  51. d->unhold.empty() &&
  52. d->install.empty() &&
  53. d->deinstall.empty() &&
  54. d->purge.empty() &&
  55. d->error.empty();
  56. }
  57. bool StateChanges::Save(bool const DiscardOutput)
  58. {
  59. d->error.clear();
  60. if (d->hold.empty() && d->unhold.empty() && d->install.empty() && d->deinstall.empty() && d->purge.empty())
  61. return true;
  62. std::vector<std::string> Args = debSystem::GetDpkgBaseCommand();
  63. // ensure dpkg knows about the package so that it keeps the status we set
  64. if (d->hold.empty() == false || d->install.empty() == false)
  65. {
  66. APT::VersionVector makeDpkgAvailable;
  67. auto const notInstalled = [](pkgCache::VerIterator const &V) { return V.ParentPkg()->CurrentVer == 0; };
  68. std::copy_if(d->hold.begin(), d->hold.end(), std::back_inserter(makeDpkgAvailable), notInstalled);
  69. std::copy_if(d->install.begin(), d->install.end(), std::back_inserter(makeDpkgAvailable), notInstalled);
  70. if (makeDpkgAvailable.empty() == false)
  71. {
  72. auto const BaseArgs = Args.size();
  73. Args.push_back("--merge-avail");
  74. // FIXME: supported only since 1.17.7 in dpkg
  75. Args.push_back("-");
  76. int dummyAvail = -1;
  77. pid_t const dpkgMergeAvail = debSystem::ExecDpkg(Args, &dummyAvail, nullptr, true);
  78. FILE* dpkg = fdopen(dummyAvail, "w");
  79. for (auto const &V: makeDpkgAvailable)
  80. fprintf(dpkg, "Package: %s\nVersion: 0~\nArchitecture: %s\nMaintainer: Dummy Example <dummy@example.org>\n"
  81. "Description: dummy package record\n A record is needed to put a package on hold, so here it is.\n\n", V.ParentPkg().Name(), V.Arch());
  82. fclose(dpkg);
  83. ExecWait(dpkgMergeAvail, "dpkg --merge-avail", true);
  84. Args.erase(Args.begin() + BaseArgs, Args.end());
  85. }
  86. }
  87. bool const dpkgMultiArch = _system->MultiArchSupported();
  88. Args.push_back("--set-selections");
  89. int selections = -1;
  90. pid_t const dpkgSelections = debSystem::ExecDpkg(Args, &selections, nullptr, DiscardOutput);
  91. FILE* dpkg = fdopen(selections, "w");
  92. std::string state;
  93. auto const dpkgName = [&](pkgCache::VerIterator const &V) {
  94. pkgCache::PkgIterator P = V.ParentPkg();
  95. if (dpkgMultiArch == false)
  96. fprintf(dpkg, "%s %s\n", P.FullName(true).c_str(), state.c_str());
  97. else
  98. fprintf(dpkg, "%s:%s %s\n", P.Name(), V.Arch(), state.c_str());
  99. };
  100. for (auto const &V: d->unhold)
  101. {
  102. if (V.ParentPkg()->CurrentVer != 0)
  103. state = "install";
  104. else
  105. state = "deinstall";
  106. dpkgName(V);
  107. }
  108. if (d->purge.empty() == false)
  109. {
  110. state = "purge";
  111. std::for_each(d->purge.begin(), d->purge.end(), dpkgName);
  112. }
  113. if (d->deinstall.empty() == false)
  114. {
  115. state = "deinstall";
  116. std::for_each(d->deinstall.begin(), d->deinstall.end(), dpkgName);
  117. }
  118. if (d->hold.empty() == false)
  119. {
  120. state = "hold";
  121. std::for_each(d->hold.begin(), d->hold.end(), dpkgName);
  122. }
  123. if (d->install.empty() == false)
  124. {
  125. state = "install";
  126. std::for_each(d->install.begin(), d->install.end(), dpkgName);
  127. }
  128. fclose(dpkg);
  129. if (ExecWait(dpkgSelections, "dpkg --set-selections") == false)
  130. {
  131. std::move(d->purge.begin(), d->purge.end(), std::back_inserter(d->error));
  132. d->purge.clear();
  133. std::move(d->deinstall.begin(), d->deinstall.end(), std::back_inserter(d->error));
  134. d->deinstall.clear();
  135. std::move(d->hold.begin(), d->hold.end(), std::back_inserter(d->error));
  136. d->hold.clear();
  137. std::move(d->unhold.begin(), d->unhold.end(), std::back_inserter(d->error));
  138. d->unhold.clear();
  139. std::move(d->install.begin(), d->install.end(), std::back_inserter(d->error));
  140. d->install.clear();
  141. }
  142. return d->error.empty();
  143. }
  144. StateChanges::StateChanges() : d(new StateChanges::Private()) {}
  145. StateChanges::StateChanges(StateChanges&&) = default;
  146. StateChanges& StateChanges::operator=(StateChanges&&) = default;
  147. StateChanges::~StateChanges() = default;
  148. }