first proof-of-concept for a fix for #731738
[ntk/apt.git] / apt-pkg / install-progress.cc
CommitLineData
e6ad8031
MV
1#include <apt-pkg/configuration.h>
2#include <apt-pkg/fileutl.h>
31f97d7b 3#include <apt-pkg/strutl.h>
af36becc 4#include <apt-pkg/install-progress.h>
c7ea1eba 5
6c5ae8ed 6#include <apti18n.h>
31f97d7b
MV
7
8#include <termios.h>
9#include <sys/ioctl.h>
e6ad8031 10#include <sstream>
5e9458e2 11#include <fcntl.h>
31f97d7b 12
e96e4e9c 13
31f97d7b
MV
14namespace APT {
15namespace Progress {
16
61f954bf
MV
17
18/* Return a APT::Progress::PackageManager based on the global
19 * apt configuration (i.e. APT::Status-Fd and APT::Status-deb822-Fd)
20 */
bd5f39b3
MV
21PackageManager* PackageManagerProgressFactory()
22{
23 // select the right progress
24 int status_fd = _config->FindI("APT::Status-Fd", -1);
25 int status_deb822_fd = _config->FindI("APT::Status-deb822-Fd", -1);
26
27 APT::Progress::PackageManager *progress = NULL;
28 if (status_deb822_fd > 0)
29 progress = new APT::Progress::PackageManagerProgressDeb822Fd(
30 status_deb822_fd);
31 else if (status_fd > 0)
32 progress = new APT::Progress::PackageManagerProgressFd(status_fd);
33 else if(_config->FindB("Dpkg::Progress-Fancy", false) == true)
34 progress = new APT::Progress::PackageManagerFancy();
35 else if (_config->FindB("Dpkg::Progress",
36 _config->FindB("DpkgPM::Progress", false)) == true)
37 progress = new APT::Progress::PackageManagerText();
38 else
39 progress = new APT::Progress::PackageManager();
40 return progress;
41}
42
6c5ae8ed 43bool PackageManager::StatusChanged(std::string PackageName,
e6ad8031
MV
44 unsigned int StepsDone,
45 unsigned int TotalSteps,
46 std::string HumanReadableAction)
6c5ae8ed
MV
47{
48 int reporting_steps = _config->FindI("DpkgPM::Reporting-Steps", 1);
49 percentage = StepsDone/(float)TotalSteps * 100.0;
50 strprintf(progress_str, _("Progress: [%3i%%]"), (int)percentage);
51
52 if(percentage < (last_reported_progress + reporting_steps))
53 return false;
54
55 return true;
56}
57
e6ad8031 58PackageManagerProgressFd::PackageManagerProgressFd(int progress_fd)
65dbd5a1 59 : StepsDone(0), StepsTotal(1)
e6ad8031
MV
60{
61 OutStatusFd = progress_fd;
62}
63
f9935b1c
MV
64void PackageManagerProgressFd::WriteToStatusFd(std::string s)
65{
66 if(OutStatusFd <= 0)
67 return;
68 FileFd::Write(OutStatusFd, s.c_str(), s.size());
69}
70
e45c4617 71void PackageManagerProgressFd::StartDpkg()
e6ad8031 72{
5e9458e2
MV
73 if(OutStatusFd <= 0)
74 return;
75
76 // FIXME: use SetCloseExec here once it taught about throwing
77 // exceptions instead of doing _exit(100) on failure
78 fcntl(OutStatusFd,F_SETFD,FD_CLOEXEC);
e6ad8031
MV
79
80 // send status information that we are about to fork dpkg
f9935b1c
MV
81 std::ostringstream status;
82 status << "pmstatus:dpkg-exec:"
83 << (StepsDone/float(StepsTotal)*100.0)
84 << ":" << _("Running dpkg")
85 << std::endl;
86 WriteToStatusFd(status.str());
e6ad8031
MV
87}
88
a22fdebf 89void PackageManagerProgressFd::Stop()
e6ad8031 90{
e6ad8031
MV
91}
92
93void PackageManagerProgressFd::Error(std::string PackageName,
94 unsigned int StepsDone,
95 unsigned int TotalSteps,
96 std::string ErrorMessage)
97{
98 std::ostringstream status;
99 status << "pmerror:" << PackageName
100 << ":" << (StepsDone/float(TotalSteps)*100.0)
101 << ":" << ErrorMessage
102 << std::endl;
f9935b1c 103 WriteToStatusFd(status.str());
e6ad8031
MV
104}
105
106void PackageManagerProgressFd::ConffilePrompt(std::string PackageName,
107 unsigned int StepsDone,
108 unsigned int TotalSteps,
109 std::string ConfMessage)
110{
111 std::ostringstream status;
112 status << "pmconffile:" << PackageName
113 << ":" << (StepsDone/float(TotalSteps)*100.0)
114 << ":" << ConfMessage
115 << std::endl;
f9935b1c 116 WriteToStatusFd(status.str());
e6ad8031
MV
117}
118
119
120bool PackageManagerProgressFd::StatusChanged(std::string PackageName,
121 unsigned int xStepsDone,
122 unsigned int xTotalSteps,
123 std::string pkg_action)
124{
125 StepsDone = xStepsDone;
126 StepsTotal = xTotalSteps;
127
128 // build the status str
129 std::ostringstream status;
dd640f3c 130 status << "pmstatus:" << StringSplit(PackageName, ":")[0]
e6ad8031
MV
131 << ":" << (StepsDone/float(StepsTotal)*100.0)
132 << ":" << pkg_action
133 << std::endl;
f9935b1c 134 WriteToStatusFd(status.str());
65dbd5a1
MV
135
136 if(_config->FindB("Debug::APT::Progress::PackageManagerFd", false) == true)
137 std::cerr << "progress: " << PackageName << " " << xStepsDone
138 << " " << xTotalSteps << " " << pkg_action
139 << std::endl;
140
141
e6ad8031
MV
142 return true;
143}
144
c7ea1eba
MV
145
146PackageManagerProgressDeb822Fd::PackageManagerProgressDeb822Fd(int progress_fd)
147 : StepsDone(0), StepsTotal(1)
148{
149 OutStatusFd = progress_fd;
150}
151
152void PackageManagerProgressDeb822Fd::WriteToStatusFd(std::string s)
153{
154 FileFd::Write(OutStatusFd, s.c_str(), s.size());
155}
156
790d41f6 157void PackageManagerProgressDeb822Fd::StartDpkg()
c7ea1eba
MV
158{
159 // FIXME: use SetCloseExec here once it taught about throwing
160 // exceptions instead of doing _exit(100) on failure
161 fcntl(OutStatusFd,F_SETFD,FD_CLOEXEC);
162
163 // send status information that we are about to fork dpkg
164 std::ostringstream status;
165 status << "Status: " << "progress" << std::endl
166 << "Percent: " << (StepsDone/float(StepsTotal)*100.0) << std::endl
167 << "Message: " << _("Running dpkg") << std::endl
168 << std::endl;
169 WriteToStatusFd(status.str());
170}
171
172void PackageManagerProgressDeb822Fd::Stop()
173{
c7ea1eba
MV
174}
175
176void PackageManagerProgressDeb822Fd::Error(std::string PackageName,
177 unsigned int StepsDone,
178 unsigned int TotalSteps,
179 std::string ErrorMessage)
180{
181 std::ostringstream status;
182 status << "Status: " << "Error" << std::endl
183 << "Package:" << PackageName << std::endl
184 << "Percent: " << (StepsDone/float(TotalSteps)*100.0) << std::endl
185 << "Message: " << ErrorMessage << std::endl
186 << std::endl;
187 WriteToStatusFd(status.str());
188}
189
190void PackageManagerProgressDeb822Fd::ConffilePrompt(std::string PackageName,
191 unsigned int StepsDone,
192 unsigned int TotalSteps,
193 std::string ConfMessage)
194{
195 std::ostringstream status;
196 status << "Status: " << "ConfFile" << std::endl
197 << "Package:" << PackageName << std::endl
198 << "Percent: " << (StepsDone/float(TotalSteps)*100.0) << std::endl
199 << "Message: " << ConfMessage << std::endl
200 << std::endl;
201 WriteToStatusFd(status.str());
202}
203
204
205bool PackageManagerProgressDeb822Fd::StatusChanged(std::string PackageName,
206 unsigned int xStepsDone,
207 unsigned int xTotalSteps,
208 std::string message)
209{
210 StepsDone = xStepsDone;
211 StepsTotal = xTotalSteps;
212
213 // build the status str
214 std::ostringstream status;
215 status << "Status: " << "progress" << std::endl
216 << "Package: " << PackageName << std::endl
217 << "Percent: " << (StepsDone/float(StepsTotal)*100.0) << std::endl
218 << "Message: " << message << std::endl
219 << std::endl;
220 WriteToStatusFd(status.str());
221
222 return true;
223}
224
e96e4e9c
MV
225int PackageManagerFancy::GetNumberTerminalRows()
226{
227 struct winsize win;
228 if(ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&win) != 0)
229 return -1;
230
231 return win.ws_row;
232}
c7ea1eba 233
db78c60c 234void PackageManagerFancy::SetupTerminalScrollArea(int nr_rows)
31f97d7b
MV
235{
236 // scroll down a bit to avoid visual glitch when the screen
237 // area shrinks by one row
238 std::cout << "\n";
239
240 // save cursor
241 std::cout << "\033[s";
242
243 // set scroll region (this will place the cursor in the top left)
244 std::cout << "\033[1;" << nr_rows - 1 << "r";
245
246 // restore cursor but ensure its inside the scrolling area
247 std::cout << "\033[u";
248 static const char *move_cursor_up = "\033[1A";
249 std::cout << move_cursor_up;
db78c60c 250
bc37330b
MV
251 // setup env for (hopefully!) ncurses
252 string s;
253 strprintf(s, "%i", nr_rows);
254 setenv("LINES", s.c_str(), 1);
255
31f97d7b
MV
256 std::flush(std::cout);
257}
258
259PackageManagerFancy::PackageManagerFancy()
31f97d7b 260{
e96e4e9c
MV
261 // setup terminal size
262 old_SIGWINCH = signal(SIGWINCH, HandleSIGWINCH);
263}
264
265PackageManagerFancy::~PackageManagerFancy()
266{
267 signal(SIGWINCH, old_SIGWINCH);
268}
269
270void PackageManagerFancy::HandleSIGWINCH(int)
271{
272 int nr_terminal_rows = GetNumberTerminalRows();
273 SetupTerminalScrollArea(nr_terminal_rows);
31f97d7b
MV
274}
275
e5e51967 276void PackageManagerFancy::Start(int child_pty)
31f97d7b 277{
e96e4e9c 278 int nr_terminal_rows = GetNumberTerminalRows();
db78c60c 279 if (nr_terminal_rows > 0)
e5e51967 280 {
db78c60c 281 SetupTerminalScrollArea(nr_terminal_rows);
e5e51967
MV
282 // *cough*
283 struct winsize win;
284 ioctl(child_pty, TIOCGWINSZ, (char *)&win);
285 win.ws_row = nr_terminal_rows - 1;
286 ioctl(child_pty, TIOCSWINSZ, (char *)&win);
287 }
31f97d7b
MV
288}
289
a22fdebf 290void PackageManagerFancy::Stop()
31f97d7b 291{
e96e4e9c 292 int nr_terminal_rows = GetNumberTerminalRows();
db78c60c
MV
293 if (nr_terminal_rows > 0)
294 {
295 SetupTerminalScrollArea(nr_terminal_rows + 1);
31f97d7b 296
db78c60c
MV
297 // override the progress line (sledgehammer)
298 static const char* clear_screen_below_cursor = "\033[J";
299 std::cout << clear_screen_below_cursor;
300 }
31f97d7b
MV
301}
302
6c5ae8ed 303bool PackageManagerFancy::StatusChanged(std::string PackageName,
31f97d7b 304 unsigned int StepsDone,
e6ad8031
MV
305 unsigned int TotalSteps,
306 std::string HumanReadableAction)
31f97d7b 307{
e6ad8031
MV
308 if (!PackageManager::StatusChanged(PackageName, StepsDone, TotalSteps,
309 HumanReadableAction))
6c5ae8ed 310 return false;
31f97d7b 311
e96e4e9c 312 int row = GetNumberTerminalRows();
31f97d7b
MV
313
314 static string save_cursor = "\033[s";
315 static string restore_cursor = "\033[u";
316
317 static string set_bg_color = "\033[42m"; // green
318 static string set_fg_color = "\033[30m"; // black
319
320 static string restore_bg = "\033[49m";
321 static string restore_fg = "\033[39m";
322
323 std::cout << save_cursor
324 // move cursor position to last row
325 << "\033[" << row << ";0f"
326 << set_bg_color
327 << set_fg_color
328 << progress_str
329 << restore_cursor
330 << restore_bg
331 << restore_fg;
332 std::flush(std::cout);
333 last_reported_progress = percentage;
6c5ae8ed
MV
334
335 return true;
31f97d7b
MV
336}
337
6c5ae8ed 338bool PackageManagerText::StatusChanged(std::string PackageName,
31f97d7b 339 unsigned int StepsDone,
e6ad8031
MV
340 unsigned int TotalSteps,
341 std::string HumanReadableAction)
31f97d7b 342{
e6ad8031 343 if (!PackageManager::StatusChanged(PackageName, StepsDone, TotalSteps, HumanReadableAction))
6c5ae8ed 344 return false;
31f97d7b
MV
345
346 std::cout << progress_str << "\r\n";
347 std::flush(std::cout);
348
349 last_reported_progress = percentage;
6c5ae8ed
MV
350
351 return true;
31f97d7b
MV
352}
353
354
c7ea1eba 355
31f97d7b
MV
356}; // namespace progress
357}; // namespace apt