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