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