]> git.saurik.com Git - apt.git/blob - apt-pkg/install-progress.cc
allow individual targets to be kept compressed
[apt.git] / apt-pkg / install-progress.cc
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 <string>
12 #include <vector>
13 #include <sys/ioctl.h>
14 #include <sstream>
15 #include <fcntl.h>
16 #include <algorithm>
17 #include <stdio.h>
18
19 #include <apti18n.h>
20
21 namespace APT {
22 namespace Progress {
23
24 PackageManager::PackageManager() : d(NULL), percentage(0.0), last_reported_progress(-1) {}
25 PackageManager::~PackageManager() {}
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 */
30 PackageManager* 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
52 bool PackageManager::StatusChanged(std::string /*PackageName*/,
53 unsigned int StepsDone,
54 unsigned int TotalSteps,
55 std::string /*HumanReadableAction*/)
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
67 PackageManagerProgressFd::PackageManagerProgressFd(int progress_fd)
68 : d(NULL), StepsDone(0), StepsTotal(1)
69 {
70 OutStatusFd = progress_fd;
71 }
72 PackageManagerProgressFd::~PackageManagerProgressFd() {}
73
74 void PackageManagerProgressFd::WriteToStatusFd(std::string s)
75 {
76 if(OutStatusFd <= 0)
77 return;
78 FileFd::Write(OutStatusFd, s.c_str(), s.size());
79 }
80
81 void PackageManagerProgressFd::StartDpkg()
82 {
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);
89
90 // send status information that we are about to fork dpkg
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());
97 }
98
99 APT_CONST void PackageManagerProgressFd::Stop()
100 {
101 }
102
103 void 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;
113 WriteToStatusFd(status.str());
114 }
115
116 void 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;
126 WriteToStatusFd(status.str());
127 }
128
129
130 bool 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;
140 status << "pmstatus:" << StringSplit(PackageName, ":")[0]
141 << ":" << (StepsDone/float(StepsTotal)*100.0)
142 << ":" << pkg_action
143 << std::endl;
144 WriteToStatusFd(status.str());
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
152 return true;
153 }
154
155
156 PackageManagerProgressDeb822Fd::PackageManagerProgressDeb822Fd(int progress_fd)
157 : d(NULL), StepsDone(0), StepsTotal(1)
158 {
159 OutStatusFd = progress_fd;
160 }
161 PackageManagerProgressDeb822Fd::~PackageManagerProgressDeb822Fd() {}
162
163 void PackageManagerProgressDeb822Fd::WriteToStatusFd(std::string s)
164 {
165 FileFd::Write(OutStatusFd, s.c_str(), s.size());
166 }
167
168 void PackageManagerProgressDeb822Fd::StartDpkg()
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
183 APT_CONST void PackageManagerProgressDeb822Fd::Stop()
184 {
185 }
186
187 void 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
201 void 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
216 bool 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
236
237 PackageManagerFancy::PackageManagerFancy()
238 : d(NULL), child_pty(-1)
239 {
240 // setup terminal size
241 old_SIGWINCH = signal(SIGWINCH, PackageManagerFancy::staticSIGWINCH);
242 instances.push_back(this);
243 }
244 std::vector<PackageManagerFancy*> PackageManagerFancy::instances;
245
246 PackageManagerFancy::~PackageManagerFancy()
247 {
248 instances.erase(find(instances.begin(), instances.end(), this));
249 signal(SIGWINCH, old_SIGWINCH);
250 }
251
252 void PackageManagerFancy::staticSIGWINCH(int signum)
253 {
254 std::vector<PackageManagerFancy *>::const_iterator I;
255 for(I = instances.begin(); I != instances.end(); ++I)
256 (*I)->HandleSIGWINCH(signum);
257 }
258
259 PackageManagerFancy::TermSize
260 PackageManagerFancy::GetTerminalSize()
261 {
262 struct winsize win;
263 PackageManagerFancy::TermSize s = { 0, 0 };
264
265 // FIXME: get from "child_pty" instead?
266 if(ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&win) != 0)
267 return s;
268
269 if(_config->FindB("Debug::InstallProgress::Fancy", false) == true)
270 std::cerr << "GetTerminalSize: " << win.ws_row << " x " << win.ws_col << std::endl;
271
272 s.rows = win.ws_row;
273 s.columns = win.ws_col;
274 return s;
275 }
276
277 void PackageManagerFancy::SetupTerminalScrollArea(int nr_rows)
278 {
279 if(_config->FindB("Debug::InstallProgress::Fancy", false) == true)
280 std::cerr << "SetupTerminalScrollArea: " << nr_rows << std::endl;
281
282 if (unlikely(nr_rows <= 1))
283 return;
284
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)
293 std::cout << "\033[0;" << nr_rows - 1 << "r";
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;
299
300 // ensure its flushed
301 std::flush(std::cout);
302
303 // setup tty size to ensure xterm/linux console are working properly too
304 // see bug #731738
305 struct winsize win;
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 }
311 }
312
313 void PackageManagerFancy::HandleSIGWINCH(int)
314 {
315 int const nr_terminal_rows = GetTerminalSize().rows;
316 SetupTerminalScrollArea(nr_terminal_rows);
317 DrawStatusLine();
318 }
319
320 void PackageManagerFancy::Start(int a_child_pty)
321 {
322 child_pty = a_child_pty;
323 int const nr_terminal_rows = GetTerminalSize().rows;
324 SetupTerminalScrollArea(nr_terminal_rows);
325 }
326
327 void PackageManagerFancy::Stop()
328 {
329 int const nr_terminal_rows = GetTerminalSize().rows;
330 if (nr_terminal_rows > 0)
331 {
332 SetupTerminalScrollArea(nr_terminal_rows + 1);
333
334 // override the progress line (sledgehammer)
335 static const char* clear_screen_below_cursor = "\033[J";
336 std::cout << clear_screen_below_cursor;
337 }
338 child_pty = -1;
339 }
340
341 std::string
342 PackageManagerFancy::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
361 bool PackageManagerFancy::StatusChanged(std::string PackageName,
362 unsigned int StepsDone,
363 unsigned int TotalSteps,
364 std::string HumanReadableAction)
365 {
366 if (!PackageManager::StatusChanged(PackageName, StepsDone, TotalSteps,
367 HumanReadableAction))
368 return false;
369
370 return DrawStatusLine();
371 }
372 bool PackageManagerFancy::DrawStatusLine()
373 {
374 PackageManagerFancy::TermSize const size = GetTerminalSize();
375 if (unlikely(size.rows < 1 || size.columns < 1))
376 return false;
377
378 static std::string save_cursor = "\033[s";
379 static std::string restore_cursor = "\033[u";
380
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"));
387
388 static std::string restore_bg = "\033[49m";
389 static std::string restore_fg = "\033[39m";
390
391 std::cout << save_cursor
392 // move cursor position to last row
393 << "\033[" << size.rows << ";0f"
394 << set_bg_color
395 << set_fg_color
396 << progress_str
397 << restore_bg
398 << restore_fg;
399 std::flush(std::cout);
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();
406 float current_percent = percentage / 100.0;
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
417 last_reported_progress = percentage;
418
419 return true;
420 }
421
422 bool PackageManagerText::StatusChanged(std::string PackageName,
423 unsigned int StepsDone,
424 unsigned int TotalSteps,
425 std::string HumanReadableAction)
426 {
427 if (!PackageManager::StatusChanged(PackageName, StepsDone, TotalSteps, HumanReadableAction))
428 return false;
429
430 std::cout << progress_str << "\r\n";
431 std::flush(std::cout);
432
433 last_reported_progress = percentage;
434
435 return true;
436 }
437
438 PackageManagerText::PackageManagerText() : PackageManager(), d(NULL) {}
439 PackageManagerText::~PackageManagerText() {}
440
441
442
443
444 } // namespace progress
445 } // namespace apt