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