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