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