From 4755175ad26eaff1765407bb01f8820f394f847b Mon Sep 17 00:00:00 2001 From: "A. Wilcox" Date: Thu, 24 Oct 2019 02:20:33 -0500 Subject: hscript: Fully implement Timezone, add tests --- hscript/meta.cc | 92 +++++++++++++++++++++++++++++++++++++++++++++++++------ hscript/meta.hh | 7 +++++ hscript/script.cc | 42 ++++++++++++++++++++++++- 3 files changed, 131 insertions(+), 10 deletions(-) (limited to 'hscript') diff --git a/hscript/meta.cc b/hscript/meta.cc index 79881f4..9f0da76 100644 --- a/hscript/meta.cc +++ b/hscript/meta.cc @@ -19,8 +19,8 @@ # include /* strerror */ # include /* errno */ # include /* chmod */ -# include #endif /* HAS_INSTALL_ENV */ +#include /* access - used by tz code even in RT env */ #include "meta.hh" #include "util/output.hh" @@ -183,6 +183,20 @@ Key *PkgInstall::parseFromData(const std::string &data, int lineno, int *errors, } +/* LCOV_EXCL_START */ +bool PkgInstall::validate(ScriptOptions) const { + /* Any validation errors would have occurred above. */ + return true; +} + + +bool PkgInstall::execute(ScriptOptions) const { + /* Package installation is handled in Script::execute. */ + return true; +} +/* LCOV_EXCL_STOP */ + + /* All ISO 639-1 language codes. * Source: https://www.loc.gov/standards/iso639-2/ISO-639-2_utf-8.txt * Python to construct table: @@ -318,18 +332,78 @@ bool Firmware::execute(ScriptOptions) const { } -/* LCOV_EXCL_START */ -bool PkgInstall::validate(ScriptOptions) const { - /* Any validation errors would have occurred above. */ - return true; +Key *Timezone::parseFromData(const std::string &data, int lineno, int *errors, + int *warnings) { + if(data.find_first_of(" .\\") != std::string::npos || data[0] == '/') { + if(errors) *errors += 1; + output_error("installfile:" + std::to_string(lineno), + "timezone: invalid timezone name"); + return nullptr; + } + + if(access("/usr/share/zoneinfo", X_OK) != 0) { + if(warnings) *warnings += 1; + output_warning("installfile:" + std::to_string(lineno), + "timezone: can't determine validity of timezone", + "zoneinfo data is missing or inaccessible"); + } else { + std::string zi_path = "/usr/share/zoneinfo/" + data; + if(access(zi_path.c_str(), F_OK) != 0) { + if(errors) *errors += 1; + output_error("installfile:" + std::to_string(lineno), + "timezone: unknown timezone '" + data + "'"); + return nullptr; + } + } + + return new Timezone(lineno, data); } +bool Timezone::execute(ScriptOptions opts) const { + if(opts.test(Simulate)) { + /* If the target doesn't have tzdata installed, copy the zoneinfo from + * the Horizon environment. */ + std::cout << "([ -f /target/usr/share/zoneinfo/" << this->value() + << " ] && ln -s /usr/share/zoneinfo/" << this->value() + << " /target/etc/localtime) || cp /usr/share/zoneinfo/" + << this->value() << " /target/etc/localtime"; + return true; + } -bool PkgInstall::execute(ScriptOptions) const { - /* Package installation is handled in Script::execute. */ - return true; +#ifdef HAS_INSTALL_ENV + std::string zi_path = "/usr/share/zoneinfo/" + this->value(); + std::string target_zi = "/target" + zi_path; + if(access(target_zi.c_str(), F_OK) == 0) { + if(symlink(zi_path.c_str(), "/target/etc/localtime") != 0) { + output_error("installfile:" + std::to_string(this->lineno()), + "timezone: failed to create symbolic link", + strerror(errno)); + return false; + } + return true; + } else { + /* The target doesn't have tzdata installed. We copy the zoneinfo + * file from the Horizon environment to the target. */ + std::ifstream zoneinfo(zi_path, std::ios::binary); + if(!zoneinfo) { + output_error("installfile:" + std::to_string(this->lineno()), + "timezone: failed to open zoneinfo file"); + return false; + } + std::ofstream output("/target/etc/localtime"); + if(!output) { + output_error("installfile:" + std::to_string(this->lineno()), + "timezone: failed to prepare target environment"); + return false; + } + + output << zoneinfo.rdbuf(); + return true; + } +#else + return false; +#endif } -/* LCOV_EXCL_STOP */ Key *Repository::parseFromData(const std::string &data, int lineno, int *errors, diff --git a/hscript/meta.hh b/hscript/meta.hh index 7dc3f68..b2e1963 100644 --- a/hscript/meta.hh +++ b/hscript/meta.hh @@ -66,6 +66,13 @@ public: }; class Timezone : public StringKey { +private: + Timezone(int _line, const std::string &my_zone) : + StringKey(_line, my_zone) {} +public: + static Key *parseFromData(const std::string &data, int lineno, int *errors, + int *warnings); + bool execute(ScriptOptions) const override; }; class Repository : public StringKey { diff --git a/hscript/script.cc b/hscript/script.cc index 0e99059..63ea3de 100644 --- a/hscript/script.cc +++ b/hscript/script.cc @@ -90,6 +90,9 @@ struct Script::ScriptPrivate { std::unique_ptr rootpw; /*! The system language. */ std::unique_ptr lang; + /* keymap */ + /*! The system timezone. */ + std::unique_ptr tzone; /*! Network addressing configuration */ std::vector< std::unique_ptr > addresses; @@ -141,6 +144,8 @@ struct Script::ScriptPrivate { return store_lang(obj, lineno, errors, warnings, opts); } else if(key_name == "firmware") { return store_firmware(obj, lineno, errors, warnings, opts); + } else if(key_name == "timezone") { + return store_timezone(obj, lineno, errors, warnings, opts); } else if(key_name == "repository") { std::unique_ptr repo(dynamic_cast(obj)); this->repos.push_back(std::move(repo)); @@ -263,6 +268,18 @@ struct Script::ScriptPrivate { return true; } + bool store_timezone(Keys::Key *obj, int lineno, int *errors, int *, + ScriptOptions) { + if(this->tzone) { + DUPLICATE_ERROR(this->tzone, std::string("timezone"), + this->tzone->value()) + return false; + } + std::unique_ptr t(dynamic_cast(obj)); + this->tzone = std::move(t); + return true; + } + bool store_username(Keys::Key *obj, int lineno, int *errors, int *, ScriptOptions) { if(accounts.size() >= 255) { @@ -560,7 +577,22 @@ bool Script::validate() const { if(!this->internal->firmware->validate(this->opts)) failures++; #endif - /* timezone */ + /* REQ: Runner.Execute.timezone */ + if(!internal->tzone) { + Keys::Timezone *utc = dynamic_cast( + Horizon::Keys::Timezone::parseFromData("UTC", 0, + &failures, nullptr) + ); + if(!utc) { + output_error("internal", "failed to create default timezone"); + return false; + } + std::unique_ptr zone(utc); + this->internal->tzone = std::move(zone); + } + + /* REQ: Runner.Validate.timezone */ + if(!this->internal->tzone->validate(this->opts)) failures++; /* REQ: Script.repository */ if(this->internal->repos.size() == 0) { @@ -900,6 +932,14 @@ bool Script::execute() const { return false; } + /* keymap */ + /* UserAccounts */ + + if(!this->internal->tzone->execute(opts)) { + EXECUTE_FAILURE("post-metadata"); + return false; + } + output_step_end("post-metadata"); return true; } -- cgit v1.2.3-60-g2f50