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