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