]> git.saurik.com Git - apt.git/blame - apt-pkg/install-progress.cc
properly handle SIGWINCH in PackageManagerFancy again
[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>
5ed88785 12#include <algorithm>
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
5ed88785
MV
225
226PackageManagerFancy::PackageManagerFancy()
227 : child_pty(-1)
228{
229 // setup terminal size
230 old_SIGWINCH = signal(SIGWINCH, PackageManagerFancy::staticSIGWINCH);
231 instances.push_back(this);
232}
233std::vector<PackageManagerFancy*> PackageManagerFancy::instances;
234
235PackageManagerFancy::~PackageManagerFancy()
236{
237 instances.erase(find(instances.begin(), instances.end(), this));
238 signal(SIGWINCH, old_SIGWINCH);
239}
240
241void PackageManagerFancy::staticSIGWINCH(int signum)
242{
243 std::vector<PackageManagerFancy *>::const_iterator I;
244 for(I = instances.begin(); I != instances.end(); I++)
245 (*I)->HandleSIGWINCH(signum);
246}
247
e96e4e9c
MV
248int PackageManagerFancy::GetNumberTerminalRows()
249{
250 struct winsize win;
5ed88785 251 // FIXME: get from "child_pty" instead?
e96e4e9c
MV
252 if(ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&win) != 0)
253 return -1;
5ed88785
MV
254
255 if(_config->FindB("Debug::InstallProgress::Fancy", false) == true)
256 std::cerr << "GetNumberTerminalRows: " << win.ws_row << std::endl;
e96e4e9c
MV
257
258 return win.ws_row;
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
31f97d7b
MV
266 // scroll down a bit to avoid visual glitch when the screen
267 // area shrinks by one row
268 std::cout << "\n";
269
270 // save cursor
271 std::cout << "\033[s";
272
273 // set scroll region (this will place the cursor in the top left)
5ed88785 274 std::cout << "\033[0;" << nr_rows - 1 << "r";
31f97d7b
MV
275
276 // restore cursor but ensure its inside the scrolling area
277 std::cout << "\033[u";
278 static const char *move_cursor_up = "\033[1A";
279 std::cout << move_cursor_up;
db78c60c 280
5ed88785 281 // ensure its flushed
31f97d7b 282 std::flush(std::cout);
31f97d7b 283
5ed88785
MV
284 // setup tty size to ensure xterm/linux console are working properly too
285 // see bug #731738
286 struct winsize win;
287 ioctl(child_pty, TIOCGWINSZ, (char *)&win);
288 win.ws_row = nr_rows - 1;
289 ioctl(child_pty, TIOCSWINSZ, (char *)&win);
e96e4e9c
MV
290}
291
292void PackageManagerFancy::HandleSIGWINCH(int)
293{
294 int nr_terminal_rows = GetNumberTerminalRows();
295 SetupTerminalScrollArea(nr_terminal_rows);
31f97d7b
MV
296}
297
5ed88785 298void PackageManagerFancy::Start(int a_child_pty)
31f97d7b 299{
5ed88785 300 child_pty = a_child_pty;
e96e4e9c 301 int nr_terminal_rows = GetNumberTerminalRows();
db78c60c 302 if (nr_terminal_rows > 0)
4754718a 303 {
db78c60c 304 SetupTerminalScrollArea(nr_terminal_rows);
4754718a 305 }
31f97d7b
MV
306}
307
a22fdebf 308void PackageManagerFancy::Stop()
31f97d7b 309{
e96e4e9c 310 int nr_terminal_rows = GetNumberTerminalRows();
db78c60c
MV
311 if (nr_terminal_rows > 0)
312 {
313 SetupTerminalScrollArea(nr_terminal_rows + 1);
31f97d7b 314
db78c60c
MV
315 // override the progress line (sledgehammer)
316 static const char* clear_screen_below_cursor = "\033[J";
317 std::cout << clear_screen_below_cursor;
318 }
5ed88785 319 child_pty = -1;
31f97d7b
MV
320}
321
6c5ae8ed 322bool PackageManagerFancy::StatusChanged(std::string PackageName,
31f97d7b 323 unsigned int StepsDone,
e6ad8031
MV
324 unsigned int TotalSteps,
325 std::string HumanReadableAction)
31f97d7b 326{
e6ad8031
MV
327 if (!PackageManager::StatusChanged(PackageName, StepsDone, TotalSteps,
328 HumanReadableAction))
6c5ae8ed 329 return false;
31f97d7b 330
e96e4e9c 331 int row = GetNumberTerminalRows();
31f97d7b
MV
332
333 static string save_cursor = "\033[s";
334 static string restore_cursor = "\033[u";
335
336 static string set_bg_color = "\033[42m"; // green
337 static string set_fg_color = "\033[30m"; // black
338
339 static string restore_bg = "\033[49m";
340 static string restore_fg = "\033[39m";
341
342 std::cout << save_cursor
343 // move cursor position to last row
344 << "\033[" << row << ";0f"
345 << set_bg_color
346 << set_fg_color
347 << progress_str
348 << restore_cursor
349 << restore_bg
350 << restore_fg;
351 std::flush(std::cout);
352 last_reported_progress = percentage;
6c5ae8ed
MV
353
354 return true;
31f97d7b
MV
355}
356
6c5ae8ed 357bool PackageManagerText::StatusChanged(std::string PackageName,
31f97d7b 358 unsigned int StepsDone,
e6ad8031
MV
359 unsigned int TotalSteps,
360 std::string HumanReadableAction)
31f97d7b 361{
e6ad8031 362 if (!PackageManager::StatusChanged(PackageName, StepsDone, TotalSteps, HumanReadableAction))
6c5ae8ed 363 return false;
31f97d7b
MV
364
365 std::cout << progress_str << "\r\n";
366 std::flush(std::cout);
367
368 last_reported_progress = percentage;
6c5ae8ed
MV
369
370 return true;
31f97d7b
MV
371}
372
373
c7ea1eba 374
31f97d7b
MV
375}; // namespace progress
376}; // namespace apt