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