dpkgpm.cc 74 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291
  1. // -*- mode: cpp; mode: fold -*-
  2. // Description /*{{{*/
  3. /* ######################################################################
  4. DPKG Package Manager - Provide an interface to dpkg
  5. ##################################################################### */
  6. /*}}}*/
  7. // Includes /*{{{*/
  8. #include <config.h>
  9. #include <apt-pkg/cachefile.h>
  10. #include <apt-pkg/configuration.h>
  11. #include <apt-pkg/depcache.h>
  12. #include <apt-pkg/dpkgpm.h>
  13. #include <apt-pkg/debsystem.h>
  14. #include <apt-pkg/error.h>
  15. #include <apt-pkg/fileutl.h>
  16. #include <apt-pkg/install-progress.h>
  17. #include <apt-pkg/packagemanager.h>
  18. #include <apt-pkg/strutl.h>
  19. #include <apt-pkg/statechanges.h>
  20. #include <apt-pkg/cacheiterators.h>
  21. #include <apt-pkg/macros.h>
  22. #include <apt-pkg/pkgcache.h>
  23. #include <apt-pkg/version.h>
  24. #include <errno.h>
  25. #include <fcntl.h>
  26. #include <grp.h>
  27. #include <pwd.h>
  28. #include <signal.h>
  29. #include <stddef.h>
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <sys/ioctl.h>
  33. #include <sys/select.h>
  34. #include <sys/stat.h>
  35. #include <sys/time.h>
  36. #include <sys/wait.h>
  37. #include <sys/types.h>
  38. #include <dirent.h>
  39. #include <termios.h>
  40. #include <time.h>
  41. #include <unistd.h>
  42. #include <algorithm>
  43. #include <array>
  44. #include <cstring>
  45. #include <iostream>
  46. #include <map>
  47. #include <set>
  48. #include <string>
  49. #include <type_traits>
  50. #include <utility>
  51. #include <unordered_set>
  52. #include <vector>
  53. #include <sstream>
  54. #include <numeric>
  55. #include <apti18n.h>
  56. /*}}}*/
  57. extern char **environ;
  58. using namespace std;
  59. APT_PURE static string AptHistoryRequestingUser() /*{{{*/
  60. {
  61. const char* EnvKeys[]{"SUDO_UID", "PKEXEC_UID", "PACKAGEKIT_CALLER_UID"};
  62. for (const auto &Key: EnvKeys)
  63. {
  64. if (getenv(Key) != nullptr)
  65. {
  66. int uid = atoi(getenv(Key));
  67. if (uid > 0) {
  68. struct passwd pwd;
  69. struct passwd *result;
  70. char buf[255];
  71. if (getpwuid_r(uid, &pwd, buf, sizeof(buf), &result) == 0 && result != NULL) {
  72. std::string res;
  73. strprintf(res, "%s (%d)", pwd.pw_name, uid);
  74. return res;
  75. }
  76. }
  77. }
  78. }
  79. return "";
  80. }
  81. /*}}}*/
  82. APT_PURE static unsigned int EnvironmentSize() /*{{{*/
  83. {
  84. unsigned int size = 0;
  85. char **envp = environ;
  86. while (*envp != NULL)
  87. size += strlen (*envp++) + 1;
  88. return size;
  89. }
  90. /*}}}*/
  91. class pkgDPkgPMPrivate /*{{{*/
  92. {
  93. public:
  94. pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
  95. term_out(NULL), history_out(NULL),
  96. progress(NULL), tt_is_valid(false), master(-1),
  97. slave(NULL), protect_slave_from_dying(-1),
  98. direct_stdin(false)
  99. {
  100. dpkgbuf[0] = '\0';
  101. }
  102. ~pkgDPkgPMPrivate()
  103. {
  104. }
  105. bool stdin_is_dev_null;
  106. // the buffer we use for the dpkg status-fd reading
  107. char dpkgbuf[1024];
  108. size_t dpkgbuf_pos;
  109. FILE *term_out;
  110. FILE *history_out;
  111. string dpkg_error;
  112. APT::Progress::PackageManager *progress;
  113. // pty stuff
  114. struct termios tt;
  115. bool tt_is_valid;
  116. int master;
  117. char * slave;
  118. int protect_slave_from_dying;
  119. // signals
  120. sigset_t sigmask;
  121. sigset_t original_sigmask;
  122. bool direct_stdin;
  123. };
  124. /*}}}*/
  125. namespace
  126. {
  127. // Maps the dpkg "processing" info to human readable names. Entry 0
  128. // of each array is the key, entry 1 is the value.
  129. const std::pair<const char *, const char *> PackageProcessingOps[] = {
  130. std::make_pair("install", N_("Installing %s")),
  131. std::make_pair("configure", N_("Configuring %s")),
  132. std::make_pair("remove", N_("Removing %s")),
  133. std::make_pair("purge", N_("Completely removing %s")),
  134. std::make_pair("disappear", N_("Noting disappearance of %s")),
  135. std::make_pair("trigproc", N_("Running post-installation trigger %s"))
  136. };
  137. const std::pair<const char *, const char *> * const PackageProcessingOpsBegin = PackageProcessingOps;
  138. const std::pair<const char *, const char *> * const PackageProcessingOpsEnd = PackageProcessingOps + sizeof(PackageProcessingOps) / sizeof(PackageProcessingOps[0]);
  139. // Predicate to test whether an entry in the PackageProcessingOps
  140. // array matches a string.
  141. class MatchProcessingOp
  142. {
  143. const char *target;
  144. public:
  145. explicit MatchProcessingOp(const char *the_target)
  146. : target(the_target)
  147. {
  148. }
  149. bool operator()(const std::pair<const char *, const char *> &pair) const
  150. {
  151. return strcmp(pair.first, target) == 0;
  152. }
  153. };
  154. }
  155. // ionice - helper function to ionice the given PID /*{{{*/
  156. /* there is no C header for ionice yet - just the syscall interface
  157. so we use the binary from util-linux */
  158. static bool ionice(int PID)
  159. {
  160. if (!FileExists("/usr/bin/ionice"))
  161. return false;
  162. pid_t Process = ExecFork();
  163. if (Process == 0)
  164. {
  165. char buf[32];
  166. snprintf(buf, sizeof(buf), "-p%d", PID);
  167. const char *Args[4];
  168. Args[0] = "/usr/bin/ionice";
  169. Args[1] = "-c3";
  170. Args[2] = buf;
  171. Args[3] = 0;
  172. execv(Args[0], (char **)Args);
  173. }
  174. return ExecWait(Process, "ionice");
  175. }
  176. /*}}}*/
  177. // FindNowVersion - Helper to find a Version in "now" state /*{{{*/
  178. // ---------------------------------------------------------------------
  179. /* This is helpful when a package is no longer installed but has residual
  180. * config files
  181. */
  182. static
  183. pkgCache::VerIterator FindNowVersion(const pkgCache::PkgIterator &Pkg)
  184. {
  185. pkgCache::VerIterator Ver;
  186. for (Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
  187. for (pkgCache::VerFileIterator Vf = Ver.FileList(); Vf.end() == false; ++Vf)
  188. for (pkgCache::PkgFileIterator F = Vf.File(); F.end() == false; ++F)
  189. {
  190. if (F.Archive() != 0 && strcmp(F.Archive(), "now") == 0)
  191. return Ver;
  192. }
  193. return Ver;
  194. }
  195. /*}}}*/
  196. static pkgCache::VerIterator FindToBeRemovedVersion(pkgCache::PkgIterator const &Pkg)/*{{{*/
  197. {
  198. auto const PV = Pkg.CurrentVer();
  199. if (PV.end() == false)
  200. return PV;
  201. return FindNowVersion(Pkg);
  202. }
  203. /*}}}*/
  204. // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
  205. // ---------------------------------------------------------------------
  206. /* */
  207. pkgDPkgPM::pkgDPkgPM(pkgDepCache *Cache)
  208. : pkgPackageManager(Cache),d(new pkgDPkgPMPrivate()), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
  209. {
  210. }
  211. /*}}}*/
  212. // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
  213. // ---------------------------------------------------------------------
  214. /* */
  215. pkgDPkgPM::~pkgDPkgPM()
  216. {
  217. delete d;
  218. }
  219. /*}}}*/
  220. // DPkgPM::Install - Install a package /*{{{*/
  221. // ---------------------------------------------------------------------
  222. /* Add an install operation to the sequence list */
  223. bool pkgDPkgPM::Install(PkgIterator Pkg,string File)
  224. {
  225. if (File.empty() == true || Pkg.end() == true)
  226. return _error->Error("Internal Error, No file name for %s",Pkg.FullName().c_str());
  227. // If the filename string begins with DPkg::Chroot-Directory, return the
  228. // substr that is within the chroot so dpkg can access it.
  229. string const chrootdir = _config->FindDir("DPkg::Chroot-Directory","/");
  230. if (chrootdir != "/" && File.find(chrootdir) == 0)
  231. {
  232. size_t len = chrootdir.length();
  233. if (chrootdir.at(len - 1) == '/')
  234. len--;
  235. List.push_back(Item(Item::Install,Pkg,File.substr(len)));
  236. }
  237. else
  238. List.push_back(Item(Item::Install,Pkg,File));
  239. return true;
  240. }
  241. /*}}}*/
  242. // DPkgPM::Configure - Configure a package /*{{{*/
  243. // ---------------------------------------------------------------------
  244. /* Add a configure operation to the sequence list */
  245. bool pkgDPkgPM::Configure(PkgIterator Pkg)
  246. {
  247. if (Pkg.end() == true)
  248. return false;
  249. List.push_back(Item(Item::Configure, Pkg));
  250. // Use triggers for config calls if we configure "smart"
  251. // as otherwise Pre-Depends will not be satisfied, see #526774
  252. if (_config->FindB("DPkg::TriggersPending", false) == true)
  253. List.push_back(Item(Item::TriggersPending, PkgIterator()));
  254. return true;
  255. }
  256. /*}}}*/
  257. // DPkgPM::Remove - Remove a package /*{{{*/
  258. // ---------------------------------------------------------------------
  259. /* Add a remove operation to the sequence list */
  260. bool pkgDPkgPM::Remove(PkgIterator Pkg,bool Purge)
  261. {
  262. if (Pkg.end() == true)
  263. return false;
  264. if (Purge == true)
  265. List.push_back(Item(Item::Purge,Pkg));
  266. else
  267. List.push_back(Item(Item::Remove,Pkg));
  268. return true;
  269. }
  270. /*}}}*/
  271. // DPkgPM::SendPkgInfo - Send info for install-pkgs hook /*{{{*/
  272. // ---------------------------------------------------------------------
  273. /* This is part of the helper script communication interface, it sends
  274. very complete information down to the other end of the pipe.*/
  275. bool pkgDPkgPM::SendV2Pkgs(FILE *F)
  276. {
  277. return SendPkgsInfo(F, 2);
  278. }
  279. bool pkgDPkgPM::SendPkgsInfo(FILE * const F, unsigned int const &Version)
  280. {
  281. // This version of APT supports only v3, so don't sent higher versions
  282. if (Version <= 3)
  283. fprintf(F,"VERSION %u\n", Version);
  284. else
  285. fprintf(F,"VERSION 3\n");
  286. /* Write out all of the configuration directives by walking the
  287. configuration tree */
  288. const Configuration::Item *Top = _config->Tree(0);
  289. for (; Top != 0;)
  290. {
  291. if (Top->Value.empty() == false)
  292. {
  293. fprintf(F,"%s=%s\n",
  294. QuoteString(Top->FullTag(),"=\"\n").c_str(),
  295. QuoteString(Top->Value,"\n").c_str());
  296. }
  297. if (Top->Child != 0)
  298. {
  299. Top = Top->Child;
  300. continue;
  301. }
  302. while (Top != 0 && Top->Next == 0)
  303. Top = Top->Parent;
  304. if (Top != 0)
  305. Top = Top->Next;
  306. }
  307. fprintf(F,"\n");
  308. // Write out the package actions in order.
  309. for (vector<Item>::iterator I = List.begin(); I != List.end(); ++I)
  310. {
  311. if(I->Pkg.end() == true)
  312. continue;
  313. pkgDepCache::StateCache &S = Cache[I->Pkg];
  314. fprintf(F,"%s ",I->Pkg.Name());
  315. // Current version which we are going to replace
  316. pkgCache::VerIterator CurVer = I->Pkg.CurrentVer();
  317. if (CurVer.end() == true && (I->Op == Item::Remove || I->Op == Item::Purge))
  318. CurVer = FindNowVersion(I->Pkg);
  319. if (CurVer.end() == true)
  320. {
  321. if (Version <= 2)
  322. fprintf(F, "- ");
  323. else
  324. fprintf(F, "- - none ");
  325. }
  326. else
  327. {
  328. fprintf(F, "%s ", CurVer.VerStr());
  329. if (Version >= 3)
  330. fprintf(F, "%s %s ", CurVer.Arch(), CurVer.MultiArchType());
  331. }
  332. // Show the compare operator between current and install version
  333. if (S.InstallVer != 0)
  334. {
  335. pkgCache::VerIterator const InstVer = S.InstVerIter(Cache);
  336. int Comp = 2;
  337. if (CurVer.end() == false)
  338. Comp = InstVer.CompareVer(CurVer);
  339. if (Comp < 0)
  340. fprintf(F,"> ");
  341. else if (Comp == 0)
  342. fprintf(F,"= ");
  343. else if (Comp > 0)
  344. fprintf(F,"< ");
  345. fprintf(F, "%s ", InstVer.VerStr());
  346. if (Version >= 3)
  347. fprintf(F, "%s %s ", InstVer.Arch(), InstVer.MultiArchType());
  348. }
  349. else
  350. {
  351. if (Version <= 2)
  352. fprintf(F, "> - ");
  353. else
  354. fprintf(F, "> - - none ");
  355. }
  356. // Show the filename/operation
  357. if (I->Op == Item::Install)
  358. {
  359. // No errors here..
  360. if (I->File[0] != '/')
  361. fprintf(F,"**ERROR**\n");
  362. else
  363. fprintf(F,"%s\n",I->File.c_str());
  364. }
  365. else if (I->Op == Item::Configure)
  366. fprintf(F,"**CONFIGURE**\n");
  367. else if (I->Op == Item::Remove ||
  368. I->Op == Item::Purge)
  369. fprintf(F,"**REMOVE**\n");
  370. if (ferror(F) != 0)
  371. return false;
  372. }
  373. return true;
  374. }
  375. /*}}}*/
  376. // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
  377. // ---------------------------------------------------------------------
  378. /* This looks for a list of scripts to run from the configuration file
  379. each one is run and is fed on standard input a list of all .deb files
  380. that are due to be installed. */
  381. bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf)
  382. {
  383. bool result = true;
  384. Configuration::Item const *Opts = _config->Tree(Cnf);
  385. if (Opts == 0 || Opts->Child == 0)
  386. return true;
  387. Opts = Opts->Child;
  388. sighandler_t old_sigpipe = signal(SIGPIPE, SIG_IGN);
  389. sighandler_t old_sigint = signal(SIGINT, SIG_IGN);
  390. sighandler_t old_sigquit = signal(SIGQUIT, SIG_IGN);
  391. unsigned int Count = 1;
  392. for (; Opts != 0; Opts = Opts->Next, Count++)
  393. {
  394. if (Opts->Value.empty() == true)
  395. continue;
  396. if(_config->FindB("Debug::RunScripts", false) == true)
  397. std::clog << "Running external script with list of all .deb file: '"
  398. << Opts->Value << "'" << std::endl;
  399. // Determine the protocol version
  400. string OptSec = Opts->Value;
  401. string::size_type Pos;
  402. if ((Pos = OptSec.find(' ')) == string::npos || Pos == 0)
  403. Pos = OptSec.length();
  404. OptSec = "DPkg::Tools::Options::" + string(Opts->Value.c_str(),Pos);
  405. unsigned int Version = _config->FindI(OptSec+"::Version",1);
  406. unsigned int InfoFD = _config->FindI(OptSec + "::InfoFD", STDIN_FILENO);
  407. // Create the pipes
  408. std::set<int> KeepFDs;
  409. MergeKeepFdsFromConfiguration(KeepFDs);
  410. int Pipes[2];
  411. if (pipe(Pipes) != 0) {
  412. result = _error->Errno("pipe","Failed to create IPC pipe to subprocess");
  413. break;
  414. }
  415. if (InfoFD != (unsigned)Pipes[0])
  416. SetCloseExec(Pipes[0],true);
  417. else
  418. KeepFDs.insert(Pipes[0]);
  419. SetCloseExec(Pipes[1],true);
  420. // Purified Fork for running the script
  421. pid_t Process = ExecFork(KeepFDs);
  422. if (Process == 0)
  423. {
  424. // Setup the FDs
  425. dup2(Pipes[0], InfoFD);
  426. SetCloseExec(STDOUT_FILENO,false);
  427. SetCloseExec(STDIN_FILENO,false);
  428. SetCloseExec(STDERR_FILENO,false);
  429. string hookfd;
  430. strprintf(hookfd, "%d", InfoFD);
  431. setenv("APT_HOOK_INFO_FD", hookfd.c_str(), 1);
  432. debSystem::DpkgChrootDirectory();
  433. const char *Args[4];
  434. Args[0] = "/bin/sh";
  435. Args[1] = "-c";
  436. Args[2] = Opts->Value.c_str();
  437. Args[3] = 0;
  438. execv(Args[0],(char **)Args);
  439. _exit(100);
  440. }
  441. close(Pipes[0]);
  442. FILE *F = fdopen(Pipes[1],"w");
  443. if (F == 0) {
  444. result = _error->Errno("fdopen","Failed to open new FD");
  445. break;
  446. }
  447. // Feed it the filenames.
  448. if (Version <= 1)
  449. {
  450. for (vector<Item>::iterator I = List.begin(); I != List.end(); ++I)
  451. {
  452. // Only deal with packages to be installed from .deb
  453. if (I->Op != Item::Install)
  454. continue;
  455. // No errors here..
  456. if (I->File[0] != '/')
  457. continue;
  458. /* Feed the filename of each package that is pending install
  459. into the pipe. */
  460. fprintf(F,"%s\n",I->File.c_str());
  461. if (ferror(F) != 0)
  462. break;
  463. }
  464. }
  465. else
  466. SendPkgsInfo(F, Version);
  467. fclose(F);
  468. // Clean up the sub process
  469. if (ExecWait(Process,Opts->Value.c_str()) == false) {
  470. result = _error->Error("Failure running script %s",Opts->Value.c_str());
  471. break;
  472. }
  473. }
  474. signal(SIGINT, old_sigint);
  475. signal(SIGPIPE, old_sigpipe);
  476. signal(SIGQUIT, old_sigquit);
  477. return result;
  478. }
  479. /*}}}*/
  480. // DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/
  481. // ---------------------------------------------------------------------
  482. /*
  483. */
  484. void pkgDPkgPM::DoStdin(int master)
  485. {
  486. unsigned char input_buf[256] = {0,};
  487. ssize_t len = read(STDIN_FILENO, input_buf, sizeof(input_buf));
  488. if (len)
  489. FileFd::Write(master, input_buf, len);
  490. else
  491. d->stdin_is_dev_null = true;
  492. }
  493. /*}}}*/
  494. // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
  495. // ---------------------------------------------------------------------
  496. /*
  497. * read the terminal pty and write log
  498. */
  499. void pkgDPkgPM::DoTerminalPty(int master)
  500. {
  501. unsigned char term_buf[1024] = {0,0, };
  502. ssize_t len=read(master, term_buf, sizeof(term_buf));
  503. if(len == -1 && errno == EIO)
  504. {
  505. // this happens when the child is about to exit, we
  506. // give it time to actually exit, otherwise we run
  507. // into a race so we sleep for half a second.
  508. struct timespec sleepfor = { 0, 500000000 };
  509. nanosleep(&sleepfor, NULL);
  510. return;
  511. }
  512. if(len <= 0)
  513. return;
  514. FileFd::Write(1, term_buf, len);
  515. if(d->term_out)
  516. fwrite(term_buf, len, sizeof(char), d->term_out);
  517. }
  518. /*}}}*/
  519. // DPkgPM::ProcessDpkgStatusBuf /*{{{*/
  520. void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
  521. {
  522. bool const Debug = _config->FindB("Debug::pkgDPkgProgressReporting",false);
  523. if (Debug == true)
  524. std::clog << "got from dpkg '" << line << "'" << std::endl;
  525. /* dpkg sends strings like this:
  526. 'status: <pkg>: <pkg qstate>'
  527. 'status: <pkg>:<arch>: <pkg qstate>'
  528. 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
  529. 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
  530. */
  531. // we need to split on ": " (note the appended space) as the ':' is
  532. // part of the pkgname:arch information that dpkg sends
  533. //
  534. // A dpkg error message may contain additional ":" (like
  535. // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
  536. // so we need to ensure to not split too much
  537. std::vector<std::string> list = StringSplit(line, ": ", 4);
  538. if(list.size() < 3)
  539. {
  540. if (Debug == true)
  541. std::clog << "ignoring line: not enough ':'" << std::endl;
  542. return;
  543. }
  544. // build the (prefix, pkgname, action) tuple, position of this
  545. // is different for "processing" or "status" messages
  546. std::string prefix = APT::String::Strip(list[0]);
  547. std::string pkgname;
  548. std::string action;
  549. // "processing" has the form "processing: action: pkg or trigger"
  550. // with action = ["install", "upgrade", "configure", "remove", "purge",
  551. // "disappear", "trigproc"]
  552. if (prefix == "processing")
  553. {
  554. pkgname = APT::String::Strip(list[2]);
  555. action = APT::String::Strip(list[1]);
  556. // we don't care for the difference (as dpkg doesn't really either)
  557. if (action == "upgrade")
  558. action = "install";
  559. }
  560. // "status" has the form: "status: pkg: state"
  561. // with state in ["half-installed", "unpacked", "half-configured",
  562. // "installed", "config-files", "not-installed"]
  563. else if (prefix == "status")
  564. {
  565. pkgname = APT::String::Strip(list[1]);
  566. action = APT::String::Strip(list[2]);
  567. } else {
  568. if (Debug == true)
  569. std::clog << "unknown prefix '" << prefix << "'" << std::endl;
  570. return;
  571. }
  572. /* handle the special cases first:
  573. errors look like this:
  574. 'status: /var/cache/apt/archives/krecipes_0.8.1-0ubuntu1_i386.deb : error : trying to overwrite `/usr/share/doc/kde/HTML/en/krecipes/krectip.png', which is also in package krecipes-data
  575. and conffile-prompt like this
  576. 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
  577. */
  578. if (prefix == "status")
  579. {
  580. if(action == "error")
  581. {
  582. d->progress->Error(pkgname, PackagesDone, PackagesTotal,
  583. list[3]);
  584. pkgFailures++;
  585. WriteApportReport(pkgname.c_str(), list[3].c_str());
  586. return;
  587. }
  588. else if(action == "conffile-prompt")
  589. {
  590. d->progress->ConffilePrompt(pkgname, PackagesDone, PackagesTotal,
  591. list[3]);
  592. return;
  593. }
  594. }
  595. // at this point we know that we should have a valid pkgname, so build all
  596. // the info from it
  597. // dpkg does not always send "pkgname:arch" so we add it here if needed
  598. if (pkgname.find(":") == std::string::npos)
  599. {
  600. // find the package in the group that is touched by dpkg
  601. // if there are multiple pkgs dpkg would send us a full pkgname:arch
  602. pkgCache::GrpIterator Grp = Cache.FindGrp(pkgname);
  603. if (Grp.end() == false)
  604. for (auto P = Grp.PackageList(); P.end() != true; P = Grp.NextPkg(P))
  605. if(Cache[P].Keep() == false || Cache[P].ReInstall() == true)
  606. {
  607. auto fullname = P.FullName();
  608. if (Cache[P].Delete() && PackageOps[fullname].size() <= PackageOpsDone[fullname])
  609. continue;
  610. pkgname = std::move(fullname);
  611. break;
  612. }
  613. }
  614. std::string arch = "";
  615. if (pkgname.find(":") != string::npos)
  616. arch = StringSplit(pkgname, ":")[1];
  617. std::string i18n_pkgname = pkgname;
  618. if (arch.size() != 0)
  619. strprintf(i18n_pkgname, "%s (%s)", StringSplit(pkgname, ":")[0].c_str(), arch.c_str());
  620. // 'processing' from dpkg looks like
  621. // 'processing: action: pkg'
  622. if(prefix == "processing")
  623. {
  624. const std::pair<const char *, const char *> * const iter =
  625. std::find_if(PackageProcessingOpsBegin,
  626. PackageProcessingOpsEnd,
  627. MatchProcessingOp(action.c_str()));
  628. if(iter == PackageProcessingOpsEnd)
  629. {
  630. if (Debug == true)
  631. std::clog << "ignoring unknown action: " << action << std::endl;
  632. return;
  633. }
  634. std::string msg;
  635. strprintf(msg, _(iter->second), i18n_pkgname.c_str());
  636. d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg);
  637. // FIXME: this needs a muliarch testcase
  638. // FIXME2: is "pkgname" here reliable with dpkg only sending us
  639. // short pkgnames?
  640. if (action == "disappear")
  641. handleDisappearAction(pkgname);
  642. return;
  643. }
  644. if (prefix == "status")
  645. {
  646. std::vector<struct DpkgState> &states = PackageOps[pkgname];
  647. if (action == "triggers-pending")
  648. {
  649. if (Debug == true)
  650. std::clog << "(parsed from dpkg) pkg: " << pkgname
  651. << " action: " << action << " (prefix 2 to "
  652. << PackageOpsDone[pkgname] << " of " << states.size() << ")" << endl;
  653. states.insert(states.begin(), {"installed", N_("Installed %s")});
  654. states.insert(states.begin(), {"half-configured", N_("Configuring %s")});
  655. PackagesTotal += 2;
  656. }
  657. else if(PackageOpsDone[pkgname] < states.size())
  658. {
  659. char const * next_action = states[PackageOpsDone[pkgname]].state;
  660. if (next_action)
  661. {
  662. /*
  663. if (action == "half-installed" && strcmp("half-configured", next_action) == 0 &&
  664. PackageOpsDone[pkg] + 2 < states.size() && action == states[PackageOpsDone[pkg] + 2].state)
  665. {
  666. if (Debug == true)
  667. std::clog << "(parsed from dpkg) pkg: " << short_pkgname << " action: " << action
  668. << " pending trigger defused by unpack" << std::endl;
  669. // unpacking a package defuses the pending trigger
  670. PackageOpsDone[pkg] += 2;
  671. PackagesDone += 2;
  672. next_action = states[PackageOpsDone[pkg]].state;
  673. }
  674. */
  675. if (Debug == true)
  676. std::clog << "(parsed from dpkg) pkg: " << pkgname
  677. << " action: " << action << " (expected: '" << next_action << "' "
  678. << PackageOpsDone[pkgname] << " of " << states.size() << ")" << endl;
  679. // check if the package moved to the next dpkg state
  680. if(action == next_action)
  681. {
  682. // only read the translation if there is actually a next action
  683. char const * const translation = _(states[PackageOpsDone[pkgname]].str);
  684. // we moved from one dpkg state to a new one, report that
  685. ++PackageOpsDone[pkgname];
  686. ++PackagesDone;
  687. std::string msg;
  688. strprintf(msg, translation, i18n_pkgname.c_str());
  689. d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg);
  690. }
  691. else if (action == "unpacked" && strcmp(next_action, "config-files") == 0)
  692. {
  693. // in a crossgrade what looked like a remove first is really an unpack over it
  694. ++PackageOpsDone[pkgname];
  695. ++PackagesDone;
  696. auto const Pkg = Cache.FindPkg(pkgname);
  697. if (likely(Pkg.end() == false))
  698. {
  699. auto const Grp = Pkg.Group();
  700. if (likely(Grp.end() == false))
  701. {
  702. for (auto P = Grp.PackageList(); P.end() != true; P = Grp.NextPkg(P))
  703. if(Cache[P].Install())
  704. {
  705. auto && Ops = PackageOps[P.FullName()];
  706. auto const unpackOp = std::find_if(Ops.cbegin(), Ops.cend(), [](DpkgState const &s) { return strcmp(s.state, "unpacked") == 0; });
  707. if (unpackOp != Ops.cend())
  708. {
  709. auto const skipped = std::distance(Ops.cbegin(), unpackOp);
  710. PackagesDone += skipped;
  711. PackageOpsDone[P.FullName()] += skipped;
  712. break;
  713. }
  714. }
  715. }
  716. }
  717. }
  718. }
  719. }
  720. }
  721. }
  722. /*}}}*/
  723. // DPkgPM::handleDisappearAction /*{{{*/
  724. void pkgDPkgPM::handleDisappearAction(string const &pkgname)
  725. {
  726. pkgCache::PkgIterator Pkg = Cache.FindPkg(pkgname);
  727. if (unlikely(Pkg.end() == true))
  728. return;
  729. // record the package name for display and stuff later
  730. disappearedPkgs.insert(Pkg.FullName(true));
  731. // the disappeared package was auto-installed - nothing to do
  732. if ((Cache[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
  733. return;
  734. pkgCache::VerIterator PkgVer = Cache[Pkg].InstVerIter(Cache);
  735. if (unlikely(PkgVer.end() == true))
  736. return;
  737. /* search in the list of dependencies for (Pre)Depends,
  738. check if this dependency has a Replaces on our package
  739. and if so transfer the manual installed flag to it */
  740. for (pkgCache::DepIterator Dep = PkgVer.DependsList(); Dep.end() != true; ++Dep)
  741. {
  742. if (Dep->Type != pkgCache::Dep::Depends &&
  743. Dep->Type != pkgCache::Dep::PreDepends)
  744. continue;
  745. pkgCache::PkgIterator Tar = Dep.TargetPkg();
  746. if (unlikely(Tar.end() == true))
  747. continue;
  748. // the package is already marked as manual
  749. if ((Cache[Tar].Flags & pkgCache::Flag::Auto) != pkgCache::Flag::Auto)
  750. continue;
  751. pkgCache::VerIterator TarVer = Cache[Tar].InstVerIter(Cache);
  752. if (TarVer.end() == true)
  753. continue;
  754. for (pkgCache::DepIterator Rep = TarVer.DependsList(); Rep.end() != true; ++Rep)
  755. {
  756. if (Rep->Type != pkgCache::Dep::Replaces)
  757. continue;
  758. if (Pkg != Rep.TargetPkg())
  759. continue;
  760. // okay, they are strongly connected - transfer manual-bit
  761. if (Debug == true)
  762. std::clog << "transfer manual-bit from disappeared »" << pkgname << "« to »" << Tar.FullName() << "«" << std::endl;
  763. Cache[Tar].Flags &= ~Flag::Auto;
  764. break;
  765. }
  766. }
  767. }
  768. /*}}}*/
  769. // DPkgPM::DoDpkgStatusFd /*{{{*/
  770. void pkgDPkgPM::DoDpkgStatusFd(int statusfd)
  771. {
  772. ssize_t const len = read(statusfd, &d->dpkgbuf[d->dpkgbuf_pos],
  773. (sizeof(d->dpkgbuf)/sizeof(d->dpkgbuf[0])) - d->dpkgbuf_pos);
  774. if(len <= 0)
  775. return;
  776. d->dpkgbuf_pos += (len / sizeof(d->dpkgbuf[0]));
  777. // process line by line from the buffer
  778. char *p = d->dpkgbuf, *q = nullptr;
  779. while((q=(char*)memchr(p, '\n', (d->dpkgbuf + d->dpkgbuf_pos) - p)) != nullptr)
  780. {
  781. *q = '\0';
  782. ProcessDpkgStatusLine(p);
  783. p = q + 1; // continue with next line
  784. }
  785. // check if we stripped the buffer clean
  786. if (p > (d->dpkgbuf + d->dpkgbuf_pos))
  787. {
  788. d->dpkgbuf_pos = 0;
  789. return;
  790. }
  791. // otherwise move the unprocessed tail to the start and update pos
  792. memmove(d->dpkgbuf, p, (p - d->dpkgbuf));
  793. d->dpkgbuf_pos = (d->dpkgbuf + d->dpkgbuf_pos) - p;
  794. }
  795. /*}}}*/
  796. // DPkgPM::WriteHistoryTag /*{{{*/
  797. void pkgDPkgPM::WriteHistoryTag(string const &tag, string value)
  798. {
  799. size_t const length = value.length();
  800. if (length == 0)
  801. return;
  802. // poor mans rstrip(", ")
  803. if (value[length-2] == ',' && value[length-1] == ' ')
  804. value.erase(length - 2, 2);
  805. fprintf(d->history_out, "%s: %s\n", tag.c_str(), value.c_str());
  806. } /*}}}*/
  807. // DPkgPM::OpenLog /*{{{*/
  808. bool pkgDPkgPM::OpenLog()
  809. {
  810. string const logdir = _config->FindDir("Dir::Log");
  811. if(CreateAPTDirectoryIfNeeded(logdir, logdir) == false)
  812. // FIXME: use a better string after freeze
  813. return _error->Error(_("Directory '%s' missing"), logdir.c_str());
  814. // get current time
  815. char timestr[200];
  816. time_t const t = time(NULL);
  817. struct tm tm_buf;
  818. struct tm const * const tmp = localtime_r(&t, &tm_buf);
  819. strftime(timestr, sizeof(timestr), "%F %T", tmp);
  820. // open terminal log
  821. string const logfile_name = flCombine(logdir,
  822. _config->Find("Dir::Log::Terminal"));
  823. if (!logfile_name.empty())
  824. {
  825. d->term_out = fopen(logfile_name.c_str(),"a");
  826. if (d->term_out == NULL)
  827. return _error->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name.c_str());
  828. setvbuf(d->term_out, NULL, _IONBF, 0);
  829. SetCloseExec(fileno(d->term_out), true);
  830. if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
  831. {
  832. struct passwd *pw = getpwnam("root");
  833. struct group *gr = getgrnam("adm");
  834. if (pw != NULL && gr != NULL && chown(logfile_name.c_str(), pw->pw_uid, gr->gr_gid) != 0)
  835. _error->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name.c_str());
  836. }
  837. if (chmod(logfile_name.c_str(), 0640) != 0)
  838. _error->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name.c_str());
  839. fprintf(d->term_out, "\nLog started: %s\n", timestr);
  840. }
  841. // write your history
  842. string const history_name = flCombine(logdir,
  843. _config->Find("Dir::Log::History"));
  844. if (!history_name.empty())
  845. {
  846. d->history_out = fopen(history_name.c_str(),"a");
  847. if (d->history_out == NULL)
  848. return _error->WarningE("OpenLog", _("Could not open file '%s'"), history_name.c_str());
  849. SetCloseExec(fileno(d->history_out), true);
  850. chmod(history_name.c_str(), 0644);
  851. fprintf(d->history_out, "\nStart-Date: %s\n", timestr);
  852. string remove, purge, install, reinstall, upgrade, downgrade;
  853. for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
  854. {
  855. enum { CANDIDATE, CANDIDATE_AUTO, CURRENT_CANDIDATE, CURRENT } infostring;
  856. string *line = NULL;
  857. #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
  858. if (Cache[I].NewInstall() == true)
  859. HISTORYINFO(install, CANDIDATE_AUTO)
  860. else if (Cache[I].ReInstall() == true)
  861. HISTORYINFO(reinstall, CANDIDATE)
  862. else if (Cache[I].Upgrade() == true)
  863. HISTORYINFO(upgrade, CURRENT_CANDIDATE)
  864. else if (Cache[I].Downgrade() == true)
  865. HISTORYINFO(downgrade, CURRENT_CANDIDATE)
  866. else if (Cache[I].Delete() == true)
  867. HISTORYINFO((Cache[I].Purge() ? purge : remove), CURRENT)
  868. else
  869. continue;
  870. #undef HISTORYINFO
  871. line->append(I.FullName(false)).append(" (");
  872. switch (infostring) {
  873. case CANDIDATE: line->append(Cache[I].CandVersion); break;
  874. case CANDIDATE_AUTO:
  875. line->append(Cache[I].CandVersion);
  876. if ((Cache[I].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
  877. line->append(", automatic");
  878. break;
  879. case CURRENT_CANDIDATE: line->append(Cache[I].CurVersion).append(", ").append(Cache[I].CandVersion); break;
  880. case CURRENT: line->append(Cache[I].CurVersion); break;
  881. }
  882. line->append("), ");
  883. }
  884. if (_config->Exists("Commandline::AsString") == true)
  885. WriteHistoryTag("Commandline", _config->Find("Commandline::AsString"));
  886. std::string RequestingUser = AptHistoryRequestingUser();
  887. if (RequestingUser != "")
  888. WriteHistoryTag("Requested-By", RequestingUser);
  889. WriteHistoryTag("Install", install);
  890. WriteHistoryTag("Reinstall", reinstall);
  891. WriteHistoryTag("Upgrade", upgrade);
  892. WriteHistoryTag("Downgrade",downgrade);
  893. WriteHistoryTag("Remove",remove);
  894. WriteHistoryTag("Purge",purge);
  895. fflush(d->history_out);
  896. }
  897. return true;
  898. }
  899. /*}}}*/
  900. // DPkg::CloseLog /*{{{*/
  901. bool pkgDPkgPM::CloseLog()
  902. {
  903. char timestr[200];
  904. time_t t = time(NULL);
  905. struct tm tm_buf;
  906. struct tm *tmp = localtime_r(&t, &tm_buf);
  907. strftime(timestr, sizeof(timestr), "%F %T", tmp);
  908. if(d->term_out)
  909. {
  910. fprintf(d->term_out, "Log ended: ");
  911. fprintf(d->term_out, "%s", timestr);
  912. fprintf(d->term_out, "\n");
  913. fclose(d->term_out);
  914. }
  915. d->term_out = NULL;
  916. if(d->history_out)
  917. {
  918. if (disappearedPkgs.empty() == false)
  919. {
  920. string disappear;
  921. for (std::set<std::string>::const_iterator d = disappearedPkgs.begin();
  922. d != disappearedPkgs.end(); ++d)
  923. {
  924. pkgCache::PkgIterator P = Cache.FindPkg(*d);
  925. disappear.append(*d);
  926. if (P.end() == true)
  927. disappear.append(", ");
  928. else
  929. disappear.append(" (").append(Cache[P].CurVersion).append("), ");
  930. }
  931. WriteHistoryTag("Disappeared", disappear);
  932. }
  933. if (d->dpkg_error.empty() == false)
  934. fprintf(d->history_out, "Error: %s\n", d->dpkg_error.c_str());
  935. fprintf(d->history_out, "End-Date: %s\n", timestr);
  936. fclose(d->history_out);
  937. }
  938. d->history_out = NULL;
  939. return true;
  940. }
  941. /*}}}*/
  942. // DPkgPM::BuildPackagesProgressMap /*{{{*/
  943. void pkgDPkgPM::BuildPackagesProgressMap()
  944. {
  945. // map the dpkg states to the operations that are performed
  946. // (this is sorted in the same way as Item::Ops)
  947. static const std::array<std::array<DpkgState, 3>, 4> DpkgStatesOpMap = {{
  948. // Install operation
  949. {{
  950. {"half-installed", N_("Preparing %s")},
  951. {"unpacked", N_("Unpacking %s") },
  952. {nullptr, nullptr}
  953. }},
  954. // Configure operation
  955. {{
  956. {"unpacked",N_("Preparing to configure %s") },
  957. {"half-configured", N_("Configuring %s") },
  958. { "installed", N_("Installed %s")},
  959. }},
  960. // Remove operation
  961. {{
  962. {"half-configured", N_("Preparing for removal of %s")},
  963. {"half-installed", N_("Removing %s")},
  964. {"config-files", N_("Removed %s")},
  965. }},
  966. // Purge operation
  967. {{
  968. {"config-files", N_("Preparing to completely remove %s")},
  969. {"not-installed", N_("Completely removed %s")},
  970. {nullptr, nullptr}
  971. }},
  972. }};
  973. static_assert(Item::Purge == 3, "Enum item has unexpected index for mapping array");
  974. // init the PackageOps map, go over the list of packages that
  975. // that will be [installed|configured|removed|purged] and add
  976. // them to the PackageOps map (the dpkg states it goes through)
  977. // and the PackageOpsTranslations (human readable strings)
  978. for (auto &&I : List)
  979. {
  980. if(I.Pkg.end() == true)
  981. continue;
  982. string const name = I.Pkg.FullName();
  983. PackageOpsDone[name] = 0;
  984. auto AddToPackageOps = std::back_inserter(PackageOps[name]);
  985. if (I.Op == Item::Purge && I.Pkg->CurrentVer != 0)
  986. {
  987. // purging a package which is installed first passes through remove states
  988. auto const DpkgOps = DpkgStatesOpMap[Item::Remove];
  989. std::copy(DpkgOps.begin(), DpkgOps.end(), AddToPackageOps);
  990. PackagesTotal += DpkgOps.size();
  991. }
  992. auto const DpkgOps = DpkgStatesOpMap[I.Op];
  993. std::copy_if(DpkgOps.begin(), DpkgOps.end(), AddToPackageOps, [&](DpkgState const &state) {
  994. if (state.state == nullptr)
  995. return false;
  996. ++PackagesTotal;
  997. return true;
  998. });
  999. }
  1000. /* one extra: We don't want the progress bar to reach 100%, especially not
  1001. if we call dpkg --configure --pending and process a bunch of triggers
  1002. while showing 100%. Also, spindown takes a while, so never reaching 100%
  1003. is way more correct than reaching 100% while still doing stuff even if
  1004. doing it this way is slightly bending the rules */
  1005. ++PackagesTotal;
  1006. }
  1007. /*}}}*/
  1008. bool pkgDPkgPM::Go(int StatusFd) /*{{{*/
  1009. {
  1010. APT::Progress::PackageManager *progress = NULL;
  1011. if (StatusFd == -1)
  1012. progress = APT::Progress::PackageManagerProgressFactory();
  1013. else
  1014. progress = new APT::Progress::PackageManagerProgressFd(StatusFd);
  1015. return Go(progress);
  1016. }
  1017. /*}}}*/
  1018. void pkgDPkgPM::StartPtyMagic() /*{{{*/
  1019. {
  1020. if (_config->FindB("Dpkg::Use-Pty", true) == false)
  1021. {
  1022. d->master = -1;
  1023. if (d->slave != NULL)
  1024. free(d->slave);
  1025. d->slave = NULL;
  1026. return;
  1027. }
  1028. if (isatty(STDIN_FILENO) == 0)
  1029. d->direct_stdin = true;
  1030. _error->PushToStack();
  1031. d->master = posix_openpt(O_RDWR | O_NOCTTY);
  1032. if (d->master == -1)
  1033. _error->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
  1034. else if (unlockpt(d->master) == -1)
  1035. _error->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d->master);
  1036. else
  1037. {
  1038. #ifdef HAVE_PTSNAME_R
  1039. char slave_name[64]; // 64 is used by bionic
  1040. if (ptsname_r(d->master, slave_name, sizeof(slave_name)) != 0)
  1041. #else
  1042. char const * const slave_name = ptsname(d->master);
  1043. if (slave_name == NULL)
  1044. #endif
  1045. _error->Errno("ptsname", "Getting name for slave of master fd %d failed!", d->master);
  1046. else
  1047. {
  1048. d->slave = strdup(slave_name);
  1049. if (d->slave == NULL)
  1050. _error->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name, d->master);
  1051. else if (grantpt(d->master) == -1)
  1052. _error->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name, d->master);
  1053. else if (tcgetattr(STDIN_FILENO, &d->tt) == 0)
  1054. {
  1055. d->tt_is_valid = true;
  1056. struct termios raw_tt;
  1057. // copy window size of stdout if its a 'good' terminal
  1058. if (tcgetattr(STDOUT_FILENO, &raw_tt) == 0)
  1059. {
  1060. struct winsize win;
  1061. if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) < 0)
  1062. _error->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
  1063. if (ioctl(d->master, TIOCSWINSZ, &win) < 0)
  1064. _error->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d->master);
  1065. }
  1066. if (tcsetattr(d->master, TCSANOW, &d->tt) == -1)
  1067. _error->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d->master);
  1068. raw_tt = d->tt;
  1069. cfmakeraw(&raw_tt);
  1070. raw_tt.c_lflag &= ~ECHO;
  1071. raw_tt.c_lflag |= ISIG;
  1072. // block SIGTTOU during tcsetattr to prevent a hang if
  1073. // the process is a member of the background process group
  1074. // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
  1075. sigemptyset(&d->sigmask);
  1076. sigaddset(&d->sigmask, SIGTTOU);
  1077. sigprocmask(SIG_BLOCK,&d->sigmask, &d->original_sigmask);
  1078. if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_tt) == -1)
  1079. _error->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
  1080. sigprocmask(SIG_SETMASK, &d->original_sigmask, NULL);
  1081. }
  1082. if (d->slave != NULL)
  1083. {
  1084. /* on linux, closing (and later reopening) all references to the slave
  1085. makes the slave a death end, so we open it here to have one open all
  1086. the time. We could use this fd in SetupSlavePtyMagic() for linux, but
  1087. on kfreebsd we get an incorrect ("step like") output then while it has
  1088. no problem with closing all references… so to avoid platform specific
  1089. code here we combine both and be happy once more */
  1090. d->protect_slave_from_dying = open(d->slave, O_RDWR | O_CLOEXEC | O_NOCTTY);
  1091. }
  1092. }
  1093. }
  1094. if (_error->PendingError() == true)
  1095. {
  1096. if (d->master != -1)
  1097. {
  1098. close(d->master);
  1099. d->master = -1;
  1100. }
  1101. if (d->slave != NULL)
  1102. {
  1103. free(d->slave);
  1104. d->slave = NULL;
  1105. }
  1106. _error->DumpErrors(std::cerr, GlobalError::DEBUG, false);
  1107. }
  1108. _error->RevertToStack();
  1109. }
  1110. /*}}}*/
  1111. void pkgDPkgPM::SetupSlavePtyMagic() /*{{{*/
  1112. {
  1113. if(d->master == -1 || d->slave == NULL)
  1114. return;
  1115. if (close(d->master) == -1)
  1116. _error->FatalE("close", "Closing master %d in child failed!", d->master);
  1117. d->master = -1;
  1118. if (setsid() == -1)
  1119. _error->FatalE("setsid", "Starting a new session for child failed!");
  1120. int const slaveFd = open(d->slave, O_RDWR | O_NOCTTY);
  1121. if (slaveFd == -1)
  1122. _error->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
  1123. else if (ioctl(slaveFd, TIOCSCTTY, 0) < 0)
  1124. _error->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd);
  1125. else
  1126. {
  1127. unsigned short i = 0;
  1128. if (d->direct_stdin == true)
  1129. ++i;
  1130. for (; i < 3; ++i)
  1131. if (dup2(slaveFd, i) == -1)
  1132. _error->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd, i);
  1133. if (d->tt_is_valid == true && tcsetattr(STDIN_FILENO, TCSANOW, &d->tt) < 0)
  1134. _error->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd);
  1135. }
  1136. if (slaveFd != -1)
  1137. close(slaveFd);
  1138. }
  1139. /*}}}*/
  1140. void pkgDPkgPM::StopPtyMagic() /*{{{*/
  1141. {
  1142. if (d->slave != NULL)
  1143. free(d->slave);
  1144. d->slave = NULL;
  1145. if (d->protect_slave_from_dying != -1)
  1146. {
  1147. close(d->protect_slave_from_dying);
  1148. d->protect_slave_from_dying = -1;
  1149. }
  1150. if(d->master >= 0)
  1151. {
  1152. if (d->tt_is_valid == true && tcsetattr(STDIN_FILENO, TCSAFLUSH, &d->tt) == -1)
  1153. _error->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
  1154. close(d->master);
  1155. d->master = -1;
  1156. }
  1157. }
  1158. /*}}}*/
  1159. static void cleanUpTmpDir(char * const tmpdir) /*{{{*/
  1160. {
  1161. if (tmpdir == nullptr)
  1162. return;
  1163. DIR * const D = opendir(tmpdir);
  1164. if (D == nullptr)
  1165. _error->Errno("opendir", _("Unable to read %s"), tmpdir);
  1166. else
  1167. {
  1168. auto const dfd = dirfd(D);
  1169. for (struct dirent *Ent = readdir(D); Ent != nullptr; Ent = readdir(D))
  1170. {
  1171. if (Ent->d_name[0] == '.')
  1172. continue;
  1173. #ifdef _DIRENT_HAVE_D_TYPE
  1174. if (unlikely(Ent->d_type != DT_LNK && Ent->d_type != DT_UNKNOWN))
  1175. continue;
  1176. #endif
  1177. if (unlikely(unlinkat(dfd, Ent->d_name, 0) != 0))
  1178. break;
  1179. }
  1180. closedir(D);
  1181. rmdir(tmpdir);
  1182. }
  1183. free(tmpdir);
  1184. }
  1185. /*}}}*/
  1186. // DPkgPM::Go - Run the sequence /*{{{*/
  1187. // ---------------------------------------------------------------------
  1188. /* This globs the operations and calls dpkg
  1189. *
  1190. * If it is called with a progress object apt will report the install
  1191. * progress to this object. It maps the dpkg states a package goes
  1192. * through to human readable (and i10n-able)
  1193. * names and calculates a percentage for each step.
  1194. */
  1195. static bool ItemIsEssential(pkgDPkgPM::Item const &I)
  1196. {
  1197. static auto const cachegen = _config->Find("pkgCacheGen::Essential");
  1198. if (cachegen == "none" || cachegen == "native")
  1199. return true;
  1200. if (unlikely(I.Pkg.end()))
  1201. return true;
  1202. return (I.Pkg->Flags & pkgCache::Flag::Essential) != 0;
  1203. }
  1204. bool pkgDPkgPM::ExpandPendingCalls(std::vector<Item> &List, pkgDepCache &Cache)
  1205. {
  1206. {
  1207. std::unordered_set<decltype(pkgCache::Package::ID)> alreadyRemoved;
  1208. for (auto && I : List)
  1209. if (I.Op == Item::Remove || I.Op == Item::Purge)
  1210. alreadyRemoved.insert(I.Pkg->ID);
  1211. std::remove_reference<decltype(List)>::type AppendList;
  1212. for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
  1213. if (Cache[Pkg].Delete() && alreadyRemoved.insert(Pkg->ID).second == true)
  1214. AppendList.emplace_back(Cache[Pkg].Purge() ? Item::Purge : Item::Remove, Pkg);
  1215. std::move(AppendList.begin(), AppendList.end(), std::back_inserter(List));
  1216. }
  1217. {
  1218. std::unordered_set<decltype(pkgCache::Package::ID)> alreadyConfigured;
  1219. for (auto && I : List)
  1220. if (I.Op == Item::Configure)
  1221. alreadyConfigured.insert(I.Pkg->ID);
  1222. std::remove_reference<decltype(List)>::type AppendList;
  1223. for (auto && I : List)
  1224. if (I.Op == Item::Install && alreadyConfigured.insert(I.Pkg->ID).second == true)
  1225. AppendList.emplace_back(Item::Configure, I.Pkg);
  1226. for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
  1227. if (Pkg.State() == pkgCache::PkgIterator::NeedsConfigure && alreadyConfigured.insert(Pkg->ID).second == true)
  1228. AppendList.emplace_back(Item::Configure, Pkg);
  1229. std::move(AppendList.begin(), AppendList.end(), std::back_inserter(List));
  1230. }
  1231. return true;
  1232. }
  1233. bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
  1234. {
  1235. // explicitely remove&configure everything for hookscripts and progress building
  1236. // we need them only temporarily through, so keep the length and erase afterwards
  1237. decltype(List)::const_iterator::difference_type explicitIdx =
  1238. std::distance(List.cbegin(), List.cend());
  1239. ExpandPendingCalls(List, Cache);
  1240. /* if dpkg told us that it has already done everything to the package we wanted it to do,
  1241. we shouldn't ask it for "more" later. That can e.g. happen if packages without conffiles
  1242. are purged as they will have pass through the purge states on remove already */
  1243. auto const StripAlreadyDoneFrom = [&](APT::VersionVector & Pending) {
  1244. Pending.erase(std::remove_if(Pending.begin(), Pending.end(), [&](pkgCache::VerIterator const &Ver) {
  1245. auto const PN = Ver.ParentPkg().FullName();
  1246. auto const POD = PackageOpsDone.find(PN);
  1247. if (POD == PackageOpsDone.end())
  1248. return false;
  1249. return PackageOps[PN].size() <= POD->second;
  1250. }), Pending.end());
  1251. };
  1252. pkgPackageManager::SigINTStop = false;
  1253. d->progress = progress;
  1254. // Generate the base argument list for dpkg
  1255. std::vector<std::string> const sArgs = debSystem::GetDpkgBaseCommand();
  1256. std::vector<const char *> Args(sArgs.size(), NULL);
  1257. std::transform(sArgs.begin(), sArgs.end(), Args.begin(),
  1258. [](std::string const &s) { return s.c_str(); });
  1259. unsigned long long const StartSize = std::accumulate(sArgs.begin(), sArgs.end(), 0llu,
  1260. [](unsigned long long const i, std::string const &s) { return i + s.length(); });
  1261. size_t const BaseArgs = Args.size();
  1262. fd_set rfds;
  1263. struct timespec tv;
  1264. // try to figure out the max environment size
  1265. int OSArgMax = sysconf(_SC_ARG_MAX);
  1266. if(OSArgMax < 0)
  1267. OSArgMax = 32*1024;
  1268. OSArgMax -= EnvironmentSize() - 2*1024;
  1269. unsigned int const MaxArgBytes = _config->FindI("Dpkg::MaxArgBytes", OSArgMax);
  1270. bool const NoTriggers = _config->FindB("DPkg::NoTriggers", true);
  1271. if (RunScripts("DPkg::Pre-Invoke") == false)
  1272. return false;
  1273. if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
  1274. return false;
  1275. auto const noopDPkgInvocation = _config->FindB("Debug::pkgDPkgPM",false);
  1276. // store auto-bits as they are supposed to be after dpkg is run
  1277. if (noopDPkgInvocation == false)
  1278. Cache.writeStateFile(NULL);
  1279. bool dpkg_recursive_install = _config->FindB("dpkg::install::recursive", false);
  1280. if (_config->FindB("dpkg::install::recursive::force", false) == false)
  1281. {
  1282. // dpkg uses a sorted treewalk since that version which enables the workaround to work
  1283. auto const dpkgpkg = Cache.FindPkg("dpkg");
  1284. if (likely(dpkgpkg.end() == false && dpkgpkg->CurrentVer != 0))
  1285. dpkg_recursive_install = Cache.VS().CmpVersion("1.18.5", dpkgpkg.CurrentVer().VerStr()) <= 0;
  1286. }
  1287. // no point in doing this dance for a handful of packages only
  1288. unsigned int const dpkg_recursive_install_min = _config->FindB("dpkg::install::recursive::minimum", 5);
  1289. // FIXME: workaround for dpkg bug, see our ./test-bug-740843-versioned-up-down-breaks test
  1290. bool const dpkg_recursive_install_numbered = _config->FindB("dpkg::install::recursive::numbered", true);
  1291. // for the progress
  1292. BuildPackagesProgressMap();
  1293. APT::StateChanges approvedStates;
  1294. if (_config->FindB("dpkg::selection::remove::approved", true))
  1295. {
  1296. for (auto && I : List)
  1297. if (I.Op == Item::Purge)
  1298. approvedStates.Purge(FindToBeRemovedVersion(I.Pkg));
  1299. else if (I.Op == Item::Remove)
  1300. approvedStates.Remove(FindToBeRemovedVersion(I.Pkg));
  1301. }
  1302. // Skip removes if we install another architecture of this package soon (crossgrade)
  1303. // We can't just skip them all the time as it could be an ordering requirement [of another package]
  1304. if ((approvedStates.Remove().empty() == false || approvedStates.Purge().empty() == false) &&
  1305. _config->FindB("dpkg::remove::crossgrade::implicit", true) == true)
  1306. {
  1307. std::unordered_set<decltype(pkgCache::Package::ID)> crossgraded;
  1308. std::vector<std::pair<Item*, std::string>> toCrossgrade;
  1309. auto const PlanedEnd = std::next(List.begin(), explicitIdx);
  1310. for (auto I = List.begin(); I != PlanedEnd; ++I)
  1311. {
  1312. if (I->Op != Item::Remove && I->Op != Item::Purge)
  1313. continue;
  1314. auto const Grp = I->Pkg.Group();
  1315. size_t installedInstances = 0;
  1316. for (auto Pkg = Grp.PackageList(); Pkg.end() == false; Pkg = Grp.NextPkg(Pkg))
  1317. if (Pkg->CurrentVer != 0 || Cache[Pkg].Install())
  1318. ++installedInstances;
  1319. if (installedInstances == 2)
  1320. {
  1321. auto const FirstInstall = std::find_if_not(I, List.end(),
  1322. [](Item const &i) { return i.Op == Item::Remove || i.Op == Item::Purge; });
  1323. auto const LastInstall = std::find_if_not(FirstInstall, List.end(),
  1324. [](Item const &i) { return i.Op == Item::Install; });
  1325. auto const crosser = std::find_if(FirstInstall, LastInstall,
  1326. [&I](Item const &i) { return i.Pkg->Group == I->Pkg->Group; });
  1327. if (crosser != LastInstall)
  1328. {
  1329. crossgraded.insert(I->Pkg->ID);
  1330. toCrossgrade.emplace_back(&(*I), crosser->Pkg.FullName());
  1331. }
  1332. }
  1333. }
  1334. for (auto I = PlanedEnd; I != List.end(); ++I)
  1335. {
  1336. if (I->Op != Item::Remove && I->Op != Item::Purge)
  1337. continue;
  1338. auto const Grp = I->Pkg.Group();
  1339. for (auto Pkg = Grp.PackageList(); Pkg.end() == false; Pkg = Grp.NextPkg(Pkg))
  1340. {
  1341. if (Pkg == I->Pkg || Cache[Pkg].Install() == false)
  1342. continue;
  1343. toCrossgrade.emplace_back(&(*I), Pkg.FullName());
  1344. break;
  1345. }
  1346. }
  1347. for (auto C : toCrossgrade)
  1348. {
  1349. // we never do purges on packages which are crossgraded, even if "requested"
  1350. if (C.first->Op == Item::Purge)
  1351. {
  1352. C.first->Op = Item::Remove; // crossgrades should never be purged
  1353. auto && Purges = approvedStates.Purge();
  1354. auto const Ver = std::find_if(
  1355. #if __GNUC__ >= 5 || (__GNUC_MINOR__ >= 9 && __GNUC__ >= 4)
  1356. Purges.cbegin(), Purges.cend(),
  1357. #else
  1358. Purges.begin(), Purges.end(),
  1359. #endif
  1360. [&C](pkgCache::VerIterator const &V) { return V.ParentPkg() == C.first->Pkg; });
  1361. approvedStates.Remove(*Ver);
  1362. Purges.erase(Ver);
  1363. auto && RemOp = PackageOps[C.first->Pkg.FullName()];
  1364. if (RemOp.size() == 5)
  1365. {
  1366. RemOp.erase(std::next(RemOp.begin(), 3), RemOp.end());
  1367. PackagesTotal -= 2;
  1368. }
  1369. else
  1370. _error->Warning("Unexpected amount of planned ops for package %s: %lu", C.first->Pkg.FullName().c_str(), RemOp.size());
  1371. }
  1372. }
  1373. if (crossgraded.empty() == false)
  1374. {
  1375. auto const oldsize = List.size();
  1376. List.erase(std::remove_if(List.begin(), PlanedEnd,
  1377. [&crossgraded](Item const &i){
  1378. return (i.Op == Item::Remove || i.Op == Item::Purge) &&
  1379. crossgraded.find(i.Pkg->ID) != crossgraded.end();
  1380. }), PlanedEnd);
  1381. explicitIdx -= (oldsize - List.size());
  1382. }
  1383. }
  1384. APT::StateChanges currentStates;
  1385. if (_config->FindB("dpkg::selection::current::saveandrestore", true))
  1386. {
  1387. for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
  1388. if (Pkg->CurrentVer == 0)
  1389. continue;
  1390. else if (Pkg->SelectedState == pkgCache::State::Purge)
  1391. currentStates.Purge(FindToBeRemovedVersion(Pkg));
  1392. else if (Pkg->SelectedState == pkgCache::State::DeInstall)
  1393. currentStates.Remove(FindToBeRemovedVersion(Pkg));
  1394. if (currentStates.empty() == false)
  1395. {
  1396. APT::StateChanges cleanStates;
  1397. for (auto && P: currentStates.Remove())
  1398. cleanStates.Install(P);
  1399. for (auto && P: currentStates.Purge())
  1400. cleanStates.Install(P);
  1401. if (cleanStates.Save(false) == false)
  1402. return _error->Error("Couldn't clean the currently selected dpkg states");
  1403. }
  1404. }
  1405. if (_config->FindB("dpkg::selection::remove::approved", true))
  1406. {
  1407. if (approvedStates.Save(false) == false)
  1408. {
  1409. _error->Error("Couldn't record the approved state changes as dpkg selection states");
  1410. if (currentStates.Save(false) == false)
  1411. _error->Error("Couldn't restore dpkg selection states which were present before this interaction!");
  1412. return false;
  1413. }
  1414. List.erase(std::next(List.begin(), explicitIdx), List.end());
  1415. std::vector<bool> toBeRemoved(Cache.Head().PackageCount, false);
  1416. for (auto && I: approvedStates.Remove())
  1417. toBeRemoved[I.ParentPkg()->ID] = true;
  1418. for (auto && I: approvedStates.Purge())
  1419. toBeRemoved[I.ParentPkg()->ID] = true;
  1420. for (auto && I: List)
  1421. if (I.Op == Item::Remove || I.Op == Item::Purge)
  1422. toBeRemoved[I.Pkg->ID] = false;
  1423. bool const RemovePending = std::find(toBeRemoved.begin(), toBeRemoved.end(), true) != toBeRemoved.end();
  1424. bool const PurgePending = approvedStates.Purge().empty() == false;
  1425. if (RemovePending != false || PurgePending != false)
  1426. List.emplace_back(Item::ConfigurePending, pkgCache::PkgIterator());
  1427. if (RemovePending)
  1428. List.emplace_back(Item::RemovePending, pkgCache::PkgIterator());
  1429. if (PurgePending)
  1430. List.emplace_back(Item::PurgePending, pkgCache::PkgIterator());
  1431. // support subpressing of triggers processing for special
  1432. // cases like d-i that runs the triggers handling manually
  1433. if (_config->FindB("DPkg::ConfigurePending", true))
  1434. List.emplace_back(Item::ConfigurePending, pkgCache::PkgIterator());
  1435. }
  1436. bool const TriggersPending = _config->FindB("DPkg::TriggersPending", false);
  1437. d->stdin_is_dev_null = false;
  1438. // create log
  1439. OpenLog();
  1440. bool dpkgMultiArch = debSystem::SupportsMultiArch();
  1441. // start pty magic before the loop
  1442. StartPtyMagic();
  1443. // Tell the progress that its starting and fork dpkg
  1444. d->progress->Start(d->master);
  1445. // this loop is runs once per dpkg operation
  1446. vector<Item>::const_iterator I = List.cbegin();
  1447. while (I != List.end())
  1448. {
  1449. // Do all actions with the same Op in one run
  1450. vector<Item>::const_iterator J = I;
  1451. if (TriggersPending == true)
  1452. for (; J != List.end(); ++J)
  1453. {
  1454. if (J->Op == I->Op)
  1455. continue;
  1456. if (J->Op != Item::TriggersPending)
  1457. break;
  1458. vector<Item>::const_iterator T = J + 1;
  1459. if (T != List.end() && T->Op == I->Op)
  1460. continue;
  1461. break;
  1462. }
  1463. else if (J->Op == Item::Remove || J->Op == Item::Purge)
  1464. J = std::find_if(J, List.cend(), [](Item const &I) { return I.Op != Item::Remove && I.Op != Item::Purge; });
  1465. else
  1466. J = std::find_if(J, List.cend(), [&J](Item const &I) { return I.Op != J->Op; });
  1467. auto const size = (J - I) + 10;
  1468. // start with the baseset of arguments
  1469. auto Size = StartSize;
  1470. Args.erase(Args.begin() + BaseArgs, Args.end());
  1471. Args.reserve(size);
  1472. // keep track of allocated strings for multiarch package names
  1473. std::vector<char *> Packages(size, nullptr);
  1474. int fd[2];
  1475. if (pipe(fd) != 0)
  1476. return _error->Errno("pipe","Failed to create IPC pipe to dpkg");
  1477. #define ADDARG(X) Args.push_back(X); Size += strlen(X)
  1478. #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
  1479. ADDARGC("--status-fd");
  1480. char status_fd_buf[20];
  1481. snprintf(status_fd_buf,sizeof(status_fd_buf),"%i", fd[1]);
  1482. ADDARG(status_fd_buf);
  1483. unsigned long const Op = I->Op;
  1484. if (NoTriggers == true && I->Op != Item::TriggersPending &&
  1485. (I->Op != Item::ConfigurePending || std::next(I) != List.end()))
  1486. {
  1487. ADDARGC("--no-triggers");
  1488. }
  1489. switch (I->Op)
  1490. {
  1491. case Item::Remove:
  1492. case Item::Purge:
  1493. ADDARGC("--force-depends");
  1494. if (std::any_of(I, J, ItemIsEssential))
  1495. ADDARGC("--force-remove-essential");
  1496. ADDARGC("--remove");
  1497. break;
  1498. case Item::Configure:
  1499. ADDARGC("--configure");
  1500. break;
  1501. case Item::ConfigurePending:
  1502. ADDARGC("--configure");
  1503. ADDARGC("--pending");
  1504. break;
  1505. case Item::TriggersPending:
  1506. ADDARGC("--triggers-only");
  1507. ADDARGC("--pending");
  1508. break;
  1509. case Item::RemovePending:
  1510. ADDARGC("--remove");
  1511. ADDARGC("--pending");
  1512. break;
  1513. case Item::PurgePending:
  1514. ADDARGC("--purge");
  1515. ADDARGC("--pending");
  1516. break;
  1517. case Item::Install:
  1518. ADDARGC("--unpack");
  1519. ADDARGC("--auto-deconfigure");
  1520. break;
  1521. }
  1522. char * tmpdir_to_free = nullptr;
  1523. // Write in the file or package names
  1524. if (I->Op == Item::Install)
  1525. {
  1526. auto const installsToDo = J - I;
  1527. if (dpkg_recursive_install == true && dpkg_recursive_install_min < installsToDo)
  1528. {
  1529. std::string tmpdir;
  1530. strprintf(tmpdir, "%s/apt-dpkg-install-XXXXXX", GetTempDir().c_str());
  1531. tmpdir_to_free = strndup(tmpdir.data(), tmpdir.length());
  1532. if (mkdtemp(tmpdir_to_free) == nullptr)
  1533. return _error->Errno("DPkg::Go", "mkdtemp of %s failed in preparation of calling dpkg unpack", tmpdir_to_free);
  1534. char p = 1;
  1535. for (auto c = installsToDo - 1; (c = c/10) != 0; ++p);
  1536. for (unsigned long n = 0; I != J; ++n, ++I)
  1537. {
  1538. if (I->File[0] != '/')
  1539. return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str());
  1540. auto file = flNotDir(I->File);
  1541. if (flExtension(file) != "deb")
  1542. file.append(".deb");
  1543. std::string linkpath;
  1544. if (dpkg_recursive_install_numbered)
  1545. strprintf(linkpath, "%s/%.*lu-%s", tmpdir_to_free, p, n, file.c_str());
  1546. else
  1547. strprintf(linkpath, "%s/%s", tmpdir_to_free, file.c_str());
  1548. if (symlink(I->File.c_str(), linkpath.c_str()) != 0)
  1549. return _error->Errno("DPkg::Go", "Symlinking %s to %s failed!", I->File.c_str(), linkpath.c_str());
  1550. }
  1551. ADDARGC("--recursive");
  1552. ADDARG(tmpdir_to_free);
  1553. }
  1554. else
  1555. {
  1556. for (;I != J && Size < MaxArgBytes; ++I)
  1557. {
  1558. if (I->File[0] != '/')
  1559. return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str());
  1560. Args.push_back(I->File.c_str());
  1561. Size += I->File.length();
  1562. }
  1563. }
  1564. }
  1565. else if (I->Op == Item::RemovePending)
  1566. {
  1567. ++I;
  1568. StripAlreadyDoneFrom(approvedStates.Remove());
  1569. if (approvedStates.Remove().empty())
  1570. continue;
  1571. }
  1572. else if (I->Op == Item::PurgePending)
  1573. {
  1574. ++I;
  1575. // explicit removes of packages without conffiles passthrough the purge states instantly, too.
  1576. // Setting these non-installed packages up for purging generates 'unknown pkg' warnings from dpkg
  1577. StripAlreadyDoneFrom(approvedStates.Purge());
  1578. if (approvedStates.Purge().empty())
  1579. continue;
  1580. std::remove_reference<decltype(approvedStates.Remove())>::type approvedRemoves;
  1581. std::swap(approvedRemoves, approvedStates.Remove());
  1582. // we apply it again here as an explicit remove in the ordering will have cleared the purge state
  1583. if (approvedStates.Save(false) == false)
  1584. {
  1585. _error->Error("Couldn't record the approved purges as dpkg selection states");
  1586. if (currentStates.Save(false) == false)
  1587. _error->Error("Couldn't restore dpkg selection states which were present before this interaction!");
  1588. return false;
  1589. }
  1590. std::swap(approvedRemoves, approvedStates.Remove());
  1591. }
  1592. else
  1593. {
  1594. string const nativeArch = _config->Find("APT::Architecture");
  1595. unsigned long const oldSize = I->Pkg.end() == false ? Size : 0;
  1596. for (;I != J && Size < MaxArgBytes; ++I)
  1597. {
  1598. if((*I).Pkg.end() == true)
  1599. continue;
  1600. if (I->Op == Item::Configure && disappearedPkgs.find(I->Pkg.FullName(true)) != disappearedPkgs.end())
  1601. continue;
  1602. // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
  1603. if (dpkgMultiArch == false && (I->Pkg.Arch() == nativeArch ||
  1604. strcmp(I->Pkg.Arch(), "all") == 0 ||
  1605. strcmp(I->Pkg.Arch(), "none") == 0))
  1606. {
  1607. char const * const name = I->Pkg.Name();
  1608. ADDARG(name);
  1609. }
  1610. else
  1611. {
  1612. pkgCache::VerIterator PkgVer;
  1613. std::string name = I->Pkg.Name();
  1614. if (Op == Item::Remove)
  1615. PkgVer = I->Pkg.CurrentVer();
  1616. else if (Op == Item::Purge)
  1617. {
  1618. // we purge later with --purge --pending, so if it isn't installed (aka rc-only), skip it here
  1619. PkgVer = I->Pkg.CurrentVer();
  1620. if (PkgVer.end() == true)
  1621. continue;
  1622. }
  1623. else
  1624. PkgVer = Cache[I->Pkg].InstVerIter(Cache);
  1625. if (strcmp(I->Pkg.Arch(), "none") == 0)
  1626. ; // never arch-qualify a package without an arch
  1627. else if (PkgVer.end() == false)
  1628. name.append(":").append(PkgVer.Arch());
  1629. else
  1630. _error->Warning("Can not find PkgVer for '%s'", name.c_str());
  1631. char * const fullname = strdup(name.c_str());
  1632. Packages.push_back(fullname);
  1633. ADDARG(fullname);
  1634. }
  1635. }
  1636. // skip configure action if all sheduled packages disappeared
  1637. if (oldSize == Size)
  1638. continue;
  1639. }
  1640. #undef ADDARGC
  1641. #undef ADDARG
  1642. J = I;
  1643. if (noopDPkgInvocation == true)
  1644. {
  1645. for (std::vector<const char *>::const_iterator a = Args.begin();
  1646. a != Args.end(); ++a)
  1647. clog << *a << ' ';
  1648. clog << endl;
  1649. for (std::vector<char *>::const_iterator p = Packages.begin();
  1650. p != Packages.end(); ++p)
  1651. free(*p);
  1652. Packages.clear();
  1653. close(fd[0]);
  1654. close(fd[1]);
  1655. cleanUpTmpDir(tmpdir_to_free);
  1656. continue;
  1657. }
  1658. Args.push_back(NULL);
  1659. cout << flush;
  1660. clog << flush;
  1661. cerr << flush;
  1662. /* Mask off sig int/quit. We do this because dpkg also does when
  1663. it forks scripts. What happens is that when you hit ctrl-c it sends
  1664. it to all processes in the group. Since dpkg ignores the signal
  1665. it doesn't die but we do! So we must also ignore it */
  1666. sighandler_t old_SIGQUIT = signal(SIGQUIT,SIG_IGN);
  1667. sighandler_t old_SIGINT = signal(SIGINT,SigINT);
  1668. // Check here for any SIGINT
  1669. if (pkgPackageManager::SigINTStop && (Op == Item::Remove || Op == Item::Purge || Op == Item::Install))
  1670. break;
  1671. // ignore SIGHUP as well (debian #463030)
  1672. sighandler_t old_SIGHUP = signal(SIGHUP,SIG_IGN);
  1673. // now run dpkg
  1674. d->progress->StartDpkg();
  1675. std::set<int> KeepFDs;
  1676. KeepFDs.insert(fd[1]);
  1677. MergeKeepFdsFromConfiguration(KeepFDs);
  1678. pid_t Child = ExecFork(KeepFDs);
  1679. if (Child == 0)
  1680. {
  1681. // This is the child
  1682. SetupSlavePtyMagic();
  1683. close(fd[0]); // close the read end of the pipe
  1684. debSystem::DpkgChrootDirectory();
  1685. if (chdir(_config->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
  1686. _exit(100);
  1687. if (_config->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO))
  1688. {
  1689. int Flags;
  1690. int dummy = 0;
  1691. if ((Flags = fcntl(STDIN_FILENO,F_GETFL,dummy)) < 0)
  1692. _exit(100);
  1693. // Discard everything in stdin before forking dpkg
  1694. if (fcntl(STDIN_FILENO,F_SETFL,Flags | O_NONBLOCK) < 0)
  1695. _exit(100);
  1696. while (read(STDIN_FILENO,&dummy,1) == 1);
  1697. if (fcntl(STDIN_FILENO,F_SETFL,Flags & (~(long)O_NONBLOCK)) < 0)
  1698. _exit(100);
  1699. }
  1700. // if color support isn't enabled/disabled explicitly tell
  1701. // dpkg to use the same state apt is using for its color support
  1702. if (_config->FindB("APT::Color", false) == true)
  1703. setenv("DPKG_COLORS", "always", 0);
  1704. else
  1705. setenv("DPKG_COLORS", "never", 0);
  1706. execvp(Args[0], (char**) &Args[0]);
  1707. cerr << "Could not exec dpkg!" << endl;
  1708. _exit(100);
  1709. }
  1710. // we read from dpkg here
  1711. int const _dpkgin = fd[0];
  1712. close(fd[1]); // close the write end of the pipe
  1713. // apply ionice
  1714. if (_config->FindB("DPkg::UseIoNice", false) == true)
  1715. ionice(Child);
  1716. // setups fds
  1717. sigemptyset(&d->sigmask);
  1718. sigprocmask(SIG_BLOCK,&d->sigmask,&d->original_sigmask);
  1719. /* free vectors (and therefore memory) as we don't need the included data anymore */
  1720. for (std::vector<char *>::const_iterator p = Packages.begin();
  1721. p != Packages.end(); ++p)
  1722. free(*p);
  1723. Packages.clear();
  1724. // the result of the waitpid call
  1725. int Status = 0;
  1726. int res;
  1727. bool waitpid_failure = false;
  1728. while ((res=waitpid(Child,&Status, WNOHANG)) != Child) {
  1729. if(res < 0) {
  1730. // error handling, waitpid returned -1
  1731. if (errno == EINTR)
  1732. continue;
  1733. waitpid_failure = true;
  1734. break;
  1735. }
  1736. // wait for input or output here
  1737. FD_ZERO(&rfds);
  1738. if (d->master >= 0 && d->direct_stdin == false && d->stdin_is_dev_null == false)
  1739. FD_SET(STDIN_FILENO, &rfds);
  1740. FD_SET(_dpkgin, &rfds);
  1741. if(d->master >= 0)
  1742. FD_SET(d->master, &rfds);
  1743. tv.tv_sec = 0;
  1744. tv.tv_nsec = d->progress->GetPulseInterval();
  1745. auto const select_ret = pselect(max(d->master, _dpkgin)+1, &rfds, NULL, NULL,
  1746. &tv, &d->original_sigmask);
  1747. d->progress->Pulse();
  1748. if (select_ret == 0)
  1749. continue;
  1750. else if (select_ret < 0 && errno == EINTR)
  1751. continue;
  1752. else if (select_ret < 0)
  1753. {
  1754. perror("select() returned error");
  1755. continue;
  1756. }
  1757. if(d->master >= 0 && FD_ISSET(d->master, &rfds))
  1758. DoTerminalPty(d->master);
  1759. if(d->master >= 0 && FD_ISSET(0, &rfds))
  1760. DoStdin(d->master);
  1761. if(FD_ISSET(_dpkgin, &rfds))
  1762. DoDpkgStatusFd(_dpkgin);
  1763. }
  1764. close(_dpkgin);
  1765. // Restore sig int/quit
  1766. signal(SIGQUIT,old_SIGQUIT);
  1767. signal(SIGINT,old_SIGINT);
  1768. signal(SIGHUP,old_SIGHUP);
  1769. cleanUpTmpDir(tmpdir_to_free);
  1770. if (waitpid_failure == true)
  1771. {
  1772. strprintf(d->dpkg_error, "Sub-process %s couldn't be waited for.",Args[0]);
  1773. _error->Error("%s", d->dpkg_error.c_str());
  1774. break;
  1775. }
  1776. // Check for an error code.
  1777. if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
  1778. {
  1779. // if it was set to "keep-dpkg-running" then we won't return
  1780. // here but keep the loop going and just report it as a error
  1781. // for later
  1782. bool const stopOnError = _config->FindB("Dpkg::StopOnError",true);
  1783. if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV)
  1784. strprintf(d->dpkg_error, "Sub-process %s received a segmentation fault.",Args[0]);
  1785. else if (WIFEXITED(Status) != 0)
  1786. strprintf(d->dpkg_error, "Sub-process %s returned an error code (%u)",Args[0],WEXITSTATUS(Status));
  1787. else
  1788. strprintf(d->dpkg_error, "Sub-process %s exited unexpectedly",Args[0]);
  1789. _error->Error("%s", d->dpkg_error.c_str());
  1790. if(stopOnError)
  1791. break;
  1792. }
  1793. }
  1794. // dpkg is done at this point
  1795. StopPtyMagic();
  1796. CloseLog();
  1797. if (d->dpkg_error.empty() == false)
  1798. {
  1799. // no point in reseting packages we already completed removal for
  1800. StripAlreadyDoneFrom(approvedStates.Remove());
  1801. StripAlreadyDoneFrom(approvedStates.Purge());
  1802. APT::StateChanges undo;
  1803. auto && undoRem = approvedStates.Remove();
  1804. std::move(undoRem.begin(), undoRem.end(), std::back_inserter(undo.Install()));
  1805. auto && undoPur = approvedStates.Purge();
  1806. std::move(undoPur.begin(), undoPur.end(), std::back_inserter(undo.Install()));
  1807. approvedStates.clear();
  1808. if (undo.Save(false) == false)
  1809. _error->Error("Couldn't revert dpkg selection for approved remove/purge after an error was encountered!");
  1810. }
  1811. StripAlreadyDoneFrom(currentStates.Remove());
  1812. StripAlreadyDoneFrom(currentStates.Purge());
  1813. if (currentStates.Save(false) == false)
  1814. _error->Error("Couldn't restore dpkg selection states which were present before this interaction!");
  1815. if (pkgPackageManager::SigINTStop)
  1816. _error->Warning(_("Operation was interrupted before it could finish"));
  1817. if (noopDPkgInvocation == false)
  1818. {
  1819. std::string const oldpkgcache = _config->FindFile("Dir::cache::pkgcache");
  1820. if (oldpkgcache.empty() == false && RealFileExists(oldpkgcache) == true &&
  1821. RemoveFile("pkgDPkgPM::Go", oldpkgcache))
  1822. {
  1823. std::string const srcpkgcache = _config->FindFile("Dir::cache::srcpkgcache");
  1824. if (srcpkgcache.empty() == false && RealFileExists(srcpkgcache) == true)
  1825. {
  1826. _error->PushToStack();
  1827. pkgCacheFile CacheFile;
  1828. CacheFile.BuildCaches(NULL, true);
  1829. _error->RevertToStack();
  1830. }
  1831. }
  1832. }
  1833. // disappearing packages can forward their auto-bit
  1834. if (disappearedPkgs.empty() == false)
  1835. Cache.writeStateFile(NULL);
  1836. d->progress->Stop();
  1837. if (RunScripts("DPkg::Post-Invoke") == false)
  1838. return false;
  1839. return d->dpkg_error.empty();
  1840. }
  1841. void SigINT(int /*sig*/) {
  1842. pkgPackageManager::SigINTStop = true;
  1843. }
  1844. /*}}}*/
  1845. // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
  1846. // ---------------------------------------------------------------------
  1847. /* */
  1848. void pkgDPkgPM::Reset()
  1849. {
  1850. List.erase(List.begin(),List.end());
  1851. }
  1852. /*}}}*/
  1853. // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
  1854. // ---------------------------------------------------------------------
  1855. /* */
  1856. void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg)
  1857. {
  1858. // If apport doesn't exist or isn't installed do nothing
  1859. // This e.g. prevents messages in 'universes' without apport
  1860. pkgCache::PkgIterator apportPkg = Cache.FindPkg("apport");
  1861. if (apportPkg.end() == true || apportPkg->CurrentVer == 0)
  1862. return;
  1863. string pkgname, reportfile, pkgver, arch;
  1864. string::size_type pos;
  1865. FILE *report;
  1866. if (_config->FindB("Dpkg::ApportFailureReport", true) == false)
  1867. {
  1868. std::clog << "configured to not write apport reports" << std::endl;
  1869. return;
  1870. }
  1871. // only report the first errors
  1872. if(pkgFailures > _config->FindI("APT::Apport::MaxReports", 3))
  1873. {
  1874. std::clog << _("No apport report written because MaxReports is reached already") << std::endl;
  1875. return;
  1876. }
  1877. // check if its not a follow up error
  1878. const char *needle = dgettext("dpkg", "dependency problems - leaving unconfigured");
  1879. if(strstr(errormsg, needle) != NULL) {
  1880. std::clog << _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl;
  1881. return;
  1882. }
  1883. // do not report disk-full failures
  1884. if(strstr(errormsg, strerror(ENOSPC)) != NULL) {
  1885. std::clog << _("No apport report written because the error message indicates a disk full error") << std::endl;
  1886. return;
  1887. }
  1888. // do not report out-of-memory failures
  1889. if(strstr(errormsg, strerror(ENOMEM)) != NULL ||
  1890. strstr(errormsg, "failed to allocate memory") != NULL) {
  1891. std::clog << _("No apport report written because the error message indicates a out of memory error") << std::endl;
  1892. return;
  1893. }
  1894. // do not report bugs regarding inaccessible local files
  1895. if(strstr(errormsg, strerror(ENOENT)) != NULL ||
  1896. strstr(errormsg, "cannot access archive") != NULL) {
  1897. std::clog << _("No apport report written because the error message indicates an issue on the local system") << std::endl;
  1898. return;
  1899. }
  1900. // do not report errors encountered when decompressing packages
  1901. if(strstr(errormsg, "--fsys-tarfile returned error exit status 2") != NULL) {
  1902. std::clog << _("No apport report written because the error message indicates an issue on the local system") << std::endl;
  1903. return;
  1904. }
  1905. // do not report dpkg I/O errors, this is a format string, so we compare
  1906. // the prefix and the suffix of the error with the dpkg error message
  1907. vector<string> io_errors;
  1908. io_errors.push_back(string("failed to read"));
  1909. io_errors.push_back(string("failed to write"));
  1910. io_errors.push_back(string("failed to seek"));
  1911. io_errors.push_back(string("unexpected end of file or stream"));
  1912. for (vector<string>::iterator I = io_errors.begin(); I != io_errors.end(); ++I)
  1913. {
  1914. vector<string> list = VectorizeString(dgettext("dpkg", (*I).c_str()), '%');
  1915. if (list.size() > 1) {
  1916. // we need to split %s, VectorizeString only allows char so we need
  1917. // to kill the "s" manually
  1918. if (list[1].size() > 1) {
  1919. list[1].erase(0, 1);
  1920. if(strstr(errormsg, list[0].c_str()) &&
  1921. strstr(errormsg, list[1].c_str())) {
  1922. std::clog << _("No apport report written because the error message indicates a dpkg I/O error") << std::endl;
  1923. return;
  1924. }
  1925. }
  1926. }
  1927. }
  1928. // get the pkgname and reportfile
  1929. pkgname = flNotDir(pkgpath);
  1930. pos = pkgname.find('_');
  1931. if(pos != string::npos)
  1932. pkgname = pkgname.substr(0, pos);
  1933. // find the package version and source package name
  1934. pkgCache::PkgIterator Pkg = Cache.FindPkg(pkgname);
  1935. if (Pkg.end() == true)
  1936. {
  1937. if (pos == std::string::npos || _config->FindB("dpkg::install::recursive::numbered", true) == false)
  1938. return;
  1939. auto const dash = pkgname.find_first_not_of("0123456789");
  1940. if (dash == std::string::npos || pkgname[dash] != '-')
  1941. return;
  1942. pkgname.erase(0, dash + 1);
  1943. Pkg = Cache.FindPkg(pkgname);
  1944. if (Pkg.end() == true)
  1945. return;
  1946. }
  1947. pkgCache::VerIterator Ver = Cache.GetCandidateVersion(Pkg);
  1948. if (Ver.end() == true)
  1949. return;
  1950. pkgver = Ver.VerStr() == NULL ? "unknown" : Ver.VerStr();
  1951. // if the file exists already, we check:
  1952. // - if it was reported already (touched by apport).
  1953. // If not, we do nothing, otherwise
  1954. // we overwrite it. This is the same behaviour as apport
  1955. // - if we have a report with the same pkgversion already
  1956. // then we skip it
  1957. _config->CndSet("Dir::Apport", "var/crash");
  1958. reportfile = flCombine(_config->FindDir("Dir::Apport", "var/crash"), pkgname+".0.crash");
  1959. if(FileExists(reportfile))
  1960. {
  1961. struct stat buf;
  1962. char strbuf[255];
  1963. // check atime/mtime
  1964. stat(reportfile.c_str(), &buf);
  1965. if(buf.st_mtime > buf.st_atime)
  1966. return;
  1967. // check if the existing report is the same version
  1968. report = fopen(reportfile.c_str(),"r");
  1969. while(fgets(strbuf, sizeof(strbuf), report) != NULL)
  1970. {
  1971. if(strstr(strbuf,"Package:") == strbuf)
  1972. {
  1973. char pkgname[255], version[255];
  1974. if(sscanf(strbuf, "Package: %254s %254s", pkgname, version) == 2)
  1975. if(strcmp(pkgver.c_str(), version) == 0)
  1976. {
  1977. fclose(report);
  1978. return;
  1979. }
  1980. }
  1981. }
  1982. fclose(report);
  1983. }
  1984. // now write the report
  1985. arch = _config->Find("APT::Architecture");
  1986. report = fopen(reportfile.c_str(),"w");
  1987. if(report == NULL)
  1988. return;
  1989. if(_config->FindB("DPkgPM::InitialReportOnly",false) == true)
  1990. chmod(reportfile.c_str(), 0);
  1991. else
  1992. chmod(reportfile.c_str(), 0600);
  1993. fprintf(report, "ProblemType: Package\n");
  1994. fprintf(report, "Architecture: %s\n", arch.c_str());
  1995. time_t now = time(NULL);
  1996. char ctime_buf[26]; // need at least 26 bytes according to ctime(3)
  1997. fprintf(report, "Date: %s" , ctime_r(&now, ctime_buf));
  1998. fprintf(report, "Package: %s %s\n", pkgname.c_str(), pkgver.c_str());
  1999. fprintf(report, "SourcePackage: %s\n", Ver.SourcePkgName());
  2000. fprintf(report, "ErrorMessage:\n %s\n", errormsg);
  2001. // ensure that the log is flushed
  2002. if(d->term_out)
  2003. fflush(d->term_out);
  2004. // attach terminal log it if we have it
  2005. string logfile_name = _config->FindFile("Dir::Log::Terminal");
  2006. if (!logfile_name.empty())
  2007. {
  2008. FILE *log = NULL;
  2009. fprintf(report, "DpkgTerminalLog:\n");
  2010. log = fopen(logfile_name.c_str(),"r");
  2011. if(log != NULL)
  2012. {
  2013. char buf[1024];
  2014. while( fgets(buf, sizeof(buf), log) != NULL)
  2015. fprintf(report, " %s", buf);
  2016. fprintf(report, " \n");
  2017. fclose(log);
  2018. }
  2019. }
  2020. // attach history log it if we have it
  2021. string histfile_name = _config->FindFile("Dir::Log::History");
  2022. if (!histfile_name.empty())
  2023. {
  2024. fprintf(report, "DpkgHistoryLog:\n");
  2025. FILE* log = fopen(histfile_name.c_str(),"r");
  2026. if(log != NULL)
  2027. {
  2028. char buf[1024];
  2029. while( fgets(buf, sizeof(buf), log) != NULL)
  2030. fprintf(report, " %s", buf);
  2031. fclose(log);
  2032. }
  2033. }
  2034. // log the ordering, see dpkgpm.h and the "Ops" enum there
  2035. fprintf(report, "AptOrdering:\n");
  2036. for (auto && I : List)
  2037. {
  2038. char const * opstr = nullptr;
  2039. switch (I.Op)
  2040. {
  2041. case Item::Install: opstr = "Install"; break;
  2042. case Item::Configure: opstr = "Configure"; break;
  2043. case Item::Remove: opstr = "Remove"; break;
  2044. case Item::Purge: opstr = "Purge"; break;
  2045. case Item::ConfigurePending: opstr = "ConfigurePending"; break;
  2046. case Item::TriggersPending: opstr = "TriggersPending"; break;
  2047. case Item::RemovePending: opstr = "RemovePending"; break;
  2048. case Item::PurgePending: opstr = "PurgePending"; break;
  2049. }
  2050. auto const pkgname = I.Pkg.end() ? "NULL" : I.Pkg.FullName();
  2051. fprintf(report, " %s: %s\n", pkgname.c_str(), opstr);
  2052. }
  2053. // attach dmesg log (to learn about segfaults)
  2054. if (FileExists("/bin/dmesg"))
  2055. {
  2056. fprintf(report, "Dmesg:\n");
  2057. FILE *log = popen("/bin/dmesg","r");
  2058. if(log != NULL)
  2059. {
  2060. char buf[1024];
  2061. while( fgets(buf, sizeof(buf), log) != NULL)
  2062. fprintf(report, " %s", buf);
  2063. pclose(log);
  2064. }
  2065. }
  2066. // attach df -l log (to learn about filesystem status)
  2067. if (FileExists("/bin/df"))
  2068. {
  2069. fprintf(report, "Df:\n");
  2070. FILE *log = popen("/bin/df -l","r");
  2071. if(log != NULL)
  2072. {
  2073. char buf[1024];
  2074. while( fgets(buf, sizeof(buf), log) != NULL)
  2075. fprintf(report, " %s", buf);
  2076. pclose(log);
  2077. }
  2078. }
  2079. fclose(report);
  2080. }
  2081. /*}}}*/