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