dpkgpm.cc 59 KB

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