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