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