From ad646f43c093b23e3cc1edc19c4852d14e68926a Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Mon, 17 Mar 2003 22:23:33 +0000 Subject: [PATCH] 2003-03-16 Robert Collins * package_db.h (PackageDBConnectedIterator): Typedef for connected loop detection collection iterator. * package_db.cc (ConnectedLoopDetector): An implementation of R.E. Tarjans strongly connected set visitor algorithm. * postinstall.cc (do_postinstall): Use the new iterator for visiting postinstall scripts. 2003-03-16 Robert Collins * install.cc: Introduce Installer class. (init_dialog): Rename to Installer::initDialog. (progress): Rename to Installer::progress. (standard_dirs): Rename to Installer::StandardDirs. (uninstall_one): Rename to Installer::uninstallOne. (replace_one): Rename to Installer::replaceOne. (log_ror_failure): Rename to Installer::replaceOnRebootFailed. (log_ror_success): Rename to Installer::replaceOnRebootSucceeded. (install_one_source): Rename to Installer::installOneSource. Note script files as they are installed. * package_version.cc (packageversion::addScript): Implement. (packageversion::scripts): Implement. * package_version.h (packageversion::addScript): Record the presence of a script. * script.h (Script): New class to track scripts. * postinstall.cc (do_postinstall): Iterate through the package listed scripts before searching for scripts. * String++.cc (String::substr): Second argument needed to be signed. * String++.h (String::substr): Second argument needed to be signed. --- ChangeLog | 26 +++++++++ String++.cc | 2 +- String++.h | 2 +- install.cc | 105 +++++++++++++++++---------------- package_db.cc | 141 +++++++++++++++++++++++++++++++++++++++++++++ package_db.h | 8 +++ package_version.cc | 28 +++++++++ package_version.h | 7 +++ postinstall.cc | 13 +++++ script.cc | 25 ++++++++ script.h | 9 +++ 11 files changed, 315 insertions(+), 51 deletions(-) diff --git a/ChangeLog b/ChangeLog index 351da72a..bb094599 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,29 @@ +2003-03-16 Robert Collins + + * package_db.h (PackageDBConnectedIterator): Typedef for connected loop detection collection iterator. + * package_db.cc (ConnectedLoopDetector): An implementation of R.E. Tarjans strongly connected set visitor algorithm. + * postinstall.cc (do_postinstall): Use the new iterator for visiting postinstall scripts. + +2003-03-16 Robert Collins + + * install.cc: Introduce Installer class. + (init_dialog): Rename to Installer::initDialog. + (progress): Rename to Installer::progress. + (standard_dirs): Rename to Installer::StandardDirs. + (uninstall_one): Rename to Installer::uninstallOne. + (replace_one): Rename to Installer::replaceOne. + (log_ror_failure): Rename to Installer::replaceOnRebootFailed. + (log_ror_success): Rename to Installer::replaceOnRebootSucceeded. + (install_one_source): Rename to Installer::installOneSource. + Note script files as they are installed. + * package_version.cc (packageversion::addScript): Implement. + (packageversion::scripts): Implement. + * package_version.h (packageversion::addScript): Record the presence of a script. + * script.h (Script): New class to track scripts. + * postinstall.cc (do_postinstall): Iterate through the package listed scripts before searching for scripts. + * String++.cc (String::substr): Second argument needed to be signed. + * String++.h (String::substr): Second argument needed to be signed. + 2003-03-16 Max Bowsher * .cvsignore: Create, to ignore configure, aclocal.m4, Makefile.in and diff --git a/String++.cc b/String++.cc index 4d089a86..56ca450e 100644 --- a/String++.cc +++ b/String++.cc @@ -124,7 +124,7 @@ String::find(char aChar) const } String -String::substr(size_t start, size_t len) const +String::substr(size_t start, int len) const { // Adapt the C++ string class return string(cstr_oneuse()).substr(start, len); diff --git a/String++.h b/String++.h index bd8f6f9a..81c6561e 100644 --- a/String++.h +++ b/String++.h @@ -43,7 +43,7 @@ public: // pretends to be const !! inline size_t size() const; // number of characters (!= storage size). size_t find (char) const; - String substr (size_t start = 0, size_t len = -1) const; + String substr (size_t start = 0, int len = -1) const; // operator == and != can be done if/when we have a 'casesensitive' flag to // the constructors // - means this sorts to the left of the parameter diff --git a/install.cc b/install.cc index 686c24b4..def8b207 100644 --- a/install.cc +++ b/install.cc @@ -1,5 +1,6 @@ /* * Copyright (c) 2000, Red Hat, Inc. + * Copyright (c) 2003, Robert Collins * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -9,7 +10,7 @@ * A copy of the GNU General Public License can be found at * http://www.gnu.org/ * - * Written by DJ Delorie + * Originally Written by DJ Delorie * */ @@ -79,28 +80,44 @@ static BoolOption NoReplaceOnReboot (false, 'r', "no-replaceonreboot", "Disable replacing in-use files on next " "reboot."); -static void -init_dialog () +class Installer +{ + public: + static const char *StandardDirs[]; + Installer(); + void initDialog(); + void progress (int bytes); + void uninstallOne (packagemeta &); + int replaceOne (packagemeta &); + void replaceOnRebootFailed (String const &fn); + void replaceOnRebootSucceeded (String const &fn, bool &rebootneeded); + int installOneSource (packagemeta &, packagesource &, String const &, String const &, package_type_t); + int errors; +}; + +Installer::Installer() : errors(0) +{ +} + +void +Installer::initDialog() { Progress.SetText2 (""); Progress.SetText3 (""); } -static void -progress (int bytes) +void +Installer::progress (int bytes) { if (package_bytes > 0) - { Progress.SetBar1 (bytes, package_bytes); - } if (total_bytes > 0) - { Progress.SetBar2 (total_bytes_sofar + bytes, total_bytes); - } } -static const char *standard_dirs[] = { +const char * +Installer::StandardDirs[] = { "/bin", "/etc", "/lib", @@ -120,16 +137,11 @@ static const char *standard_dirs[] = { }; static int num_installs, num_replacements, num_uninstalls; -static void uninstall_one (packagemeta &); -static int replace_one (packagemeta &); -static int install_one_source (packagemeta &, packagesource &, String const &, - String const &, package_type_t); static void md5_one (const packagesource& source); static bool rebootneeded; -/* FIXME: upgrades should be a method too */ -static void -uninstall_one (packagemeta & pkgm) +void +Installer::uninstallOne (packagemeta & pkgm) { Progress.SetText1 ("Uninstalling..."); Progress.SetText2 (pkgm.name.cstr_oneuse()); @@ -144,8 +156,8 @@ uninstall_one (packagemeta & pkgm) * ASSUMPTIONS: pkgm is installed. * pkgm has a desired package. */ -static int -replace_one (packagemeta & pkg) +int +Installer::replaceOne (packagemeta &pkg) { int errors = 0; Progress.SetText1 ("Replacing..."); @@ -155,7 +167,7 @@ replace_one (packagemeta & pkg) pkg.uninstall (); errors += - install_one_source (pkg, *pkg.desired.source(), "cygfile://","/", package_binary); + installOneSource (pkg, *pkg.desired.source(), "cygfile://","/", package_binary); if (!errors) pkg.installed = pkg.desired; num_replacements++; @@ -164,8 +176,8 @@ replace_one (packagemeta & pkg) /* log failed scheduling of replace-on-reboot of a given file. */ /* also increment errors. */ -static void -log_ror_failure (String const &fn, int &errors) +void +Installer::replaceOnRebootFailed (String const &fn) { log (LOG_TIMESTAMP, "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)", @@ -177,8 +189,8 @@ log_ror_failure (String const &fn, int &errors) /* log successful scheduling of replace-on-reboot of a given file. */ /* also set rebootneeded. */ -static void -log_ror_success (String const &fn, bool &rebootneeded) +void +Installer::replaceOnRebootSucceeded (String const &fn, bool &rebootneeded) { log (LOG_TIMESTAMP, "Scheduled reboot replacement of file %s with %s", @@ -188,11 +200,10 @@ log_ror_success (String const &fn, bool &rebootneeded) } /* install one source at a given prefix. */ -static int -install_one_source (packagemeta & pkgm, packagesource & source, +int +Installer::installOneSource (packagemeta & pkgm, packagesource & source, String const &prefixURL, String const &prefixPath, package_type_t type) { - int errors = 0; Progress.SetText2 (source.Base ()); if (!source.Cached () || !io_stream::exists (source.Cached ())) { @@ -243,6 +254,8 @@ install_one_source (packagemeta & pkgm, packagesource & source, } String canonicalfn = prefixPath + fn; + if (Script::isAScript (fn)) + pkgm.desired.addScript (Script (canonicalfn)); Progress.SetText3 (canonicalfn.cstr_oneuse()); log (LOG_BABBLE, String("Installing file ") + prefixURL + prefixPath + fn); @@ -261,7 +274,7 @@ install_one_source (packagemeta & pkgm, packagesource & source, log (LOG_PLAIN, String("Unable to install file ") + prefixURL + prefixPath + fn); - errors++; + ++errors; } else //switch Win32::OS @@ -277,7 +290,7 @@ install_one_source (packagemeta & pkgm, packagesource & source, source, MAX_PATH); if (!len || len > MAX_PATH) { - log_ror_failure (fn, errors); + replaceOnRebootFailed(fn); } else { @@ -287,20 +300,14 @@ install_one_source (packagemeta & pkgm, packagesource & source, fn).cstr_oneuse(), dest, MAX_PATH); if (!len || len > MAX_PATH) - { - log_ror_failure (fn, errors); - } + replaceOnRebootFailed (fn); else /* trigger a replacement on reboot */ if (!WritePrivateProfileString ("rename", dest, source, "WININIT.INI")) - { - log_ror_failure (fn, errors); - } + replaceOnRebootFailed (fn); else - { - log_ror_success (fn, rebootneeded); - } + replaceOnRebootSucceeded (fn, rebootneeded); } } break; @@ -316,17 +323,16 @@ install_one_source (packagemeta & pkgm, packagesource & source, MOVEFILE_DELAY_UNTIL_REBOOT | MOVEFILE_REPLACE_EXISTING)) { - log_ror_failure (fn, errors); + replaceOnRebootFailed (fn); } else { - log_ror_success (fn, rebootneeded); + replaceOnRebootSucceeded (fn, rebootneeded); } break; } } } - progress (tmp->tell ()); num_installs++; } @@ -335,7 +341,6 @@ install_one_source (packagemeta & pkgm, packagesource & source, total_bytes_sofar += package_bytes; } - progress (0); int df = diskfull (get_root_dir ().cstr_oneuse()); @@ -353,17 +358,18 @@ install_one (packagemeta & pkg) { int errors = 0; + Installer myInstaller; if (pkg.installed != pkg.desired && pkg.desired.picked()) { errors += - install_one_source (pkg, *pkg.desired.source(), "cygfile://","/", + myInstaller.installOneSource (pkg, *pkg.desired.source(), "cygfile://","/", package_binary); if (!errors) pkg.installed = pkg.desired; } if (pkg.desired.sourcePackage().picked()) errors += - install_one_source (pkg, *pkg.desired.sourcePackage().source(), "cygfile://","/usr/src/", + myInstaller.installOneSource (pkg, *pkg.desired.sourcePackage().source(), "cygfile://","/usr/src/", package_source); /* FIXME: make a upgrade method and reinstate this */ @@ -453,9 +459,9 @@ do_install_thread (HINSTANCE h, HWND owner) io_stream::mkpath_p (PATH_TO_DIR, String ("file://") + get_root_dir ()); - for (i = 0; standard_dirs[i]; i++) + for (i = 0; Installer::StandardDirs[i]; i++) { - String p = cygpath (standard_dirs[i]); + String p = cygpath (Installer::StandardDirs[i]); if (p.size()) io_stream::mkpath_p (PATH_TO_DIR, String ("file://") + p); } @@ -464,7 +470,8 @@ do_install_thread (HINSTANCE h, HWND owner) io_stream *utmp = io_stream::open ("cygfile:///var/run/utmp", "wb"); delete utmp; - init_dialog (); + Installer myInstaller; + myInstaller.initDialog(); total_bytes = 0; total_bytes_sofar = 0; @@ -529,7 +536,7 @@ do_install_thread (HINSTANCE h, HWND owner) packagemeta & pkg = **i; if (pkg.installed && (!pkg.desired || (pkg.desired != pkg.installed && pkg.desired.picked ()))) - uninstall_one (pkg); + myInstaller.uninstallOne (pkg); } /* now in-place binary upgrades/reinstalls, as these may remove fils @@ -544,7 +551,7 @@ do_install_thread (HINSTANCE h, HWND owner) { try { int e = 0; - e += replace_one (pkg); + e += myInstaller.replaceOne (pkg); if (e) errors++; } diff --git a/package_db.cc b/package_db.cc index 766954a7..773fe6f5 100644 --- a/package_db.cc +++ b/package_db.cc @@ -39,6 +39,7 @@ static const char *cvsid = #include "cygpackage.h" #include "package_db.h" #include "package_meta.h" +#include "Exception.h" using namespace std; @@ -207,3 +208,143 @@ vector packagedb::sourcePackages; PackageDBActions packagedb::task = PackageDB_Install; +std::vector +packagedb::dependencyOrderedPackages; + +#include "LogSingleton.h" +#include + +class +ConnectedLoopFinder +{ + public: + ConnectedLoopFinder(); + void doIt(); + packagedb db; + size_t visited; + std::vector visitOrder; + size_t visit (size_t const nodeToVisit); + std::stack nodesInStronglyConnectedComponent; +}; + +ConnectedLoopFinder::ConnectedLoopFinder() : visited(0) +{ + for (size_t counter = 0; counter < db.packages.size(); ++counter) + visitOrder.push_back(0); +} + +void +ConnectedLoopFinder::doIt() +{ + /* XXX this could be done useing a class to hold both the visitedInIteration and the package + * meta reference. Then we could use a range, not an int loop. + */ + for (size_t i = 0; i < db.packages.size(); ++i) + { + packagemeta &pkg (*db.packages[i]); + if (pkg.installed && ! visitOrder[i]) + visit (i); + } + log (LOG_PLAIN) << "Visited: " << visited << " nodes out of " << db.packages.size() << "." << endLog; +} + +static bool +checkForInstalled (PackageSpecification *spec) +{ + packagedb db; + packagemeta *required = db.findBinary (*spec); + if (!required) + return false; + if (spec->satisfies (required->installed) + && required->desired == required->installed ) + /* done, found a satisfactory installed version that will remain + installed */ + return true; + return false; +} + +size_t +ConnectedLoopFinder::visit(size_t const nodeToVisit) +{ + if (!db.packages[nodeToVisit]->installed) + /* Can't visit this node, and it is not less than any visted node */ + return db.packages.size() + 1; + ++visited; + visitOrder[nodeToVisit] = visited; + + size_t minimumVisitId = visited; + nodesInStronglyConnectedComponent.push(nodeToVisit); + + vector *>::iterator dp = db.packages[nodeToVisit]->installed.depends ()->begin(); + /* walk through each and clause (a link in the graph) */ + while (dp != db.packages[nodeToVisit]->installed.depends ()->end()) + { + /* check each or clause for an installed match */ + vector ::iterator i = + find_if ((*dp)->begin(), (*dp)->end(), checkForInstalled); + if (i != (*dp)->end()) + { + /* we found an installed ok package */ + /* visit it if needed */ + /* UGLY. Need to refactor. iterators in the outer would help as we could simply + * vist the iterator + */ + size_t nodeJustVisited = 0; + while (nodeJustVisited < db.packages.size() && db.packages[nodeJustVisited]->name.casecompare((*i)->packageName())) + ++nodeJustVisited; + if (nodeJustVisited == db.packages.size()) + log (LOG_PLAIN) << "Search for package '" << (*i)->packageName() << "' failed." << endLog; + else + { + if (visitOrder[nodeJustVisited]) + minimumVisitId = std::min (minimumVisitId, visitOrder[nodeJustVisited]); + else + minimumVisitId = std::min (minimumVisitId, visit (nodeJustVisited)); + } + /* next and clause */ + ++dp; + continue; + } + /* not installed or not available we ignore */ + ++dp; + } + + if (minimumVisitId == visitOrder[nodeToVisit]) + { + size_t popped; + do { + popped = nodesInStronglyConnectedComponent.top(); + nodesInStronglyConnectedComponent.pop(); + db.dependencyOrderedPackages.push_back(db.packages[popped]); + /* mark as displayed in a connected component */ + visitOrder[popped] = db.packages.size() + 2; + } while (popped != nodeToVisit); + } + + return minimumVisitId; +} + +PackageDBConnectedIterator +packagedb::connectedBegin() +{ + if (!dependencyOrderedPackages.size()) + { + ConnectedLoopFinder doMe; + doMe.doIt(); + log(LOG_PLAIN) << "Dependency ordered install:" << endLog; + for (std::vector::iterator i = dependencyOrderedPackages.begin(); + i != dependencyOrderedPackages.end(); ++i) + { + packagemeta &pkg (**i); + log(LOG_PLAIN) << pkg.name << endLog; + } + } + + return dependencyOrderedPackages.begin(); +} + +PackageDBConnectedIterator +packagedb::connectedEnd() +{ + return dependencyOrderedPackages.end(); +} diff --git a/package_db.h b/package_db.h index 54a3bf70..25e26e1e 100644 --- a/package_db.h +++ b/package_db.h @@ -30,6 +30,10 @@ typedef enum { PackageDB_Download } PackageDBActions; + +class packagedb; +typedef std::vector ::iterator PackageDBConnectedIterator; + /*TODO: add mutexs */ class packagedb { @@ -39,6 +43,8 @@ public: int flush (); packagemeta * findBinary (PackageSpecification const &) const; packagemeta * findSource (PackageSpecification const &) const; + PackageDBConnectedIterator connectedBegin(); + PackageDBConnectedIterator connectedEnd(); /* all seen binary packages */ static std::vector < packagemeta *> packages; /* all seen source packages */ @@ -49,6 +55,8 @@ public: static PackageDBActions task; private: static int installeddbread; /* do we have to reread this */ + friend class ConnectedLoopFinder; + static std::vector dependencyOrderedPackages; }; #endif /* _PACKAGE_DB_H_ */ diff --git a/package_version.cc b/package_version.cc index 63a71a60..086b82c0 100644 --- a/package_version.cc +++ b/package_version.cc @@ -57,6 +57,10 @@ public: void set_ldesc (String const &) {} void uninstall (){} void pick(bool const &newValue){/* Ignore attempts to pick this!. Throw an exception here if you want to detect such attemtps instead */} + virtual void addScript(Script const &) {} + virtual std::vector