Author: mvidner
Date: Thu Feb 7 11:09:57 2008
New Revision: 44225
URL: http://svn.opensuse.org/viewcvs/yast?rev=44225&view=rev
Log:
Make SCR registration lazy: to find .foo, open only foo.scr.
If that fails, only then scan all *.scr. (fate#302975)
Modified:
trunk/core/libycp/src/YCPPath.cc
trunk/core/libycp/src/include/ycp/YCPPath.h
trunk/core/package/yast2-core.changes
trunk/core/scr/src/ScriptingAgent.cc
trunk/core/scr/src/ScriptingAgent.h
Modified: trunk/core/libycp/src/YCPPath.cc
URL: http://svn.opensuse.org/viewcvs/yast/trunk/core/libycp/src/YCPPath.cc?rev=44225&r1=44224&r2=44225&view=diff
==============================================================================
--- trunk/core/libycp/src/YCPPath.cc (original)
+++ trunk/core/libycp/src/YCPPath.cc Thu Feb 7 11:09:57 2008
@@ -148,6 +148,15 @@
return postfix;
}
+YCPPath
+YCPPathRep::prefix(long index) const
+{
+ YCPPath ret;
+ for (int i = 0; i < index; ++i)
+ ret->append(components[i]);
+ return ret;
+}
+
string
YCPPathRep::component_str(long index) const
Modified: trunk/core/libycp/src/include/ycp/YCPPath.h
URL: http://svn.opensuse.org/viewcvs/yast/trunk/core/libycp/src/include/ycp/YCPPath.h?rev=44225&r1=44224&r2=44225&view=diff
==============================================================================
--- trunk/core/libycp/src/include/ycp/YCPPath.h (original)
+++ trunk/core/libycp/src/include/ycp/YCPPath.h Thu Feb 7 11:09:57 2008
@@ -119,11 +119,19 @@
/**
* Returns a postfix of the path. You must check, that the
* index you give is 0 <= i < @ref #length.
- * @return Postfix path. destroy it after use.
+ * @return Postfix path.
*/
YCPPath at(long index) const;
/**
+ * Returns a prefix of the path. You must check, that the
+ * index you give is 0 <= i < @ref #length.
+ * Informally, p == p.prefix(i) + p.at(i);
+ * @return Prefix path.
+ */
+ YCPPath prefix(long index) const;
+
+ /**
* Returns one component of the path as string. No error check
* is done for index. You must check yourself that 0 < index < @ref #length.
*/
Modified: trunk/core/package/yast2-core.changes
URL: http://svn.opensuse.org/viewcvs/yast/trunk/core/package/yast2-core.changes?rev=44225&r1=44224&r2=44225&view=diff
==============================================================================
--- trunk/core/package/yast2-core.changes (original)
+++ trunk/core/package/yast2-core.changes Thu Feb 7 11:09:57 2008
@@ -1,6 +1,8 @@
-------------------------------------------------------------------
-Thu Feb 7 10:35:35 CET 2008 - mvidner@suse.cz
+Thu Feb 7 11:09:39 CET 2008 - mvidner@suse.cz
+- Make SCR registration lazy: to find .foo, open only foo.scr.
+ If that fails, only then scan all *.scr. (fate#302975)
- Test ports using bash's /dev/tcp instead of netcat (bnc#264309).
-------------------------------------------------------------------
Modified: trunk/core/scr/src/ScriptingAgent.cc
URL: http://svn.opensuse.org/viewcvs/yast/trunk/core/scr/src/ScriptingAgent.cc?rev=44225&r1=44224&r2=44225&view=diff
==============================================================================
--- trunk/core/scr/src/ScriptingAgent.cc (original)
+++ trunk/core/scr/src/ScriptingAgent.cc Thu Feb 7 11:09:57 2008
@@ -1,5 +1,3 @@
-
-
/*
* ScriptingAgent.cc
*
@@ -28,24 +26,59 @@
ScriptingAgent::ScriptingAgent ()
+ : done_sweep (false)
{
- RegisterNewAgents ();
+ InitRegDirs ();
+ // to test the old behavior
+ if (getenv ("Y2SCRSWEEP"))
+ Sweep ();
}
ScriptingAgent::ScriptingAgent (const string& file)
+ : done_sweep (false)
{
+ InitRegDirs ();
y2debug( "Scripting agent using only SCR %s", file.c_str () );
parseSingleConfigFile (file);
}
+void
+ScriptingAgent::InitRegDirs ()
+{
+ for (int level = 0; level < Y2PathSearch::numberOfComponentLevels ();
+ level++)
+ {
+ RegistrationDir rd;
+ rd.last_changed = 0; // very old
+ rd.name = Y2PathSearch::searchPath (Y2PathSearch::GENERIC, level) + "/scrconf";
+ y2debug( "Scripting agent searching SCRs in %s", rd.name.c_str() );
+// parseConfigFiles (rd.name);
+
+ struct stat st;
+ if (stat (rd.name.c_str(), &st) != 0) {
+ y2debug ("Can't read dir %s: %m", rd.name.c_str ());
+ }
+ else {
+ y2milestone ("Agent registration: %s last changed at %s",
+ rd.name.c_str(), ctime (&rd.last_changed));
+ registration_dirs.push_back (rd);
+ }
+ }
+}
+
ScriptingAgent::~ScriptingAgent ()
{
UnregisterAllAgents ();
}
+bool less_than_inodes (const pair& a,
+ const pair& b)
+{
+ return a.first < b.first;
+}
void
ScriptingAgent::parseConfigFiles (const string &directory)
@@ -60,22 +93,35 @@
}
struct dirent *entry;
+ // Access the files in inode order, hopefully this reduces disk seeks
+ // Use a list and sort it (or not).
+ // typedef map sorted_names_t;
+ typedef list > sorted_names_t;
+ sorted_names_t sorted_names;
while ((entry = readdir (dir)))
{
- if (!strcmp (entry->d_name, ".") || !(strcmp (entry->d_name, "..")))
- continue;
-
+ char *n = entry->d_name;
// Read only *.scr files. For example TRANS.TBL makes problems
- if (strlen (entry->d_name) <= 4 ||
- strcmp (entry->d_name + strlen (entry->d_name) - 4, ".scr"))
+ if (strlen (n) <= 4 ||
+ strcmp (n + strlen (n) - 4, ".scr"))
continue;
- const string filename = directory + "/" + entry->d_name;
-
- parseSingleConfigFile (filename);
+ // sorted_names[entry->d_ino] = n;
+ sorted_names.push_back (make_pair (entry->d_ino, string(n)));
}
closedir (dir);
+
+ // TODO cache
+ if (!getenv ("Y2SCRNOSORT"))
+ sorted_names.sort(less_than_inodes);
+
+ sorted_names_t::iterator i = sorted_names.begin (), e = sorted_names.end ();
+ for (; i != e; ++i)
+ {
+ const string filename = directory + "/" + i->second;
+ parseSingleConfigFile (filename);
+ }
}
@@ -115,10 +161,16 @@
SubAgents::iterator agent = findByPath (path);
if (agent != agents.end ())
{
- y2debug ("Ignoring re-registration of path '%s'", path->toString ().c_str ());
+ // TODO promote more debugs to errors or warnings
+ y2warning ("Ignoring re-registration of path '%s'", path->toString ().c_str ());
+ // possible alternative: do not ignore
+ // - ok if the agent was not used yet (not mounted yet)
+ // - umount if mounted??
}
else
{
+ // posible optimization:
+ // dont reparse the file
RegisterAgent (path, YCPString (filename));
}
break;
@@ -258,21 +310,10 @@
agents.erase (agent);
}
+ // insert into ordered vector
agents.insert (std::lower_bound (agents.begin (), agents.end (), path),
new SCRSubAgent (path, value));
-#if 0
- // only use for testing! insecure!
- FILE* fout = fopen ("/tmp/scr-agents.txt", "w");
- for (SubAgents::const_iterator agent = agents.begin ();
- agent != agents.end (); ++agent)
- {
- fprintf (fout, "%ld %s\n", (*agent)->get_path ()->length (),
- (*agent)->get_path ()->toString ().c_str ());
- }
- fclose (fout);
-#endif
-
return YCPBoolean (true);
}
@@ -280,6 +321,7 @@
YCPBoolean
ScriptingAgent::UnregisterAgent (const YCPPath &path)
{
+ done_sweep = false;
SubAgents::iterator agent = findByPath (path);
if (agent == agents.end ())
{
@@ -297,6 +339,7 @@
YCPBoolean
ScriptingAgent::UnregisterAllAgents ()
{
+ done_sweep = false;
for (SubAgents::iterator agent = agents.begin (); agent != agents.end ();
++agent)
{
@@ -309,6 +352,7 @@
}
+// FIXME if this still should work, try registering it first?
YCPValue
ScriptingAgent::MountAgent (const YCPPath &path)
{
@@ -322,6 +366,7 @@
}
+// FIXME if this still should work, try registering them first?
YCPValue
ScriptingAgent::MountAllAgents ()
{
@@ -363,9 +408,11 @@
return YCPBoolean (true);
}
-YCPBoolean
-ScriptingAgent::RegisterNewAgents ()
+// unconditionally read all dirs
+void
+ScriptingAgent::Sweep ()
{
+ y2debug ("Scripting agent sweeping");
for (int level = 0; level < Y2PathSearch::numberOfComponentLevels ();
level++)
{
@@ -373,37 +420,89 @@
y2debug( "Scripting agent searching SCRs in %s", dir.c_str() );
parseConfigFiles (dir);
}
+ done_sweep = true;
+}
+
+
+
+YCPBoolean
+ScriptingAgent::RegisterNewAgents ()
+{
+ Sweep ();
return YCPBoolean (true);
}
-YCPValue
-ScriptingAgent::executeSubagentCommand (const char *command,
- const YCPPath &path,
- const YCPValue &arg,
- const YCPValue &optpar)
+
+// cannot return "success" because we can register an unrelated path
+void
+ScriptingAgent::tryRegister (const YCPPath &path)
{
- y2debug( "ScriptingAgent::executeSubagentCommand: %s", command );
- y2debug( "path: %s", path->toString ().c_str ());
- y2debug( "arg: %s", arg.isNull() ? "null" : arg->toString().c_str ());
- y2debug( "opt: %s", optpar.isNull() ? "null" : optpar->toString().c_str ());
-
- /**
- * Find the agent where the agent's path and the given path have
- * the longest match. Example:
- *
- * agent net at .etc.network
- * agent isdn at .etc.network.isdn
- *
- * The command Read (.etc.network.isdn.line0) will call agent
- * isdn with Read (.line0). The command Read (.etc.network)
- * will call agent net with Read (.).
- */
-
- /*
- * The list of agents is sorted. So we can simply take the first
- * that does match.
- */
+ string basename = path->toString().substr(1) + ".scr"; // skip "."
+
+ list<RegistrationDir>::iterator
+ i = registration_dirs.begin(),
+ e = registration_dirs.end();
+ for (; i != e; ++i) {
+ RegistrationDir & rd = *i;
+ struct stat st;
+ if (stat (rd.name.c_str(), &st) != 0) {
+ y2debug ("Can't read dir %s: %m", rd.name.c_str ());
+ }
+ else {
+ // COBE, this caching logic is bogus
+ //if (rd.last_changed < st.st_mtime) {
+ // change detected!
+ //rd.last_changed = st.st_mtime;
+ //y2milestone ("Agent registration: %s last changed at %s",
+ // rd.name.c_str(), ctime (&rd.last_changed));
+ string fname = rd.name + '/' + basename;
+ struct stat st;
+ if (stat (fname.c_str (), &st) == 0) {
+ parseSingleConfigFile (fname);
+ break; // found
+ }
+ }
+ }
+}
+
+YCPList
+ScriptingAgent::dirSubagents (const YCPPath &path)
+{
+ const long path_length = path->length ();
+ YCPList dir_list = YCPList ();
+ for (SubAgents::const_iterator it = agents.begin ();
+ it != agents.end (); ++it)
+ {
+ YCPPath it_path = (*it)->get_path ();
+ if (path->isPrefixOf (it_path))
+ {
+ YCPString str = YCPString (it_path->component_str (path_length));
+ const int size = dir_list->size ();
+ if (size == 0 || !dir_list->value (size - 1)->equal (str))
+ dir_list->add (str);
+ }
+ }
+ return dir_list;
+}
+
+/**
+ * Find the agent where the agent's path and the given path have
+ * the longest match. Example:
+ *
+ * agent net at .etc.network
+ * agent isdn at .etc.network.isdn
+ *
+ * The command Read (.etc.network.isdn.line0) will call agent
+ * isdn with Read (.line0). The command Read (.etc.network)
+ * will call agent net with Read (.).
+ */
+
+ScriptingAgent::SubAgents::const_iterator
+ScriptingAgent::findSubagent (const YCPPath &path)
+{
+ // The list of agents is sorted. So we can simply take the first
+ // that does match.
SubAgents::const_iterator agent = agents.end ();
// known bug: doesn't work with const_reverse_iterator
@@ -416,33 +515,57 @@
break;
}
}
+ return agent;
+}
- if (agent == agents.end ())
- {
- // Special case to have the possibility of Dir (.rc) or similar...
- if (strcmp (command, "Dir") != 0)
- {
- return YCPError ("Couldn't find an agent to handle '" +
- path->toString () + "'");
- }
+// finds agent, registering it (or all of them) if necessary
+ScriptingAgent::SubAgents::const_iterator
+ScriptingAgent::findAndRegisterSubagent (const YCPPath &path)
+{
+ SubAgents::const_iterator agent = findSubagent (path);
+ if (agent != agents.end ())
+ return agent;
+
+ // no such agent registered.
+ // try registering by guessing its scr file name
- const long path_length = path->length ();
- YCPList dir_list = YCPList ();
+ // i = 0 gives the root path. we may need some caching after all for ".scr"
+ int i;
+ for (i = path->length(); i >= 0; --i) {
+ tryRegister (path->prefix (i));
+
+ agent = findSubagent (path); // retry
+ if (agent != agents.end ())
+ return agent;
+ }
+
+ // guess did not work. register all.
+ Sweep ();
+ return findSubagent (path);
+}
- for (SubAgents::const_iterator it = agents.begin ();
- it != agents.end (); ++it)
+YCPValue
+ScriptingAgent::executeSubagentCommand (const char *command,
+ const YCPPath &path,
+ const YCPValue &arg,
+ const YCPValue &optpar)
+{
+ y2debug( "ScriptingAgent::executeSubagentCommand: %s", command );
+ y2debug( "path: %s", path->toString ().c_str ());
+ y2debug( "arg: %s", arg.isNull() ? "null" : arg->toString().c_str ());
+ y2debug( "opt: %s", optpar.isNull() ? "null" : optpar->toString().c_str ());
+
+ SubAgents::const_iterator agent = findAndRegisterSubagent (path);
+ if (agent == agents.end ()) {
+ bool cmd_is_dir = strcmp (command, "Dir") == 0;
+ // Special case to have the possibility of Dir (.sysconfig) or similar...
+ if (cmd_is_dir)
{
- YCPPath it_path = (*it)->get_path ();
- if (path->isPrefixOf (it_path))
- {
- YCPString str = YCPString (it_path->component_str (path_length));
- const int size = dir_list->size ();
- if (size == 0 || !dir_list->value (size - 1)->equal (str))
- dir_list->add (str);
- }
+ return dirSubagents (path);
}
-
- return dir_list;
+ // if all fails:
+ return YCPError ("Couldn't find an agent to handle '" +
+ path->toString () + "'");
}
(*agent)->mount (this);
Modified: trunk/core/scr/src/ScriptingAgent.h
URL: http://svn.opensuse.org/viewcvs/yast/trunk/core/scr/src/ScriptingAgent.h?rev=44225&r1=44224&r2=44225&view=diff
==============================================================================
--- trunk/core/scr/src/ScriptingAgent.h (original)
+++ trunk/core/scr/src/ScriptingAgent.h Thu Feb 7 11:09:57 2008
@@ -11,6 +11,7 @@
#ifndef ScriptingAgent_h
#define ScriptingAgent_h
+#include
#include
#include
#include "SCRSubAgent.h"
@@ -24,10 +25,12 @@
public:
/**
- * Constructor. Also scans for scr-files.
+ * Constructor.
*/
ScriptingAgent ();
+ // used only in agent-ini/testsuite...?
+ // TODO try to eliminate it
/**
* Constructor. Load only a single SCR.
*
@@ -41,14 +44,14 @@
~ScriptingAgent ();
/**
- * Reads data. Destroy the result after use.
+ * Reads data.
* @param path Specifies what part of the subtree should
* be read. The path is specified _relatively_ to Root()!
*/
virtual YCPValue Read (const YCPPath &path, const YCPValue &arg = YCPNull (), const YCPValue &opt = YCPNull ());
/**
- * Writes data. Destroy the result after use.
+ * Writes data.
*/
virtual YCPBoolean Write (const YCPPath &path, const YCPValue &value,
const YCPValue &arg = YCPNull ());
@@ -70,9 +73,12 @@
virtual YCPMap Error (const YCPPath &path);
/**
- * Handle the commands 'UnregisterAgent',
- * 'UnregisterAllAgents', 'MountAgent', 'MountAllAgents',
- * 'UnmountAgent' and 'UnmountAllAgents'.
+ * Handle the commands
+ * MountAgent, MountAllAgents, UnmountAllAgents,
+ * YaST2Version, SuSEVersion.
+ * Formerly also
+ * 'UnregisterAgent', 'UnregisterAllAgents',
+ * 'UnmountAgent' which are now builtins.
*/
YCPValue otherCommand (const YCPTerm &term);
@@ -100,8 +106,30 @@
private:
+ // once we have to do a sweep (read all scr files because of
+ // a Dir or we were not lucky with a path patch), set this flag so
+ // that we do not unnecessarily sweep again
+ bool done_sweep;
+
+ // FIXME rethink the caching
+ struct RegistrationDir {
+ string name;
+ time_t last_changed; //!< st_mtime of the dir
+ };
+
+ /**
+ * Where to look for *.scr files, in order of preference
+ */
+ list<RegistrationDir> registration_dirs;
+
+ /**
+ * Populate registration_dirs
+ */
+ void InitRegDirs ();
+
/**
* Type and list of subagents
+ * The vector is sorted by path
*/
typedef vector SubAgents;
SubAgents agents;
@@ -124,11 +152,46 @@
YCPValue UnmountAllAgents ();
/**
- * Register new agents
+ * Read all registration files.
+ */
+ void Sweep ();
+
+ /**
+ * Register new agents. (bnc#245508#c16)
+ * Rescan the scrconf registration directories and register any
+ * agents at new(!) paths. Agents, even new ones, on paths that
+ * are registered already, will not be replaced. This means that
+ * .oes.specific.agent will start to work but something like
+ * adding
+ * /usr/local/etc/sysconfig to .sysconfig.network would not.
*/
YCPBoolean RegisterNewAgents ();
/**
+ * For .foo.bar.baz, register foo.bar.baz.scr, or foo.bar.scr, or foo.scr.
+ * BTW we can register an unrelated path because this is just a heuristic.
+ */
+ void tryRegister (const YCPPath &path);
+
+ /**
+ * Iterate thru @ref agents
+ * @return end if not found
+ */
+ SubAgents::const_iterator findSubagent (const YCPPath &path);
+
+ /**
+ * Find it in @ref agents, registering if necessary, sweeping if necessary
+ * @see tryRegister
+ * @see Sweep
+ */
+ SubAgents::const_iterator findAndRegisterSubagent (const YCPPath &path);
+
+ /**
+ * If a SCR::Dir falls inside our tree, we have to provide a listing
+ */
+ YCPList dirSubagents (const YCPPath &path);
+
+ /**
* Calls a subagent to execute a Read, Write, Dir or other command
* @param command the command like "Read", "Dir", ..
* @param path All commands take a path as first parameter. Here you
@@ -146,19 +209,20 @@
/**
* Find agent exactly matching path. Returns agents.end () if the path
* isn't covered by any agent.
+ * Does not try to register.
*/
SubAgents::iterator findByPath (const YCPPath &path);
/**
- * Parses the given directory and all its subdirectories for
- * SCR configuration files and evaluates them with the SCR
- * interpreter.
+ * Parses all SCR configuration files in the given directory,
+ * registers the agents.
+ * (If a SCR path is already registered, keep the old one.)
*/
void parseConfigFiles (const string &directory);
/**
- * Parses a single SCR configuration file and evaluates them with the SCR
- * interpreter.
+ * Parses a single SCR configuration file, registers the agent.
+ * (If the SCR path is already registered, keep the old one.)
*/
void parseSingleConfigFile (const string &file);
--
To unsubscribe, e-mail: yast-commit+unsubscribe@opensuse.org
For additional commands, e-mail: yast-commit+help@opensuse.org