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