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