]> git.saurik.com Git - apt.git/blame_incremental - apt-pkg/install-progress.cc
save and restore selection states before/after calling dpkg
[apt.git] / apt-pkg / install-progress.cc
... / ...
CommitLineData
1#include <config.h>
2
3#include <apt-pkg/configuration.h>
4#include <apt-pkg/fileutl.h>
5#include <apt-pkg/strutl.h>
6#include <apt-pkg/install-progress.h>
7
8#include <signal.h>
9#include <unistd.h>
10#include <iostream>
11#include <vector>
12#include <sys/ioctl.h>
13#include <fcntl.h>
14#include <algorithm>
15#include <stdio.h>
16#include <sstream>
17
18#include <apti18n.h>
19
20namespace APT {
21namespace Progress {
22
23PackageManager::PackageManager() : d(NULL), percentage(0.0), last_reported_progress(-1) {}
24PackageManager::~PackageManager() {}
25
26/* Return a APT::Progress::PackageManager based on the global
27 * apt configuration (i.e. APT::Status-Fd and APT::Status-deb822-Fd)
28 */
29PackageManager* PackageManagerProgressFactory()
30{
31 // select the right progress
32 int status_fd = _config->FindI("APT::Status-Fd", -1);
33 int status_deb822_fd = _config->FindI("APT::Status-deb822-Fd", -1);
34
35 APT::Progress::PackageManager *progress = NULL;
36 if (status_deb822_fd > 0)
37 progress = new APT::Progress::PackageManagerProgressDeb822Fd(
38 status_deb822_fd);
39 else if (status_fd > 0)
40 progress = new APT::Progress::PackageManagerProgressFd(status_fd);
41 else if(_config->FindB("Dpkg::Progress-Fancy", false) == true)
42 progress = new APT::Progress::PackageManagerFancy();
43 else if (_config->FindB("Dpkg::Progress",
44 _config->FindB("DpkgPM::Progress", false)) == true)
45 progress = new APT::Progress::PackageManagerText();
46 else
47 progress = new APT::Progress::PackageManager();
48 return progress;
49}
50
51bool PackageManager::StatusChanged(std::string /*PackageName*/,
52 unsigned int StepsDone,
53 unsigned int TotalSteps,
54 std::string /*HumanReadableAction*/)
55{
56 int reporting_steps = _config->FindI("DpkgPM::Reporting-Steps", 1);
57 percentage = StepsDone/(float)TotalSteps * 100.0;
58 strprintf(progress_str, _("Progress: [%3i%%]"), (int)percentage);
59
60 if(percentage < (last_reported_progress + reporting_steps))
61 return false;
62
63 return true;
64}
65
66PackageManagerProgressFd::PackageManagerProgressFd(int progress_fd)
67 : d(NULL), StepsDone(0), StepsTotal(1)
68{
69 OutStatusFd = progress_fd;
70}
71PackageManagerProgressFd::~PackageManagerProgressFd() {}
72
73void PackageManagerProgressFd::WriteToStatusFd(std::string s)
74{
75 if(OutStatusFd <= 0)
76 return;
77 FileFd::Write(OutStatusFd, s.c_str(), s.size());
78}
79
80void PackageManagerProgressFd::StartDpkg()
81{
82 if(OutStatusFd <= 0)
83 return;
84
85 // FIXME: use SetCloseExec here once it taught about throwing
86 // exceptions instead of doing _exit(100) on failure
87 fcntl(OutStatusFd,F_SETFD,FD_CLOEXEC);
88
89 // send status information that we are about to fork dpkg
90 std::string status;
91 strprintf(status, "pmstatus:dpkg-exec:%.4f:%s\n",
92 (StepsDone/float(StepsTotal)*100.0), _("Running dpkg"));
93 WriteToStatusFd(std::move(status));
94}
95
96APT_CONST void PackageManagerProgressFd::Stop()
97{
98}
99
100void PackageManagerProgressFd::Error(std::string PackageName,
101 unsigned int StepsDone,
102 unsigned int TotalSteps,
103 std::string ErrorMessage)
104{
105 std::string status;
106 strprintf(status, "pmerror:%s:%.4f:%s\n", PackageName.c_str(),
107 (StepsDone/float(TotalSteps)*100.0), ErrorMessage.c_str());
108 WriteToStatusFd(std::move(status));
109}
110
111void PackageManagerProgressFd::ConffilePrompt(std::string PackageName,
112 unsigned int StepsDone,
113 unsigned int TotalSteps,
114 std::string ConfMessage)
115{
116 std::string status;
117 strprintf(status, "pmconffile:%s:%.4f:%s\n", PackageName.c_str(),
118 (StepsDone/float(TotalSteps)*100.0), ConfMessage.c_str());
119 WriteToStatusFd(std::move(status));
120}
121
122
123bool PackageManagerProgressFd::StatusChanged(std::string PackageName,
124 unsigned int xStepsDone,
125 unsigned int xTotalSteps,
126 std::string pkg_action)
127{
128 StepsDone = xStepsDone;
129 StepsTotal = xTotalSteps;
130
131 // build the status str
132 std::string status;
133 strprintf(status, "pmstatus:%s:%.4f:%s\n", StringSplit(PackageName, ":")[0].c_str(),
134 (StepsDone/float(StepsTotal)*100.0), pkg_action.c_str());
135 WriteToStatusFd(std::move(status));
136
137 if(_config->FindB("Debug::APT::Progress::PackageManagerFd", false) == true)
138 std::cerr << "progress: " << PackageName << " " << xStepsDone
139 << " " << xTotalSteps << " " << pkg_action
140 << std::endl;
141
142
143 return true;
144}
145
146
147PackageManagerProgressDeb822Fd::PackageManagerProgressDeb822Fd(int progress_fd)
148 : d(NULL), StepsDone(0), StepsTotal(1)
149{
150 OutStatusFd = progress_fd;
151}
152PackageManagerProgressDeb822Fd::~PackageManagerProgressDeb822Fd() {}
153
154void PackageManagerProgressDeb822Fd::WriteToStatusFd(std::string s)
155{
156 FileFd::Write(OutStatusFd, s.c_str(), s.size());
157}
158
159void PackageManagerProgressDeb822Fd::StartDpkg()
160{
161 // FIXME: use SetCloseExec here once it taught about throwing
162 // exceptions instead of doing _exit(100) on failure
163 fcntl(OutStatusFd,F_SETFD,FD_CLOEXEC);
164
165 // send status information that we are about to fork dpkg
166 std::string status;
167 strprintf(status, "Status: %s\nPercent: %.4f\nMessage: %s\n\n", "progress",
168 (StepsDone/float(StepsTotal)*100.0), _("Running dpkg"));
169 WriteToStatusFd(std::move(status));
170}
171
172APT_CONST void PackageManagerProgressDeb822Fd::Stop()
173{
174}
175
176void PackageManagerProgressDeb822Fd::Error(std::string PackageName,
177 unsigned int StepsDone,
178 unsigned int TotalSteps,
179 std::string ErrorMessage)
180{
181 std::string status;
182 strprintf(status, "Status: %s\nPackage: %s\nPercent: %.4f\nMessage: %s\n\n", "Error",
183 PackageName.c_str(), (StepsDone/float(TotalSteps)*100.0), ErrorMessage.c_str());
184 WriteToStatusFd(std::move(status));
185}
186
187void PackageManagerProgressDeb822Fd::ConffilePrompt(std::string PackageName,
188 unsigned int StepsDone,
189 unsigned int TotalSteps,
190 std::string ConfMessage)
191{
192 std::string status;
193 strprintf(status, "Status: %s\nPackage: %s\nPercent: %.4f\nMessage: %s\n\n", "ConfFile",
194 PackageName.c_str(), (StepsDone/float(TotalSteps)*100.0), ConfMessage.c_str());
195 WriteToStatusFd(std::move(status));
196}
197
198
199bool PackageManagerProgressDeb822Fd::StatusChanged(std::string PackageName,
200 unsigned int xStepsDone,
201 unsigned int xTotalSteps,
202 std::string message)
203{
204 StepsDone = xStepsDone;
205 StepsTotal = xTotalSteps;
206
207 std::string status;
208 strprintf(status, "Status: %s\nPackage: %s\nPercent: %.4f\nMessage: %s\n\n", "progress",
209 PackageName.c_str(), (StepsDone/float(StepsTotal)*100.0), message.c_str());
210 WriteToStatusFd(std::move(status));
211 return true;
212}
213
214
215PackageManagerFancy::PackageManagerFancy()
216 : d(NULL), child_pty(-1)
217{
218 // setup terminal size
219 old_SIGWINCH = signal(SIGWINCH, PackageManagerFancy::staticSIGWINCH);
220 instances.push_back(this);
221}
222std::vector<PackageManagerFancy*> PackageManagerFancy::instances;
223
224PackageManagerFancy::~PackageManagerFancy()
225{
226 instances.erase(find(instances.begin(), instances.end(), this));
227 signal(SIGWINCH, old_SIGWINCH);
228}
229
230void PackageManagerFancy::staticSIGWINCH(int signum)
231{
232 std::vector<PackageManagerFancy *>::const_iterator I;
233 for(I = instances.begin(); I != instances.end(); ++I)
234 (*I)->HandleSIGWINCH(signum);
235}
236
237PackageManagerFancy::TermSize
238PackageManagerFancy::GetTerminalSize()
239{
240 struct winsize win;
241 PackageManagerFancy::TermSize s = { 0, 0 };
242
243 // FIXME: get from "child_pty" instead?
244 if(ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&win) != 0)
245 return s;
246
247 if(_config->FindB("Debug::InstallProgress::Fancy", false) == true)
248 std::cerr << "GetTerminalSize: " << win.ws_row << " x " << win.ws_col << std::endl;
249
250 s.rows = win.ws_row;
251 s.columns = win.ws_col;
252 return s;
253}
254
255void PackageManagerFancy::SetupTerminalScrollArea(int nr_rows)
256{
257 if(_config->FindB("Debug::InstallProgress::Fancy", false) == true)
258 std::cerr << "SetupTerminalScrollArea: " << nr_rows << std::endl;
259
260 if (unlikely(nr_rows <= 1))
261 return;
262
263 // scroll down a bit to avoid visual glitch when the screen
264 // area shrinks by one row
265 std::cout << "\n";
266
267 // save cursor
268 std::cout << "\0337";
269
270 // set scroll region (this will place the cursor in the top left)
271 std::cout << "\033[0;" << std::to_string(nr_rows - 1) << "r";
272
273 // restore cursor but ensure its inside the scrolling area
274 std::cout << "\0338";
275 static const char *move_cursor_up = "\033[1A";
276 std::cout << move_cursor_up;
277
278 // ensure its flushed
279 std::flush(std::cout);
280
281 // setup tty size to ensure xterm/linux console are working properly too
282 // see bug #731738
283 struct winsize win;
284 if (ioctl(child_pty, TIOCGWINSZ, (char *)&win) != -1)
285 {
286 win.ws_row = nr_rows - 1;
287 ioctl(child_pty, TIOCSWINSZ, (char *)&win);
288 }
289}
290
291void PackageManagerFancy::HandleSIGWINCH(int)
292{
293 int const nr_terminal_rows = GetTerminalSize().rows;
294 SetupTerminalScrollArea(nr_terminal_rows);
295 DrawStatusLine();
296}
297
298void PackageManagerFancy::Start(int a_child_pty)
299{
300 child_pty = a_child_pty;
301 int const nr_terminal_rows = GetTerminalSize().rows;
302 SetupTerminalScrollArea(nr_terminal_rows);
303}
304
305void PackageManagerFancy::Stop()
306{
307 int const nr_terminal_rows = GetTerminalSize().rows;
308 if (nr_terminal_rows > 0)
309 {
310 SetupTerminalScrollArea(nr_terminal_rows + 1);
311
312 // override the progress line (sledgehammer)
313 static const char* clear_screen_below_cursor = "\033[J";
314 std::cout << clear_screen_below_cursor;
315 std::flush(std::cout);
316 }
317 child_pty = -1;
318}
319
320std::string
321PackageManagerFancy::GetTextProgressStr(float Percent, int OutputSize)
322{
323 std::string output;
324 int i;
325
326 // should we raise a exception here instead?
327 if (Percent < 0.0 || Percent > 1.0 || OutputSize < 3)
328 return output;
329
330 int BarSize = OutputSize - 2; // bar without the leading "[" and trailing "]"
331 output += "[";
332 for(i=0; i < BarSize*Percent; i++)
333 output += "#";
334 for (/*nothing*/; i < BarSize; i++)
335 output += ".";
336 output += "]";
337 return output;
338}
339
340bool PackageManagerFancy::StatusChanged(std::string PackageName,
341 unsigned int StepsDone,
342 unsigned int TotalSteps,
343 std::string HumanReadableAction)
344{
345 if (!PackageManager::StatusChanged(PackageName, StepsDone, TotalSteps,
346 HumanReadableAction))
347 return false;
348
349 return DrawStatusLine();
350}
351bool PackageManagerFancy::DrawStatusLine()
352{
353 PackageManagerFancy::TermSize const size = GetTerminalSize();
354 if (unlikely(size.rows < 1 || size.columns < 1))
355 return false;
356
357 static std::string save_cursor = "\0337";
358 static std::string restore_cursor = "\0338";
359
360 // green
361 static std::string set_bg_color = DeQuoteString(
362 _config->Find("Dpkg::Progress-Fancy::Progress-fg", "%1b[42m"));
363 // black
364 static std::string set_fg_color = DeQuoteString(
365 _config->Find("Dpkg::Progress-Fancy::Progress-bg", "%1b[30m"));
366
367 static std::string restore_bg = "\033[49m";
368 static std::string restore_fg = "\033[39m";
369
370 std::cout << save_cursor
371 // move cursor position to last row
372 << "\033[" << std::to_string(size.rows) << ";0f"
373 << set_bg_color
374 << set_fg_color
375 << progress_str
376 << restore_bg
377 << restore_fg;
378 std::flush(std::cout);
379
380 // draw text progress bar
381 if (_config->FindB("Dpkg::Progress-Fancy::Progress-Bar", true))
382 {
383 int padding = 4;
384 float progressbar_size = size.columns - padding - progress_str.size();
385 float current_percent = percentage / 100.0;
386 std::cout << " "
387 << GetTextProgressStr(current_percent, progressbar_size)
388 << " ";
389 std::flush(std::cout);
390 }
391
392 // restore
393 std::cout << restore_cursor;
394 std::flush(std::cout);
395
396 last_reported_progress = percentage;
397
398 return true;
399}
400
401bool PackageManagerText::StatusChanged(std::string PackageName,
402 unsigned int StepsDone,
403 unsigned int TotalSteps,
404 std::string HumanReadableAction)
405{
406 if (!PackageManager::StatusChanged(PackageName, StepsDone, TotalSteps, HumanReadableAction))
407 return false;
408
409 std::cout << progress_str << "\r\n";
410 std::flush(std::cout);
411
412 last_reported_progress = percentage;
413
414 return true;
415}
416
417PackageManagerText::PackageManagerText() : PackageManager(), d(NULL) {}
418PackageManagerText::~PackageManagerText() {}
419
420
421
422
423} // namespace progress
424} // namespace apt