diff options
Diffstat (limited to 'apt-pkg')
-rw-r--r-- | apt-pkg/algorithms.cc | 310 | ||||
-rw-r--r-- | apt-pkg/algorithms.h | 18 | ||||
-rw-r--r-- | apt-pkg/contrib/fileutl.cc | 36 | ||||
-rw-r--r-- | apt-pkg/contrib/fileutl.h | 2 | ||||
-rw-r--r-- | apt-pkg/deb/dpkgpm.cc | 539 | ||||
-rw-r--r-- | apt-pkg/deb/dpkgpm.h | 30 | ||||
-rw-r--r-- | apt-pkg/install-progress.cc | 332 | ||||
-rw-r--r-- | apt-pkg/install-progress.h | 146 | ||||
-rw-r--r-- | apt-pkg/makefile | 6 | ||||
-rw-r--r-- | apt-pkg/packagemanager.cc | 71 | ||||
-rw-r--r-- | apt-pkg/packagemanager.h | 25 | ||||
-rw-r--r-- | apt-pkg/update.cc | 126 | ||||
-rw-r--r-- | apt-pkg/update.h | 21 | ||||
-rw-r--r-- | apt-pkg/upgrade.cc | 263 | ||||
-rw-r--r-- | apt-pkg/upgrade.h | 30 |
15 files changed, 1283 insertions, 672 deletions
diff --git a/apt-pkg/algorithms.cc b/apt-pkg/algorithms.cc index 69d4acd83..8644a8138 100644 --- a/apt-pkg/algorithms.cc +++ b/apt-pkg/algorithms.cc @@ -336,217 +336,6 @@ bool pkgFixBroken(pkgDepCache &Cache) return Fix.Resolve(true); } /*}}}*/ -// DistUpgrade - Distribution upgrade /*{{{*/ -// --------------------------------------------------------------------- -/* This autoinstalls every package and then force installs every - pre-existing package. This creates the initial set of conditions which - most likely contain problems because too many things were installed. - - The problem resolver is used to resolve the problems. - */ -bool pkgDistUpgrade(pkgDepCache &Cache) -{ - std::string const solver = _config->Find("APT::Solver", "internal"); - if (solver != "internal") { - OpTextProgress Prog(*_config); - return EDSP::ResolveExternal(solver.c_str(), Cache, false, true, false, &Prog); - } - - pkgDepCache::ActionGroup group(Cache); - - /* Upgrade all installed packages first without autoinst to help the resolver - in versioned or-groups to upgrade the old solver instead of installing - a new one (if the old solver is not the first one [anymore]) */ - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - if (I->CurrentVer != 0) - Cache.MarkInstall(I, false, 0, false); - - /* Auto upgrade all installed packages, this provides the basis - for the installation */ - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - if (I->CurrentVer != 0) - Cache.MarkInstall(I, true, 0, false); - - /* Now, install each essential package which is not installed - (and not provided by another package in the same name group) */ - std::string essential = _config->Find("pkgCacheGen::Essential", "all"); - if (essential == "all") - { - for (pkgCache::GrpIterator G = Cache.GrpBegin(); G.end() == false; ++G) - { - bool isEssential = false; - bool instEssential = false; - for (pkgCache::PkgIterator P = G.PackageList(); P.end() == false; P = G.NextPkg(P)) - { - if ((P->Flags & pkgCache::Flag::Essential) != pkgCache::Flag::Essential) - continue; - isEssential = true; - if (Cache[P].Install() == true) - { - instEssential = true; - break; - } - } - if (isEssential == false || instEssential == true) - continue; - pkgCache::PkgIterator P = G.FindPreferredPkg(); - Cache.MarkInstall(P, true, 0, false); - } - } - else if (essential != "none") - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - if ((I->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential) - Cache.MarkInstall(I, true, 0, false); - - /* We do it again over all previously installed packages to force - conflict resolution on them all. */ - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - if (I->CurrentVer != 0) - Cache.MarkInstall(I, false, 0, false); - - pkgProblemResolver Fix(&Cache); - - // Hold back held packages. - if (_config->FindB("APT::Ignore-Hold",false) == false) - { - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - { - if (I->SelectedState == pkgCache::State::Hold) - { - Fix.Protect(I); - Cache.MarkKeep(I, false, false); - } - } - } - - return Fix.Resolve(); -} - /*}}}*/ -// AllUpgrade - Upgrade as many packages as possible /*{{{*/ -// --------------------------------------------------------------------- -/* Right now the system must be consistent before this can be called. - It also will not change packages marked for install, it only tries - to install packages not marked for install */ -bool pkgAllUpgrade(pkgDepCache &Cache) -{ - std::string const solver = _config->Find("APT::Solver", "internal"); - if (solver != "internal") { - OpTextProgress Prog(*_config); - return EDSP::ResolveExternal(solver.c_str(), Cache, true, false, false, &Prog); - } - - pkgDepCache::ActionGroup group(Cache); - - pkgProblemResolver Fix(&Cache); - - if (Cache.BrokenCount() != 0) - return false; - - // Upgrade all installed packages - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - { - if (Cache[I].Install() == true) - Fix.Protect(I); - - if (_config->FindB("APT::Ignore-Hold",false) == false) - if (I->SelectedState == pkgCache::State::Hold) - continue; - - if (I->CurrentVer != 0 && Cache[I].InstallVer != 0) - Cache.MarkInstall(I, false, 0, false); - } - - return Fix.ResolveByKeep(); -} - /*}}}*/ -// AllUpgradeNoDelete - Upgrade without removing packages /*{{{*/ -// --------------------------------------------------------------------- -/* Right now the system must be consistent before this can be called. - * Upgrade as much as possible without deleting anything (useful for - * stable systems) - */ -bool pkgAllUpgradeNoDelete(pkgDepCache &Cache) -{ - pkgDepCache::ActionGroup group(Cache); - - pkgProblemResolver Fix(&Cache); - - if (Cache.BrokenCount() != 0) - return false; - - // provide the initial set of stuff we want to upgrade by marking - // all upgradable packages for upgrade - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - { - if (I->CurrentVer != 0 && Cache[I].InstallVer != 0) - { - if (_config->FindB("APT::Ignore-Hold",false) == false) - if (I->SelectedState == pkgCache::State::Hold) - continue; - - Cache.MarkInstall(I, false, 0, false); - } - } - - // then let auto-install loose - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - if (Cache[I].Install()) - Cache.MarkInstall(I, true, 0, false); - - // ... but it may remove stuff, we we need to clean up afterwards again - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - if (Cache[I].Delete() == true) - Cache.MarkKeep(I, false, false); - - // resolve remaining issues via keep - return Fix.ResolveByKeep(); -} - /*}}}*/ -// MinimizeUpgrade - Minimizes the set of packages to be upgraded /*{{{*/ -// --------------------------------------------------------------------- -/* This simply goes over the entire set of packages and tries to keep - each package marked for upgrade. If a conflict is generated then - the package is restored. */ -bool pkgMinimizeUpgrade(pkgDepCache &Cache) -{ - pkgDepCache::ActionGroup group(Cache); - - if (Cache.BrokenCount() != 0) - return false; - - // We loop for 10 tries to get the minimal set size. - bool Change = false; - unsigned int Count = 0; - do - { - Change = false; - for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) - { - // Not interesting - if (Cache[I].Upgrade() == false || Cache[I].NewInstall() == true) - continue; - - // Keep it and see if that is OK - Cache.MarkKeep(I, false, false); - if (Cache.BrokenCount() != 0) - Cache.MarkInstall(I, false, 0, false); - else - { - // If keep didnt actually do anything then there was no change.. - if (Cache[I].Upgrade() == false) - Change = true; - } - } - ++Count; - } - while (Change == true && Count < 10); - - if (Cache.BrokenCount() != 0) - return _error->Error("Internal Error in pkgMinimizeUpgrade"); - - return true; -} - /*}}}*/ // ProblemResolver::pkgProblemResolver - Constructor /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -1548,102 +1337,3 @@ void pkgPrioSortList(pkgCache &Cache,pkgCache::Version **List) qsort(List,Count,sizeof(*List),PrioComp); } /*}}}*/ -// ListUpdate - construct Fetcher and update the cache files /*{{{*/ -// --------------------------------------------------------------------- -/* This is a simple wrapper to update the cache. it will fetch stuff - * from the network (or any other sources defined in sources.list) - */ -bool ListUpdate(pkgAcquireStatus &Stat, - pkgSourceList &List, - int PulseInterval) -{ - pkgAcquire Fetcher; - if (Fetcher.Setup(&Stat, _config->FindDir("Dir::State::Lists")) == false) - return false; - - // Populate it with the source selection - if (List.GetIndexes(&Fetcher) == false) - return false; - - return AcquireUpdate(Fetcher, PulseInterval, true); -} - /*}}}*/ -// AcquireUpdate - take Fetcher and update the cache files /*{{{*/ -// --------------------------------------------------------------------- -/* This is a simple wrapper to update the cache with a provided acquire - * If you only need control over Status and the used SourcesList use - * ListUpdate method instead. - */ -bool AcquireUpdate(pkgAcquire &Fetcher, int const PulseInterval, - bool const RunUpdateScripts, bool const ListCleanup) -{ - // Run scripts - if (RunUpdateScripts == true) - RunScripts("APT::Update::Pre-Invoke"); - - pkgAcquire::RunResult res; - if(PulseInterval > 0) - res = Fetcher.Run(PulseInterval); - else - res = Fetcher.Run(); - - if (res == pkgAcquire::Failed) - return false; - - bool Failed = false; - bool TransientNetworkFailure = false; - for (pkgAcquire::ItemIterator I = Fetcher.ItemsBegin(); - I != Fetcher.ItemsEnd(); ++I) - { - if ((*I)->Status == pkgAcquire::Item::StatDone) - continue; - - (*I)->Finished(); - - ::URI uri((*I)->DescURI()); - uri.User.clear(); - uri.Password.clear(); - string descUri = string(uri); - _error->Warning(_("Failed to fetch %s %s\n"), descUri.c_str(), - (*I)->ErrorText.c_str()); - - if ((*I)->Status == pkgAcquire::Item::StatTransientNetworkError) - { - TransientNetworkFailure = true; - continue; - } - - Failed = true; - } - - // Clean out any old list files - // Keep "APT::Get::List-Cleanup" name for compatibility, but - // this is really a global option for the APT library now - if (!TransientNetworkFailure && !Failed && ListCleanup == true && - (_config->FindB("APT::Get::List-Cleanup",true) == true && - _config->FindB("APT::List-Cleanup",true) == true)) - { - if (Fetcher.Clean(_config->FindDir("Dir::State::lists")) == false || - Fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/") == false) - // something went wrong with the clean - return false; - } - - if (TransientNetworkFailure == true) - _error->Warning(_("Some index files failed to download. They have been ignored, or old ones used instead.")); - else if (Failed == true) - return _error->Error(_("Some index files failed to download. They have been ignored, or old ones used instead.")); - - - // Run the success scripts if all was fine - if (RunUpdateScripts == true) - { - if(!TransientNetworkFailure && !Failed) - RunScripts("APT::Update::Post-Invoke-Success"); - - // Run the other scripts - RunScripts("APT::Update::Post-Invoke"); - } - return true; -} - /*}}}*/ diff --git a/apt-pkg/algorithms.h b/apt-pkg/algorithms.h index a499db8ba..5a9a77415 100644 --- a/apt-pkg/algorithms.h +++ b/apt-pkg/algorithms.h @@ -43,7 +43,12 @@ using std::ostream; #endif -class pkgAcquireStatus; +#ifndef APT_9_CLEANER_HEADERS +// include pkg{DistUpgrade,AllUpgrade,MiniizeUpgrade} here for compatiblity +#include <apt-pkg/upgrade.h> +#include <apt-pkg/update.h> +#endif + class pkgSimulate : public pkgPackageManager /*{{{*/ { @@ -85,6 +90,7 @@ private: /*}}}*/ class pkgProblemResolver /*{{{*/ { + private: /** \brief dpointer placeholder (for later in case we need it) */ void *d; @@ -140,20 +146,10 @@ class pkgProblemResolver /*{{{*/ ~pkgProblemResolver(); }; /*}}}*/ -bool pkgDistUpgrade(pkgDepCache &Cache); bool pkgApplyStatus(pkgDepCache &Cache); bool pkgFixBroken(pkgDepCache &Cache); -bool pkgAllUpgrade(pkgDepCache &Cache); - -bool pkgAllUpgradeNoDelete(pkgDepCache &Cache); - -bool pkgMinimizeUpgrade(pkgDepCache &Cache); - void pkgPrioSortList(pkgCache &Cache,pkgCache::Version **List); -bool ListUpdate(pkgAcquireStatus &progress, pkgSourceList &List, int PulseInterval=0); -bool AcquireUpdate(pkgAcquire &Fetcher, int const PulseInterval = 0, - bool const RunUpdateScripts = true, bool const ListCleanup = true); #endif diff --git a/apt-pkg/contrib/fileutl.cc b/apt-pkg/contrib/fileutl.cc index 0261119ba..d2be276c7 100644 --- a/apt-pkg/contrib/fileutl.cc +++ b/apt-pkg/contrib/fileutl.cc @@ -767,6 +767,26 @@ bool WaitFd(int Fd,bool write,unsigned long timeout) otherwise acts like normal fork. */ pid_t ExecFork() { + set<int> KeepFDs; + + // FIXME: remove looking at APT::Keep-Fds eventually, its a hack + Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds"); + if (Opts != 0 && Opts->Child != 0) + { + Opts = Opts->Child; + for (; Opts != 0; Opts = Opts->Next) + { + if (Opts->Value.empty() == true) + continue; + int fd = atoi(Opts->Value.c_str()); + KeepFDs.insert(fd); + } + } + return ExecFork(KeepFDs); +} + +pid_t ExecFork(std::set<int> KeepFDs) +{ // Fork off the process pid_t Process = fork(); if (Process < 0) @@ -786,22 +806,8 @@ pid_t ExecFork() signal(SIGCONT,SIG_DFL); signal(SIGTSTP,SIG_DFL); - set<int> KeepFDs; - Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds"); - if (Opts != 0 && Opts->Child != 0) - { - Opts = Opts->Child; - for (; Opts != 0; Opts = Opts->Next) - { - if (Opts->Value.empty() == true) - continue; - int fd = atoi(Opts->Value.c_str()); - KeepFDs.insert(fd); - } - } - // Close all of our FDs - just in case - for (int K = 3; K != 40; K++) + for (int K = 3; K != sysconf(_SC_OPEN_MAX); K++) { if(KeepFDs.find(K) == KeepFDs.end()) fcntl(K,F_SETFD,FD_CLOEXEC); diff --git a/apt-pkg/contrib/fileutl.h b/apt-pkg/contrib/fileutl.h index decd64d9d..63a999c30 100644 --- a/apt-pkg/contrib/fileutl.h +++ b/apt-pkg/contrib/fileutl.h @@ -26,6 +26,7 @@ #include <string> #include <vector> +#include <set> #include <zlib.h> @@ -182,6 +183,7 @@ void SetCloseExec(int Fd,bool Close); void SetNonBlock(int Fd,bool Block); bool WaitFd(int Fd,bool write = false,unsigned long timeout = 0); pid_t ExecFork(); +pid_t ExecFork(std::set<int> keep_fds); bool ExecWait(pid_t Pid,const char *Name,bool Reap = false); // File string manipulators diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index 9731060be..1a8790f29 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -19,6 +19,7 @@ #include <apt-pkg/fileutl.h> #include <apt-pkg/cachefile.h> #include <apt-pkg/packagemanager.h> +#include <apt-pkg/install-progress.h> #include <unistd.h> #include <stdlib.h> @@ -53,15 +54,12 @@ class pkgDPkgPMPrivate public: pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0), term_out(NULL), history_out(NULL), - last_reported_progress(0.0), nr_terminal_rows(0), - fancy_progress_output(false) + progress(NULL), master(-1), slave(-1) { dpkgbuf[0] = '\0'; - if(_config->FindB("Dpkg::Progress-Fancy", false) == true) - { - fancy_progress_output = true; - _config->Set("DpkgPM::Progress", true); - } + } + ~pkgDPkgPMPrivate() + { } bool stdin_is_dev_null; // the buffer we use for the dpkg status-fd reading @@ -70,10 +68,17 @@ public: FILE *term_out; FILE *history_out; string dpkg_error; + APT::Progress::PackageManager *progress; + + // pty stuff + struct termios tt; + int master; + int slave; + + // signals + sigset_t sigmask; + sigset_t original_sigmask; - float last_reported_progress; - int nr_terminal_rows; - bool fancy_progress_output; }; namespace @@ -136,6 +141,20 @@ ionice(int PID) return ExecWait(Process, "ionice"); } +static std::string getDpkgExecutable() +{ + string Tmp = _config->Find("Dir::Bin::dpkg","dpkg"); + string const dpkgChrootDir = _config->FindDir("DPkg::Chroot-Directory", "/"); + size_t dpkgChrootLen = dpkgChrootDir.length(); + if (dpkgChrootDir != "/" && Tmp.find(dpkgChrootDir) == 0) + { + if (dpkgChrootDir[dpkgChrootLen - 1] == '/') + --dpkgChrootLen; + Tmp = Tmp.substr(dpkgChrootLen); + } + return Tmp; +} + // dpkgChrootDirectory - chrooting for dpkg if needed /*{{{*/ static void dpkgChrootDirectory() { @@ -396,17 +415,20 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) unsigned int InfoFD = _config->FindI(OptSec + "::InfoFD", STDIN_FILENO); // Create the pipes + std::set<int> KeepFDs; int Pipes[2]; if (pipe(Pipes) != 0) return _error->Errno("pipe","Failed to create IPC pipe to subprocess"); if (InfoFD != (unsigned)Pipes[0]) SetCloseExec(Pipes[0],true); else - _config->Set("APT::Keep-Fds::", Pipes[0]); + KeepFDs.insert(Pipes[0]); + + SetCloseExec(Pipes[1],true); // Purified Fork for running the script - pid_t Process = ExecFork(); + pid_t Process = ExecFork(KeepFDs); if (Process == 0) { // Setup the FDs @@ -428,8 +450,6 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) execv(Args[0],(char **)Args); _exit(100); } - if (InfoFD == (unsigned)Pipes[0]) - _config->Clear("APT::Keep-Fds", Pipes[0]); close(Pipes[0]); FILE *F = fdopen(Pipes[1],"w"); if (F == 0) @@ -512,7 +532,7 @@ void pkgDPkgPM::DoTerminalPty(int master) // --------------------------------------------------------------------- /* */ -void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line) +void pkgDPkgPM::ProcessDpkgStatusLine(char *line) { bool const Debug = _config->FindB("Debug::pkgDPkgProgressReporting",false); if (Debug == true) @@ -554,16 +574,6 @@ void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line) { pkgname = APT::String::Strip(list[2]); action = APT::String::Strip(list[1]); - - // this is what we support in the processing stage - if(action != "install" && action != "configure" && - action != "remove" && action != "purge" && action != "purge") - { - if (Debug == true) - std::clog << "ignoring processing action: '" << action - << "'" << std::endl; - return; - } } // "status" has the form: "status: pkg: state" // with state in ["half-installed", "unpacked", "half-configured", @@ -590,28 +600,16 @@ void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line) { if(action == "error") { - status << "pmerror:" << list[1] - << ":" << (PackagesDone/float(PackagesTotal)*100.0) - << ":" << list[3] - << endl; - if(OutStatusFd > 0) - FileFd::Write(OutStatusFd, status.str().c_str(), status.str().size()); - if (Debug == true) - std::clog << "send: '" << status.str() << "'" << endl; + d->progress->Error(list[1], PackagesDone, PackagesTotal, + list[3]); pkgFailures++; WriteApportReport(list[1].c_str(), list[3].c_str()); return; } else if(action == "conffile") { - status << "pmconffile:" << list[1] - << ":" << (PackagesDone/float(PackagesTotal)*100.0) - << ":" << list[3] - << endl; - if(OutStatusFd > 0) - FileFd::Write(OutStatusFd, status.str().c_str(), status.str().size()); - if (Debug == true) - std::clog << "send: '" << status.str() << "'" << endl; + d->progress->ConffilePrompt(list[1], PackagesDone, PackagesTotal, + list[3]); return; } } @@ -664,16 +662,8 @@ void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line) return; } std::string msg; - strprintf(msg, _(iter->second), short_pkgname.c_str()); - - status << "pmstatus:" << short_pkgname - << ":" << (PackagesDone/float(PackagesTotal)*100.0) - << ":" << msg - << endl; - if(OutStatusFd > 0) - FileFd::Write(OutStatusFd, status.str().c_str(), status.str().size()); - if (Debug == true) - std::clog << "send: '" << status.str() << "'" << endl; + strprintf(msg, _(iter->second), i18n_pkgname.c_str()); + d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg); // FIXME: this needs a muliarch testcase // FIXME2: is "pkgname" here reliable with dpkg only sending us @@ -696,23 +686,14 @@ void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line) // action const char *translation = _(states[PackageOpsDone[pkg]].str); std::string msg; - strprintf(msg, translation, short_pkgname.c_str()); - + // we moved from one dpkg state to a new one, report that PackageOpsDone[pkg]++; PackagesDone++; - // build the status str - status << "pmstatus:" << short_pkgname - << ":" << (PackagesDone/float(PackagesTotal)*100.0) - << ":" << msg - << endl; - if(_config->FindB("DPkgPM::Progress", false) == true) - SendTerminalProgress(PackagesDone/float(PackagesTotal)*100.0); + + strprintf(msg, translation, i18n_pkgname.c_str()); + d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg); - if(OutStatusFd > 0) - FileFd::Write(OutStatusFd, status.str().c_str(), status.str().size()); - if (Debug == true) - std::clog << "send: '" << status.str() << "'" << endl; } if (Debug == true) std::clog << "(parsed from dpkg) pkg: " << short_pkgname @@ -723,12 +704,13 @@ void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line) // 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; + + // record the package name for display and stuff later + disappearedPkgs.insert(Pkg.FullName(true)); + // the disappeared package was auto-installed - nothing to do if ((Cache[Pkg].Flags & pkgCache::Flag::Auto) == pkgCache::Flag::Auto) return; @@ -771,7 +753,7 @@ void pkgDPkgPM::handleDisappearAction(string const &pkgname) // --------------------------------------------------------------------- /* */ -void pkgDPkgPM::DoDpkgStatusFd(int statusfd, int OutStatusFd) +void pkgDPkgPM::DoDpkgStatusFd(int statusfd) { char *p, *q; int len; @@ -786,7 +768,7 @@ void pkgDPkgPM::DoDpkgStatusFd(int statusfd, int OutStatusFd) while((q=(char*)memchr(p, '\n', d->dpkgbuf+d->dpkgbuf_pos-p)) != NULL) { *q = 0; - ProcessDpkgStatusLine(OutStatusFd, p); + ProcessDpkgStatusLine(p); p=q+1; // continue with next line } @@ -952,50 +934,6 @@ bool pkgDPkgPM::CloseLog() return true; } /*}}}*/ -// DPkgPM::SendTerminalProgress /*{{{*/ -// --------------------------------------------------------------------- -/* Send progress info to the terminal - */ -void pkgDPkgPM::SendTerminalProgress(float percentage) -{ - int reporting_steps = _config->FindI("DpkgPM::Reporting-Steps", 1); - - if(percentage < (d->last_reported_progress + reporting_steps)) - return; - - std::string progress_str; - strprintf(progress_str, _("Progress: [%3i%%]"), (int)percentage); - if (d->fancy_progress_output) - { - int row = d->nr_terminal_rows; - - static string save_cursor = "\033[s"; - static string restore_cursor = "\033[u"; - - static string set_bg_color = "\033[42m"; // green - static string set_fg_color = "\033[30m"; // black - - static string restore_bg = "\033[49m"; - static string restore_fg = "\033[39m"; - - std::cout << save_cursor - // move cursor position to last row - << "\033[" << row << ";0f" - << set_bg_color - << set_fg_color - << progress_str - << restore_cursor - << restore_bg - << restore_fg; - } - else - { - std::cout << progress_str << "\r\n"; - } - std::flush(std::cout); - - d->last_reported_progress = percentage; -} /*}}}*/ /*{{{*/ // This implements a racy version of pselect for those architectures @@ -1017,73 +955,150 @@ static int racy_pselect(int nfds, fd_set *readfds, fd_set *writefds, sigprocmask(SIG_SETMASK, &origmask, 0); return retval; } -/*}}}*/ + /*}}}*/ -void pkgDPkgPM::SetupTerminalScrollArea(int nr_rows) +// DPkgPM::BuildPackagesProgressMap /*{{{*/ +void pkgDPkgPM::BuildPackagesProgressMap() { - if(!d->fancy_progress_output) - return; + // map the dpkg states to the operations that are performed + // (this is sorted in the same way as Item::Ops) + static const struct DpkgState DpkgStatesOpMap[][7] = { + // Install operation + { + {"half-installed", N_("Preparing %s")}, + {"unpacked", N_("Unpacking %s") }, + {NULL, NULL} + }, + // Configure operation + { + {"unpacked",N_("Preparing to configure %s") }, + {"half-configured", N_("Configuring %s") }, + { "installed", N_("Installed %s")}, + {NULL, NULL} + }, + // Remove operation + { + {"half-configured", N_("Preparing for removal of %s")}, + {"half-installed", N_("Removing %s")}, + {"config-files", N_("Removed %s")}, + {NULL, NULL} + }, + // Purge operation + { + {"config-files", N_("Preparing to completely remove %s")}, + {"not-installed", N_("Completely removed %s")}, + {NULL, NULL} + }, + }; - // scroll down a bit to avoid visual glitch when the screen - // area shrinks by one row - std::cout << "\n"; - - // save cursor - std::cout << "\033[s"; - - // set scroll region (this will place the cursor in the top left) - std::cout << "\033[1;" << nr_rows - 1 << "r"; - - // restore cursor but ensure its inside the scrolling area - std::cout << "\033[u"; - static const char *move_cursor_up = "\033[1A"; - std::cout << move_cursor_up; - std::flush(std::cout); + // init the PackageOps map, go over the list of packages that + // that will be [installed|configured|removed|purged] and add + // them to the PackageOps map (the dpkg states it goes through) + // and the PackageOpsTranslations (human readable strings) + for (vector<Item>::const_iterator I = List.begin(); I != List.end(); ++I) + { + if((*I).Pkg.end() == true) + continue; + + string const name = (*I).Pkg.FullName(); + PackageOpsDone[name] = 0; + for(int i=0; (DpkgStatesOpMap[(*I).Op][i]).state != NULL; ++i) + { + PackageOps[name].push_back(DpkgStatesOpMap[(*I).Op][i]); + PackagesTotal++; + } + } +} + /*}}}*/ +#if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR < 13) +bool pkgDPkgPM::Go(int StatusFd) +{ + APT::Progress::PackageManager *progress = NULL; + if (StatusFd == -1) + progress = APT::Progress::PackageManagerProgressFactory(); + else + progress = new APT::Progress::PackageManagerProgressFd(StatusFd); + + return GoNoABIBreak(progress); } +#endif -void pkgDPkgPM::CleanupTerminal() +void pkgDPkgPM::StartPtyMagic() { - // reset scroll area - SetupTerminalScrollArea(d->nr_terminal_rows + 1); - if(d->fancy_progress_output) + // setup the pty and stuff + struct winsize win; + + // if tcgetattr does not return zero there was a error + // and we do not do any pty magic + _error->PushToStack(); + if (tcgetattr(STDOUT_FILENO, &d->tt) == 0) { - // override the progress line (sledgehammer) - static const char* clear_screen_below_cursor = "\033[J"; - std::cout << clear_screen_below_cursor; - std::flush(std::cout); - } + ioctl(1, TIOCGWINSZ, (char *)&win); + if (openpty(&d->master, &d->slave, NULL, &d->tt, &win) < 0) + { + _error->Errno("openpty", _("Can not write log (%s)"), _("Is /dev/pts mounted?")); + d->master = d->slave = -1; + } else { + struct termios rtt; + rtt = d->tt; + cfmakeraw(&rtt); + rtt.c_lflag &= ~ECHO; + rtt.c_lflag |= ISIG; + // block SIGTTOU during tcsetattr to prevent a hang if + // the process is a member of the background process group + // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html + sigemptyset(&d->sigmask); + sigaddset(&d->sigmask, SIGTTOU); + sigprocmask(SIG_BLOCK,&d->sigmask, &d->original_sigmask); + tcsetattr(0, TCSAFLUSH, &rtt); + sigprocmask(SIG_SETMASK, &d->original_sigmask, 0); + } + } + // complain only if stdout is either a terminal (but still failed) or is an invalid + // descriptor otherwise we would complain about redirection to e.g. /dev/null as well. + else if (isatty(STDOUT_FILENO) == 1 || errno == EBADF) + _error->Errno("tcgetattr", _("Can not write log (%s)"), _("Is stdout a terminal?")); + + if (_error->PendingError() == true) + _error->DumpErrors(std::cerr); + _error->RevertToStack(); } +void pkgDPkgPM::StopPtyMagic() +{ + if(d->slave > 0) + close(d->slave); + if(d->master >= 0) + { + tcsetattr(0, TCSAFLUSH, &d->tt); + close(d->master); + } +} // DPkgPM::Go - Run the sequence /*{{{*/ // --------------------------------------------------------------------- /* This globs the operations and calls dpkg - * - * If it is called with "OutStatusFd" set to a valid file descriptor - * apt will report the install progress over this fd. It maps the - * dpkg states a package goes through to human readable (and i10n-able) + * + * If it is called with a progress object apt will report the install + * progress to this object. It maps the dpkg states a package goes + * through to human readable (and i10n-able) * names and calculates a percentage for each step. -*/ -bool pkgDPkgPM::Go(int OutStatusFd) + */ +#if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR >= 13) +bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) +#else +bool pkgDPkgPM::GoNoABIBreak(APT::Progress::PackageManager *progress) +#endif { pkgPackageManager::SigINTStop = false; + d->progress = progress; // Generate the base argument list for dpkg - std::vector<const char *> Args; unsigned long StartSize = 0; - string Tmp = _config->Find("Dir::Bin::dpkg","dpkg"); - { - string const dpkgChrootDir = _config->FindDir("DPkg::Chroot-Directory", "/"); - size_t dpkgChrootLen = dpkgChrootDir.length(); - if (dpkgChrootDir != "/" && Tmp.find(dpkgChrootDir) == 0) - { - if (dpkgChrootDir[dpkgChrootLen - 1] == '/') - --dpkgChrootLen; - Tmp = Tmp.substr(dpkgChrootLen); - } - } - Args.push_back(Tmp.c_str()); - StartSize += Tmp.length(); + std::vector<const char *> Args; + std::string DpkgExecutable = getDpkgExecutable(); + Args.push_back(DpkgExecutable.c_str()); + StartSize += DpkgExecutable.length(); // Stick in any custom dpkg options Configuration::Item const *Opts = _config->Tree("DPkg::Options"); @@ -1120,8 +1135,6 @@ bool pkgDPkgPM::Go(int OutStatusFd) fd_set rfds; struct timespec tv; - sigset_t sigmask; - sigset_t original_sigmask; unsigned int const MaxArgs = _config->FindI("Dpkg::MaxArgs",8*1024); unsigned int const MaxArgBytes = _config->FindI("Dpkg::MaxArgBytes",32*1024); @@ -1140,54 +1153,8 @@ bool pkgDPkgPM::Go(int OutStatusFd) if (_config->FindB("DPkg::ConfigurePending", SmartConf) == true) List.push_back(Item(Item::ConfigurePending, PkgIterator())); - // map the dpkg states to the operations that are performed - // (this is sorted in the same way as Item::Ops) - static const struct DpkgState DpkgStatesOpMap[][7] = { - // Install operation - { - {"half-installed", N_("Preparing %s")}, - {"unpacked", N_("Unpacking %s") }, - {NULL, NULL} - }, - // Configure operation - { - {"unpacked",N_("Preparing to configure %s") }, - {"half-configured", N_("Configuring %s") }, - { "installed", N_("Installed %s")}, - {NULL, NULL} - }, - // Remove operation - { - {"half-configured", N_("Preparing for removal of %s")}, - {"half-installed", N_("Removing %s")}, - {"config-files", N_("Removed %s")}, - {NULL, NULL} - }, - // Purge operation - { - {"config-files", N_("Preparing to completely remove %s")}, - {"not-installed", N_("Completely removed %s")}, - {NULL, NULL} - }, - }; - - // init the PackageOps map, go over the list of packages that - // that will be [installed|configured|removed|purged] and add - // them to the PackageOps map (the dpkg states it goes through) - // and the PackageOpsTranslations (human readable strings) - for (vector<Item>::const_iterator I = List.begin(); I != List.end(); ++I) - { - if((*I).Pkg.end() == true) - continue; - - string const name = (*I).Pkg.FullName(); - PackageOpsDone[name] = 0; - for(int i=0; (DpkgStatesOpMap[(*I).Op][i]).state != NULL; ++i) - { - PackageOps[name].push_back(DpkgStatesOpMap[(*I).Op][i]); - PackagesTotal++; - } - } + // for the progress + BuildPackagesProgressMap(); d->stdin_is_dev_null = false; @@ -1209,8 +1176,15 @@ bool pkgDPkgPM::Go(int OutStatusFd) dpkgMultiArch = true; } - // this loop is runs once per operation - for (vector<Item>::const_iterator I = List.begin(); I != List.end();) + // start pty magic before the loop + StartPtyMagic(); + + // Tell the progress that its starting and fork dpkg + d->progress->Start(); + + // this loop is runs once per dpkg operation + vector<Item>::const_iterator I = List.begin(); + while (I != List.end()) { // Do all actions with the same Op in one run vector<Item>::const_iterator J = I; @@ -1331,7 +1305,7 @@ bool pkgDPkgPM::Go(int OutStatusFd) { if((*I).Pkg.end() == true) continue; - if (I->Op == Item::Configure && disappearedPkgs.find(I->Pkg.Name()) != disappearedPkgs.end()) + if (I->Op == Item::Configure && disappearedPkgs.find(I->Pkg.FullName(true)) != disappearedPkgs.end()) continue; // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian if (dpkgMultiArch == false && (I->Pkg.Arch() == nativeArch || @@ -1401,74 +1375,23 @@ bool pkgDPkgPM::Go(int OutStatusFd) // ignore SIGHUP as well (debian #463030) sighandler_t old_SIGHUP = signal(SIGHUP,SIG_IGN); - struct termios tt; - struct winsize win; - int master = -1; - int slave = -1; - - // if tcgetattr does not return zero there was a error - // and we do not do any pty magic - _error->PushToStack(); - if (tcgetattr(STDOUT_FILENO, &tt) == 0) - { - ioctl(1, TIOCGWINSZ, (char *)&win); - d->nr_terminal_rows = win.ws_row; - if (openpty(&master, &slave, NULL, &tt, &win) < 0) - { - _error->Errno("openpty", _("Can not write log (%s)"), _("Is /dev/pts mounted?")); - master = slave = -1; - } else { - struct termios rtt; - rtt = tt; - cfmakeraw(&rtt); - rtt.c_lflag &= ~ECHO; - rtt.c_lflag |= ISIG; - // block SIGTTOU during tcsetattr to prevent a hang if - // the process is a member of the background process group - // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html - sigemptyset(&sigmask); - sigaddset(&sigmask, SIGTTOU); - sigprocmask(SIG_BLOCK,&sigmask, &original_sigmask); - tcsetattr(0, TCSAFLUSH, &rtt); - sigprocmask(SIG_SETMASK, &original_sigmask, 0); - } - } - // complain only if stdout is either a terminal (but still failed) or is an invalid - // descriptor otherwise we would complain about redirection to e.g. /dev/null as well. - else if (isatty(STDOUT_FILENO) == 1 || errno == EBADF) - _error->Errno("tcgetattr", _("Can not write log (%s)"), _("Is stdout a terminal?")); - - if (_error->PendingError() == true) - _error->DumpErrors(std::cerr); - _error->RevertToStack(); - - // Fork dpkg - pid_t Child; - _config->Set("APT::Keep-Fds::",fd[1]); - // send status information that we are about to fork dpkg - if(OutStatusFd > 0) { - ostringstream status; - status << "pmstatus:dpkg-exec:" - << (PackagesDone/float(PackagesTotal)*100.0) - << ":" << _("Running dpkg") - << endl; - FileFd::Write(OutStatusFd, status.str().c_str(), status.str().size()); - } - - Child = ExecFork(); - // This is the child + // now run dpkg + d->progress->StartDpkg(); + std::set<int> KeepFDs; + KeepFDs.insert(fd[1]); + pid_t Child = ExecFork(KeepFDs); if (Child == 0) { - - if(slave >= 0 && master >= 0) + // This is the child + if(d->slave >= 0 && d->master >= 0) { setsid(); - ioctl(slave, TIOCSCTTY, 0); - close(master); - dup2(slave, 0); - dup2(slave, 1); - dup2(slave, 2); - close(slave); + ioctl(d->slave, TIOCSCTTY, 0); + close(d->master); + dup2(d->slave, 0); + dup2(d->slave, 1); + dup2(d->slave, 2); + close(d->slave); } close(fd[0]); // close the read end of the pipe @@ -1492,9 +1415,6 @@ bool pkgDPkgPM::Go(int OutStatusFd) if (fcntl(STDIN_FILENO,F_SETFL,Flags & (~(long)O_NONBLOCK)) < 0) _exit(100); } - // setup terminal - SetupTerminalScrollArea(d->nr_terminal_rows); - SendTerminalProgress(PackagesDone/float(PackagesTotal)*100.0); /* No Job Control Stop Env is a magic dpkg var that prevents it from using sigstop */ @@ -1508,9 +1428,6 @@ bool pkgDPkgPM::Go(int OutStatusFd) if (_config->FindB("DPkg::UseIoNice", false) == true) ionice(Child); - // clear the Keep-Fd again - _config->Clear("APT::Keep-Fds",fd[1]); - // Wait for dpkg int Status = 0; @@ -1518,12 +1435,9 @@ bool pkgDPkgPM::Go(int OutStatusFd) int const _dpkgin = fd[0]; close(fd[1]); // close the write end of the pipe - if(slave > 0) - close(slave); - // setups fds - sigemptyset(&sigmask); - sigprocmask(SIG_BLOCK,&sigmask,&original_sigmask); + sigemptyset(&d->sigmask); + sigprocmask(SIG_BLOCK,&d->sigmask,&d->original_sigmask); /* free vectors (and therefore memory) as we don't need the included data anymore */ for (std::vector<char *>::const_iterator p = Packages.begin(); @@ -1552,18 +1466,19 @@ bool pkgDPkgPM::Go(int OutStatusFd) // wait for input or output here FD_ZERO(&rfds); - if (master >= 0 && !d->stdin_is_dev_null) + if (d->master >= 0 && !d->stdin_is_dev_null) FD_SET(0, &rfds); FD_SET(_dpkgin, &rfds); - if(master >= 0) - FD_SET(master, &rfds); - tv.tv_sec = 1; - tv.tv_nsec = 0; - select_ret = pselect(max(master, _dpkgin)+1, &rfds, NULL, NULL, - &tv, &original_sigmask); + if(d->master >= 0) + FD_SET(d->master, &rfds); + tv.tv_sec = 0; + tv.tv_nsec = d->progress->GetPulseInterval(); + select_ret = pselect(max(d->master, _dpkgin)+1, &rfds, NULL, NULL, + &tv, &d->original_sigmask); if (select_ret < 0 && (errno == EINVAL || errno == ENOSYS)) - select_ret = racy_pselect(max(master, _dpkgin)+1, &rfds, NULL, - NULL, &tv, &original_sigmask); + select_ret = racy_pselect(max(d->master, _dpkgin)+1, &rfds, NULL, + NULL, &tv, &d->original_sigmask); + d->progress->Pulse(); if (select_ret == 0) continue; else if (select_ret < 0 && errno == EINTR) @@ -1574,12 +1489,12 @@ bool pkgDPkgPM::Go(int OutStatusFd) continue; } - if(master >= 0 && FD_ISSET(master, &rfds)) - DoTerminalPty(master); - if(master >= 0 && FD_ISSET(0, &rfds)) - DoStdin(master); + if(d->master >= 0 && FD_ISSET(d->master, &rfds)) + DoTerminalPty(d->master); + if(d->master >= 0 && FD_ISSET(0, &rfds)) + DoStdin(d->master); if(FD_ISSET(_dpkgin, &rfds)) - DoDpkgStatusFd(_dpkgin, OutStatusFd); + DoDpkgStatusFd(_dpkgin); } close(_dpkgin); @@ -1588,13 +1503,6 @@ bool pkgDPkgPM::Go(int OutStatusFd) signal(SIGINT,old_SIGINT); signal(SIGHUP,old_SIGHUP); - - if(master >= 0) - { - tcsetattr(0, TCSAFLUSH, &tt); - close(master); - } - // Check for an error code. if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0) { @@ -1619,18 +1527,15 @@ bool pkgDPkgPM::Go(int OutStatusFd) if(stopOnError) { CloseLog(); - CleanupTerminal(); + d->progress->Stop(); return false; } } } - CloseLog(); - // dpkg is done at this point - if(_config->FindB("DPkgPM::Progress", false) == true) - SendTerminalProgress(100); - - CleanupTerminal(); + d->progress->Stop(); + StopPtyMagic(); + CloseLog(); if (pkgPackageManager::SigINTStop) _error->Warning(_("Operation was interrupted before it could finish")); diff --git a/apt-pkg/deb/dpkgpm.h b/apt-pkg/deb/dpkgpm.h index 1a58e1af5..06318d94f 100644 --- a/apt-pkg/deb/dpkgpm.h +++ b/apt-pkg/deb/dpkgpm.h @@ -15,6 +15,7 @@ #include <map> #include <stdio.h> #include <apt-pkg/macros.h> +#include <apt-pkg/init.h> #ifndef APT_8_CLEANER_HEADERS using std::vector; @@ -23,6 +24,7 @@ using std::map; class pkgDPkgPMPrivate; + class pkgDPkgPM : public pkgPackageManager { private: @@ -83,11 +85,12 @@ class pkgDPkgPM : public pkgPackageManager __deprecated bool SendV2Pkgs(FILE *F); bool SendPkgsInfo(FILE * const F, unsigned int const &Version); void WriteHistoryTag(std::string const &tag, std::string value); + std::string ExpandShortPackageName(pkgDepCache &Cache, + const std::string &short_pkgname); // Terminal progress void SetupTerminalScrollArea(int nr_scrolled_rows); void SendTerminalProgress(float percentage); - void CleanupTerminal(); // apport integration void WriteApportReport(const char *pkgpath, const char *errormsg); @@ -95,18 +98,39 @@ class pkgDPkgPM : public pkgPackageManager // dpkg log bool OpenLog(); bool CloseLog(); + + // helper + void BuildPackagesProgressMap(); + void StartPtyMagic(); + void StopPtyMagic(); // input processing void DoStdin(int master); void DoTerminalPty(int master); - void DoDpkgStatusFd(int statusfd, int OutStatusFd); - void ProcessDpkgStatusLine(int OutStatusFd, char *line); + void DoDpkgStatusFd(int statusfd); + void ProcessDpkgStatusLine(char *line); +#if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR < 13) + void DoDpkgStatusFd(int statusfd, int unused) { + DoDpkgStatusFd(statusfd); + } + void ProcessDpkgStatusLine(int unused, char *line) { + ProcessDpkgStatusLine(line); + } +#endif + // The Actuall installation implementation virtual bool Install(PkgIterator Pkg,std::string File); virtual bool Configure(PkgIterator Pkg); virtual bool Remove(PkgIterator Pkg,bool Purge = false); + +#if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR >= 13) + virtual bool Go(APT::Progress::PackageManager *progress); +#else virtual bool Go(int StatusFd=-1); + bool GoNoABIBreak(APT::Progress::PackageManager *progress); +#endif + virtual void Reset(); public: diff --git a/apt-pkg/install-progress.cc b/apt-pkg/install-progress.cc new file mode 100644 index 000000000..09b1bef9e --- /dev/null +++ b/apt-pkg/install-progress.cc @@ -0,0 +1,332 @@ +#include <apt-pkg/configuration.h> +#include <apt-pkg/fileutl.h> +#include <apt-pkg/strutl.h> +#include <apt-pkg/install-progress.h> + +#include <apti18n.h> + +#include <termios.h> +#include <sys/ioctl.h> +#include <sstream> +#include <fcntl.h> + +namespace APT { +namespace Progress { + + +/* Return a APT::Progress::PackageManager based on the global + * apt configuration (i.e. APT::Status-Fd and APT::Status-deb822-Fd) + */ +PackageManager* PackageManagerProgressFactory() +{ + // select the right progress + int status_fd = _config->FindI("APT::Status-Fd", -1); + int status_deb822_fd = _config->FindI("APT::Status-deb822-Fd", -1); + + APT::Progress::PackageManager *progress = NULL; + if (status_deb822_fd > 0) + progress = new APT::Progress::PackageManagerProgressDeb822Fd( + status_deb822_fd); + else if (status_fd > 0) + progress = new APT::Progress::PackageManagerProgressFd(status_fd); + else if(_config->FindB("Dpkg::Progress-Fancy", false) == true) + progress = new APT::Progress::PackageManagerFancy(); + else if (_config->FindB("Dpkg::Progress", + _config->FindB("DpkgPM::Progress", false)) == true) + progress = new APT::Progress::PackageManagerText(); + else + progress = new APT::Progress::PackageManager(); + return progress; +} + +bool PackageManager::StatusChanged(std::string PackageName, + unsigned int StepsDone, + unsigned int TotalSteps, + std::string HumanReadableAction) +{ + int reporting_steps = _config->FindI("DpkgPM::Reporting-Steps", 1); + percentage = StepsDone/(float)TotalSteps * 100.0; + strprintf(progress_str, _("Progress: [%3i%%]"), (int)percentage); + + if(percentage < (last_reported_progress + reporting_steps)) + return false; + + return true; +} + +PackageManagerProgressFd::PackageManagerProgressFd(int progress_fd) + : StepsDone(0), StepsTotal(1) +{ + OutStatusFd = progress_fd; +} + +void PackageManagerProgressFd::WriteToStatusFd(std::string s) +{ + if(OutStatusFd <= 0) + return; + FileFd::Write(OutStatusFd, s.c_str(), s.size()); +} + +void PackageManagerProgressFd::StartDpkg() +{ + if(OutStatusFd <= 0) + return; + + // FIXME: use SetCloseExec here once it taught about throwing + // exceptions instead of doing _exit(100) on failure + fcntl(OutStatusFd,F_SETFD,FD_CLOEXEC); + + // send status information that we are about to fork dpkg + std::ostringstream status; + status << "pmstatus:dpkg-exec:" + << (StepsDone/float(StepsTotal)*100.0) + << ":" << _("Running dpkg") + << std::endl; + WriteToStatusFd(status.str()); +} + +void PackageManagerProgressFd::Stop() +{ +} + +void PackageManagerProgressFd::Error(std::string PackageName, + unsigned int StepsDone, + unsigned int TotalSteps, + std::string ErrorMessage) +{ + std::ostringstream status; + status << "pmerror:" << PackageName + << ":" << (StepsDone/float(TotalSteps)*100.0) + << ":" << ErrorMessage + << std::endl; + WriteToStatusFd(status.str()); +} + +void PackageManagerProgressFd::ConffilePrompt(std::string PackageName, + unsigned int StepsDone, + unsigned int TotalSteps, + std::string ConfMessage) +{ + std::ostringstream status; + status << "pmconffile:" << PackageName + << ":" << (StepsDone/float(TotalSteps)*100.0) + << ":" << ConfMessage + << std::endl; + WriteToStatusFd(status.str()); +} + + +bool PackageManagerProgressFd::StatusChanged(std::string PackageName, + unsigned int xStepsDone, + unsigned int xTotalSteps, + std::string pkg_action) +{ + StepsDone = xStepsDone; + StepsTotal = xTotalSteps; + + // build the status str + std::ostringstream status; + status << "pmstatus:" << StringSplit(PackageName, ":")[0] + << ":" << (StepsDone/float(StepsTotal)*100.0) + << ":" << pkg_action + << std::endl; + WriteToStatusFd(status.str()); + + if(_config->FindB("Debug::APT::Progress::PackageManagerFd", false) == true) + std::cerr << "progress: " << PackageName << " " << xStepsDone + << " " << xTotalSteps << " " << pkg_action + << std::endl; + + + return true; +} + + +PackageManagerProgressDeb822Fd::PackageManagerProgressDeb822Fd(int progress_fd) + : StepsDone(0), StepsTotal(1) +{ + OutStatusFd = progress_fd; +} + +void PackageManagerProgressDeb822Fd::WriteToStatusFd(std::string s) +{ + FileFd::Write(OutStatusFd, s.c_str(), s.size()); +} + +void PackageManagerProgressDeb822Fd::StartDpkg() +{ + // FIXME: use SetCloseExec here once it taught about throwing + // exceptions instead of doing _exit(100) on failure + fcntl(OutStatusFd,F_SETFD,FD_CLOEXEC); + + // send status information that we are about to fork dpkg + std::ostringstream status; + status << "Status: " << "progress" << std::endl + << "Percent: " << (StepsDone/float(StepsTotal)*100.0) << std::endl + << "Message: " << _("Running dpkg") << std::endl + << std::endl; + WriteToStatusFd(status.str()); +} + +void PackageManagerProgressDeb822Fd::Stop() +{ +} + +void PackageManagerProgressDeb822Fd::Error(std::string PackageName, + unsigned int StepsDone, + unsigned int TotalSteps, + std::string ErrorMessage) +{ + std::ostringstream status; + status << "Status: " << "Error" << std::endl + << "Package:" << PackageName << std::endl + << "Percent: " << (StepsDone/float(TotalSteps)*100.0) << std::endl + << "Message: " << ErrorMessage << std::endl + << std::endl; + WriteToStatusFd(status.str()); +} + +void PackageManagerProgressDeb822Fd::ConffilePrompt(std::string PackageName, + unsigned int StepsDone, + unsigned int TotalSteps, + std::string ConfMessage) +{ + std::ostringstream status; + status << "Status: " << "ConfFile" << std::endl + << "Package:" << PackageName << std::endl + << "Percent: " << (StepsDone/float(TotalSteps)*100.0) << std::endl + << "Message: " << ConfMessage << std::endl + << std::endl; + WriteToStatusFd(status.str()); +} + + +bool PackageManagerProgressDeb822Fd::StatusChanged(std::string PackageName, + unsigned int xStepsDone, + unsigned int xTotalSteps, + std::string message) +{ + StepsDone = xStepsDone; + StepsTotal = xTotalSteps; + + // build the status str + std::ostringstream status; + status << "Status: " << "progress" << std::endl + << "Package: " << PackageName << std::endl + << "Percent: " << (StepsDone/float(StepsTotal)*100.0) << std::endl + << "Message: " << message << std::endl + << std::endl; + WriteToStatusFd(status.str()); + + return true; +} + + +void PackageManagerFancy::SetupTerminalScrollArea(int nr_rows) +{ + // scroll down a bit to avoid visual glitch when the screen + // area shrinks by one row + std::cout << "\n"; + + // save cursor + std::cout << "\033[s"; + + // set scroll region (this will place the cursor in the top left) + std::cout << "\033[1;" << nr_rows - 1 << "r"; + + // restore cursor but ensure its inside the scrolling area + std::cout << "\033[u"; + static const char *move_cursor_up = "\033[1A"; + std::cout << move_cursor_up; + + // setup env for (hopefully!) ncurses + string s; + strprintf(s, "%i", nr_rows); + setenv("LINES", s.c_str(), 1); + + std::flush(std::cout); +} + +PackageManagerFancy::PackageManagerFancy() + : nr_terminal_rows(-1) +{ + struct winsize win; + if(ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&win) == 0) + { + nr_terminal_rows = win.ws_row; + } +} + +void PackageManagerFancy::Start() +{ + if (nr_terminal_rows > 0) + SetupTerminalScrollArea(nr_terminal_rows); +} + +void PackageManagerFancy::Stop() +{ + if (nr_terminal_rows > 0) + { + SetupTerminalScrollArea(nr_terminal_rows + 1); + + // override the progress line (sledgehammer) + static const char* clear_screen_below_cursor = "\033[J"; + std::cout << clear_screen_below_cursor; + } +} + +bool PackageManagerFancy::StatusChanged(std::string PackageName, + unsigned int StepsDone, + unsigned int TotalSteps, + std::string HumanReadableAction) +{ + if (!PackageManager::StatusChanged(PackageName, StepsDone, TotalSteps, + HumanReadableAction)) + return false; + + int row = nr_terminal_rows; + + static string save_cursor = "\033[s"; + static string restore_cursor = "\033[u"; + + static string set_bg_color = "\033[42m"; // green + static string set_fg_color = "\033[30m"; // black + + static string restore_bg = "\033[49m"; + static string restore_fg = "\033[39m"; + + std::cout << save_cursor + // move cursor position to last row + << "\033[" << row << ";0f" + << set_bg_color + << set_fg_color + << progress_str + << restore_cursor + << restore_bg + << restore_fg; + std::flush(std::cout); + last_reported_progress = percentage; + + return true; +} + +bool PackageManagerText::StatusChanged(std::string PackageName, + unsigned int StepsDone, + unsigned int TotalSteps, + std::string HumanReadableAction) +{ + if (!PackageManager::StatusChanged(PackageName, StepsDone, TotalSteps, HumanReadableAction)) + return false; + + std::cout << progress_str << "\r\n"; + std::flush(std::cout); + + last_reported_progress = percentage; + + return true; +} + + + +}; // namespace progress +}; // namespace apt diff --git a/apt-pkg/install-progress.h b/apt-pkg/install-progress.h new file mode 100644 index 000000000..d721c6373 --- /dev/null +++ b/apt-pkg/install-progress.h @@ -0,0 +1,146 @@ +#ifndef PKGLIB_IPROGRESS_H +#define PKGLIB_IPROGRESS_H + +#include <string> +#include <unistd.h> + + +namespace APT { +namespace Progress { + + class PackageManager; + PackageManager* PackageManagerProgressFactory(); + + class PackageManager + { + private: + /** \brief dpointer placeholder */ + void *d; + + protected: + std::string progress_str; + float percentage; + int last_reported_progress; + + public: + PackageManager() + : percentage(0.0), last_reported_progress(-1) {}; + virtual ~PackageManager() {}; + + /* Global Start/Stop */ + virtual void Start() {}; + virtual void Stop() {}; + + /* When dpkg is invoked (may happen multiple times for each + * install/remove block + */ + virtual void StartDpkg() {}; + + virtual pid_t fork() {return fork(); }; + + virtual void Pulse() {}; + virtual long GetPulseInterval() { + return 500000; + }; + + virtual bool StatusChanged(std::string PackageName, + unsigned int StepsDone, + unsigned int TotalSteps, + std::string HumanReadableAction) ; + virtual void Error(std::string PackageName, + unsigned int StepsDone, + unsigned int TotalSteps, + std::string ErrorMessage) {}; + virtual void ConffilePrompt(std::string PackageName, + unsigned int StepsDone, + unsigned int TotalSteps, + std::string ConfMessage) {}; + }; + + class PackageManagerProgressFd : public PackageManager + { + protected: + int OutStatusFd; + int StepsDone; + int StepsTotal; + void WriteToStatusFd(std::string msg); + + public: + PackageManagerProgressFd(int progress_fd); + + virtual void StartDpkg(); + virtual void Stop(); + + virtual bool StatusChanged(std::string PackageName, + unsigned int StepsDone, + unsigned int TotalSteps, + std::string HumanReadableAction); + virtual void Error(std::string PackageName, + unsigned int StepsDone, + unsigned int TotalSteps, + std::string ErrorMessage); + virtual void ConffilePrompt(std::string PackageName, + unsigned int StepsDone, + unsigned int TotalSteps, + std::string ConfMessage); + + }; + + class PackageManagerProgressDeb822Fd : public PackageManager + { + protected: + int OutStatusFd; + int StepsDone; + int StepsTotal; + void WriteToStatusFd(std::string msg); + + public: + PackageManagerProgressDeb822Fd(int progress_fd); + + virtual void StartDpkg(); + virtual void Stop(); + + virtual bool StatusChanged(std::string PackageName, + unsigned int StepsDone, + unsigned int TotalSteps, + std::string HumanReadableAction); + virtual void Error(std::string PackageName, + unsigned int StepsDone, + unsigned int TotalSteps, + std::string ErrorMessage); + virtual void ConffilePrompt(std::string PackageName, + unsigned int StepsDone, + unsigned int TotalSteps, + std::string ConfMessage); + }; + + class PackageManagerFancy : public PackageManager + { + protected: + int nr_terminal_rows; + void SetupTerminalScrollArea(int nr_rows); + + public: + PackageManagerFancy(); + virtual void Start(); + virtual void Stop(); + virtual bool StatusChanged(std::string PackageName, + unsigned int StepsDone, + unsigned int TotalSteps, + std::string HumanReadableAction); + }; + + class PackageManagerText : public PackageManager + { + public: + virtual bool StatusChanged(std::string PackageName, + unsigned int StepsDone, + unsigned int TotalSteps, + std::string HumanReadableAction); + }; + + +}; // namespace Progress +}; // namespace APT + +#endif diff --git a/apt-pkg/makefile b/apt-pkg/makefile index 59729faf5..a90131f80 100644 --- a/apt-pkg/makefile +++ b/apt-pkg/makefile @@ -43,7 +43,8 @@ SOURCE+= pkgcache.cc version.cc depcache.cc \ srcrecords.cc cachefile.cc versionmatch.cc policy.cc \ pkgsystem.cc indexfile.cc pkgcachegen.cc acquire-item.cc \ indexrecords.cc vendor.cc vendorlist.cc cdrom.cc indexcopy.cc \ - aptconfiguration.cc cachefilter.cc cacheset.cc edsp.cc + aptconfiguration.cc cachefilter.cc cacheset.cc edsp.cc \ + install-progress.cc upgrade.cc update.cc HEADERS+= algorithms.h depcache.h pkgcachegen.h cacheiterators.h \ orderlist.h sourcelist.h packagemanager.h tagfile.h \ init.h pkgcache.h version.h progress.h pkgrecords.h \ @@ -51,7 +52,8 @@ HEADERS+= algorithms.h depcache.h pkgcachegen.h cacheiterators.h \ clean.h srcrecords.h cachefile.h versionmatch.h policy.h \ pkgsystem.h indexfile.h metaindex.h indexrecords.h vendor.h \ vendorlist.h cdrom.h indexcopy.h aptconfiguration.h \ - cachefilter.h cacheset.h edsp.h + cachefilter.h cacheset.h edsp.h install-progress.h \ + upgrade.h update.h # Source code for the debian specific components # In theory the deb headers do not need to be exported.. diff --git a/apt-pkg/packagemanager.cc b/apt-pkg/packagemanager.cc index 8c0d2e855..3fdd9b637 100644 --- a/apt-pkg/packagemanager.cc +++ b/apt-pkg/packagemanager.cc @@ -26,7 +26,6 @@ #include <apt-pkg/sptr.h> #include <iostream> -#include <fcntl.h> #include <apti18n.h> /*}}}*/ @@ -1028,32 +1027,78 @@ pkgPackageManager::OrderResult pkgPackageManager::OrderInstall() return Completed; } +// PM::DoInstallPostFork - compat /*{{{*/ +// --------------------------------------------------------------------- + /*}}}*/ +#if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR >= 13) +pkgPackageManager::OrderResult +pkgPackageManager::DoInstallPostFork(int statusFd) +{ + APT::Progress::PackageManager *progress = new + APT::Progress::PackageManagerProgressFd(statusFd); + pkgPackageManager::OrderResult res = DoInstallPostFork(progress); + delete progress; + return res; +} /*}}}*/ // PM::DoInstallPostFork - Does install part that happens after the fork /*{{{*/ // --------------------------------------------------------------------- pkgPackageManager::OrderResult -pkgPackageManager::DoInstallPostFork(int statusFd) +pkgPackageManager::DoInstallPostFork(APT::Progress::PackageManager *progress) { - if(statusFd > 0) - // FIXME: use SetCloseExec here once it taught about throwing - // exceptions instead of doing _exit(100) on failure - fcntl(statusFd,F_SETFD,FD_CLOEXEC); - bool goResult = Go(statusFd); - if(goResult == false) - return Failed; - - return Res; + bool goResult = Go(progress); + if(goResult == false) + return Failed; + + return Res; }; +#else +pkgPackageManager::OrderResult +pkgPackageManager::DoInstallPostFork(int statusFd) +{ + bool goResult = Go(statusFd); + if(goResult == false) + return Failed; + + return Res; +} +#endif + /*}}}*/ +// PM::DoInstall - Does the installation /*{{{*/ +// --------------------------------------------------------------------- +/* compat */ +#if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR >= 13) +pkgPackageManager::OrderResult +pkgPackageManager::DoInstall(int statusFd) +{ + APT::Progress::PackageManager *progress = new + APT::Progress::PackageManagerProgressFd(statusFd); + OrderResult res = DoInstall(progress); + delete progress; + return res; + } +#else +pkgPackageManager::OrderResult pkgPackageManager::DoInstall(int statusFd) +{ + if(DoInstallPreFork() == Failed) + return Failed; + return DoInstallPostFork(statusFd); +} +#endif + /*}}}*/ // PM::DoInstall - Does the installation /*{{{*/ // --------------------------------------------------------------------- /* This uses the filenames in FileNames and the information in the DepCache to perform the installation of packages.*/ -pkgPackageManager::OrderResult pkgPackageManager::DoInstall(int statusFd) +#if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR >= 13) +pkgPackageManager::OrderResult +pkgPackageManager::DoInstall(APT::Progress::PackageManager *progress) { if(DoInstallPreFork() == Failed) return Failed; - return DoInstallPostFork(statusFd); + return DoInstallPostFork(progress); } +#endif /*}}}*/ diff --git a/apt-pkg/packagemanager.h b/apt-pkg/packagemanager.h index 1a6a9f01c..853b9bac8 100644 --- a/apt-pkg/packagemanager.h +++ b/apt-pkg/packagemanager.h @@ -23,7 +23,10 @@ #ifndef PKGLIB_PACKAGEMANAGER_H #define PKGLIB_PACKAGEMANAGER_H +#include <apt-pkg/macros.h> #include <apt-pkg/pkgcache.h> +#include <apt-pkg/install-progress.h> +#include <apt-pkg/init.h> #include <string> #include <iostream> @@ -39,6 +42,8 @@ class pkgDepCache; class pkgSourceList; class pkgOrderList; class pkgRecords; + + class pkgPackageManager : protected pkgCache::Namespace { public: @@ -83,7 +88,12 @@ class pkgPackageManager : protected pkgCache::Namespace virtual bool Install(PkgIterator /*Pkg*/,std::string /*File*/) {return false;}; virtual bool Configure(PkgIterator /*Pkg*/) {return false;}; virtual bool Remove(PkgIterator /*Pkg*/,bool /*Purge*/=false) {return false;}; +#if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR >= 13) + virtual bool Go(APT::Progress::PackageManager *progress) {return true;}; +#else virtual bool Go(int statusFd=-1) {return true;}; +#endif + virtual void Reset() {}; // the result of the operation @@ -96,7 +106,13 @@ class pkgPackageManager : protected pkgCache::Namespace pkgRecords *Recs); // Do the installation +#if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR >= 13) + OrderResult DoInstall(APT::Progress::PackageManager *progress); + // compat + __deprecated OrderResult DoInstall(int statusFd=-1); +#else OrderResult DoInstall(int statusFd=-1); +#endif // stuff that needs to be done before the fork() of a library that // uses apt @@ -104,9 +120,16 @@ class pkgPackageManager : protected pkgCache::Namespace Res = OrderInstall(); return Res; }; - +#if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR >= 13) // stuff that needs to be done after the fork + OrderResult DoInstallPostFork(APT::Progress::PackageManager *progress); + // compat + __deprecated OrderResult DoInstallPostFork(int statusFd=-1); +#else OrderResult DoInstallPostFork(int statusFd=-1); +#endif + + // ? bool FixMissing(); /** \brief returns all packages dpkg let disappear */ diff --git a/apt-pkg/update.cc b/apt-pkg/update.cc new file mode 100644 index 000000000..97be5490b --- /dev/null +++ b/apt-pkg/update.cc @@ -0,0 +1,126 @@ + +// Include Files /*{{{*/ +#include <config.h> + +#include <apt-pkg/algorithms.h> +#include <apt-pkg/update.h> +#include <apt-pkg/error.h> +#include <apt-pkg/configuration.h> +#include <apt-pkg/version.h> +#include <apt-pkg/sptr.h> +#include <apt-pkg/acquire-item.h> +#include <apt-pkg/edsp.h> +#include <apt-pkg/sourcelist.h> +#include <apt-pkg/fileutl.h> +#include <apt-pkg/progress.h> + +#include <sys/types.h> +#include <cstdlib> +#include <algorithm> +#include <iostream> +#include <stdio.h> + +#include <apti18n.h> + /*}}}*/ + +using namespace std; + +// ListUpdate - construct Fetcher and update the cache files /*{{{*/ +// --------------------------------------------------------------------- +/* This is a simple wrapper to update the cache. it will fetch stuff + * from the network (or any other sources defined in sources.list) + */ +bool ListUpdate(pkgAcquireStatus &Stat, + pkgSourceList &List, + int PulseInterval) +{ + pkgAcquire Fetcher; + if (Fetcher.Setup(&Stat, _config->FindDir("Dir::State::Lists")) == false) + return false; + + // Populate it with the source selection + if (List.GetIndexes(&Fetcher) == false) + return false; + + return AcquireUpdate(Fetcher, PulseInterval, true); +} + /*}}}*/ +// AcquireUpdate - take Fetcher and update the cache files /*{{{*/ +// --------------------------------------------------------------------- +/* This is a simple wrapper to update the cache with a provided acquire + * If you only need control over Status and the used SourcesList use + * ListUpdate method instead. + */ +bool AcquireUpdate(pkgAcquire &Fetcher, int const PulseInterval, + bool const RunUpdateScripts, bool const ListCleanup) +{ + // Run scripts + if (RunUpdateScripts == true) + RunScripts("APT::Update::Pre-Invoke"); + + pkgAcquire::RunResult res; + if(PulseInterval > 0) + res = Fetcher.Run(PulseInterval); + else + res = Fetcher.Run(); + + if (res == pkgAcquire::Failed) + return false; + + bool Failed = false; + bool TransientNetworkFailure = false; + for (pkgAcquire::ItemIterator I = Fetcher.ItemsBegin(); + I != Fetcher.ItemsEnd(); ++I) + { + if ((*I)->Status == pkgAcquire::Item::StatDone) + continue; + + (*I)->Finished(); + + ::URI uri((*I)->DescURI()); + uri.User.clear(); + uri.Password.clear(); + string descUri = string(uri); + _error->Warning(_("Failed to fetch %s %s\n"), descUri.c_str(), + (*I)->ErrorText.c_str()); + + if ((*I)->Status == pkgAcquire::Item::StatTransientNetworkError) + { + TransientNetworkFailure = true; + continue; + } + + Failed = true; + } + + // Clean out any old list files + // Keep "APT::Get::List-Cleanup" name for compatibility, but + // this is really a global option for the APT library now + if (!TransientNetworkFailure && !Failed && ListCleanup == true && + (_config->FindB("APT::Get::List-Cleanup",true) == true && + _config->FindB("APT::List-Cleanup",true) == true)) + { + if (Fetcher.Clean(_config->FindDir("Dir::State::lists")) == false || + Fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/") == false) + // something went wrong with the clean + return false; + } + + if (TransientNetworkFailure == true) + _error->Warning(_("Some index files failed to download. They have been ignored, or old ones used instead.")); + else if (Failed == true) + return _error->Error(_("Some index files failed to download. They have been ignored, or old ones used instead.")); + + + // Run the success scripts if all was fine + if (RunUpdateScripts == true) + { + if(!TransientNetworkFailure && !Failed) + RunScripts("APT::Update::Post-Invoke-Success"); + + // Run the other scripts + RunScripts("APT::Update::Post-Invoke"); + } + return true; +} + /*}}}*/ diff --git a/apt-pkg/update.h b/apt-pkg/update.h new file mode 100644 index 000000000..3835644de --- /dev/null +++ b/apt-pkg/update.h @@ -0,0 +1,21 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +/* ###################################################################### + + Update - ListUpdate releated code + + ##################################################################### */ + /*}}}*/ + +#ifndef PKGLIB_UPDATE_H +#define PKGLIB_UPDATE_H + +class pkgAcquireStatus; + + +bool ListUpdate(pkgAcquireStatus &progress, pkgSourceList &List, int PulseInterval=0); +bool AcquireUpdate(pkgAcquire &Fetcher, int const PulseInterval = 0, + bool const RunUpdateScripts = true, bool const ListCleanup = true); + + +#endif diff --git a/apt-pkg/upgrade.cc b/apt-pkg/upgrade.cc new file mode 100644 index 000000000..f06f6d40d --- /dev/null +++ b/apt-pkg/upgrade.cc @@ -0,0 +1,263 @@ + +// Include Files /*{{{*/ +#include <config.h> + +#include <apt-pkg/algorithms.h> +#include <apt-pkg/upgrade.h> +#include <apt-pkg/error.h> +#include <apt-pkg/configuration.h> +#include <apt-pkg/version.h> +#include <apt-pkg/sptr.h> +#include <apt-pkg/acquire-item.h> +#include <apt-pkg/edsp.h> +#include <apt-pkg/sourcelist.h> +#include <apt-pkg/fileutl.h> +#include <apt-pkg/progress.h> + +#include <sys/types.h> +#include <cstdlib> +#include <algorithm> +#include <iostream> +#include <stdio.h> + +#include <apti18n.h> + /*}}}*/ + +// DistUpgrade - Distribution upgrade /*{{{*/ +// --------------------------------------------------------------------- +/* This autoinstalls every package and then force installs every + pre-existing package. This creates the initial set of conditions which + most likely contain problems because too many things were installed. + + The problem resolver is used to resolve the problems. + */ +bool pkgDistUpgrade(pkgDepCache &Cache) +{ + std::string const solver = _config->Find("APT::Solver", "internal"); + if (solver != "internal") { + OpTextProgress Prog(*_config); + return EDSP::ResolveExternal(solver.c_str(), Cache, false, true, false, &Prog); + } + + pkgDepCache::ActionGroup group(Cache); + + /* Upgrade all installed packages first without autoinst to help the resolver + in versioned or-groups to upgrade the old solver instead of installing + a new one (if the old solver is not the first one [anymore]) */ + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + if (I->CurrentVer != 0) + Cache.MarkInstall(I, false, 0, false); + + /* Auto upgrade all installed packages, this provides the basis + for the installation */ + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + if (I->CurrentVer != 0) + Cache.MarkInstall(I, true, 0, false); + + /* Now, install each essential package which is not installed + (and not provided by another package in the same name group) */ + std::string essential = _config->Find("pkgCacheGen::Essential", "all"); + if (essential == "all") + { + for (pkgCache::GrpIterator G = Cache.GrpBegin(); G.end() == false; ++G) + { + bool isEssential = false; + bool instEssential = false; + for (pkgCache::PkgIterator P = G.PackageList(); P.end() == false; P = G.NextPkg(P)) + { + if ((P->Flags & pkgCache::Flag::Essential) != pkgCache::Flag::Essential) + continue; + isEssential = true; + if (Cache[P].Install() == true) + { + instEssential = true; + break; + } + } + if (isEssential == false || instEssential == true) + continue; + pkgCache::PkgIterator P = G.FindPreferredPkg(); + Cache.MarkInstall(P, true, 0, false); + } + } + else if (essential != "none") + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + if ((I->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential) + Cache.MarkInstall(I, true, 0, false); + + /* We do it again over all previously installed packages to force + conflict resolution on them all. */ + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + if (I->CurrentVer != 0) + Cache.MarkInstall(I, false, 0, false); + + pkgProblemResolver Fix(&Cache); + + // Hold back held packages. + if (_config->FindB("APT::Ignore-Hold",false) == false) + { + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + { + if (I->SelectedState == pkgCache::State::Hold) + { + Fix.Protect(I); + Cache.MarkKeep(I, false, false); + } + } + } + + return Fix.Resolve(); +} + /*}}}*/ +// AllUpgradeNoNewPackages - Upgrade but no removals or new pkgs /*{{{*/ +static bool pkgAllUpgradeNoNewPackages(pkgDepCache &Cache) +{ + std::string const solver = _config->Find("APT::Solver", "internal"); + if (solver != "internal") { + OpTextProgress Prog(*_config); + return EDSP::ResolveExternal(solver.c_str(), Cache, true, false, false, &Prog); + } + + pkgDepCache::ActionGroup group(Cache); + + pkgProblemResolver Fix(&Cache); + + if (Cache.BrokenCount() != 0) + return false; + + // Upgrade all installed packages + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + { + if (Cache[I].Install() == true) + Fix.Protect(I); + + if (_config->FindB("APT::Ignore-Hold",false) == false) + if (I->SelectedState == pkgCache::State::Hold) + continue; + + if (I->CurrentVer != 0 && Cache[I].InstallVer != 0) + Cache.MarkInstall(I, false, 0, false); + } + + return Fix.ResolveByKeep(); +} + /*}}}*/ +// AllUpgradeWithNewInstalls - Upgrade + install new packages as needed /*{{{*/ +// --------------------------------------------------------------------- +/* Right now the system must be consistent before this can be called. + * Upgrade as much as possible without deleting anything (useful for + * stable systems) + */ +static bool pkgAllUpgradeWithNewPackages(pkgDepCache &Cache) +{ + pkgDepCache::ActionGroup group(Cache); + + pkgProblemResolver Fix(&Cache); + + if (Cache.BrokenCount() != 0) + return false; + + // provide the initial set of stuff we want to upgrade by marking + // all upgradable packages for upgrade + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + { + if (I->CurrentVer != 0 && Cache[I].InstallVer != 0) + { + if (_config->FindB("APT::Ignore-Hold",false) == false) + if (I->SelectedState == pkgCache::State::Hold) + continue; + + Cache.MarkInstall(I, false, 0, false); + } + } + + // then let auto-install loose + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + if (Cache[I].Install()) + Cache.MarkInstall(I, true, 0, false); + + // ... but it may remove stuff, we we need to clean up afterwards again + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + if (Cache[I].Delete() == true) + Cache.MarkKeep(I, false, false); + + // resolve remaining issues via keep + return Fix.ResolveByKeep(); +} + /*}}}*/ +// AllUpgrade - Upgrade as many packages as possible /*{{{*/ +// --------------------------------------------------------------------- +/* Right now the system must be consistent before this can be called. + It also will not change packages marked for install, it only tries + to install packages not marked for install */ +bool pkgAllUpgrade(pkgDepCache &Cache) +{ + return pkgAllUpgradeNoNewPackages(Cache); +} + /*}}}*/ +// MinimizeUpgrade - Minimizes the set of packages to be upgraded /*{{{*/ +// --------------------------------------------------------------------- +/* This simply goes over the entire set of packages and tries to keep + each package marked for upgrade. If a conflict is generated then + the package is restored. */ +bool pkgMinimizeUpgrade(pkgDepCache &Cache) +{ + pkgDepCache::ActionGroup group(Cache); + + if (Cache.BrokenCount() != 0) + return false; + + // We loop for 10 tries to get the minimal set size. + bool Change = false; + unsigned int Count = 0; + do + { + Change = false; + for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I) + { + // Not interesting + if (Cache[I].Upgrade() == false || Cache[I].NewInstall() == true) + continue; + + // Keep it and see if that is OK + Cache.MarkKeep(I, false, false); + if (Cache.BrokenCount() != 0) + Cache.MarkInstall(I, false, 0, false); + else + { + // If keep didnt actually do anything then there was no change.. + if (Cache[I].Upgrade() == false) + Change = true; + } + } + ++Count; + } + while (Change == true && Count < 10); + + if (Cache.BrokenCount() != 0) + return _error->Error("Internal Error in pkgMinimizeUpgrade"); + + return true; +} + /*}}}*/ +// APT::Upgrade::Upgrade - Upgrade using a specific strategy /*{{{*/ +bool APT::Upgrade::Upgrade(pkgDepCache &Cache, int mode) +{ + if (mode == 0) + { + return pkgDistUpgrade(Cache); + } + else if ((mode & ~FORBID_REMOVE_PACKAGES) == 0) + { + return pkgAllUpgradeWithNewPackages(Cache); + } + else if ((mode & ~(FORBID_REMOVE_PACKAGES|FORBID_INSTALL_NEW_PACKAGES)) == 0) + { + return pkgAllUpgradeNoNewPackages(Cache); + } + else + _error->Error("pkgAllUpgrade called with unsupported mode %i", mode); + + return false; +} + /*}}}*/ diff --git a/apt-pkg/upgrade.h b/apt-pkg/upgrade.h new file mode 100644 index 000000000..c4973472f --- /dev/null +++ b/apt-pkg/upgrade.h @@ -0,0 +1,30 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +/* ###################################################################### + + Upgrade - Upgrade/DistUpgrade releated code + + ##################################################################### */ + /*}}}*/ + +#ifndef PKGLIB_UPGRADE_H +#define PKGLIB_UPGRADE_H + +namespace APT { + namespace Upgrade { + // FIXME: make this "enum class UpgradeMode {" once we enable c++11 + enum UpgradeMode { + FORBID_REMOVE_PACKAGES = 1, + FORBID_INSTALL_NEW_PACKAGES = 2, + }; + bool Upgrade(pkgDepCache &Cache, int UpgradeMode); + } +} + +// please use APT::Upgrade::Upgrade() instead +bool pkgDistUpgrade(pkgDepCache &Cache); +bool pkgAllUpgrade(pkgDepCache &Cache); +bool pkgMinimizeUpgrade(pkgDepCache &Cache); + + +#endif |