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