summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bin/schroot/schroot-main-base.cc1
-rw-r--r--bin/schroot/schroot-options-base.cc2
-rw-r--r--bin/schroot/schroot-options-base.h5
-rw-r--r--bin/schroot/schroot-options.cc17
-rw-r--r--man/schroot-setup.5.man5
-rw-r--r--man/schroot.1.man10
-rw-r--r--man/schroot.conf.5.man40
-rw-r--r--po/POTFILES.in1
-rw-r--r--sbuild/Makefile.am2
-rw-r--r--sbuild/sbuild-basic-keyfile.h310
-rw-r--r--sbuild/sbuild-chroot-facet-userdata.cc356
-rw-r--r--sbuild/sbuild-chroot-facet-userdata.h221
-rw-r--r--sbuild/sbuild-chroot.cc65
-rw-r--r--sbuild/sbuild-keyfile-base.cc7
-rw-r--r--sbuild/sbuild-keyfile-base.h1
-rw-r--r--sbuild/sbuild-session.cc30
-rw-r--r--sbuild/sbuild-session.h18
-rw-r--r--sbuild/sbuild-types.h4
-rw-r--r--test/.gitignore1
-rw-r--r--test/Makefile.am7
-rw-r--r--test/sbuild-chroot-facet-userdata.cc156
-rw-r--r--test/test-sbuild-chroot.h21
22 files changed, 1274 insertions, 6 deletions
diff --git a/bin/schroot/schroot-main-base.cc b/bin/schroot/schroot-main-base.cc
index 91b17778..41eff69e 100644
--- a/bin/schroot/schroot-main-base.cc
+++ b/bin/schroot/schroot-main-base.cc
@@ -351,6 +351,7 @@ main_base::run_impl ()
this->session->set_verbosity("quiet");
else if (this->options->verbose)
this->session->set_verbosity("verbose");
+ this->session->set_user_options(this->options->useroptions_map);
/* Run session. */
this->session->run();
diff --git a/bin/schroot/schroot-options-base.cc b/bin/schroot/schroot-options-base.cc
index 32848cca..891a5cc1 100644
--- a/bin/schroot/schroot-options-base.cc
+++ b/bin/schroot/schroot-options-base.cc
@@ -57,6 +57,8 @@ options_base::options_base ():
exclude_aliases(false),
session_name(),
session_force(false),
+ useroptions(),
+ useroptions_map(),
chroot(_("Chroot selection")),
chrootenv(_("Chroot environment")),
session_actions(_("Session actions")),
diff --git a/bin/schroot/schroot-options-base.h b/bin/schroot/schroot-options-base.h
index e74917d9..738a564f 100644
--- a/bin/schroot/schroot-options-base.h
+++ b/bin/schroot/schroot-options-base.h
@@ -25,6 +25,7 @@
#include <schroot-base/schroot-base-options.h>
#include <string>
+#include <vector>
#ifdef HAVE_TR1_MEMORY
#include <tr1/memory>
@@ -107,6 +108,10 @@ namespace schroot
std::string session_name;
/// Force session operations.
bool session_force;
+ /// Options as a key=value list.
+ sbuild::string_list useroptions;
+ /// Options in a string-string map.
+ sbuild::string_map useroptions_map;
protected:
/**
diff --git a/bin/schroot/schroot-options.cc b/bin/schroot/schroot-options.cc
index 6c451be1..4fa10cd8 100644
--- a/bin/schroot/schroot-options.cc
+++ b/bin/schroot/schroot-options.cc
@@ -1,4 +1,4 @@
-/* Copyright © 2005-2007 Roger Leigh <rleigh@debian.org>
+/* Copyright © 2005-2012 Roger Leigh <rleigh@debian.org>
*
* schroot is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
@@ -69,7 +69,9 @@ options::add_options ()
("user,u", opt::value<std::string>(&this->user),
_("Username (default current user)"))
("preserve-environment,p",
- _("Preserve user environment"));
+ _("Preserve user environment"))
+ ("option,o", opt::value<sbuild::string_list>(&this->useroptions),
+ _("Set option"));
session_actions.add_options()
("automatic-session",
@@ -133,4 +135,15 @@ options::check_options ()
this->all_sessions = true;
this->all_source_chroots = true;
}
+
+ for (sbuild::string_list::const_iterator pos = this->useroptions.begin();
+ pos != this->useroptions.end();
+ ++pos)
+ {
+ std::string::size_type sep = pos->find_first_of('=', 0);
+ std::string key = pos->substr(0,sep);
+ ++sep;
+ std::string value = pos->substr(sep);
+ this->useroptions_map.insert(std::make_pair(key,value));
+ }
}
diff --git a/man/schroot-setup.5.man b/man/schroot-setup.5.man
index f8189799..8a877817 100644
--- a/man/schroot-setup.5.man
+++ b/man/schroot-setup.5.man
@@ -191,6 +191,11 @@ The name of the LVM snapshot device.
CHROOT_LVM_SNAPSHOT_OPTIONS
Options to pass to
.BR lvcreate (8).
+.SS Custom variables
+.PP
+Custom keys set in \fIschroot.conf\fP will be uppercased and set in the
+environment as described in
+.BR schroot.conf (5).
.SH FILES
.SS Setup script configuration
The directory \f[BI]\*[SCHROOT_SYSCONF_DIR]/default\fP contains the default
diff --git a/man/schroot.1.man b/man/schroot.1.man
index 7eda1d5c..2eec12b5 100644
--- a/man/schroot.1.man
+++ b/man/schroot.1.man
@@ -37,6 +37,7 @@ schroot \- securely enter a chroot environment
.RB [ "\-c \fIchroot\fP" \[or] "\-\-chroot=\fIchroot\fP"
.RB " \[or] " \-\-all " \[or] " \-\-all\-chroots " \[or] " \-\-all\-source\-chroots " \[or] " \-\-all\-sessions ]
.RB " \[or] " \-\-exclude\-aliases
+.RB [ -o \[or] "\-\-option=\fIkey=value\fP" ]
.RB [ \-\- ]
.RB [ COMMAND " [ " ARG1 " [ " ARG2 " [ " ARGn ]]]]
.SH DESCRIPTION
@@ -186,6 +187,15 @@ Preserve the user's environment inside the chroot environment. The default is
to use a clean environment; this option copies the entire user environment and
sets it in the session. The environment variables allowed are subject to
certain restrictions; see the section \[lq]\fIEnvironment\fP\[rq], below.
+.TP
+.BR \-o ", " \-\-option=\fIkey=value\fP
+Set an option. The value of selected configuration keys in \fIschroot.conf\fP
+may be modified using this option. The key must be present in the
+\f[CI]user\-modifiable\-keys\fP configuration key in \fIschroot.conf\fP, or
+additionally the \f[CI]user\-modifiable\-keys\fP key if running as (or
+switching to) the root user. The key and value set here will be set in the
+environment of the setup scripts, and may hence be used to customise the chroot
+on a per-session basis.
.SS Session actions
.TP
.BR \-\-automatic\-session
diff --git a/man/schroot.conf.5.man b/man/schroot.conf.5.man
index 0897c411..eb53219c 100644
--- a/man/schroot.conf.5.man
+++ b/man/schroot.conf.5.man
@@ -377,6 +377,46 @@ created. The default is \[oq]\*[SCHROOT_OVERLAY_DIR]\[cq].
\f[CBI]union\-underlay\-directory\fP\f[CI]=directory\fP
Specify the directory where the read-only underlying directories will be
created. The default is \[oq]\*[SCHROOT_UNDERLAY_DIR]\[cq].
+.SS Customisation
+.PP
+In addition to the configuration keys listed above, it is possible to add
+custom keys. These keys will be used to add additional environment
+variables to the setup script environment when setup scripts are run. The only
+restriction is that the key name consists only of alphanumeric characters and
+hyphens, begins with an alphabet character and contains at least one period.
+That is to say, that it matches the extended regular expression
+\[lq]^([a-z][a-z0-9]*\\.)+[a-z][a-z0-9-]*$\[rq].
+.PP
+For example:
+.RS
+.EX
+debian.apt-update=true
+debian.distribution=unstable
+.EE
+.RE
+.PP
+would set the following environment:
+.RS
+.EX
+DEBIAN_APT_UPDATE=true
+DEBIAN_DISTRIBUTION=unstable
+.EE
+.RE
+.PP
+Note that it is an error to use different key names which would set the same
+environment variable by mixing periods and hyphens.
+.PP
+Custom configuration keys may also be modified at runtime using the
+\fI\-\-option\fP option. However, for security, only selected keys may be
+modified. These keys are specified using the following options:
+.TP
+\f[CBI]user\-modifiable\-keys=\fP\f[CI]key1,key2,..\fP
+Set the keys which users may modify using \fI\-\-option\fP.
+.TP
+\f[CBI]root\-modifiable\-keys=\fP\f[CI]key1,key2,..\fP Set the keys which the
+root user may modify using \fI\-\-option\fP. Note that the root user may use
+the keys specified in \f[CI]user\-modifiable\-keys\fP in addition to those
+specified here.
.SS Localisation
.PP
Some keys may be localised in multiple languages. This is achieved by adding
diff --git a/po/POTFILES.in b/po/POTFILES.in
index f593120f..73738568 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -50,6 +50,7 @@ sbuild/sbuild-chroot-facet-session-clonable.cc
sbuild/sbuild-chroot-facet-source.cc
sbuild/sbuild-chroot-facet-source-clonable.cc
sbuild/sbuild-chroot-facet-union.cc
+sbuild/sbuild-chroot-facet-userdata.cc
sbuild/sbuild-chroot-file.cc
sbuild/sbuild-chroot-loopback.cc
sbuild/sbuild-chroot-lvm-snapshot.cc
diff --git a/sbuild/Makefile.am b/sbuild/Makefile.am
index 9c065fd6..8f5f68dd 100644
--- a/sbuild/Makefile.am
+++ b/sbuild/Makefile.am
@@ -38,6 +38,7 @@ sbuild_public_h_sources = \
sbuild-chroot-facet-session-clonable.h \
sbuild-chroot-facet-source.h \
sbuild-chroot-facet-source-clonable.h \
+ sbuild-chroot-facet-userdata.h \
sbuild-chroot-file.h \
sbuild-chroot-plain.h \
sbuild-chroot-config.h \
@@ -118,6 +119,7 @@ sbuild_public_cc_sources = \
sbuild-chroot-facet-session-clonable.cc \
sbuild-chroot-facet-source.cc \
sbuild-chroot-facet-source-clonable.cc \
+ sbuild-chroot-facet-userdata.cc \
sbuild-chroot-file.cc \
sbuild-chroot-plain.cc \
sbuild-chroot-config.cc \
diff --git a/sbuild/sbuild-basic-keyfile.h b/sbuild/sbuild-basic-keyfile.h
index 5478080e..6cc00d51 100644
--- a/sbuild/sbuild-basic-keyfile.h
+++ b/sbuild/sbuild-basic-keyfile.h
@@ -537,6 +537,89 @@ namespace sbuild
}
/**
+ * Get a key value as a set.
+ *
+ * @param group the group the key is in.
+ * @param key the key to get.
+ * @param container the container to store the key's value in.
+ * The value type must be settable from an istream and be
+ * copyable. The set must be a container with a standard insert
+ * method.
+ * @returns true if the key was found, otherwise false (in which
+ * case value will be undefined).
+ */
+ template <typename C>
+ bool
+ get_set_value (group_name_type const& group,
+ key_type const& key,
+ C& container) const
+ {
+ value_type item_value;
+ if (get_value(group, key, item_value))
+ {
+ value_list items = split_string(item_value,
+ this->separator);
+ for (typename value_list::const_iterator pos = items.begin();
+ pos != items.end();
+ ++pos
+ )
+ {
+ typename C::value_type tmp;
+
+ try
+ {
+ parse_value(*pos, tmp);
+ }
+ catch (parse_value_error const& e)
+ {
+ size_type line = get_line(group, key);
+ if (line)
+ {
+ error ep(line, group, key, PASSTHROUGH_LGK, e);
+ log_exception_warning(ep);
+ }
+ else
+ {
+ error ep(group, key, PASSTHROUGH_GK, e);
+ log_exception_warning(ep);
+ }
+ return false;
+ }
+
+ container.insert(tmp);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Get a key value as a set. If the value does not exist, is
+ * deprecated or obsolete, warn appropriately.
+ *
+ * @param group the group the key is in.
+ * @param key the key to get.
+ * @param priority the priority of the option.
+ * @param container the container to store the key's value in.
+ * The value type must be settable from an istream and be
+ * copyable. The set must be a container with a standard insert
+ * method.
+ * @returns true if the key was found, otherwise false (in which
+ * case value will be undefined).
+ */
+ template <typename C>
+ bool
+ get_set_value (group_name_type const& group,
+ key_type const& key,
+ priority priority,
+ C& container) const
+ {
+ bool status = get_set_value(group, key, container);
+ check_priority(group, key, priority, status);
+ return status;
+ }
+
+ /**
* Set a key value.
*
* @param group the group the key is in.
@@ -671,7 +754,7 @@ namespace sbuild
{
value_type strval;
- for (I pos = begin; pos != end; ++ pos)
+ for (I pos = begin; pos != end;)
{
std::ostringstream os;
os.imbue(std::locale::classic());
@@ -679,7 +762,7 @@ namespace sbuild
if (os)
{
strval += os.str();
- if (pos + 1 != end)
+ if (++pos != end)
strval += this->separator;
}
}
@@ -688,6 +771,75 @@ namespace sbuild
}
/**
+ * Set a key value from a set.
+ *
+ * @param group the group the key is in.
+ * @param key the key to set.
+ * @param begin an iterator referring to the start of the
+ * set. The value type must allow output to an ostream.
+ * @param end an iterator referring to the end of the set.
+ */
+ template <typename I>
+ void
+ set_set_value (group_name_type const& group,
+ key_type const& key,
+ I begin,
+ I end)
+ {
+ std::vector<typename std::iterator_traits<I>::value_type> l(begin, end);
+ std::sort(l.begin(), l.end());
+ set_list_value(group, key, l.begin(), l.end());
+ }
+
+ /**
+ * Set a key value from a set.
+ *
+ * @param group the group the key is in.
+ * @param key the key to set.
+ * @param begin an iterator referring to the start of the
+ * set. The value type must allow output to an ostream.
+ * @param end an iterator referring to the end of the set.
+ * @param comment the comment for this key.
+ */
+ template <typename I>
+ void
+ set_set_value (group_name_type const& group,
+ key_type const& key,
+ I begin,
+ I end,
+ comment_type const& comment)
+ {
+ std::vector<typename std::iterator_traits<I>::value_type> l(begin, end);
+ std::sort(l.begin(), l.end());
+ set_list_value(group, key, l.begin(), l.end(), comment);
+ }
+
+ /**
+ * Set a key value from a set.
+ *
+ * @param group the group the key is in.
+ * @param key the key to set.
+ * @param begin an iterator referring to the start of the
+ * set. The value type must allow output to an ostream.
+ * @param end an iterator referring to the end of the set.
+ * @param comment the comment for this key.
+ * @param line the line number in the input file, or 0 otherwise.
+ */
+ template <typename I>
+ void
+ set_set_value (group_name_type const& group,
+ key_type const& key,
+ I begin,
+ I end,
+ comment_type const& comment,
+ size_type line)
+ {
+ std::vector<typename std::iterator_traits<I>::value_type> l(begin, end);
+ std::sort(l.begin(), l.end());
+ set_list_value(group, key, l.begin(), l.end(), comment, line);
+ }
+
+ /**
* Remove a group.
*
* @param group the group to remove.
@@ -1035,6 +1187,75 @@ namespace sbuild
}
/**
+ * Set a key set value from an object method return value. The
+ * method must return a container with begin() and end() methods
+ * which return forward iterators. This is the same as calling
+ * set_set_value directly, but handles exceptions being thrown by
+ * the object method, which are turned into error exceptions.
+ *
+ * @param object the object to use.
+ * @param method the object method to call.
+ * @param basic_keyfile the basic_keyfile to use.
+ * @param group the group the key is in.
+ * @param key the key to set.
+ */
+ template<class C, typename T>
+ static void
+ set_object_set_value (C const& object,
+ T (C::* method)() const,
+ basic_keyfile& basic_keyfile,
+ group_name_type const& group,
+ key_type const& key)
+ {
+ try
+ {
+ if (method)
+ basic_keyfile.set_set_value(group, key,
+ (object.*method)().begin(),
+ (object.*method)().end());
+ }
+ catch (std::runtime_error const& e)
+ {
+ throw error(group, key, PASSTHROUGH_GK, e);
+ }
+ }
+
+ /**
+ * Set a key set value from an object method return value. The
+ * method must return a container reference with begin() and end()
+ * methods which return forward iterators. This is the same as
+ * calling set_set_value directly, but handles exceptions being
+ * thrown by the object method, which are turned into error
+ * exceptions.
+ *
+ * @param object the object to use.
+ * @param method the object method to call.
+ * @param basic_keyfile the basic_keyfile to use.
+ * @param group the group the key is in.
+ * @param key the key to set.
+ */
+ template<class C, typename T>
+ static void
+ set_object_set_value (C const& object,
+ T const& (C::* method)() const,
+ basic_keyfile& basic_keyfile,
+ group_name_type const& group,
+ key_type const& key)
+ {
+ try
+ {
+ if (method)
+ basic_keyfile.set_set_value(group, key,
+ (object.*method)().begin(),
+ (object.*method)().end());
+ }
+ catch (std::runtime_error const& e)
+ {
+ throw error(group, key, PASSTHROUGH_GK, e);
+ }
+ }
+
+ /**
* Get a key value and set it in an object using an object method.
* This is the same as calling get_value directly, but handles
* exceptions being thrown by the object method, and
@@ -1198,6 +1419,91 @@ namespace sbuild
group, key, e);
}
}
+
+ /**
+ * Get a key set value and set it in an object using an object
+ * method. This is the same as calling get_set_value directly,
+ * but handles exceptions being thrown by the object method, and
+ * deserialisation errors, which are turned into error exceptions
+ * pointing to the group, key and line number in the input file.
+ *
+ * @param object the object to use.
+ * @param method the object method to call.
+ * @param basic_keyfile the basic_keyfile to use.
+ * @param group the group the key is in.
+ * @param key the key to set.
+ * @param priority the key priority.
+ */
+ template<class C, typename T>
+ static void
+ get_object_set_value (C& object,
+ void (C::* method)(T param),
+ basic_keyfile const& basic_keyfile,
+ group_name_type const& group,
+ key_type const& key,
+ basic_keyfile::priority priority)
+ {
+ try
+ {
+ T value;
+ if (basic_keyfile.get_set_value(group, key, priority, value)
+ && method)
+ (object.*method)(value);
+ }
+ catch (std::runtime_error const& e)
+ {
+ size_type line = basic_keyfile.get_line(group, key);
+ if (line)
+ throw error(line, group, key, PASSTHROUGH_LGK, e);
+ else
+ throw error(group, key, PASSTHROUGH_GK, e);
+ throw error(basic_keyfile.get_line(group, key),
+ group, key, e);
+ }
+ }
+
+ /**
+ * Get a key set value and set it by reference in an object using
+ * an object method. This is the same as calling get_set_value
+ * directly, but handles exceptions being thrown by the object
+ * method, and deserialisation errors, which are turned into error
+ * exceptions pointing to the group, key and line number in the
+ * input file.
+ *
+ * @param object the object to use.
+ * @param method the object method to call.
+ * @param basic_keyfile the basic_keyfile to use.
+ * @param group the group the key is in.
+ * @param key the key to set.
+ * @param priority the key priority.
+ */
+ template<class C, typename T>
+ static void
+ get_object_set_value (C& object,
+ void (C::* method)(T const& param),
+ basic_keyfile const& basic_keyfile,
+ group_name_type const& group,
+ key_type const& key,
+ basic_keyfile::priority priority)
+ {
+ try
+ {
+ T value;
+ if (basic_keyfile.get_set_value(group, key, priority, value)
+ && method)
+ (object.*method)(value);
+ }
+ catch (std::runtime_error const& e)
+ {
+ size_type line = basic_keyfile.get_line(group, key);
+ if (line)
+ throw error(line, group, key, PASSTHROUGH_LGK, e);
+ else
+ throw error(group, key, PASSTHROUGH_GK, e);
+ throw error(basic_keyfile.get_line(group, key),
+ group, key, e);
+ }
+ }
};
}
diff --git a/sbuild/sbuild-chroot-facet-userdata.cc b/sbuild/sbuild-chroot-facet-userdata.cc
new file mode 100644
index 00000000..1b84c33b
--- /dev/null
+++ b/sbuild/sbuild-chroot-facet-userdata.cc
@@ -0,0 +1,356 @@
+/* Copyright © 2005-2009,2012 Roger Leigh <rleigh@debian.org>
+ *
+ * schroot is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * schroot is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ *********************************************************************/
+
+#include <config.h>
+
+#include "sbuild-chroot.h"
+#include "sbuild-chroot-facet-userdata.h"
+#include "sbuild-regex.h"
+
+#include <locale>
+
+#include <boost/format.hpp>
+
+using boost::format;
+using namespace sbuild;
+
+namespace
+{
+ typedef std::pair<chroot_facet_userdata::error_code,const char *> emap;
+
+ /**
+ * This is a list of the supported error codes. It's used to
+ * construct the real error codes map.
+ */
+ emap init_errors[] =
+ {
+ emap(chroot_facet_userdata::ENV_AMBIGUOUS,
+ N_("Environment variable ‘%1%’ is ambiguous")),
+ emap(chroot_facet_userdata::KEY_AMBIGUOUS,
+ N_("Configuration key ‘%1%’ is ambiguous")),
+ emap(chroot_facet_userdata::KEY_DISALLOWED,
+ N_("Configuration key ‘%1%’ is not permitted to be modified.")),
+ emap(chroot_facet_userdata::KEYNAME_INVALID,
+ N_("Configuration key name ‘%1%’ is not a permitted name."))
+ };
+
+ bool
+ validate_keyname(std::string const& key)
+ {
+ // Valid names consist of one (or more) namespaces which consist
+ // of [a-z][a-z0-9] followed by a . (namespace separator). The
+ // key name following the separator is [a-z][a-z0-9-]. When
+ // converted to an environment variable, all alphabet characters
+ // are uppercased, and all periods and hyphens converted to
+ // underscores.
+ static sbuild::regex permitted("^([a-z][a-z0-9]*\\.)+[a-z][a-z0-9-]*$");
+
+ // These would permit clashes with existing setup environment
+ // variables, and potentially security issues if they were
+ // user-settable.
+ static sbuild::regex reserved("^(auth|chroot|host|libexec|mount|session|setup|status|sysconf)\\.");
+
+ return regex_search(key, permitted) && !regex_search(key, reserved);
+ }
+
+ std::string
+ envname(std::string const& key)
+ {
+ std::string ret(key);
+
+ static const std::ctype<char>& ct = std::use_facet<std::ctype<char> >(std::locale::classic());
+ for (std::string::iterator pos = ret.begin(); pos != ret.end(); ++pos)
+ {
+ *pos = ct.toupper(*pos);
+ if (*pos == '-' || *pos == '.')
+ *pos = '_';
+ }
+ return ret;
+ }
+}
+
+template<>
+error<chroot_facet_userdata::error_code>::map_type
+error<chroot_facet_userdata::error_code>::error_strings
+(init_errors,
+ init_errors + (sizeof(init_errors) / sizeof(init_errors[0])));
+
+chroot_facet_userdata::chroot_facet_userdata ():
+ chroot_facet(),
+ userdata(),
+ env(),
+ user_modifiable_keys(),
+ root_modifiable_keys()
+{
+}
+
+chroot_facet_userdata::~chroot_facet_userdata ()
+{
+}
+
+chroot_facet_userdata::ptr
+chroot_facet_userdata::create ()
+{
+ return ptr(new chroot_facet_userdata());
+}
+
+chroot_facet::ptr
+chroot_facet_userdata::clone () const
+{
+ return ptr(new chroot_facet_userdata(*this));
+}
+
+std::string const&
+chroot_facet_userdata::get_name () const
+{
+ static const std::string name("userdata");
+
+ return name;
+}
+
+void
+chroot_facet_userdata::setup_env (chroot const& chroot,
+ environment& env) const
+{
+ for (string_map::const_iterator pos = userdata.begin();
+ pos != userdata.end();
+ ++pos)
+ {
+ std::string name = envname(pos->first);
+ std::string dummy;
+ if (!env.get(name, dummy))
+ env.add(name, pos->second);
+ else
+ {
+ error e(name, ENV_AMBIGUOUS);
+ format fmt(_("Configuration keys additional to ‘%1%’ would set this setup script environment variable"));
+ fmt % pos->first;
+ e.set_reason(fmt.str());
+ throw e;
+ }
+ }
+}
+
+sbuild::chroot::session_flags
+chroot_facet_userdata::get_session_flags (chroot const& chroot) const
+{
+ return sbuild::chroot::SESSION_NOFLAGS;
+}
+
+void
+chroot_facet_userdata::get_details (chroot const& chroot,
+ format_detail& detail) const
+{
+ string_list userkeys(this->user_modifiable_keys.begin(),
+ this->user_modifiable_keys.end());
+ std::sort(userkeys.begin(), userkeys.end());
+
+ string_list rootkeys(this->root_modifiable_keys.begin(),
+ this->root_modifiable_keys.end());
+ std::sort(rootkeys.begin(), rootkeys.end());
+
+ detail.add(_("User Modifiable Keys"), userkeys);
+ detail.add(_("Root Modifiable Keys"), rootkeys);
+ detail.add(_("User Data"), "");
+
+ string_list keys;
+ for (string_map::const_iterator pos = userdata.begin();
+ pos != userdata.end();
+ ++pos)
+ keys.push_back(pos->first);
+ std::sort(keys.begin(), keys.end());
+
+ for (string_list::const_iterator pos = keys.begin();
+ pos != keys.end();
+ ++pos)
+ {
+ string_map::const_iterator key = userdata.find(*pos);
+ if (key != userdata.end())
+ {
+ std::string name(" ");
+ name += key->first;
+ detail.add(name, key->second);
+ }
+ }
+}
+
+string_map const&
+chroot_facet_userdata::get_data () const
+{
+ return userdata;
+}
+
+bool
+chroot_facet_userdata::get_data (std::string const& key,
+ std::string& value) const
+{
+ string_map::const_iterator pos = this->userdata.find(key);
+ bool found = (pos != this->userdata.end());
+ if (found)
+ value = pos->second;
+ return found;
+}
+
+void
+chroot_facet_userdata::set_data (std::string const& key,
+ std::string const& value)
+{
+ if (!validate_keyname(key))
+ throw error(key, KEYNAME_INVALID);
+
+ string_map::const_iterator inserted = userdata.find(key);
+ if (inserted == userdata.end()) // Requires uniqueness checking.
+ {
+ std::string name = envname(key);
+ string_set::const_iterator found = this->env.find(name);
+ if (found != this->env.end())
+ {
+ error e(key, KEY_AMBIGUOUS);
+ format fmt(_("More than one configuration key would set the ‘%1%’ environment variable"));
+ fmt % name;
+ e.set_reason(fmt.str());
+ throw e;
+ }
+ this->env.insert(name);
+ }
+ else
+ this->userdata.erase(key);
+
+ this->userdata.insert(std::make_pair(key, value));
+}
+
+void
+chroot_facet_userdata::set_data (string_map const& data)
+{
+ for (string_map::const_iterator pos = data.begin();
+ pos != data.end();
+ ++pos)
+ set_data(pos->first, pos->second);
+}
+
+void
+chroot_facet_userdata::set_user_data(string_map const& data)
+{
+ set_data(data, this->user_modifiable_keys, false);
+}
+
+void
+chroot_facet_userdata::set_root_data(string_map const& data)
+{
+ // root can use both user and root keys, so combine the sets.
+ string_set modifiable_keys;
+ set_union(this->user_modifiable_keys.begin(),
+ this->user_modifiable_keys.end(),
+ this->root_modifiable_keys.begin(),
+ this->root_modifiable_keys.end(),
+ inserter(modifiable_keys, modifiable_keys.begin()));
+ set_data(data, modifiable_keys, true);
+}
+
+void
+chroot_facet_userdata::set_data(string_map const& data,
+ string_set const& allowed_keys,
+ bool root)
+{
+ // Require the key to be present in order to set it. This ensures
+ // that the key name has been pre-validated.
+ for (string_map::const_iterator pos = data.begin();
+ pos != data.end();
+ ++pos)
+ {
+ string_set::const_iterator allowed = allowed_keys.find(pos->first);
+ if (allowed == allowed_keys.end())
+ {
+ error e(pos->first, KEY_DISALLOWED);
+ if (root)
+ e.set_reason(_("The key is not present in user-modifiable-keys or root-modifiable-keys"));
+ else
+ e.set_reason(_("The key is not present in user-modifiable-keys"));
+ throw e;
+ }
+ set_data(pos->first, pos->second);
+ }
+}
+
+string_set const&
+chroot_facet_userdata::get_user_modifiable_keys() const
+{
+ return this->user_modifiable_keys;
+}
+
+void
+chroot_facet_userdata::set_user_modifiable_keys (string_set const& keys)
+{
+ this->user_modifiable_keys = keys;
+}
+
+string_set const&
+chroot_facet_userdata::get_root_modifiable_keys() const
+{
+ return this->root_modifiable_keys;
+}
+
+void
+chroot_facet_userdata::set_root_modifiable_keys (string_set const& keys)
+{
+ this->root_modifiable_keys = keys;
+}
+
+void
+chroot_facet_userdata::get_keyfile (chroot const& chroot,
+ keyfile& keyfile) const
+{
+ keyfile::set_object_set_value(*this,
+ &chroot_facet_userdata::get_user_modifiable_keys,
+ keyfile, chroot.get_name(),
+ "user-modifiable-keys");
+
+ keyfile::set_object_set_value(*this,
+ &chroot_facet_userdata::get_root_modifiable_keys,
+ keyfile, chroot.get_name(),
+ "root-modifiable-keys");
+
+ for (string_map::const_iterator pos = userdata.begin();
+ pos != userdata.end();
+ ++pos)
+ {
+ keyfile.set_value(chroot.get_name(),
+ pos->first,
+ pos->second);
+ }
+}
+
+void
+chroot_facet_userdata::set_keyfile (chroot& chroot,
+ keyfile const& keyfile,
+ string_list& used_keys)
+{
+ keyfile::get_object_set_value(*this,
+ &chroot_facet_userdata::set_user_modifiable_keys,
+ keyfile, chroot.get_name(),
+ "user-modifiable-keys",
+ keyfile::PRIORITY_OPTIONAL);
+ used_keys.push_back("user-modifiable-keys");
+
+ keyfile::get_object_set_value(*this,
+ &chroot_facet_userdata::set_root_modifiable_keys,
+ keyfile, chroot.get_name(),
+ "root-modifiable-keys",
+ keyfile::PRIORITY_OPTIONAL);
+ used_keys.push_back("root-modifiable-keys");
+}
diff --git a/sbuild/sbuild-chroot-facet-userdata.h b/sbuild/sbuild-chroot-facet-userdata.h
new file mode 100644
index 00000000..1b686208
--- /dev/null
+++ b/sbuild/sbuild-chroot-facet-userdata.h
@@ -0,0 +1,221 @@
+/* Copyright © 2005-2009,2012 Roger Leigh <rleigh@debian.org>
+ *
+ * schroot is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * schroot is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ *********************************************************************/
+
+#ifndef SBUILD_CHROOT_FACET_USERDATA_H
+#define SBUILD_CHROOT_FACET_USERDATA_H
+
+#include <sbuild/sbuild-chroot-facet.h>
+#include <sbuild/sbuild-custom-error.h>
+#include <sbuild/sbuild-types.h>
+
+namespace sbuild
+{
+
+ /**
+ * Chroot support for extensible user metadata.
+ *
+ * This facet contains user-specific configuration, both additional
+ * keys in schroot.conf, and also set from the command-line.
+ */
+ class chroot_facet_userdata : public chroot_facet
+ {
+ public:
+ /// Error codes.
+ enum error_code
+ {
+ ENV_AMBIGUOUS, ///< Environment variable name is ambiguous.
+ KEY_AMBIGUOUS, ///< Configuration key name is ambiguous.
+ KEY_DISALLOWED, ///< Configuration key is not allowed to be modified.
+ KEYNAME_INVALID ///< Invalid name for configuration key.
+ };
+
+ /// Exception type.
+ typedef custom_error<error_code> error;
+
+ /// A shared_ptr to a chroot facet object.
+ typedef std::shared_ptr<chroot_facet_userdata> ptr;
+
+ /// A shared_ptr to a const chroot facet object.
+ typedef std::shared_ptr<const chroot_facet_userdata> const_ptr;
+
+ private:
+ /// The constructor.
+ chroot_facet_userdata ();
+
+ public:
+ /// The destructor.
+ virtual ~chroot_facet_userdata ();
+
+ /**
+ * Create a chroot facet.
+ *
+ * @returns a shared_ptr to the new chroot facet.
+ */
+ static ptr
+ create ();
+
+ virtual chroot_facet::ptr
+ clone () const;
+
+ virtual std::string const&
+ get_name () const;
+
+ virtual void
+ setup_env (chroot const& chroot,
+ environment& env) const;
+
+ virtual chroot::session_flags
+ get_session_flags (chroot const& chroot) const;
+
+ virtual void
+ get_details (chroot const& chroot,
+ format_detail& detail) const;
+
+ virtual void
+ get_keyfile (chroot const& chroot,
+ keyfile& keyfile) const;
+
+ virtual void
+ set_keyfile (chroot& chroot,
+ keyfile const& keyfile,
+ string_list& used_keys);
+
+ /**
+ * Get user data as a map of key-value pairs.
+ *
+ * @returns a reference to a string map.
+ */
+ string_map const&
+ get_data () const;
+
+ /**
+ * Get the value of a single user data key.
+ *
+ * @param key the key to search for.
+ * @param value the string to store the key's value in. Only
+ * modified if the key is found.
+ * @returns true if found, false if not found.
+ */
+ bool
+ get_data (std::string const& key,
+ std::string& value) const;
+
+ /**
+ * Set user data from a string map. Note that this method does
+ * not perform permissions checking.
+ *
+ * @param data the user data to set.
+ */
+ void
+ set_data (string_map const& data);
+
+ /**
+ * Set a single key-value pair. Note that this method does not
+ * perform permissions checking.
+ *
+ * @param key the key to set.
+ * @param value the value of the key.
+ */
+ void
+ set_data (std::string const& key,
+ std::string const& value);
+
+ /**
+ * Get the set of keys allowed to be modified by a user.
+ *
+ * @returns a string set of keys.
+ */
+ string_set const&
+ get_user_modifiable_keys () const;
+
+ /**
+ * Set the set of keys allowed to be modified by a user.
+ *
+ * @param keys a string set of keys.
+ */
+ void
+ set_user_modifiable_keys (string_set const& keys);
+
+ /**
+ * Get the set of keys allowed to be modified by root.
+ *
+ * @returns a string set of keys.
+ */
+ string_set const&
+ get_root_modifiable_keys () const;
+
+ /**
+ * Set the set of keys allowed to be modified by root.
+ *
+ * @param keys a string set of keys.
+ */
+ void
+ set_root_modifiable_keys (string_set const& keys);
+
+ /**
+ * Set data for the current user. Only keys set using
+ * set_user_modifiable_keys() are permitted to be set, otherwise
+ * an exception will be thrown.
+ *
+ * @param string_map a map of key-value pairs.
+ */
+ void
+ set_user_data(string_map const& data);
+
+ /**
+ * Set data for root. Only keys set using
+ * set_user_modifiable_keys() and set_root_modifiable_keys() are
+ * permitted to be set, otherwise an exception will be thrown.
+ *
+ * @param data a map of key-value pairs.
+ */
+ void
+ set_root_data(string_map const& data);
+
+ private:
+ /**
+ * Generic function for setting data for any user.
+ *
+ * @param data a map of key-value pairs.
+ * @param allowed_keys the keys which may be used.
+ * @param root whether or not the user is the root user.
+ */
+ void
+ set_data(string_map const& data,
+ string_set const& allowed_keys,
+ bool root);
+
+ /// Mapping between user keys and values.
+ string_map userdata;
+ /// Environment checking.
+ string_set env;
+ /// Keys modifiable by users.
+ string_set user_modifiable_keys;
+ /// Keys modifiable by root.
+ string_set root_modifiable_keys;
+ };
+
+}
+
+#endif /* SBUILD_CHROOT_FACET_USERDATA_H */
+
+/*
+ * Local Variables:
+ * mode:C++
+ * End:
+ */
diff --git a/sbuild/sbuild-chroot.cc b/sbuild/sbuild-chroot.cc
index af4c512f..a7e5bee2 100644
--- a/sbuild/sbuild-chroot.cc
+++ b/sbuild/sbuild-chroot.cc
@@ -40,6 +40,7 @@
#include "sbuild-chroot-facet-session.h"
#include "sbuild-chroot-facet-session-clonable.h"
#include "sbuild-chroot-facet-source.h"
+#include "sbuild-chroot-facet-userdata.h"
#include "sbuild-fdstream.h"
#include "sbuild-lock.h"
@@ -120,6 +121,7 @@ sbuild::chroot::chroot ():
{
add_facet(sbuild::chroot_facet_personality::create());
add_facet(sbuild::chroot_facet_session_clonable::create());
+ add_facet(sbuild::chroot_facet_userdata::create());
}
sbuild::chroot::chroot (const chroot& rhs):
@@ -739,7 +741,68 @@ sbuild::chroot::set_keyfile (keyfile const& keyfile)
(*pos)->set_keyfile(*this, keyfile, used_keys);
}
- keyfile.check_keys(this->get_name(), used_keys);
+ // Check for keys which weren't set above. These may be either
+ // invalid keys or user-set keys. The latter must have a namespace
+ // separated with one or more periods. These may be later
+ // overridden by the user on the commandline.
+ {
+ std::string const& group = this->get_name();
+ const string_list total(keyfile.get_keys(group));
+
+ const string_set a(total.begin(), total.end());
+ const string_set b(used_keys.begin(), used_keys.end());
+
+ string_set unused;
+
+ set_difference(a.begin(), a.end(),
+ b.begin(), b.end(),
+ inserter(unused, unused.begin()));
+
+ string_map userdata_keys;
+ chroot_facet_userdata::ptr userdata =
+ get_facet<chroot_facet_userdata>();
+ for (string_set::const_iterator pos = unused.begin();
+ pos != unused.end();
+ ++pos)
+ {
+ if (userdata)
+ {
+ try
+ {
+ std::string value;
+ if (keyfile.get_value(get_name(), *pos, value))
+ userdata->set_data(*pos, value);
+ }
+ catch (std::runtime_error const& e)
+ {
+ keyfile::size_type line = keyfile.get_line(group, *pos);
+ keyfile::error w(line, group, *pos,
+ keyfile::PASSTHROUGH_LGK, e.what());
+
+ try
+ {
+ sbuild::error_base const& r =
+ dynamic_cast<sbuild::error_base const&>(e);
+ w.set_reason(r.get_reason());
+ }
+ catch (...)
+ {
+ }
+ log_exception_warning(w);
+ }
+ }
+ else
+ {
+ // This should never happen since userdata should
+ // always be present. Note the error is duplicated in
+ // the facet.
+ keyfile::size_type line = keyfile.get_line(group, *pos);
+ keyfile::error e(line, group, keyfile::INVALID_KEY, *pos);
+ e.set_reason(_("This option is not valid for this chroot type"));
+ log_exception_warning(e);
+ }
+ }
+ }
}
void
diff --git a/sbuild/sbuild-keyfile-base.cc b/sbuild/sbuild-keyfile-base.cc
index 401beb4b..e7dba9fe 100644
--- a/sbuild/sbuild-keyfile-base.cc
+++ b/sbuild/sbuild-keyfile-base.cc
@@ -71,6 +71,11 @@ namespace
emap(keyfile_base::INVALID_GROUP,
N_("line %1%: Invalid group: “%4%”")),
// TRANSLATORS: %1% = line number in configuration file
+ // TRANSLATORS: %2% = group name ("[groupname]" in configuration file)
+ // TRANSLATORS: %4% = key name ("keyname=value" in configuration file)
+ emap(keyfile_base::INVALID_KEY,
+ N_("line %1% [%2%]: Invalid key ‘%4%’ used")),
+ // TRANSLATORS: %1% = line number in configuration file
// TRANSLATORS: %4% = line contents as read from the configuration file
emap(keyfile_base::INVALID_LINE,
N_("line %1%: Invalid line: “%4%”")),
@@ -124,7 +129,7 @@ namespace
// TRANSLATORS: %2% = group name ("[groupname]" in configuration file)
// TRANSLATORS: %4% = key name ("keyname=value" in configuration file)
emap(keyfile_base::UNKNOWN_KEY,
- N_("line %1% [%2%]: Unknown key ‘%4%’ used")),
+ N_("line %1% [%2%]: Unknown key ‘%4%’ used"))
};
}
diff --git a/sbuild/sbuild-keyfile-base.h b/sbuild/sbuild-keyfile-base.h
index a6f0e601..2e8c3f20 100644
--- a/sbuild/sbuild-keyfile-base.h
+++ b/sbuild/sbuild-keyfile-base.h
@@ -64,6 +64,7 @@ namespace sbuild
DUPLICATE_GROUP, ///< The group is a duplicate.
DUPLICATE_KEY, ///< The key is a duplicate.
INVALID_GROUP, ///< The group is invalid.
+ INVALID_KEY, ///< The key is invalid.
INVALID_LINE, ///< The line is invalid.
MISSING_KEY, ///< The key is missing.
MISSING_KEY_NL, ///< The key is missing (no line specified).
diff --git a/sbuild/sbuild-session.cc b/sbuild/sbuild-session.cc
index 6212ae57..9a061a1f 100644
--- a/sbuild/sbuild-session.cc
+++ b/sbuild/sbuild-session.cc
@@ -22,6 +22,7 @@
#include "sbuild-chroot-facet-personality.h"
#include "sbuild-chroot-facet-session.h"
#include "sbuild-chroot-facet-session-clonable.h"
+#include "sbuild-chroot-facet-userdata.h"
#ifdef SBUILD_FEATURE_PAM
#include "sbuild-auth-pam.h"
#include "sbuild-auth-pam-conv.h"
@@ -212,6 +213,7 @@ session::session (std::string const& service,
termios_ok(false),
verbosity(),
preserve_environment(false),
+ user_options(),
cwd(sbuild::getcwd())
{
}
@@ -292,6 +294,18 @@ session::set_preserve_environment (bool preserve_environment)
this->preserve_environment = preserve_environment;
}
+string_map const&
+session::get_user_options () const
+{
+ return this->user_options;
+}
+
+void
+session::set_user_options (string_map const& user_options)
+{
+ this->user_options = user_options;
+}
+
bool
session::get_force () const
{
@@ -667,6 +681,22 @@ session::run_impl ()
if (!this->verbosity.empty())
chroot->set_verbosity(this->verbosity);
+ // Set user options.
+ chroot_facet_userdata::ptr userdata =
+ chroot->get_facet<chroot_facet_userdata>();
+ if (userdata)
+ {
+ // If the user running the command is root, or the user
+ // being switched to is root, permit setting of
+ // root-modifiable options in addition to
+ // user-modifiable options.
+ if (this->authstat->get_uid() == 0 ||
+ this->authstat->get_ruid() == 0)
+ userdata->set_root_data(this->user_options);
+ else
+ userdata->set_user_data(this->user_options);
+ }
+
// Following authentication success, default child status to
// success so that operations such as beginning, ending and
// recovering sessions will return success unless an
diff --git a/sbuild/sbuild-session.h b/sbuild/sbuild-session.h
index 59404d9c..668f8bdb 100644
--- a/sbuild/sbuild-session.h
+++ b/sbuild/sbuild-session.h
@@ -221,6 +221,22 @@ namespace sbuild
set_preserve_environment (bool preserve_environment);
/**
+ * Get user options.
+ *
+ * @returns map of user options.
+ */
+ string_map const&
+ get_user_options () const;
+
+ /**
+ * Set user options.
+ *
+ * @param user_options map of user options.
+ */
+ void
+ set_user_options (string_map const& user_options);
+
+ /**
* Get the force status of this session.
*
* @returns true if operation will be forced, otherwise false.
@@ -534,6 +550,8 @@ namespace sbuild
std::string verbosity;
/// Preserve environment?
bool preserve_environment;
+ /// User-defined options.
+ string_map user_options;
protected:
/// Current working directory.
diff --git a/sbuild/sbuild-types.h b/sbuild/sbuild-types.h
index 26a51410..c1ae4078 100644
--- a/sbuild/sbuild-types.h
+++ b/sbuild/sbuild-types.h
@@ -23,6 +23,7 @@
#include <ctime>
#include <ios>
#include <locale>
+#include <map>
#include <set>
#include <string>
#include <vector>
@@ -39,6 +40,9 @@ namespace sbuild
/// A string set.
typedef std::set<std::string> string_set;
+ /// A string map.
+ typedef std::map<std::string, std::string> string_map;
+
/**
* A date representation.
*/
diff --git a/test/.gitignore b/test/.gitignore
index ab5602c1..2252e2e5 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -7,6 +7,7 @@ sbuild-chroot-btrfs-snapshot
sbuild-chroot-lvm-snapshot
sbuild-chroot-plain
sbuild-chroot-config
+sbuild-chroot-facet-userdata
sbuild-environment
sbuild-keyfile
sbuild-lock
diff --git a/test/Makefile.am b/test/Makefile.am
index 003f7f2f..e3fe5d16 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -36,6 +36,7 @@ noinst_PROGRAMS = \
sbuild-chroot-lvm-snapshot \
sbuild-chroot-btrfs-snapshot \
sbuild-chroot-config \
+ sbuild-chroot-facet-userdata \
sbuild-environment \
sbuild-keyfile \
sbuild-lock \
@@ -60,6 +61,7 @@ TESTS = setup-test-data \
sbuild-chroot-lvm-snapshot \
sbuild-chroot-btrfs-snapshot \
sbuild-chroot-config \
+ sbuild-chroot-facet-userdata \
sbuild-environment \
sbuild-keyfile \
sbuild-log \
@@ -138,6 +140,11 @@ sbuild_chroot_loopback_SOURCES = \
test-sbuild-chroot.h
sbuild_chroot_loopback_LDADD = libtest.la
+sbuild_chroot_facet_userdata_SOURCES = \
+ sbuild-chroot-facet-userdata.cc \
+ test-sbuild-chroot.h
+sbuild_chroot_facet_userdata_LDADD = libtest.la
+
sbuild_chroot_config_SOURCES = sbuild-chroot-config.cc
sbuild_chroot_config_LDADD = libtest.la
diff --git a/test/sbuild-chroot-facet-userdata.cc b/test/sbuild-chroot-facet-userdata.cc
new file mode 100644
index 00000000..f6674773
--- /dev/null
+++ b/test/sbuild-chroot-facet-userdata.cc
@@ -0,0 +1,156 @@
+/* Copyright © 2006-2008 Roger Leigh <rleigh@debian.org>
+ *
+ * schroot is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * schroot is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ *********************************************************************/
+
+#include <config.h>
+
+#include <sbuild/sbuild-chroot-directory.h>
+#include <sbuild/sbuild-chroot-facet-userdata.h>
+
+#include <cppunit/extensions/HelperMacros.h>
+
+using namespace CppUnit;
+
+using sbuild::_;
+
+class test_chroot_facet_userdata : public TestFixture
+{
+ CPPUNIT_TEST_SUITE(test_chroot_facet_userdata);
+ CPPUNIT_TEST(test_data_set);
+ CPPUNIT_TEST_EXCEPTION(test_data_fail1, sbuild::chroot_facet_userdata::error);
+ CPPUNIT_TEST_EXCEPTION(test_data_fail2, sbuild::chroot_facet_userdata::error);
+ CPPUNIT_TEST(test_user_set);
+ CPPUNIT_TEST_EXCEPTION(test_user_fail1, sbuild::chroot_facet_userdata::error);
+ CPPUNIT_TEST_EXCEPTION(test_user_fail2, sbuild::chroot_facet_userdata::error);
+ CPPUNIT_TEST(test_root_set);
+ CPPUNIT_TEST_EXCEPTION(test_root_fail, sbuild::chroot_facet_userdata::error);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ test_chroot_facet_userdata():
+ chroot(),
+ userdata()
+ {}
+
+ void setUp()
+ {
+ chroot = sbuild::chroot::create("directory");
+ CPPUNIT_ASSERT(chroot);
+
+ userdata = chroot->get_facet<sbuild::chroot_facet_userdata>();
+ CPPUNIT_ASSERT(userdata);
+
+ sbuild::string_set userkeys;
+ userkeys.insert("sbuild.resolver");
+ userkeys.insert("debian.dist");
+ userkeys.insert("sbuild.purge");
+ sbuild::string_set rootkeys;
+ rootkeys.insert("debian.apt-update");
+ userdata->set_user_modifiable_keys(userkeys);
+ userdata->set_root_modifiable_keys(rootkeys);
+ }
+
+ void tearDown()
+ {
+ this->chroot = sbuild::chroot::ptr();
+ this->userdata = sbuild::chroot_facet_userdata::ptr();
+ }
+
+ void test_data_set()
+ {
+ userdata->set_data("custom.test1", "testval");
+ userdata->set_data("sbuild.resolver", "apt");
+
+ std::string t1;
+ CPPUNIT_ASSERT(userdata->get_data("custom.test1", t1));
+ CPPUNIT_ASSERT(t1 == "testval");
+
+ std::string t2;
+ CPPUNIT_ASSERT(userdata->get_data("sbuild.resolver", t2));
+ CPPUNIT_ASSERT(t2 == "apt");
+
+ std::string t3("invalid");
+ CPPUNIT_ASSERT(!userdata->get_data("invalidkey", t3));
+ CPPUNIT_ASSERT(t3 == "invalid");
+ }
+
+ void test_data_fail1()
+ {
+ userdata->set_data("custom", "testval");
+ }
+
+ void test_data_fail2()
+ {
+ userdata->set_data("custom.key.set", "testval1");
+ userdata->set_data("custom.key_set", "testval2");
+ }
+
+ void test_user_set()
+ {
+ sbuild::string_map d;
+ d.insert(std::make_pair("sbuild.resolver", "aptitude"));
+ userdata->set_user_data(d);
+
+ std::string t1;
+ CPPUNIT_ASSERT(userdata->get_data("sbuild.resolver", t1));
+ CPPUNIT_ASSERT(t1 == "aptitude");
+ }
+
+ void test_user_fail1()
+ {
+ sbuild::string_map d;
+ d.insert(std::make_pair("sbuild.apt-update", "true"));
+ userdata->set_user_data(d);
+ }
+
+ void test_user_fail2()
+ {
+ // Use root key.
+ sbuild::string_map d;
+ d.insert(std::make_pair("debian.apt-update", "false"));
+ userdata->set_user_data(d);
+ }
+
+ void test_root_set()
+ {
+ sbuild::string_map d;
+ d.insert(std::make_pair("sbuild.resolver", "aptitude"));
+ d.insert(std::make_pair("debian.apt-update", "false"));
+ userdata->set_root_data(d);
+
+ std::string t1;
+ CPPUNIT_ASSERT(userdata->get_data("sbuild.resolver", t1));
+ CPPUNIT_ASSERT(t1 == "aptitude");
+
+ std::string t2;
+ CPPUNIT_ASSERT(userdata->get_data("debian.apt-update", t2));
+ CPPUNIT_ASSERT(t2 == "false");
+ }
+
+ void test_root_fail()
+ {
+ sbuild::string_map d;
+ d.insert(std::make_pair("invalid.key", "testv"));
+ userdata->set_root_data(d);
+ }
+
+private:
+ sbuild::chroot::ptr chroot;
+ sbuild::chroot_facet_userdata::ptr userdata;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(test_chroot_facet_userdata);
diff --git a/test/test-sbuild-chroot.h b/test/test-sbuild-chroot.h
index b68c2e94..efd1d1c6 100644
--- a/test/test-sbuild-chroot.h
+++ b/test/test-sbuild-chroot.h
@@ -27,7 +27,9 @@
#include <sbuild/sbuild-chroot-facet-source.h>
#include <sbuild/sbuild-chroot-facet-source-clonable.h>
#include <sbuild/sbuild-chroot-facet-union.h>
+#include <sbuild/sbuild-chroot-facet-userdata.h>
#include <sbuild/sbuild-i18n.h>
+#include <sbuild/sbuild-types.h>
#include <sbuild/sbuild-util.h>
#include <algorithm>
@@ -168,6 +170,21 @@ public:
usrc->set_source_groups(sbuild::split_string("sgroup1,sgroup2", ","));
usrc->set_source_root_groups(sbuild::split_string("sgroup3,sgroup4", ","));
}
+
+ sbuild::chroot_facet_userdata::ptr pusr
+ (chroot->get_facet<sbuild::chroot_facet_userdata>());
+ if (pusr)
+ {
+ pusr->set_data("custom.test1", "testval");
+ sbuild::string_set userkeys;
+ userkeys.insert("sbuild.resolver");
+ userkeys.insert("debian.dist");
+ userkeys.insert("sbuild.purge");
+ sbuild::string_set rootkeys;
+ rootkeys.insert("debian.apt-update");
+ pusr->set_user_modifiable_keys(userkeys);
+ pusr->set_root_modifiable_keys(rootkeys);
+ }
}
void tearDown()
@@ -181,6 +198,7 @@ public:
env.add("SESSION_ID", "test-name");
env.add("CHROOT_DESCRIPTION", "test-description");
env.add("CHROOT_SCRIPT_CONFIG", sbuild::normalname(std::string(SCHROOT_SYSCONF_DIR) + "/default/config"));
+ env.add("CUSTOM_TEST1", "testval");
}
void setup_keyfile_chroot (sbuild::keyfile& keyfile,
@@ -198,6 +216,9 @@ public:
keyfile.set_value(group, "script-config", "default/config");
keyfile.set_value(group, "message-verbosity", "quiet");
keyfile.set_value(group, "preserve-environment", "false");
+ keyfile.set_value(group, "user-modifiable-keys", "debian.dist,sbuild.purge,sbuild.resolver");
+ keyfile.set_value(group, "root-modifiable-keys", "debian.apt-update");
+ keyfile.set_value(group, "custom.test1", "testval");
}
void setup_keyfile_session (sbuild::keyfile& keyfile,