Merge remote-tracking branch 'mvo/feature/deb-src-bts731102' into debian/sid
[ntk/apt.git] / apt-pkg / install-progress.cc
1 #include <apt-pkg/configuration.h>
2 #include <apt-pkg/fileutl.h>
3 #include <apt-pkg/strutl.h>
4 #include <apt-pkg/install-progress.h>
5
6 #include <apti18n.h>
7
8 #include <termios.h>
9 #include <sys/ioctl.h>
10 #include <sstream>
11 #include <fcntl.h>
12
13
14 namespace APT {
15 namespace Progress {
16
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 */
21 PackageManager* 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
43 bool PackageManager::StatusChanged(std::string PackageName,
44 unsigned int StepsDone,
45 unsigned int TotalSteps,
46 std::string HumanReadableAction)
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
58 PackageManagerProgressFd::PackageManagerProgressFd(int progress_fd)
59 : StepsDone(0), StepsTotal(1)
60 {
61 OutStatusFd = progress_fd;
62 }
63
64 void PackageManagerProgressFd::WriteToStatusFd(std::string s)
65 {
66 if(OutStatusFd <= 0)
67 return;
68 FileFd::Write(OutStatusFd, s.c_str(), s.size());
69 }
70
71 void PackageManagerProgressFd::StartDpkg()
72 {
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);
79
80 // send status information that we are about to fork dpkg
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());
87 }
88
89 void PackageManagerProgressFd::Stop()
90 {
91 }
92
93 void 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;
103 WriteToStatusFd(status.str());
104 }
105
106 void 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;
116 WriteToStatusFd(status.str());
117 }
118
119
120 bool 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;
130 status << "pmstatus:" << StringSplit(PackageName, ":")[0]
131 << ":" << (StepsDone/float(StepsTotal)*100.0)
132 << ":" << pkg_action
133 << std::endl;
134 WriteToStatusFd(status.str());
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
142 return true;
143 }
144
145
146 PackageManagerProgressDeb822Fd::PackageManagerProgressDeb822Fd(int progress_fd)
147 : StepsDone(0), StepsTotal(1)
148 {
149 OutStatusFd = progress_fd;
150 }
151
152 void PackageManagerProgressDeb822Fd::WriteToStatusFd(std::string s)
153 {
154 FileFd::Write(OutStatusFd, s.c_str(), s.size());
155 }
156
157 void PackageManagerProgressDeb822Fd::StartDpkg()
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
172 void PackageManagerProgressDeb822Fd::Stop()
173 {
174 }
175
176 void 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
190 void 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
205 bool 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
225 int 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 }
233
234 void PackageManagerFancy::SetupTerminalScrollArea(int nr_rows)
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;
250
251 // setup env for (hopefully!) ncurses
252 string s;
253 strprintf(s, "%i", nr_rows);
254 setenv("LINES", s.c_str(), 1);
255
256 std::flush(std::cout);
257 }
258
259 PackageManagerFancy::PackageManagerFancy()
260 {
261 // setup terminal size
262 old_SIGWINCH = signal(SIGWINCH, HandleSIGWINCH);
263 }
264
265 PackageManagerFancy::~PackageManagerFancy()
266 {
267 signal(SIGWINCH, old_SIGWINCH);
268 }
269
270 void PackageManagerFancy::HandleSIGWINCH(int)
271 {
272 int nr_terminal_rows = GetNumberTerminalRows();
273 SetupTerminalScrollArea(nr_terminal_rows);
274 }
275
276 void PackageManagerFancy::Start()
277 {
278 int nr_terminal_rows = GetNumberTerminalRows();
279 if (nr_terminal_rows > 0)
280 SetupTerminalScrollArea(nr_terminal_rows);
281 }
282
283 void PackageManagerFancy::Stop()
284 {
285 int nr_terminal_rows = GetNumberTerminalRows();
286 if (nr_terminal_rows > 0)
287 {
288 SetupTerminalScrollArea(nr_terminal_rows + 1);
289
290 // override the progress line (sledgehammer)
291 static const char* clear_screen_below_cursor = "\033[J";
292 std::cout << clear_screen_below_cursor;
293 }
294 }
295
296 bool PackageManagerFancy::StatusChanged(std::string PackageName,
297 unsigned int StepsDone,
298 unsigned int TotalSteps,
299 std::string HumanReadableAction)
300 {
301 if (!PackageManager::StatusChanged(PackageName, StepsDone, TotalSteps,
302 HumanReadableAction))
303 return false;
304
305 int row = GetNumberTerminalRows();
306
307 static string save_cursor = "\033[s";
308 static string restore_cursor = "\033[u";
309
310 static string set_bg_color = "\033[42m"; // green
311 static string set_fg_color = "\033[30m"; // black
312
313 static string restore_bg = "\033[49m";
314 static string restore_fg = "\033[39m";
315
316 std::cout << save_cursor
317 // move cursor position to last row
318 << "\033[" << row << ";0f"
319 << set_bg_color
320 << set_fg_color
321 << progress_str
322 << restore_cursor
323 << restore_bg
324 << restore_fg;
325 std::flush(std::cout);
326 last_reported_progress = percentage;
327
328 return true;
329 }
330
331 bool PackageManagerText::StatusChanged(std::string PackageName,
332 unsigned int StepsDone,
333 unsigned int TotalSteps,
334 std::string HumanReadableAction)
335 {
336 if (!PackageManager::StatusChanged(PackageName, StepsDone, TotalSteps, HumanReadableAction))
337 return false;
338
339 std::cout << progress_str << "\r\n";
340 std::flush(std::cout);
341
342 last_reported_progress = percentage;
343
344 return true;
345 }
346
347
348
349 }; // namespace progress
350 }; // namespace apt