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