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