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