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