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