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 <apt-pkg/prettyprinters.h>
14 class StateChanges::Private
17 APT::VersionVector hold
;
18 APT::VersionVector unhold
;
19 APT::VersionVector install
;
20 APT::VersionVector deinstall
;
21 APT::VersionVector purge
;
22 APT::VersionVector error
;
25 #define APT_GETTERSETTER(Name, Container) \
26 void StateChanges::Name(pkgCache::VerIterator const &Ver) \
28 if (Ver.end() == false) \
29 Container.push_back(Ver); \
31 APT::VersionVector& StateChanges::Name() \
35 APT_GETTERSETTER(Hold
, d
->hold
)
36 APT_GETTERSETTER(Unhold
, d
->unhold
)
37 APT_GETTERSETTER(Install
, d
->install
)
38 APT_GETTERSETTER(Remove
, d
->deinstall
)
39 APT_GETTERSETTER(Purge
, d
->purge
)
40 #undef APT_GETTERSETTER
41 APT::VersionVector
& StateChanges::Error()
46 void StateChanges::clear()
56 bool StateChanges::empty() const
58 return d
->hold
.empty() &&
61 d
->deinstall
.empty() &&
66 bool StateChanges::Save(bool const DiscardOutput
)
68 bool const Debug
= _config
->FindB("Debug::pkgDpkgPm", false);
70 if (d
->hold
.empty() && d
->unhold
.empty() && d
->install
.empty() && d
->deinstall
.empty() && d
->purge
.empty())
73 std::vector
<std::string
> Args
= debSystem::GetDpkgBaseCommand();
74 // ensure dpkg knows about the package so that it keeps the status we set
75 if (d
->hold
.empty() == false || d
->install
.empty() == false)
77 APT::VersionVector makeDpkgAvailable
;
78 auto const notInstalled
= [](pkgCache::VerIterator
const &V
) { return V
.ParentPkg()->CurrentVer
== 0; };
79 std::copy_if(d
->hold
.begin(), d
->hold
.end(), std::back_inserter(makeDpkgAvailable
), notInstalled
);
80 std::copy_if(d
->install
.begin(), d
->install
.end(), std::back_inserter(makeDpkgAvailable
), notInstalled
);
82 if (makeDpkgAvailable
.empty() == false)
84 auto const BaseArgs
= Args
.size();
85 Args
.push_back("--merge-avail");
86 // FIXME: supported only since 1.17.7 in dpkg
91 for (auto const &V
: makeDpkgAvailable
)
93 std::clog
<< "echo 'Dummy record for " << V
.ParentPkg().FullName(false) << "' | ";
94 std::copy(Args
.begin(), Args
.end(), std::ostream_iterator
<std::string
>(std::clog
, " "));
95 std::clog
<< std::endl
;
100 pid_t
const dpkgMergeAvail
= debSystem::ExecDpkg(Args
, &dummyAvail
, nullptr, true);
102 FILE* dpkg
= fdopen(dummyAvail
, "w");
103 for (auto const &V
: makeDpkgAvailable
)
104 fprintf(dpkg
, "Package: %s\nVersion: 0~\nArchitecture: %s\nMaintainer: Dummy Example <dummy@example.org>\n"
105 "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());
108 ExecWait(dpkgMergeAvail
, "dpkg --merge-avail", true);
110 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
113 bool const dpkgMultiArch
= _system
->MultiArchSupported();
115 Args
.push_back("--set-selections");
119 auto const dpkgName
= [&](pkgCache::VerIterator
const &V
) {
120 pkgCache::PkgIterator P
= V
.ParentPkg();
121 if (strcmp(V
.Arch(), "none") == 0)
122 ioprintf(std::clog
, "echo '%s %s' | ", P
.Name(), state
.c_str());
123 else if (dpkgMultiArch
== false)
124 ioprintf(std::clog
, "echo '%s %s' | ", P
.FullName(true).c_str(), state
.c_str());
126 ioprintf(std::clog
, "echo '%s:%s %s' | ", P
.Name(), V
.Arch(), state
.c_str());
127 std::copy(Args
.begin(), Args
.end(), std::ostream_iterator
<std::string
>(std::clog
, " "));
128 std::clog
<< std::endl
;
130 for (auto const &V
: d
->unhold
)
132 if (V
.ParentPkg()->CurrentVer
!= 0)
138 if (d
->purge
.empty() == false)
141 std::for_each(d
->purge
.begin(), d
->purge
.end(), dpkgName
);
143 if (d
->deinstall
.empty() == false)
146 std::for_each(d
->deinstall
.begin(), d
->deinstall
.end(), dpkgName
);
148 if (d
->hold
.empty() == false)
151 std::for_each(d
->hold
.begin(), d
->hold
.end(), dpkgName
);
153 if (d
->install
.empty() == false)
156 std::for_each(d
->install
.begin(), d
->install
.end(), dpkgName
);
162 pid_t
const dpkgSelections
= debSystem::ExecDpkg(Args
, &selections
, nullptr, DiscardOutput
);
164 FILE* dpkg
= fdopen(selections
, "w");
166 auto const dpkgName
= [&](pkgCache::VerIterator
const &V
) {
167 pkgCache::PkgIterator P
= V
.ParentPkg();
168 if (strcmp(V
.Arch(), "none") == 0)
169 fprintf(dpkg
, "%s %s\n", P
.Name(), state
.c_str());
170 else if (dpkgMultiArch
== false)
171 fprintf(dpkg
, "%s %s\n", P
.FullName(true).c_str(), state
.c_str());
173 fprintf(dpkg
, "%s:%s %s\n", P
.Name(), V
.Arch(), state
.c_str());
175 for (auto const &V
: d
->unhold
)
177 if (V
.ParentPkg()->CurrentVer
!= 0)
183 if (d
->purge
.empty() == false)
186 std::for_each(d
->purge
.begin(), d
->purge
.end(), dpkgName
);
188 if (d
->deinstall
.empty() == false)
191 std::for_each(d
->deinstall
.begin(), d
->deinstall
.end(), dpkgName
);
193 if (d
->hold
.empty() == false)
196 std::for_each(d
->hold
.begin(), d
->hold
.end(), dpkgName
);
198 if (d
->install
.empty() == false)
201 std::for_each(d
->install
.begin(), d
->install
.end(), dpkgName
);
205 if (ExecWait(dpkgSelections
, "dpkg --set-selections") == false)
207 std::move(d
->purge
.begin(), d
->purge
.end(), std::back_inserter(d
->error
));
209 std::move(d
->deinstall
.begin(), d
->deinstall
.end(), std::back_inserter(d
->error
));
210 d
->deinstall
.clear();
211 std::move(d
->hold
.begin(), d
->hold
.end(), std::back_inserter(d
->error
));
213 std::move(d
->unhold
.begin(), d
->unhold
.end(), std::back_inserter(d
->error
));
215 std::move(d
->install
.begin(), d
->install
.end(), std::back_inserter(d
->error
));
219 return d
->error
.empty();
222 StateChanges::StateChanges() : d(new StateChanges::Private()) {}
223 StateChanges::StateChanges(StateChanges
&&) = default;
224 StateChanges
& StateChanges::operator=(StateChanges
&&) = default;
225 StateChanges::~StateChanges() = default;