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