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