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