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