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