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