#include <apt-pkg/error.h>
#include <apt-pkg/sptr.h>
#include <apt-pkg/algorithms.h>
+
+#include <apt-pkg/fileutl.h>
#include <apt-pkg/configuration.h>
+#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/tagfile.h>
+
+#include <iostream>
+#include <sstream>
+#include <set>
#include <apti18n.h>
- /*}}}*/
+
+pkgDepCache::ActionGroup::ActionGroup(pkgDepCache &cache) :
+ cache(cache), released(false)
+{
+ ++cache.group_level;
+}
+
+void pkgDepCache::ActionGroup::release()
+{
+ if(!released)
+ {
+ if(cache.group_level == 0)
+ std::cerr << "W: Unbalanced action groups, expect badness" << std::endl;
+ else
+ {
+ --cache.group_level;
+
+ if(cache.group_level == 0)
+ cache.MarkAndSweep();
+ }
+
+ released = false;
+ }
+}
+
+pkgDepCache::ActionGroup::~ActionGroup()
+{
+ release();
+}
// DepCache::pkgDepCache - Constructors /*{{{*/
// ---------------------------------------------------------------------
/* */
pkgDepCache::pkgDepCache(pkgCache *pCache,Policy *Plcy) :
- Cache(pCache), PkgState(0), DepState(0)
+ group_level(0), Cache(pCache), PkgState(0), DepState(0)
{
delLocalPolicy = 0;
LocalPolicy = Plcy;
/* This allocats the extension buffers and initializes them. */
bool pkgDepCache::Init(OpProgress *Prog)
{
+ // Suppress mark updates during this operation (just in case) and
+ // run a mark operation when Init terminates.
+ ActionGroup actions(*this);
+
delete [] PkgState;
delete [] DepState;
PkgState = new StateCache[Head().PackageCount];
// Find the proper cache slot
StateCache &State = PkgState[I->ID];
State.iFlags = 0;
-
+
// Figure out the install version
State.CandidateVer = GetCandidateVer(I);
State.InstallVer = I.CurrentVer();
if(Prog != 0)
Prog->Done();
-
+
return true;
}
/*}}}*/
+bool pkgDepCache::readStateFile(OpProgress *Prog)
+{
+ FileFd state_file;
+ string state = _config->FindDir("Dir::State") + "extended_states";
+ if(FileExists(state)) {
+ state_file.Open(state, FileFd::ReadOnly);
+ int file_size = state_file.Size();
+ if(Prog != NULL)
+ Prog->OverallProgress(0, file_size, 1,
+ _("Reading state information"));
+
+ pkgTagFile tagfile(&state_file);
+ pkgTagSection section;
+ int amt=0;
+ while(tagfile.Step(section)) {
+ string pkgname = section.FindS("Package");
+ pkgCache::PkgIterator pkg=Cache->FindPkg(pkgname);
+ // Silently ignore unknown packages and packages with no actual
+ // version.
+ if(!pkg.end() && !pkg.VersionList().end()) {
+ short reason = section.FindI("Auto-Installed", 0);
+ if(reason > 0)
+ PkgState[pkg->ID].Flags |= Flag::Auto;
+ if(_config->FindB("Debug::pkgAutoRemove",false))
+ std::cout << "Auto-Installed : " << pkgname << std::endl;
+ amt+=section.size();
+ if(Prog != NULL)
+ Prog->OverallProgress(amt, file_size, 1,
+ _("Reading state information"));
+ }
+ if(Prog != NULL)
+ Prog->OverallProgress(file_size, file_size, 1,
+ _("Reading state information"));
+ }
+ }
+
+ return true;
+}
+
+bool pkgDepCache::writeStateFile(OpProgress *prog)
+{
+ if(_config->FindB("Debug::pkgAutoRemove",false))
+ std::clog << "pkgDepCache::writeStateFile()" << std::endl;
+
+ FileFd StateFile;
+ string state = _config->FindDir("Dir::State") + "extended_states";
+
+ // if it does not exist, create a empty one
+ if(!FileExists(state))
+ {
+ StateFile.Open(state, FileFd::WriteEmpty);
+ StateFile.Close();
+ }
+
+ // open it
+ if(!StateFile.Open(state, FileFd::ReadOnly))
+ return _error->Error(_("Failed to open StateFile %s"),
+ state.c_str());
+
+ FILE *OutFile;
+ string outfile = state + ".tmp";
+ if((OutFile = fopen(outfile.c_str(),"w")) == NULL)
+ return _error->Error(_("Failed to write temporary StateFile %s"),
+ outfile.c_str());
+
+ // first merge with the existing sections
+ pkgTagFile tagfile(&StateFile);
+ pkgTagSection section;
+ std::set<string> pkgs_seen;
+ const char *nullreorderlist[] = {0};
+ while(tagfile.Step(section)) {
+ string pkgname = section.FindS("Package");
+ // Silently ignore unknown packages and packages with no actual
+ // version.
+ pkgCache::PkgIterator pkg=Cache->FindPkg(pkgname);
+ if(pkg.end() || pkg.VersionList().end())
+ continue;
+ bool oldAuto = section.FindI("Auto-Installed");
+ bool newAuto = (PkgState[pkg->ID].Flags & Flag::Auto);
+ if(_config->FindB("Debug::pkgAutoRemove",false))
+ std::clog << "Update exisiting AutoInstall info: "
+ << pkg.Name() << std::endl;
+ TFRewriteData rewrite[2];
+ rewrite[0].Tag = "Auto-Installed";
+ rewrite[0].Rewrite = newAuto ? "1" : "0";
+ rewrite[0].NewTag = 0;
+ rewrite[1].Tag = 0;
+ TFRewrite(OutFile, section, nullreorderlist, rewrite);
+ fprintf(OutFile,"\n");
+ pkgs_seen.insert(pkgname);
+ }
+
+ // then write the ones we have not seen yet
+ std::ostringstream ostr;
+ for(pkgCache::PkgIterator pkg=Cache->PkgBegin(); !pkg.end(); pkg++) {
+ if(PkgState[pkg->ID].Flags & Flag::Auto) {
+ if (pkgs_seen.find(pkg.Name()) != pkgs_seen.end()) {
+ if(_config->FindB("Debug::pkgAutoRemove",false))
+ std::clog << "Skipping already written " << pkg.Name() << std::endl;
+ continue;
+ }
+ if(_config->FindB("Debug::pkgAutoRemove",false))
+ std::clog << "Writing new AutoInstall: "
+ << pkg.Name() << std::endl;
+ ostr.str(string(""));
+ ostr << "Package: " << pkg.Name()
+ << "\nAuto-Installed: 1\n\n";
+ fprintf(OutFile,ostr.str().c_str());
+ fprintf(OutFile,"\n");
+ }
+ }
+ fclose(OutFile);
+
+ // move the outfile over the real file
+ rename(outfile.c_str(), state.c_str());
+
+ return true;
+}
+
// DepCache::CheckDep - Checks a single dependency /*{{{*/
// ---------------------------------------------------------------------
/* This first checks the dependency against the main target package and
{
StateCache &State = PkgState[Pkg->ID];
- // The Package is broken
+ // The Package is broken (either minimal dep or policy dep)
if ((State.DepState & DepInstMin) != DepInstMin)
iBrokenCount += Add;
+ if ((State.DepState & DepInstPolicy) != DepInstPolicy)
+ iPolicyBrokenCount += Add;
// Bad state
if (Pkg.State() != PkgIterator::NeedsNothing)
if (Prog != 0)
Prog->Progress(Done);
+
+ readStateFile(Prog);
}
/*}}}*/
// DepCache::Update - Update the deps list of a package /*{{{*/
// ---------------------------------------------------------------------
/* This is a helper for update that only does the dep portion of the scan.
- It is mainly ment to scan reverse dependencies. */
+ It is mainly meant to scan reverse dependencies. */
void pkgDepCache::Update(DepIterator D)
{
// Update the reverse deps
// DepCache::MarkKeep - Put the package in the keep state /*{{{*/
// ---------------------------------------------------------------------
/* */
-void pkgDepCache::MarkKeep(PkgIterator const &Pkg,bool Soft)
+void pkgDepCache::MarkKeep(PkgIterator const &Pkg, bool Soft, bool FromUser)
{
// Simplifies other routines.
if (Pkg.end() == true)
Pkg.CurrentVer().Downloadable() == false)
return;
+ /** \todo Can this be moved later in the method? */
+ ActionGroup group(*this);
+
/* We changed the soft state all the time so the UI is a bit nicer
to use */
StateCache &P = PkgState[Pkg->ID];
// We dont even try to keep virtual packages..
if (Pkg->VersionList == 0)
return;
-
- P.Flags &= ~Flag::Auto;
+#if 0 // reseting the autoflag here means we lose the
+ // auto-mark information if a user selects a package for removal
+ // but changes his mind then and sets it for keep again
+ // - this makes sense as default when all Garbage dependencies
+ // are automatically marked for removal (as aptitude does).
+ // setting a package for keep then makes it no longer autoinstalled
+ // for all other use-case this action is rather suprising
+ if(FromUser && !P.Marked)
+ P.Flags &= ~Flag::Auto;
+#endif
+
RemoveSizes(Pkg);
RemoveStates(Pkg);
if (Pkg.end() == true)
return;
+ ActionGroup group(*this);
+
// Check that it is not already marked for delete
StateCache &P = PkgState[Pkg->ID];
P.iFlags &= ~(AutoKept | Purge);
else
P.Mode = ModeDelete;
P.InstallVer = 0;
- P.Flags &= Flag::Auto;
AddStates(Pkg);
Update(Pkg);
// ---------------------------------------------------------------------
/* */
void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst,
- unsigned long Depth)
+ unsigned long Depth, bool FromUser,
+ bool ForceImportantDeps)
{
if (Depth > 100)
return;
if (Pkg.end() == true)
return;
+ ActionGroup group(*this);
+
/* Check that it is not already marked for install and that it can be
installed */
StateCache &P = PkgState[Pkg->ID];
P.iFlags &= ~AutoKept;
- if (P.InstBroken() == false && (P.Mode == ModeInstall ||
+ if ((P.InstPolicyBroken() == false && P.InstBroken() == false) &&
+ (P.Mode == ModeInstall ||
P.CandidateVer == (Version *)Pkg.CurrentVer()))
{
if (P.CandidateVer == (Version *)Pkg.CurrentVer() && P.InstallVer == 0)
- MarkKeep(Pkg);
+ MarkKeep(Pkg, false, FromUser);
return;
}
// See if there is even any possible instalation candidate
if (P.CandidateVer == 0)
return;
-
// We dont even try to install virtual packages..
if (Pkg->VersionList == 0)
return;
-
/* Target the candidate version and remove the autoflag. We reset the
autoflag below if this was called recursively. Otherwise the user
should have the ability to de-auto a package by changing its state */
P.Mode = ModeInstall;
P.InstallVer = P.CandidateVer;
- P.Flags &= ~Flag::Auto;
+
+ if(FromUser)
+ {
+ // Set it to manual if it's a new install or cancelling the
+ // removal of a garbage package.
+ if(P.Status == 2 || (!Pkg.CurrentVer().end() && !P.Marked))
+ P.Flags &= ~Flag::Auto;
+ }
+ else
+ {
+ // Set it to auto if this is a new install.
+ if(P.Status == 2)
+ P.Flags |= Flag::Auto;
+ }
if (P.CandidateVer == (Version *)Pkg.CurrentVer())
P.Mode = ModeKeep;
/* Check if this dep should be consider for install. If it is a user
defined important dep and we are installed a new package then
- it will be installed. Otherwise we only worry about critical deps */
+ it will be installed. Otherwise we only check for important
+ deps that have changed from the installed version
+ */
if (IsImportantDep(Start) == false)
continue;
- if (Pkg->CurrentVer != 0 && Start.IsCritical() == false)
+
+ /* check if any ImportantDep() (but not Critial) where added
+ * since we installed the package
+ */
+ bool isNewImportantDep = false;
+ if(!ForceImportantDeps && !Start.IsCritical())
+ {
+ bool found=false;
+ VerIterator instVer = Pkg.CurrentVer();
+ if(!instVer.end())
+ {
+ for (DepIterator D = instVer.DependsList(); D.end() != true; D++)
+ {
+ //FIXME: deal better with or-groups(?)
+ DepIterator LocalStart = D;
+
+ if(IsImportantDep(D) && Start.TargetPkg() == D.TargetPkg())
+ found=true;
+ }
+ // this is a new dep if it was not found to be already
+ // a important dep of the installed pacakge
+ isNewImportantDep = !found;
+ }
+ }
+ if(isNewImportantDep)
+ if(_config->FindB("Debug::pkgDepCache::AutoInstall",false) == true)
+ std::clog << "new important dependency: "
+ << Start.TargetPkg().Name() << std::endl;
+
+ // skip important deps if the package is already installed
+ if (Pkg->CurrentVer != 0 && Start.IsCritical() == false
+ && !isNewImportantDep && !ForceImportantDeps)
continue;
/* If we are in an or group locate the first or that can
}
}
- if (InstPkg.end() == false)
+ if (InstPkg.end() == false)
{
if(_config->FindB("Debug::pkgDepCache::AutoInstall",false) == true)
std::clog << "Installing " << InstPkg.Name()
<< " as dep of " << Pkg.Name()
<< std::endl;
- MarkInstall(InstPkg,true,Depth + 1);
-
- // Set the autoflag, after MarkInstall because MarkInstall unsets it
- if (P->CurrentVer == 0)
- PkgState[InstPkg->ID].Flags |= Flag::Auto;
+ MarkInstall(InstPkg, true, Depth + 1, false, ForceImportantDeps);
}
-
continue;
}
PkgIterator Pkg = Ver.ParentPkg();
MarkDelete(Pkg);
- PkgState[Pkg->ID].Flags |= Flag::Auto;
}
continue;
}
/* */
void pkgDepCache::SetReInstall(PkgIterator const &Pkg,bool To)
{
+ ActionGroup group(*this);
+
RemoveSizes(Pkg);
RemoveStates(Pkg);
/* */
void pkgDepCache::SetCandidateVersion(VerIterator TargetVer)
{
+ ActionGroup group(*this);
+
pkgCache::PkgIterator Pkg = TargetVer.ParentPkg();
StateCache &P = PkgState[Pkg->ID];
-
+
RemoveSizes(Pkg);
RemoveStates(Pkg);
Update(Pkg);
AddSizes(Pkg);
}
+
+void pkgDepCache::MarkAuto(const PkgIterator &Pkg, bool Auto)
+{
+ StateCache &state = PkgState[Pkg->ID];
+
+ ActionGroup group(*this);
+
+ if(Auto)
+ state.Flags |= Flag::Auto;
+ else
+ state.Flags &= ~Flag::Auto;
+}
/*}}}*/
// StateCache::Update - Compute the various static display things /*{{{*/
// ---------------------------------------------------------------------
/* */
bool pkgDepCache::Policy::IsImportantDep(DepIterator Dep)
{
- return Dep.IsCritical();
+ if(Dep.IsCritical())
+ return true;
+ else if(Dep->Type == pkgCache::Dep::Recommends)
+ {
+ if ( _config->FindB("APT::Install-Recommends", false))
+ return true;
+ // we suport a special mode to only install-recommends for certain
+ // sections
+ // FIXME: this is a meant as a temporarly solution until the
+ // recommends are cleaned up
+ string s = _config->Find("APT::Install-Recommends-Section","");
+ if(s.size() > 0)
+ {
+ const char *sec = Dep.TargetPkg().Section();
+ if (sec && strcmp(sec, s.c_str()) == 0)
+ return true;
+ }
+ }
+ else if(Dep->Type == pkgCache::Dep::Suggests)
+ return _config->FindB("APT::Install-Suggests", false);
+
+ return false;
}
/*}}}*/
+
+pkgDepCache::DefaultRootSetFunc::DefaultRootSetFunc()
+ : constructedSuccessfully(false)
+{
+ Configuration::Item const *Opts;
+ Opts = _config->Tree("APT::NeverAutoRemove");
+ if (Opts != 0 && Opts->Child != 0)
+ {
+ Opts = Opts->Child;
+ for (; Opts != 0; Opts = Opts->Next)
+ {
+ if (Opts->Value.empty() == true)
+ continue;
+
+ regex_t *p = new regex_t;
+ if(regcomp(p,Opts->Value.c_str(),
+ REG_EXTENDED | REG_ICASE | REG_NOSUB) != 0)
+ {
+ regfree(p);
+ delete p;
+ _error->Error("Regex compilation error for APT::NeverAutoRemove");
+ return;
+ }
+
+ rootSetRegexp.push_back(p);
+ }
+ }
+
+ constructedSuccessfully = true;
+}
+
+pkgDepCache::DefaultRootSetFunc::~DefaultRootSetFunc()
+{
+ for(unsigned int i = 0; i < rootSetRegexp.size(); i++)
+ {
+ regfree(rootSetRegexp[i]);
+ delete rootSetRegexp[i];
+ }
+}
+
+
+bool pkgDepCache::DefaultRootSetFunc::InRootSet(const pkgCache::PkgIterator &pkg)
+{
+ for(unsigned int i = 0; i < rootSetRegexp.size(); i++)
+ if (regexec(rootSetRegexp[i], pkg.Name(), 0, 0, 0) == 0)
+ return true;
+
+ return false;
+}
+
+pkgDepCache::InRootSetFunc *pkgDepCache::GetRootSetFunc()
+{
+ DefaultRootSetFunc *f = new DefaultRootSetFunc;
+ if(f->wasConstructedSuccessfully())
+ return f;
+ else
+ {
+ delete f;
+ return NULL;
+ }
+}
+
+bool pkgDepCache::MarkFollowsRecommends()
+{
+ return _config->FindB("APT::AutoRemove::RecommendsImportant", true);
+}
+
+bool pkgDepCache::MarkFollowsSuggests()
+{
+ return _config->FindB("APT::AutoRemove::SuggestsImportant", false);
+}
+
+// the main mark algorithm
+bool pkgDepCache::MarkRequired(InRootSetFunc &userFunc)
+{
+ bool follow_recommends;
+ bool follow_suggests;
+
+ // init the states
+ for(PkgIterator p = PkgBegin(); !p.end(); ++p)
+ {
+ PkgState[p->ID].Marked = false;
+ PkgState[p->ID].Garbage = false;
+
+ // debug output
+ if(_config->FindB("Debug::pkgAutoRemove",false)
+ && PkgState[p->ID].Flags & Flag::Auto)
+ std::clog << "AutoDep: " << p.Name() << std::endl;
+ }
+
+ // init vars
+ follow_recommends = MarkFollowsRecommends();
+ follow_suggests = MarkFollowsSuggests();
+
+
+
+ // do the mark part, this is the core bit of the algorithm
+ for(PkgIterator p = PkgBegin(); !p.end(); ++p)
+ {
+ if(!(PkgState[p->ID].Flags & Flag::Auto) ||
+ (p->Flags & Flag::Essential) ||
+ userFunc.InRootSet(p))
+
+ {
+ // the package is installed (and set to keep)
+ if(PkgState[p->ID].Keep() && !p.CurrentVer().end())
+ MarkPackage(p, p.CurrentVer(),
+ follow_recommends, follow_suggests);
+ // the package is to be installed
+ else if(PkgState[p->ID].Install())
+ MarkPackage(p, PkgState[p->ID].InstVerIter(*this),
+ follow_recommends, follow_suggests);
+ }
+ }
+
+ return true;
+}
+
+// mark a single package in Mark-and-Sweep
+void pkgDepCache::MarkPackage(const pkgCache::PkgIterator &pkg,
+ const pkgCache::VerIterator &ver,
+ bool follow_recommends,
+ bool follow_suggests)
+{
+ pkgDepCache::StateCache &state = PkgState[pkg->ID];
+ VerIterator candver = state.CandidateVerIter(*this);
+ VerIterator instver = state.InstVerIter(*this);
+
+#if 0
+ // If a package was garbage-collected but is now being marked, we
+ // should re-select it
+ // For cases when a pkg is set to upgrade and this trigger the
+ // removal of a no-longer used dependency. if the pkg is set to
+ // keep again later it will result in broken deps
+ if(state.Delete() && state.RemoveReason = Unused)
+ {
+ if(ver==candver)
+ mark_install(pkg, false, false, NULL);
+ else if(ver==pkg.CurrentVer())
+ MarkKeep(pkg, false, false);
+
+ instver=state.InstVerIter(*this);
+ }
+#endif
+
+ // Ignore versions other than the InstVer, and ignore packages
+ // that are already going to be removed or just left uninstalled.
+ if(!(ver == instver && !instver.end()))
+ return;
+
+ // if we are marked already we are done
+ if(state.Marked)
+ return;
+
+ //std::cout << "Setting Marked for: " << pkg.Name() << std::endl;
+ state.Marked=true;
+
+ if(!ver.end())
+ {
+ for(DepIterator d = ver.DependsList(); !d.end(); ++d)
+ {
+ if(d->Type == Dep::Depends ||
+ d->Type == Dep::PreDepends ||
+ (follow_recommends &&
+ d->Type == Dep::Recommends) ||
+ (follow_suggests &&
+ d->Type == Dep::Suggests))
+ {
+ // Try all versions of this package.
+ for(VerIterator V = d.TargetPkg().VersionList();
+ !V.end(); ++V)
+ {
+ if(_system->VS->CheckDep(V.VerStr(), d->CompareOp, d.TargetVer()))
+ {
+ MarkPackage(V.ParentPkg(), V,
+ follow_recommends, follow_suggests);
+ }
+ }
+ // Now try virtual packages
+ for(PrvIterator prv=d.TargetPkg().ProvidesList();
+ !prv.end(); ++prv)
+ {
+ if(_system->VS->CheckDep(prv.ProvideVersion(), d->CompareOp,
+ d.TargetVer()))
+ {
+ MarkPackage(prv.OwnerPkg(), prv.OwnerVer(),
+ follow_recommends, follow_suggests);
+ }
+ }
+ }
+ }
+ }
+}
+
+bool pkgDepCache::Sweep()
+{
+ // do the sweep
+ for(PkgIterator p=PkgBegin(); !p.end(); ++p)
+ {
+ StateCache &state=PkgState[p->ID];
+
+ // if it is not marked and it is installed, it's garbage
+ if(!state.Marked && (!p.CurrentVer().end() || state.Install()))
+ {
+ state.Garbage=true;
+ if(_config->FindB("Debug::pkgAutoRemove",false))
+ std::cout << "Garbage: " << p.Name() << std::endl;
+ }
+ }
+
+ return true;
+}