summaryrefslogtreecommitdiffstats
path: root/unit/atf-src/tools/requirements.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--unit/atf-src/tools/requirements.cpp325
1 files changed, 325 insertions, 0 deletions
diff --git a/unit/atf-src/tools/requirements.cpp b/unit/atf-src/tools/requirements.cpp
new file mode 100644
index 0000000..97fcdc8
--- /dev/null
+++ b/unit/atf-src/tools/requirements.cpp
@@ -0,0 +1,325 @@
+//
+// Automated Testing Framework (atf)
+//
+// Copyright (c) 2007 The NetBSD Foundation, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+extern "C" {
+#include <sys/param.h>
+#include <sys/sysctl.h>
+}
+
+#include <cassert>
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <stdexcept>
+
+#include "config.hpp"
+#include "defs.hpp"
+#include "env.hpp"
+#include "fs.hpp"
+#include "requirements.hpp"
+#include "text.hpp"
+#include "user.hpp"
+
+namespace impl = tools;
+
+namespace {
+
+typedef std::map< std::string, std::string > vars_map;
+
+static
+bool
+has_program(const tools::fs::path& program)
+{
+ bool found = false;
+
+ if (program.is_absolute()) {
+ found = tools::fs::is_executable(program);
+ } else {
+ if (program.str().find('/') != std::string::npos)
+ throw std::runtime_error("Relative paths are not allowed "
+ "when searching for a program (" +
+ program.str() + ")");
+
+ const std::vector< std::string > dirs = tools::text::split(
+ tools::env::get("PATH"), ":");
+ for (std::vector< std::string >::const_iterator iter = dirs.begin();
+ !found && iter != dirs.end(); iter++) {
+ const tools::fs::path& p = tools::fs::path(*iter) / program;
+ if (tools::fs::is_executable(p))
+ found = true;
+ }
+ }
+
+ return found;
+}
+
+static
+std::string
+check_arch(const std::string& arches)
+{
+ const std::vector< std::string > v = tools::text::split(arches, " ");
+
+ for (std::vector< std::string >::const_iterator iter = v.begin();
+ iter != v.end(); iter++) {
+ if ((*iter) == tools::config::get("atf_arch"))
+ return "";
+ }
+
+ if (v.size() == 1)
+ return "Requires the '" + arches + "' architecture";
+ else
+ return "Requires one of the '" + arches + "' architectures";
+}
+
+static
+std::string
+check_config(const std::string& variables, const vars_map& config)
+{
+ const std::vector< std::string > v = tools::text::split(variables, " ");
+ for (std::vector< std::string >::const_iterator iter = v.begin();
+ iter != v.end(); iter++) {
+ if (config.find((*iter)) == config.end())
+ return "Required configuration variable '" + (*iter) + "' not "
+ "defined";
+ }
+ return "";
+}
+
+static
+std::string
+check_files(const std::string& progs)
+{
+ const std::vector< std::string > v = tools::text::split(progs, " ");
+ for (std::vector< std::string >::const_iterator iter = v.begin();
+ iter != v.end(); iter++) {
+ const tools::fs::path file(*iter);
+ if (!file.is_absolute())
+ throw std::runtime_error("Relative paths are not allowed when "
+ "checking for a required file (" + file.str() + ")");
+ if (!tools::fs::exists(file))
+ return "Required file '" + file.str() + "' not found";
+ }
+ return "";
+}
+
+static
+std::string
+check_machine(const std::string& machines)
+{
+ const std::vector< std::string > v = tools::text::split(machines, " ");
+
+ for (std::vector< std::string >::const_iterator iter = v.begin();
+ iter != v.end(); iter++) {
+ if ((*iter) == tools::config::get("atf_machine"))
+ return "";
+ }
+
+ if (v.size() == 1)
+ return "Requires the '" + machines + "' machine type";
+ else
+ return "Requires one of the '" + machines + "' machine types";
+}
+
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
+static
+std::string
+check_memory_sysctl(const int64_t needed, const char* sysctl_variable)
+{
+ int64_t available;
+ std::size_t available_length = sizeof(available);
+ if (::sysctlbyname(sysctl_variable, &available, &available_length,
+ NULL, 0) == -1) {
+ const char* e = std::strerror(errno);
+ return "Failed to get sysctl(hw.usermem64) value: " + std::string(e);
+ }
+
+ if (available < needed) {
+ return "Not enough memory; needed " + tools::text::to_string(needed) +
+ ", available " + tools::text::to_string(available);
+ } else
+ return "";
+}
+# if defined(__APPLE__)
+static
+std::string
+check_memory_darwin(const int64_t needed)
+{
+ return check_memory_sysctl(needed, "hw.usermem");
+}
+# elif defined(__FreeBSD__)
+static
+std::string
+check_memory_freebsd(const int64_t needed)
+{
+ return check_memory_sysctl(needed, "hw.usermem");
+}
+# elif defined(__NetBSD__)
+static
+std::string
+check_memory_netbsd(const int64_t needed)
+{
+ return check_memory_sysctl(needed, "hw.usermem64");
+}
+# else
+# error "Conditional error"
+# endif
+#else
+static
+std::string
+check_memory_unknown(const int64_t needed ATF_DEFS_ATTRIBUTE_UNUSED)
+{
+ return "";
+}
+#endif
+
+static
+std::string
+check_memory(const std::string& raw_memory)
+{
+ const int64_t needed = tools::text::to_bytes(raw_memory);
+
+#if defined(__APPLE__)
+ return check_memory_darwin(needed);
+#elif defined(__FreeBSD__)
+ return check_memory_freebsd(needed);
+#elif defined(__NetBSD__)
+ return check_memory_netbsd(needed);
+#else
+ return check_memory_unknown(needed);
+#endif
+}
+
+static
+std::string
+check_progs(const std::string& progs)
+{
+ const std::vector< std::string > v = tools::text::split(progs, " ");
+ for (std::vector< std::string >::const_iterator iter = v.begin();
+ iter != v.end(); iter++) {
+ if (!has_program(tools::fs::path(*iter)))
+ return "Required program '" + (*iter) + "' not found in the PATH";
+ }
+ return "";
+}
+
+static
+std::string
+check_user(const std::string& user, const vars_map& config)
+{
+ if (user == "root") {
+ if (!tools::user::is_root())
+ return "Requires root privileges";
+ else
+ return "";
+ } else if (user == "unprivileged") {
+ if (tools::user::is_root()) {
+ const vars_map::const_iterator iter = config.find(
+ "unprivileged-user");
+ if (iter == config.end())
+ return "Requires an unprivileged user and the "
+ "'unprivileged-user' configuration variable is not set";
+ else {
+ const std::string& unprivileged_user = (*iter).second;
+ try {
+ (void)tools::user::get_user_ids(unprivileged_user);
+ return "";
+ } catch (const std::runtime_error& e) {
+ return "Failed to get information for user " +
+ unprivileged_user;
+ }
+ }
+ } else
+ return "";
+ } else
+ throw std::runtime_error("Invalid value '" + user + "' for property "
+ "require.user");
+}
+
+} // anonymous namespace
+
+std::string
+impl::check_requirements(const vars_map& metadata,
+ const vars_map& config)
+{
+ std::string failure_reason = "";
+
+ for (vars_map::const_iterator iter = metadata.begin();
+ failure_reason.empty() && iter != metadata.end(); iter++) {
+ const std::string& name = (*iter).first;
+ const std::string& value = (*iter).second;
+ assert(!value.empty()); // Enforced by application/X-atf-tp parser.
+
+ if (name == "require.arch")
+ failure_reason = check_arch(value);
+ else if (name == "require.config")
+ failure_reason = check_config(value, config);
+ else if (name == "require.files")
+ failure_reason = check_files(value);
+ else if (name == "require.machine")
+ failure_reason = check_machine(value);
+ else if (name == "require.memory")
+ failure_reason = check_memory(value);
+ else if (name == "require.progs")
+ failure_reason = check_progs(value);
+ else if (name == "require.user")
+ failure_reason = check_user(value, config);
+ else {
+ // Unknown require.* properties are forbidden by the
+ // application/X-atf-tp parser.
+ assert(failure_reason.find("require.") != 0);
+ }
+ }
+
+ return failure_reason;
+}
+
+std::pair< int, int >
+impl::get_required_user(const vars_map& metadata,
+ const vars_map& config)
+{
+ const vars_map::const_iterator user = metadata.find(
+ "require.user");
+ if (user == metadata.end())
+ return std::make_pair(-1, -1);
+
+ if ((*user).second == "unprivileged") {
+ if (tools::user::is_root()) {
+ const vars_map::const_iterator iter = config.find(
+ "unprivileged-user");
+ try {
+ return tools::user::get_user_ids((*iter).second);
+ } catch (const std::exception& e) {
+ std::abort(); // This has been validated by check_user.
+ }
+ } else {
+ return std::make_pair(-1, -1);
+ }
+ } else
+ return std::make_pair(-1, -1);
+}