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