diff options
-rw-r--r-- | bin/schroot/schroot-main-base.cc | 1 | ||||
-rw-r--r-- | bin/schroot/schroot-options-base.cc | 2 | ||||
-rw-r--r-- | bin/schroot/schroot-options-base.h | 5 | ||||
-rw-r--r-- | bin/schroot/schroot-options.cc | 17 | ||||
-rw-r--r-- | man/schroot-setup.5.man | 5 | ||||
-rw-r--r-- | man/schroot.1.man | 10 | ||||
-rw-r--r-- | man/schroot.conf.5.man | 40 | ||||
-rw-r--r-- | po/POTFILES.in | 1 | ||||
-rw-r--r-- | sbuild/Makefile.am | 2 | ||||
-rw-r--r-- | sbuild/sbuild-basic-keyfile.h | 310 | ||||
-rw-r--r-- | sbuild/sbuild-chroot-facet-userdata.cc | 356 | ||||
-rw-r--r-- | sbuild/sbuild-chroot-facet-userdata.h | 221 | ||||
-rw-r--r-- | sbuild/sbuild-chroot.cc | 65 | ||||
-rw-r--r-- | sbuild/sbuild-keyfile-base.cc | 7 | ||||
-rw-r--r-- | sbuild/sbuild-keyfile-base.h | 1 | ||||
-rw-r--r-- | sbuild/sbuild-session.cc | 30 | ||||
-rw-r--r-- | sbuild/sbuild-session.h | 18 | ||||
-rw-r--r-- | sbuild/sbuild-types.h | 4 | ||||
-rw-r--r-- | test/.gitignore | 1 | ||||
-rw-r--r-- | test/Makefile.am | 7 | ||||
-rw-r--r-- | test/sbuild-chroot-facet-userdata.cc | 156 | ||||
-rw-r--r-- | test/test-sbuild-chroot.h | 21 |
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, |