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