]> git.saurik.com Git - apt.git/blame - apt-pkg/install-progress.cc
ensure Cnf::FindFile doesn't return files below /dev/null
[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>
e96e4e9c 17
453b82a3
DK
18#include <apti18n.h>
19
31f97d7b
MV
20namespace APT {
21namespace Progress {
22
440614ff
DK
23PackageManager::PackageManager() : d(NULL), percentage(0.0), last_reported_progress(-1) {}
24PackageManager::~PackageManager() {}
61f954bf
MV
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 */
bd5f39b3
MV
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
65512241 51bool PackageManager::StatusChanged(std::string /*PackageName*/,
e6ad8031
MV
52 unsigned int StepsDone,
53 unsigned int TotalSteps,
65512241 54 std::string /*HumanReadableAction*/)
6c5ae8ed
MV
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
e6ad8031 66PackageManagerProgressFd::PackageManagerProgressFd(int progress_fd)
6c55f07a 67 : d(NULL), StepsDone(0), StepsTotal(1)
e6ad8031
MV
68{
69 OutStatusFd = progress_fd;
70}
c8a4ce6c 71PackageManagerProgressFd::~PackageManagerProgressFd() {}
e6ad8031 72
f9935b1c
MV
73void PackageManagerProgressFd::WriteToStatusFd(std::string s)
74{
75 if(OutStatusFd <= 0)
76 return;
77 FileFd::Write(OutStatusFd, s.c_str(), s.size());
78}
79
e45c4617 80void PackageManagerProgressFd::StartDpkg()
e6ad8031 81{
5e9458e2
MV
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);
e6ad8031
MV
88
89 // send status information that we are about to fork dpkg
b58e2c7c
DK
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));
e6ad8031
MV
94}
95
a02db58f 96APT_CONST void PackageManagerProgressFd::Stop()
e6ad8031 97{
e6ad8031
MV
98}
99
100void PackageManagerProgressFd::Error(std::string PackageName,
101 unsigned int StepsDone,
102 unsigned int TotalSteps,
103 std::string ErrorMessage)
104{
b58e2c7c
DK
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));
e6ad8031
MV
109}
110
111void PackageManagerProgressFd::ConffilePrompt(std::string PackageName,
112 unsigned int StepsDone,
113 unsigned int TotalSteps,
114 std::string ConfMessage)
115{
b58e2c7c
DK
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));
e6ad8031
MV
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
b58e2c7c
DK
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));
65dbd5a1
MV
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
e6ad8031
MV
143 return true;
144}
145
c7ea1eba
MV
146
147PackageManagerProgressDeb822Fd::PackageManagerProgressDeb822Fd(int progress_fd)
6c55f07a 148 : d(NULL), StepsDone(0), StepsTotal(1)
c7ea1eba
MV
149{
150 OutStatusFd = progress_fd;
151}
c8a4ce6c 152PackageManagerProgressDeb822Fd::~PackageManagerProgressDeb822Fd() {}
c7ea1eba
MV
153
154void PackageManagerProgressDeb822Fd::WriteToStatusFd(std::string s)
155{
156 FileFd::Write(OutStatusFd, s.c_str(), s.size());
157}
158
790d41f6 159void PackageManagerProgressDeb822Fd::StartDpkg()
c7ea1eba
MV
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
b58e2c7c
DK
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));
c7ea1eba
MV
170}
171
a02db58f 172APT_CONST void PackageManagerProgressDeb822Fd::Stop()
c7ea1eba 173{
c7ea1eba
MV
174}
175
176void PackageManagerProgressDeb822Fd::Error(std::string PackageName,
177 unsigned int StepsDone,
178 unsigned int TotalSteps,
179 std::string ErrorMessage)
180{
b58e2c7c
DK
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));
c7ea1eba
MV
185}
186
187void PackageManagerProgressDeb822Fd::ConffilePrompt(std::string PackageName,
188 unsigned int StepsDone,
189 unsigned int TotalSteps,
190 std::string ConfMessage)
191{
b58e2c7c
DK
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));
c7ea1eba
MV
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
b58e2c7c
DK
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));
c7ea1eba
MV
211 return true;
212}
213
5ed88785
MV
214
215PackageManagerFancy::PackageManagerFancy()
6c55f07a 216 : d(NULL), child_pty(-1)
5ed88785
MV
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;
9ce3cfc9 233 for(I = instances.begin(); I != instances.end(); ++I)
5ed88785
MV
234 (*I)->HandleSIGWINCH(signum);
235}
236
fa211e2d
MV
237PackageManagerFancy::TermSize
238PackageManagerFancy::GetTerminalSize()
e96e4e9c
MV
239{
240 struct winsize win;
76bd63e2 241 PackageManagerFancy::TermSize s = { 0, 0 };
fa211e2d 242
5ed88785 243 // FIXME: get from "child_pty" instead?
e96e4e9c 244 if(ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&win) != 0)
fa211e2d 245 return s;
5ed88785
MV
246
247 if(_config->FindB("Debug::InstallProgress::Fancy", false) == true)
76bd63e2 248 std::cerr << "GetTerminalSize: " << win.ws_row << " x " << win.ws_col << std::endl;
fa211e2d
MV
249
250 s.rows = win.ws_row;
251 s.columns = win.ws_col;
252 return s;
e96e4e9c 253}
c7ea1eba 254
db78c60c 255void PackageManagerFancy::SetupTerminalScrollArea(int nr_rows)
31f97d7b 256{
5ed88785
MV
257 if(_config->FindB("Debug::InstallProgress::Fancy", false) == true)
258 std::cerr << "SetupTerminalScrollArea: " << nr_rows << std::endl;
259
76bd63e2
DK
260 if (unlikely(nr_rows <= 1))
261 return;
262
31f97d7b
MV
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
45b19add 268 std::cout << "\0337";
31f97d7b
MV
269
270 // set scroll region (this will place the cursor in the top left)
b58e2c7c 271 std::cout << "\033[0;" << std::to_string(nr_rows - 1) << "r";
31f97d7b
MV
272
273 // restore cursor but ensure its inside the scrolling area
45b19add 274 std::cout << "\0338";
31f97d7b
MV
275 static const char *move_cursor_up = "\033[1A";
276 std::cout << move_cursor_up;
db78c60c 277
5ed88785 278 // ensure its flushed
31f97d7b 279 std::flush(std::cout);
31f97d7b 280
5ed88785
MV
281 // setup tty size to ensure xterm/linux console are working properly too
282 // see bug #731738
283 struct winsize win;
76bd63e2
DK
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 }
e96e4e9c
MV
289}
290
291void PackageManagerFancy::HandleSIGWINCH(int)
292{
76bd63e2 293 int const nr_terminal_rows = GetTerminalSize().rows;
e96e4e9c 294 SetupTerminalScrollArea(nr_terminal_rows);
76bd63e2 295 DrawStatusLine();
31f97d7b
MV
296}
297
5ed88785 298void PackageManagerFancy::Start(int a_child_pty)
31f97d7b 299{
5ed88785 300 child_pty = a_child_pty;
76bd63e2
DK
301 int const nr_terminal_rows = GetTerminalSize().rows;
302 SetupTerminalScrollArea(nr_terminal_rows);
31f97d7b
MV
303}
304
a22fdebf 305void PackageManagerFancy::Stop()
31f97d7b 306{
76bd63e2 307 int const nr_terminal_rows = GetTerminalSize().rows;
db78c60c
MV
308 if (nr_terminal_rows > 0)
309 {
310 SetupTerminalScrollArea(nr_terminal_rows + 1);
31f97d7b 311
db78c60c
MV
312 // override the progress line (sledgehammer)
313 static const char* clear_screen_below_cursor = "\033[J";
314 std::cout << clear_screen_below_cursor;
915f4ac6 315 std::flush(std::cout);
db78c60c 316 }
5ed88785 317 child_pty = -1;
31f97d7b
MV
318}
319
fa211e2d
MV
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
6c5ae8ed 340bool PackageManagerFancy::StatusChanged(std::string PackageName,
31f97d7b 341 unsigned int StepsDone,
e6ad8031
MV
342 unsigned int TotalSteps,
343 std::string HumanReadableAction)
31f97d7b 344{
e6ad8031
MV
345 if (!PackageManager::StatusChanged(PackageName, StepsDone, TotalSteps,
346 HumanReadableAction))
6c5ae8ed 347 return false;
31f97d7b 348
76bd63e2
DK
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;
31f97d7b 356
45b19add
JM
357 static std::string save_cursor = "\0337";
358 static std::string restore_cursor = "\0338";
453b82a3 359
c34d1202
MV
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"));
453b82a3
DK
366
367 static std::string restore_bg = "\033[49m";
368 static std::string restore_fg = "\033[39m";
369
31f97d7b
MV
370 std::cout << save_cursor
371 // move cursor position to last row
b58e2c7c 372 << "\033[" << std::to_string(size.rows) << ";0f"
31f97d7b
MV
373 << set_bg_color
374 << set_fg_color
375 << progress_str
31f97d7b
MV
376 << restore_bg
377 << restore_fg;
378 std::flush(std::cout);
fa211e2d
MV
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();
76bd63e2 385 float current_percent = percentage / 100.0;
fa211e2d
MV
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
31f97d7b 396 last_reported_progress = percentage;
6c5ae8ed
MV
397
398 return true;
31f97d7b
MV
399}
400
6c5ae8ed 401bool PackageManagerText::StatusChanged(std::string PackageName,
31f97d7b 402 unsigned int StepsDone,
e6ad8031
MV
403 unsigned int TotalSteps,
404 std::string HumanReadableAction)
31f97d7b 405{
e6ad8031 406 if (!PackageManager::StatusChanged(PackageName, StepsDone, TotalSteps, HumanReadableAction))
6c5ae8ed 407 return false;
31f97d7b
MV
408
409 std::cout << progress_str << "\r\n";
410 std::flush(std::cout);
411
412 last_reported_progress = percentage;
6c5ae8ed
MV
413
414 return true;
31f97d7b
MV
415}
416
6c55f07a 417PackageManagerText::PackageManagerText() : PackageManager(), d(NULL) {}
c8a4ce6c
DK
418PackageManagerText::~PackageManagerText() {}
419
420
31f97d7b 421
c7ea1eba 422
d3e8fbb3
DK
423} // namespace progress
424} // namespace apt