]> git.saurik.com Git - apt.git/blob - apt-pkg/statechanges.cc
support dpkg debug mode in APT::StateChanges
[apt.git] / apt-pkg / statechanges.cc
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>
7
8 #include <algorithm>
9 #include <memory>
10
11 namespace APT
12 {
13
14 class StateChanges::Private
15 {
16 public:
17 APT::VersionVector hold;
18 APT::VersionVector unhold;
19 APT::VersionVector install;
20 APT::VersionVector deinstall;
21 APT::VersionVector purge;
22 APT::VersionVector error;
23 };
24
25 #define APT_GETTERSETTER(Name, Container) \
26 void StateChanges::Name(pkgCache::VerIterator const &Ver) \
27 { \
28 Container.push_back(Ver); \
29 }\
30 APT::VersionVector& StateChanges::Name() \
31 { \
32 return Container; \
33 }
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()
41 {
42 return d->error;
43 }
44
45 void StateChanges::clear()
46 {
47 d->hold.clear();
48 d->unhold.clear();
49 d->install.clear();
50 d->deinstall.clear();
51 d->purge.clear();
52 d->error.clear();
53 }
54
55 bool StateChanges::empty() const
56 {
57 return d->hold.empty() &&
58 d->unhold.empty() &&
59 d->install.empty() &&
60 d->deinstall.empty() &&
61 d->purge.empty() &&
62 d->error.empty();
63 }
64
65 bool StateChanges::Save(bool const DiscardOutput)
66 {
67 bool const Debug = _config->FindB("Debug::pkgDpkgPm", false);
68 d->error.clear();
69 if (d->hold.empty() && d->unhold.empty() && d->install.empty() && d->deinstall.empty() && d->purge.empty())
70 return true;
71
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)
75 {
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);
80
81 if (makeDpkgAvailable.empty() == false)
82 {
83 auto const BaseArgs = Args.size();
84 Args.push_back("--merge-avail");
85 // FIXME: supported only since 1.17.7 in dpkg
86 Args.push_back("-");
87 int dummyAvail = -1;
88 if (Debug)
89 {
90 for (auto const &V: makeDpkgAvailable)
91 {
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;
95 }
96 }
97 else
98 {
99 pid_t const dpkgMergeAvail = debSystem::ExecDpkg(Args, &dummyAvail, nullptr, true);
100
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());
105 fclose(dpkg);
106
107 ExecWait(dpkgMergeAvail, "dpkg --merge-avail", true);
108 }
109 Args.erase(Args.begin() + BaseArgs, Args.end());
110 }
111 }
112 bool const dpkgMultiArch = _system->MultiArchSupported();
113
114 Args.push_back("--set-selections");
115 if (Debug)
116 {
117 std::string state;
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());
124 else
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;
128 };
129 for (auto const &V: d->unhold)
130 {
131 if (V.ParentPkg()->CurrentVer != 0)
132 state = "install";
133 else
134 state = "deinstall";
135 dpkgName(V);
136 }
137 if (d->purge.empty() == false)
138 {
139 state = "purge";
140 std::for_each(d->purge.begin(), d->purge.end(), dpkgName);
141 }
142 if (d->deinstall.empty() == false)
143 {
144 state = "deinstall";
145 std::for_each(d->deinstall.begin(), d->deinstall.end(), dpkgName);
146 }
147 if (d->hold.empty() == false)
148 {
149 state = "hold";
150 std::for_each(d->hold.begin(), d->hold.end(), dpkgName);
151 }
152 if (d->install.empty() == false)
153 {
154 state = "install";
155 std::for_each(d->install.begin(), d->install.end(), dpkgName);
156 }
157 }
158 else
159 {
160 int selections = -1;
161 pid_t const dpkgSelections = debSystem::ExecDpkg(Args, &selections, nullptr, DiscardOutput);
162
163 FILE* dpkg = fdopen(selections, "w");
164 std::string state;
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());
171 else
172 fprintf(dpkg, "%s:%s %s\n", P.Name(), V.Arch(), state.c_str());
173 };
174 for (auto const &V: d->unhold)
175 {
176 if (V.ParentPkg()->CurrentVer != 0)
177 state = "install";
178 else
179 state = "deinstall";
180 dpkgName(V);
181 }
182 if (d->purge.empty() == false)
183 {
184 state = "purge";
185 std::for_each(d->purge.begin(), d->purge.end(), dpkgName);
186 }
187 if (d->deinstall.empty() == false)
188 {
189 state = "deinstall";
190 std::for_each(d->deinstall.begin(), d->deinstall.end(), dpkgName);
191 }
192 if (d->hold.empty() == false)
193 {
194 state = "hold";
195 std::for_each(d->hold.begin(), d->hold.end(), dpkgName);
196 }
197 if (d->install.empty() == false)
198 {
199 state = "install";
200 std::for_each(d->install.begin(), d->install.end(), dpkgName);
201 }
202 fclose(dpkg);
203
204 if (ExecWait(dpkgSelections, "dpkg --set-selections") == false)
205 {
206 std::move(d->purge.begin(), d->purge.end(), std::back_inserter(d->error));
207 d->purge.clear();
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));
211 d->hold.clear();
212 std::move(d->unhold.begin(), d->unhold.end(), std::back_inserter(d->error));
213 d->unhold.clear();
214 std::move(d->install.begin(), d->install.end(), std::back_inserter(d->error));
215 d->install.clear();
216 }
217 }
218 return d->error.empty();
219 }
220
221 StateChanges::StateChanges() : d(new StateChanges::Private()) {}
222 StateChanges::StateChanges(StateChanges&&) = default;
223 StateChanges& StateChanges::operator=(StateChanges&&) = default;
224 StateChanges::~StateChanges() = default;
225
226 }