From 02a2ad393de6cbea726514226e3419db6517307c Mon Sep 17 00:00:00 2001 From: "A. Wilcox" Date: Sat, 19 Oct 2019 21:35:27 -0500 Subject: hscript: Implement Firmware, and add tests --- CMakeLists.txt | 7 +++ hscript/meta.cc | 34 +++++++++++++++ hscript/meta.hh | 5 +++ hscript/script.cc | 50 ++++++++++++++++++++++ tests/fixtures/0111-firmware-true.installfile | 6 +++ tests/fixtures/0112-firmware-false.installfile | 6 +++ tests/fixtures/0113-firmware-invalid.installfile | 7 +++ tests/fixtures/0114-firmware-duplicate.installfile | 7 +++ tests/spec/validator.rb | 33 ++++++++++++++ tools/hscript-validate/validator.cc | 4 ++ 10 files changed, 159 insertions(+) create mode 100644 tests/fixtures/0111-firmware-true.installfile create mode 100644 tests/fixtures/0112-firmware-false.installfile create mode 100644 tests/fixtures/0113-firmware-invalid.installfile create mode 100644 tests/fixtures/0114-firmware-duplicate.installfile diff --git a/CMakeLists.txt b/CMakeLists.txt index 16f9f58..faf04d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,13 @@ option(BUILD_TOOLS "Enable building of tools (Validator, Simulator, etc)" ON) option(COVERAGE "Build for code coverage tests (slow)" OFF) option(VALGRIND "Run Valgrind during test phase" OFF) +option(FIRMWARE "Support loading and installation of non-libre firmware (DANGEROUS)" OFF) +mark_as_advanced(FORCE FIRMWARE) + +IF(FIRMWARE) + add_definitions(-DNON_LIBRE_FIRMWARE) +ENDIF(FIRMWARE) + IF(COVERAGE) SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} --coverage") SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} --coverage") diff --git a/hscript/meta.cc b/hscript/meta.cc index 0248af9..b7da9d2 100644 --- a/hscript/meta.cc +++ b/hscript/meta.cc @@ -167,6 +167,40 @@ Key *PkgInstall::parseFromData(const std::string &data, int lineno, int *errors, } +Key *Firmware::parseFromData(const std::string &data, int lineno, int *errors, + int *warnings) { + bool value; + if(!BooleanKey::parse(data, "installfile:" + std::to_string(lineno), + "firmware", &value)) { + if(errors) *errors += 1; + return nullptr; + } + + if(value) { +#ifdef NON_LIBRE_FIRMWARE + output_warning("installfile:" + std::to_string(lineno), + "firmware: You have requested non-libre firmware. " + "This may cause security issues, system instability, " + "and many other issues. You should not enable this " + "option unless your system absolutely requires it."); +#else + if(errors) *errors += 1; + output_error("installfile:" + std::to_string(lineno), + "firmware: You have requested non-libre firmware, " + "but this version of Horizon does not support " + "non-libre firmware.", "Installation cannot proceed."); + return nullptr; +#endif + } + return new Firmware(lineno, value); +} + +bool Firmware::execute(ScriptOptions) const { + /* By itself, this does nothing. */ + return true; +} + + /* LCOV_EXCL_START */ bool PkgInstall::validate(ScriptOptions) const { /* Any validation errors would have occurred above. */ diff --git a/hscript/meta.hh b/hscript/meta.hh index 035bb8a..8a50576 100644 --- a/hscript/meta.hh +++ b/hscript/meta.hh @@ -51,6 +51,11 @@ class Keymap : public StringKey { }; class Firmware : public BooleanKey { +private: + Firmware(int _line, bool _value) : BooleanKey(_line, _value) {} +public: + static Key *parseFromData(const std::string &, int, int*, int*); + bool execute(ScriptOptions) const override; }; class Timezone : public StringKey { diff --git a/hscript/script.cc b/hscript/script.cc index 0d1c10a..f9f16f5 100644 --- a/hscript/script.cc +++ b/hscript/script.cc @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -103,6 +104,10 @@ struct Script::ScriptPrivate { /*! Disk identification keys */ std::vector< std::unique_ptr > diskids; +#ifdef NON_LIBRE_FIRMWARE + std::unique_ptr firmware; +#endif + /*! Store +key_obj+ representing the key +key_name+. * @param key_name The name of the key that is being stored. * @param obj The Key object associated with the key. @@ -120,6 +125,8 @@ struct Script::ScriptPrivate { return store_pkginstall(obj, lineno, errors, warnings, opts); } else if(key_name == "rootpw") { return store_rootpw(obj, lineno, errors, warnings, opts); + } else if(key_name == "firmware") { + return store_firmware(obj, lineno, errors, warnings, opts); } else if(key_name == "mount") { std::unique_ptr mount(dynamic_cast(obj)); this->mounts.push_back(std::move(mount)); @@ -217,6 +224,23 @@ struct Script::ScriptPrivate { return true; } + bool store_firmware(Keys::Key *obj, int lineno, int *errors, int *warnings, + ScriptOptions opts) { + std::unique_ptr f(dynamic_cast(obj)); +#ifdef NON_LIBRE_FIRMWARE + if(this->firmware) { + DUPLICATE_ERROR(this->firmware, std::string("firmware"), + (this->firmware->test()) ? "true" : "false") + return false; + } + this->firmware = std::move(f); + return true; +#else + assert(!f->test()); + return true; +#endif + } + bool store_username(Keys::Key *obj, int lineno, int *errors, int *warnings, ScriptOptions opts) { if(accounts.size() >= 255) { @@ -532,6 +556,25 @@ bool Script::validate() const { } std::unique_ptr user_repo(user_key); this->internal->repos.push_back(std::move(user_repo)); + +#ifdef NON_LIBRE_FIRMWARE + /* REQ: Runner.Execute.firmware.Repository */ + if(this->internal->firmware && this->internal->firmware->test()) { + Keys::Repository *fw_key = dynamic_cast( + Horizon::Keys::Repository::parseFromData( + "https://distfiles.apkfission.net/adelie-stable/nonfree", + 0, nullptr, nullptr + ) + ); + if(!fw_key) { + output_error("internal", + "failed to create firmware repository"); + return false; + } + std::unique_ptr fw_repo(fw_key); + this->internal->repos.push_back(std::move(fw_repo)); + } +#endif } /* REQ: Runner.Validate.repository */ @@ -711,6 +754,13 @@ bool Script::execute() const { return false; } } + +#ifdef NON_LIBRE_FIRMWARE + /* REQ: Runner.Execute.firmware */ + if(this->internal->firmware && this->internal->firmware->test()) { + this->internal->packages.insert("linux-firmware"); + } +#endif output_step_end("pre-metadata"); /**************** NETWORK ****************/ diff --git a/tests/fixtures/0111-firmware-true.installfile b/tests/fixtures/0111-firmware-true.installfile new file mode 100644 index 0000000..a2d5ead --- /dev/null +++ b/tests/fixtures/0111-firmware-true.installfile @@ -0,0 +1,6 @@ +network false +hostname test.machine +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +firmware true diff --git a/tests/fixtures/0112-firmware-false.installfile b/tests/fixtures/0112-firmware-false.installfile new file mode 100644 index 0000000..9abc556 --- /dev/null +++ b/tests/fixtures/0112-firmware-false.installfile @@ -0,0 +1,6 @@ +network false +hostname test.machine +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +firmware false diff --git a/tests/fixtures/0113-firmware-invalid.installfile b/tests/fixtures/0113-firmware-invalid.installfile new file mode 100644 index 0000000..b823be1 --- /dev/null +++ b/tests/fixtures/0113-firmware-invalid.installfile @@ -0,0 +1,7 @@ +network false +hostname test.machine +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +# Intentional misspelling +firmware fasle diff --git a/tests/fixtures/0114-firmware-duplicate.installfile b/tests/fixtures/0114-firmware-duplicate.installfile new file mode 100644 index 0000000..372c645 --- /dev/null +++ b/tests/fixtures/0114-firmware-duplicate.installfile @@ -0,0 +1,7 @@ +network false +hostname test.machine +pkginstall adelie-base +rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/ +mount /dev/sda1 / +firmware false +firmware false diff --git a/tests/spec/validator.rb b/tests/spec/validator.rb index 023d49e..0a94dbf 100644 --- a/tests/spec/validator.rb +++ b/tests/spec/validator.rb @@ -166,6 +166,32 @@ RSpec.describe 'HorizonScript validation', :type => :aruba do run_validate expect(last_command_started).to have_output(/error: .*rootpw.*/) end + context "for 'firmware' key" do + it "always supports 'false' value" do + use_fixture '0112-firmware-false.installfile' + run_validate + expect(last_command_started).to have_output(PARSER_SUCCESS) + expect(last_command_started).to have_output(VALIDATOR_SUCCESS) + end + it "requires 'false' if built without support" do + use_fixture '0111-firmware-true.installfile' + run_validate + skip "This build supports firmware" if last_command_started.stdout =~ /supports non-free/ + expect(last_command_started).to have_output(/error: .*firmware/) + end + it "supports 'true' if built with support" do + use_fixture '0111-firmware-true.installfile' + run_validate + skip "This build does not support firmware" if last_command_started.stdout !~ /supports non-free/ + expect(last_command_started).to have_output(PARSER_SUCCESS) + expect(last_command_started).to have_output(VALIDATOR_SUCCESS) + end + it "requires a boolean value" do + use_fixture '0113-firmware-invalid.installfile' + run_validate + expect(last_command_started).to have_output(/error: .*firmware.*value/) + end + end context "for 'mount' key" do # Runner.Validate.mount. it "fails with an invalid value" do @@ -507,6 +533,13 @@ RSpec.describe 'HorizonScript validation', :type => :aruba do run_validate expect(last_command_started).to have_output(/error: .*duplicate.*rootpw/) end + # Runner.Validate.firmware. + it "fails with a duplicate 'firmware' key" do + use_fixture '0114-firmware-duplicate.installfile' + run_validate + skip "This build does not support firmware" if last_command_started.stdout !~ /supports non-free/ + expect(last_command_started).to have_output(/error: .*duplicate.*firmware/) + end end context "user account keys:" do context "'username'" do diff --git a/tools/hscript-validate/validator.cc b/tools/hscript-validate/validator.cc index 84c3be3..0d9a066 100644 --- a/tools/hscript-validate/validator.cc +++ b/tools/hscript-validate/validator.cc @@ -51,6 +51,10 @@ int main(int argc, char *argv[]) { bold_if_pretty(std::cout); std::cout << "HorizonScript Validation Utility version 0.1.0"; +#ifdef NON_LIBRE_FIRMWARE + colour_if_pretty(std::cout, "31"); + std::cout << " (supports non-free firmware)"; +#endif reset_if_pretty(std::cout); std::cout << std::endl; std::cout << "Copyright (c) 2019 Adélie Linux and contributors. AGPL-3.0 license." << std::endl; -- cgit v1.2.3-70-g09d2