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