You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

446 lines
14 KiB

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