* 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 <rbtcollins@hotmail.com>
* 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 Robert Collins <rbtcollins@hotmail.com>
+
+ * 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 <rbtcollins@hotmail.com>
+
+ * 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 <maxb@ukf.net>
* .cvsignore: Create, to ignore configure, aclocal.m4, Makefile.in and
}
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);
// 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
/*
* Copyright (c) 2000, Red Hat, Inc.
+ * Copyright (c) 2003, Robert Collins <rbtcollins@hotmail.com>
*
* 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
* A copy of the GNU General Public License can be found at
* http://www.gnu.org/
*
- * Written by DJ Delorie <dj@cygnus.com>
+ * Originally Written by DJ Delorie <dj@cygnus.com>
*
*/
"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",
};
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());
* 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...");
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++;
/* 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)",
/* 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",
}
/* 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 ()))
{
}
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);
log (LOG_PLAIN,
String("Unable to install file ") +
prefixURL + prefixPath + fn);
- errors++;
+ ++errors;
}
else
//switch Win32::OS
source, MAX_PATH);
if (!len || len > MAX_PATH)
{
- log_ror_failure (fn, errors);
+ replaceOnRebootFailed(fn);
}
else
{
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;
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++;
}
total_bytes_sofar += package_bytes;
}
-
progress (0);
int df = diskfull (get_root_dir ().cstr_oneuse());
{
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 */
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);
}
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;
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
{
try {
int e = 0;
- e += replace_one (pkg);
+ e += myInstaller.replaceOne (pkg);
if (e)
errors++;
}
#include "cygpackage.h"
#include "package_db.h"
#include "package_meta.h"
+#include "Exception.h"
using namespace std;
PackageDBActions
packagedb::task =
PackageDB_Install;
+std::vector <packagemeta *>
+packagedb::dependencyOrderedPackages;
+
+#include "LogSingleton.h"
+#include <stack>
+
+class
+ConnectedLoopFinder
+{
+ public:
+ ConnectedLoopFinder();
+ void doIt();
+ packagedb db;
+ size_t visited;
+ std::vector<size_t> visitOrder;
+ size_t visit (size_t const nodeToVisit);
+ std::stack<size_t> 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 <vector <PackageSpecification *> *>::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 <PackageSpecification *>::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<packagemeta *>::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();
+}
PackageDB_Download
} PackageDBActions;
+
+class packagedb;
+typedef std::vector <packagemeta *>::iterator PackageDBConnectedIterator;
+
/*TODO: add mutexs */
class packagedb
{
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 */
static PackageDBActions task;
private:
static int installeddbread; /* do we have to reread this */
+ friend class ConnectedLoopFinder;
+ static std::vector <packagemeta *> dependencyOrderedPackages;
};
#endif /* _PACKAGE_DB_H_ */
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 <Script> &scripts() { scripts_.clear(); return scripts_;}
+ private:
+ std::vector <Script> scripts_;
};
static _defaultversion defaultversion;
return changed;
}
+void
+packageversion::addScript(Script const &aScript)
+{
+ return data->addScript (aScript);
+}
+
+std::vector <Script> &
+packageversion::scripts()
+{
+ return data->scripts();
+}
+
/* the parent data class */
_packageversion::_packageversion ():picked (false), references (0)
{
return (picked || sourcePackage().picked());
}
+
+void
+_packageversion::addScript(Script const &aScript)
+{
+ scripts().push_back(aScript);
+}
+
+std::vector <Script> &
+_packageversion::scripts()
+{
+ return scripts_;
+}
#include "String++.h"
#include "PackageSpecification.h"
#include "PackageTrust.h"
+#include "script.h"
#include <vector>
typedef enum
/* ensure that the depends clause is satisfied */
int set_requirements (trusts deftrust = TRUST_CURR, size_t depth = 0);
+ void addScript(Script const &);
+ std::vector <Script> &scripts();
+
private:
_packageversion *data; /* Invariant: * data is always valid */
};
static package_meta * scan_package (io_stream *);
*/
size_t references;
+ virtual void addScript(Script const &);
+ virtual std::vector <Script> &scripts();
protected:
/* only meaningful for binary packages */
PackageSpecification _sourcePackage;
packageversion sourceVersion;
+ std::vector <Script> scripts_;
};
#endif /* _PACKAGE_VERSION_H_ */
#include "mount.h"
#include "script.h"
#include "FindVisitor.h"
+#include "package_db.h"
+#include "package_meta.h"
class RunFindVisitor : public FindVisitor
{
next_dialog = 0;
init_run_script ();
SetCurrentDirectory (get_root_dir ().cstr_oneuse());
+ packagedb db;
+ PackageDBConnectedIterator i = db.connectedBegin ();
+ while (i != db.connectedEnd ())
+ {
+ packagemeta & pkg = **i;
+ if (pkg.installed)
+ for (std::vector<Script>::iterator script=pkg.installed.scripts().begin(); script != pkg.installed.scripts().end(); ++script)
+ run_script ("/etc/postinstall/", script->baseName());
+ ++i;
+ }
RunFindVisitor myVisitor;
String postinst = cygpath ("/etc/postinstall");
Find (postinst).accept (myVisitor);
+
}
#include "filemanip.h"
#include "mount.h"
#include "io_stream.h"
+#include "script.h"
static String sh = String();
static const char *cmd = 0;
run_script (dir.cstr_oneuse(), (fname + ".bat").cstr_oneuse());
}
+bool
+Script::isAScript (String const &file)
+{
+ /* file may be /etc/postinstall or etc/postinstall */
+ if (file.casecompare ("/etc/postinstall/", 17) && file.casecompare ("etc/postinstall/", 16))
+ return false;
+ if (file.cstr_oneuse()[file.size() - 1] == '/')
+ return false;
+ return true;
+}
+
+Script::Script (String const &fileName) : scriptName (fileName)
+{
+
+}
+
+String
+Script::baseName()const
+{
+ String result = scriptName;
+ while (result.find ('/'))
+ result = result.substr(result.find ('/'));
+ return result;
+}
/* Run the scripts fname.sh and fname.bat, found in dir. */
void try_run_script (String const &dir, String const &fname);
+class Script {
+ public:
+ static bool isAScript (String const &file);
+ Script (String const &fileName);
+ String baseName()const;
+ private:
+ String scriptName;
+};
+
#endif /* SCRIPT_H */