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