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 Container.push_back(Ver); \
30 APT::VersionVector& StateChanges::Name() \
34 APT_GETTERSETTER(Hold
, d
->hold
)
35 APT_GETTERSETTER(Unhold
, d
->unhold
)
36 APT_GETTERSETTER(Install
, d
->install
)
37 APT_GETTERSETTER(Remove
, d
->deinstall
)
38 APT_GETTERSETTER(Purge
, d
->purge
)
39 #undef APT_GETTERSETTER
40 APT::VersionVector
& StateChanges::Error()
45 void StateChanges::clear()
55 bool StateChanges::empty() const
57 return d
->hold
.empty() &&
60 d
->deinstall
.empty() &&
65 bool StateChanges::Save(bool const DiscardOutput
)
67 bool const Debug
= _config
->FindB("Debug::pkgDpkgPm", false);
69 if (d
->hold
.empty() && d
->unhold
.empty() && d
->install
.empty() && d
->deinstall
.empty() && d
->purge
.empty())
72 std::vector
<std::string
> Args
= debSystem::GetDpkgBaseCommand();
73 // ensure dpkg knows about the package so that it keeps the status we set
74 if (d
->hold
.empty() == false || d
->install
.empty() == false)
76 APT::VersionVector makeDpkgAvailable
;
77 auto const notInstalled
= [](pkgCache::VerIterator
const &V
) { return V
.ParentPkg()->CurrentVer
== 0; };
78 std::copy_if(d
->hold
.begin(), d
->hold
.end(), std::back_inserter(makeDpkgAvailable
), notInstalled
);
79 std::copy_if(d
->install
.begin(), d
->install
.end(), std::back_inserter(makeDpkgAvailable
), notInstalled
);
81 if (makeDpkgAvailable
.empty() == false)
83 auto const BaseArgs
= Args
.size();
84 Args
.push_back("--merge-avail");
85 // FIXME: supported only since 1.17.7 in dpkg
90 for (auto const &V
: makeDpkgAvailable
)
92 std::clog
<< "echo 'Dummy record for " << V
.ParentPkg().FullName(false) << "' | ";
93 std::copy(Args
.begin(), Args
.end(), std::ostream_iterator
<std::string
>(std::clog
, " "));
94 std::clog
<< std::endl
;
99 pid_t
const dpkgMergeAvail
= debSystem::ExecDpkg(Args
, &dummyAvail
, nullptr, true);
101 FILE* dpkg
= fdopen(dummyAvail
, "w");
102 for (auto const &V
: makeDpkgAvailable
)
103 fprintf(dpkg
, "Package: %s\nVersion: 0~\nArchitecture: %s\nMaintainer: Dummy Example <dummy@example.org>\n"
104 "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());
107 ExecWait(dpkgMergeAvail
, "dpkg --merge-avail", true);
109 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
112 bool const dpkgMultiArch
= _system
->MultiArchSupported();
114 Args
.push_back("--set-selections");
118 auto const dpkgName
= [&](pkgCache::VerIterator
const &V
) {
119 pkgCache::PkgIterator P
= V
.ParentPkg();
120 if (strcmp(V
.Arch(), "none") == 0)
121 ioprintf(std::clog
, "echo '%s %s' | ", P
.Name(), state
.c_str());
122 else if (dpkgMultiArch
== false)
123 ioprintf(std::clog
, "echo '%s %s' | ", P
.FullName(true).c_str(), state
.c_str());
125 ioprintf(std::clog
, "echo '%s:%s %s' | ", P
.Name(), V
.Arch(), state
.c_str());
126 std::copy(Args
.begin(), Args
.end(), std::ostream_iterator
<std::string
>(std::clog
, " "));
127 std::clog
<< std::endl
;
129 for (auto const &V
: d
->unhold
)
131 if (V
.ParentPkg()->CurrentVer
!= 0)
137 if (d
->purge
.empty() == false)
140 std::for_each(d
->purge
.begin(), d
->purge
.end(), dpkgName
);
142 if (d
->deinstall
.empty() == false)
145 std::for_each(d
->deinstall
.begin(), d
->deinstall
.end(), dpkgName
);
147 if (d
->hold
.empty() == false)
150 std::for_each(d
->hold
.begin(), d
->hold
.end(), dpkgName
);
152 if (d
->install
.empty() == false)
155 std::for_each(d
->install
.begin(), d
->install
.end(), dpkgName
);
161 pid_t
const dpkgSelections
= debSystem::ExecDpkg(Args
, &selections
, nullptr, DiscardOutput
);
163 FILE* dpkg
= fdopen(selections
, "w");
165 auto const dpkgName
= [&](pkgCache::VerIterator
const &V
) {
166 pkgCache::PkgIterator P
= V
.ParentPkg();
167 if (strcmp(V
.Arch(), "none") == 0)
168 fprintf(dpkg
, "%s %s\n", P
.Name(), state
.c_str());
169 else if (dpkgMultiArch
== false)
170 fprintf(dpkg
, "%s %s\n", P
.FullName(true).c_str(), state
.c_str());
172 fprintf(dpkg
, "%s:%s %s\n", P
.Name(), V
.Arch(), state
.c_str());
174 for (auto const &V
: d
->unhold
)
176 if (V
.ParentPkg()->CurrentVer
!= 0)
182 if (d
->purge
.empty() == false)
185 std::for_each(d
->purge
.begin(), d
->purge
.end(), dpkgName
);
187 if (d
->deinstall
.empty() == false)
190 std::for_each(d
->deinstall
.begin(), d
->deinstall
.end(), dpkgName
);
192 if (d
->hold
.empty() == false)
195 std::for_each(d
->hold
.begin(), d
->hold
.end(), dpkgName
);
197 if (d
->install
.empty() == false)
200 std::for_each(d
->install
.begin(), d
->install
.end(), dpkgName
);
204 if (ExecWait(dpkgSelections
, "dpkg --set-selections") == false)
206 std::move(d
->purge
.begin(), d
->purge
.end(), std::back_inserter(d
->error
));
208 std::move(d
->deinstall
.begin(), d
->deinstall
.end(), std::back_inserter(d
->error
));
209 d
->deinstall
.clear();
210 std::move(d
->hold
.begin(), d
->hold
.end(), std::back_inserter(d
->error
));
212 std::move(d
->unhold
.begin(), d
->unhold
.end(), std::back_inserter(d
->error
));
214 std::move(d
->install
.begin(), d
->install
.end(), std::back_inserter(d
->error
));
218 return d
->error
.empty();
221 StateChanges::StateChanges() : d(new StateChanges::Private()) {}
222 StateChanges::StateChanges(StateChanges
&&) = default;
223 StateChanges
& StateChanges::operator=(StateChanges
&&) = default;
224 StateChanges::~StateChanges() = default;