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