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