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