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