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