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