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