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