+// DPkgPM::handleDisappearAction /*{{{*/
+void pkgDPkgPM::handleDisappearAction(string const &pkgname)
+{
+ // record the package name for display and stuff later
+ disappearedPkgs.insert(pkgname);
+
+ pkgCache::PkgIterator Pkg = Cache.FindPkg(pkgname);
+ if (unlikely(Pkg.end() == true))
+ return;
+ // the disappeared package was auto-installed - nothing to do
+ if ((Cache[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
+ return;
+ pkgCache::VerIterator PkgVer = Cache[Pkg].InstVerIter(Cache);
+ if (unlikely(PkgVer.end() == true))
+ return;
+ /* search in the list of dependencies for (Pre)Depends,
+ check if this dependency has a Replaces on our package
+ and if so transfer the manual installed flag to it */
+ for (pkgCache::DepIterator Dep = PkgVer.DependsList(); Dep.end() != true; ++Dep)
+ {
+ if (Dep->Type != pkgCache::Dep::Depends &&
+ Dep->Type != pkgCache::Dep::PreDepends)
+ continue;
+ pkgCache::PkgIterator Tar = Dep.TargetPkg();
+ if (unlikely(Tar.end() == true))
+ continue;
+ // the package is already marked as manual
+ if ((Cache[Tar].Flags & pkgCache::Flag::Auto) != pkgCache::Flag::Auto)
+ continue;
+ pkgCache::VerIterator TarVer = Cache[Tar].InstVerIter(Cache);
+ if (TarVer.end() == true)
+ continue;
+ for (pkgCache::DepIterator Rep = TarVer.DependsList(); Rep.end() != true; ++Rep)
+ {
+ if (Rep->Type != pkgCache::Dep::Replaces)
+ continue;
+ if (Pkg != Rep.TargetPkg())
+ continue;
+ // okay, they are strongly connected - transfer manual-bit
+ if (Debug == true)
+ std::clog << "transfer manual-bit from disappeared »" << pkgname << "« to »" << Tar.FullName() << "«" << std::endl;
+ Cache[Tar].Flags &= ~Flag::Auto;
+ break;
+ }
+ }
+}
+ /*}}}*/
+// DPkgPM::DoDpkgStatusFd /*{{{*/
+// ---------------------------------------------------------------------
+/*
+ */
+void pkgDPkgPM::DoDpkgStatusFd(int statusfd, int OutStatusFd)
+{
+ char *p, *q;
+ int len;
+
+ len=read(statusfd, &d->dpkgbuf[d->dpkgbuf_pos], sizeof(d->dpkgbuf)-d->dpkgbuf_pos);
+ d->dpkgbuf_pos += len;
+ if(len <= 0)
+ return;
+
+ // process line by line if we have a buffer
+ p = q = d->dpkgbuf;
+ while((q=(char*)memchr(p, '\n', d->dpkgbuf+d->dpkgbuf_pos-p)) != NULL)
+ {
+ *q = 0;
+ ProcessDpkgStatusLine(OutStatusFd, p);
+ p=q+1; // continue with next line
+ }
+
+ // now move the unprocessed bits (after the final \n that is now a 0x0)
+ // to the start and update d->dpkgbuf_pos
+ p = (char*)memrchr(d->dpkgbuf, 0, d->dpkgbuf_pos);
+ if(p == NULL)
+ return;
+
+ // we are interessted in the first char *after* 0x0
+ p++;
+
+ // move the unprocessed tail to the start and update pos
+ memmove(d->dpkgbuf, p, p-d->dpkgbuf);
+ d->dpkgbuf_pos = d->dpkgbuf+d->dpkgbuf_pos-p;
+}
+ /*}}}*/
+// DPkgPM::WriteHistoryTag /*{{{*/
+void pkgDPkgPM::WriteHistoryTag(string const &tag, string value)
+{
+ size_t const length = value.length();
+ if (length == 0)
+ return;
+ // poor mans rstrip(", ")
+ if (value[length-2] == ',' && value[length-1] == ' ')
+ value.erase(length - 2, 2);
+ fprintf(d->history_out, "%s: %s\n", tag.c_str(), value.c_str());
+} /*}}}*/
+// DPkgPM::OpenLog /*{{{*/
+bool pkgDPkgPM::OpenLog()
+{
+ string const logdir = _config->FindDir("Dir::Log");
+ if(CreateAPTDirectoryIfNeeded(logdir, logdir) == false)
+ // FIXME: use a better string after freeze
+ return _error->Error(_("Directory '%s' missing"), logdir.c_str());
+
+ // get current time
+ char timestr[200];
+ time_t const t = time(NULL);
+ struct tm const * const tmp = localtime(&t);
+ strftime(timestr, sizeof(timestr), "%F %T", tmp);
+
+ // open terminal log
+ string const logfile_name = flCombine(logdir,
+ _config->Find("Dir::Log::Terminal"));
+ if (!logfile_name.empty())
+ {
+ d->term_out = fopen(logfile_name.c_str(),"a");
+ if (d->term_out == NULL)
+ return _error->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name.c_str());
+ setvbuf(d->term_out, NULL, _IONBF, 0);
+ SetCloseExec(fileno(d->term_out), true);
+ struct passwd *pw;
+ struct group *gr;
+ pw = getpwnam("root");
+ gr = getgrnam("adm");
+ if (pw != NULL && gr != NULL)
+ chown(logfile_name.c_str(), pw->pw_uid, gr->gr_gid);
+ chmod(logfile_name.c_str(), 0644);
+ fprintf(d->term_out, "\nLog started: %s\n", timestr);
+ }
+
+ // write your history
+ string const history_name = flCombine(logdir,
+ _config->Find("Dir::Log::History"));
+ if (!history_name.empty())
+ {
+ d->history_out = fopen(history_name.c_str(),"a");
+ if (d->history_out == NULL)
+ return _error->WarningE("OpenLog", _("Could not open file '%s'"), history_name.c_str());
+ chmod(history_name.c_str(), 0644);
+ fprintf(d->history_out, "\nStart-Date: %s\n", timestr);
+ string remove, purge, install, reinstall, upgrade, downgrade;
+ for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
+ {
+ enum { CANDIDATE, CANDIDATE_AUTO, CURRENT_CANDIDATE, CURRENT } infostring;
+ string *line = NULL;
+ #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
+ if (Cache[I].NewInstall() == true)
+ HISTORYINFO(install, CANDIDATE_AUTO)
+ else if (Cache[I].ReInstall() == true)
+ HISTORYINFO(reinstall, CANDIDATE)
+ else if (Cache[I].Upgrade() == true)
+ HISTORYINFO(upgrade, CURRENT_CANDIDATE)
+ else if (Cache[I].Downgrade() == true)
+ HISTORYINFO(downgrade, CURRENT_CANDIDATE)
+ else if (Cache[I].Delete() == true)
+ HISTORYINFO((Cache[I].Purge() ? purge : remove), CURRENT)
+ else
+ continue;
+ #undef HISTORYINFO
+ line->append(I.FullName(false)).append(" (");
+ switch (infostring) {
+ case CANDIDATE: line->append(Cache[I].CandVersion); break;
+ case CANDIDATE_AUTO:
+ line->append(Cache[I].CandVersion);
+ if ((Cache[I].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto)
+ line->append(", automatic");
+ break;
+ case CURRENT_CANDIDATE: line->append(Cache[I].CurVersion).append(", ").append(Cache[I].CandVersion); break;
+ case CURRENT: line->append(Cache[I].CurVersion); break;
+ }
+ line->append("), ");
+ }
+ if (_config->Exists("Commandline::AsString") == true)
+ WriteHistoryTag("Commandline", _config->Find("Commandline::AsString"));
+ WriteHistoryTag("Install", install);
+ WriteHistoryTag("Reinstall", reinstall);
+ WriteHistoryTag("Upgrade", upgrade);
+ WriteHistoryTag("Downgrade",downgrade);
+ WriteHistoryTag("Remove",remove);
+ WriteHistoryTag("Purge",purge);
+ fflush(d->history_out);
+ }
+
+ return true;
+}
+ /*}}}*/
+// DPkg::CloseLog /*{{{*/
+bool pkgDPkgPM::CloseLog()
+{
+ char timestr[200];
+ time_t t = time(NULL);
+ struct tm *tmp = localtime(&t);
+ strftime(timestr, sizeof(timestr), "%F %T", tmp);
+
+ if(d->term_out)
+ {
+ fprintf(d->term_out, "Log ended: ");
+ fprintf(d->term_out, "%s", timestr);
+ fprintf(d->term_out, "\n");
+ fclose(d->term_out);
+ }
+ d->term_out = NULL;
+
+ if(d->history_out)
+ {
+ if (disappearedPkgs.empty() == false)
+ {
+ string disappear;
+ for (std::set<std::string>::const_iterator d = disappearedPkgs.begin();
+ d != disappearedPkgs.end(); ++d)
+ {
+ pkgCache::PkgIterator P = Cache.FindPkg(*d);
+ disappear.append(*d);
+ if (P.end() == true)
+ disappear.append(", ");
+ else
+ disappear.append(" (").append(Cache[P].CurVersion).append("), ");
+ }
+ WriteHistoryTag("Disappeared", disappear);
+ }
+ if (d->dpkg_error.empty() == false)
+ fprintf(d->history_out, "Error: %s\n", d->dpkg_error.c_str());
+ fprintf(d->history_out, "End-Date: %s\n", timestr);
+ fclose(d->history_out);
+ }
+ d->history_out = NULL;
+
+ return true;
+}
+ /*}}}*/
+/*{{{*/
+// This implements a racy version of pselect for those architectures
+// that don't have a working implementation.
+// FIXME: Probably can be removed on Lenny+1
+static int racy_pselect(int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds, const struct timespec *timeout,
+ const sigset_t *sigmask)
+{
+ sigset_t origmask;
+ struct timeval tv;
+ int retval;
+
+ tv.tv_sec = timeout->tv_sec;
+ tv.tv_usec = timeout->tv_nsec/1000;
+
+ sigprocmask(SIG_SETMASK, sigmask, &origmask);
+ retval = select(nfds, readfds, writefds, exceptfds, &tv);
+ sigprocmask(SIG_SETMASK, &origmask, 0);
+ return retval;
+}
+/*}}}*/