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