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