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