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