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