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