summaryrefslogtreecommitdiff
path: root/hscript/script_e.cc
diff options
context:
space:
mode:
authorA. Wilcox <AWilcox@Wilcox-Tech.com>2019-11-04 15:51:15 -0600
committerA. Wilcox <AWilcox@Wilcox-Tech.com>2019-11-04 15:51:15 -0600
commit0e5d52b23ae0b86e938905a332ad5b7439011dcc (patch)
treef97ab264328bec2ccdb7a9e5b54f3357d1ef3988 /hscript/script_e.cc
parent5a44e287dd8a6142c7a2ad7ddbc0570554ab149b (diff)
downloadhorizon-0e5d52b23ae0b86e938905a332ad5b7439011dcc.tar.gz
horizon-0e5d52b23ae0b86e938905a332ad5b7439011dcc.tar.bz2
horizon-0e5d52b23ae0b86e938905a332ad5b7439011dcc.tar.xz
horizon-0e5d52b23ae0b86e938905a332ad5b7439011dcc.zip
hscript: Refactor script.cc and disk.cc for maintainability
Diffstat (limited to 'hscript/script_e.cc')
-rw-r--r--hscript/script_e.cc400
1 files changed, 400 insertions, 0 deletions
diff --git a/hscript/script_e.cc b/hscript/script_e.cc
new file mode 100644
index 0000000..a35b818
--- /dev/null
+++ b/hscript/script_e.cc
@@ -0,0 +1,400 @@
+/*
+ * script_e.cc - Implementation of Script::execute
+ * libhscript, the HorizonScript library for
+ * Project Horizon
+ *
+ * Copyright (c) 2019 Adélie Linux and contributors. All rights reserved.
+ * This code is licensed under the AGPL 3.0 license, as noted in the
+ * LICENSE-code file in the root directory of this repository.
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+#include <algorithm>
+#include <fstream>
+#ifdef HAS_INSTALL_ENV
+# include <parted/parted.h>
+#endif /* HAS_INSTALL_ENV */
+
+#include "script.hh"
+#include "script_i.hh"
+
+#include "util/filesystem.hh"
+
+namespace Horizon {
+
+bool Script::execute() const {
+ bool success;
+ error_code ec;
+
+ /* assume create_directory will give us the error if removal fails */
+ if(fs::exists("/tmp/horizon", ec)) {
+ fs::remove_all("/tmp/horizon", ec);
+ }
+
+ if(!fs::create_directory("/tmp/horizon", ec)) {
+ output_error("internal", "could not create temporary directory",
+ ec.message());
+ return false;
+ }
+
+ /* REQ: Runner.Execute.Verify */
+ output_step_start("validate");
+ success = this->validate();
+ output_step_end("validate");
+ if(!success) {
+ /* REQ: Runner.Execute.Verify.Failure */
+ output_error("validator", "The HorizonScript failed validation",
+ "Check the output from the validator.");
+ return false;
+ }
+
+#define EXECUTE_FAILURE(phase) \
+ output_error(phase, "The HorizonScript failed to execute",\
+ "Check the log file for more details.")
+
+ /**************** DISK SETUP ****************/
+ output_step_start("disk");
+#ifdef HAS_INSTALL_ENV
+ if(opts.test(InstallEnvironment)) ped_device_probe_all();
+#endif /* HAS_INSTALL_ENV */
+ /* REQ: Runner.Execute.diskid */
+ for(auto &diskid : internal->diskids) {
+ if(!diskid->execute(opts)) {
+ EXECUTE_FAILURE("diskid");
+ return false;
+ }
+ }
+
+ /* REQ: Runner.Execute.disklabel */
+ for(auto &label : internal->disklabels) {
+ if(!label->execute(opts)) {
+ EXECUTE_FAILURE("disklabel");
+ return false;
+ }
+ }
+
+ /* REQ: Runner.Execute.partition */
+ /* Ensure partitions are created in on-disk order. */
+ std::sort(internal->partitions.begin(), internal->partitions.end(),
+ [](std::unique_ptr<Partition> const &e1,
+ std::unique_ptr<Partition> const &e2) {
+ return (e1->device() + "p" + std::to_string(e1->partno())) <
+ (e2->device() + "p" + std::to_string(e2->partno()));
+ });
+ for(auto &part : internal->partitions) {
+ if(!part->execute(opts)) {
+ EXECUTE_FAILURE("partition");
+ return false;
+ }
+ }
+
+ /* encrypt PVs */
+
+ /* REQ: Runner.Execute.lvm_pv */
+ for(auto &pv : internal->lvm_pvs) {
+ if(!pv->execute(opts)) {
+ EXECUTE_FAILURE("lvm_pv");
+ return false;
+ }
+ }
+
+ /* REQ: Runner.Execute.lvm_vg */
+ for(auto &vg : internal->lvm_vgs) {
+ if(!vg->execute(opts)) {
+ EXECUTE_FAILURE("lvm_vg");
+ return false;
+ }
+ }
+
+ /* REQ: Runner.Execute.lvm_lv */
+ for(auto &lv : internal->lvm_lvs) {
+ if(!lv->execute(opts)) {
+ EXECUTE_FAILURE("lvm_lv");
+ return false;
+ }
+ }
+
+ /* encrypt */
+
+ /* REQ: Runner.Execute.fs */
+ for(auto &fs : internal->fses) {
+ if(!fs->execute(opts)) {
+ EXECUTE_FAILURE("fs");
+ return false;
+ }
+ }
+
+ /* REQ: Runner.Execute.mount */
+ /* Sort by mountpoint.
+ * This ensures that any subdirectory mounts come after their parent. */
+ std::sort(internal->mounts.begin(), internal->mounts.end(),
+ [](std::unique_ptr<Mount> const &e1,
+ std::unique_ptr<Mount> const &e2) {
+ return e1->mountpoint() < e2->mountpoint();
+ });
+ for(auto &mount : internal->mounts) {
+ if(!mount->execute(opts)) {
+ EXECUTE_FAILURE("mount");
+ return false;
+ }
+ }
+#ifdef HAS_INSTALL_ENV
+ if(opts.test(InstallEnvironment)) ped_device_free_all();
+#endif /* HAS_INSTALL_ENV */
+ output_step_end("disk");
+
+ /**************** PRE PACKAGE METADATA ****************/
+ output_step_start("pre-metadata");
+
+ /* REQ: Runner.Execute.hostname */
+ if(!internal->hostname->execute(opts)) {
+ EXECUTE_FAILURE("hostname");
+ return false;
+ }
+
+ /* REQ: Runner.Execute.repository */
+ if(opts.test(Simulate)) {
+ std::cout << "mkdir -p /target/etc/apk" << std::endl;
+ }
+#ifdef HAS_INSTALL_ENV
+ else {
+ if(!fs::exists("/target/etc/apk", ec)) {
+ fs::create_directory("/target/etc/apk", ec);
+ if(ec) {
+ output_error("internal", "failed to initialise APK");
+ EXECUTE_FAILURE("pre-metadata");
+ return false;
+ }
+ }
+ }
+#endif /* HAS_INSTALL_ENV */
+ for(auto &repo : internal->repos) {
+ if(!repo->execute(opts)) {
+ EXECUTE_FAILURE("repository");
+ return false;
+ }
+ }
+
+#ifdef NON_LIBRE_FIRMWARE
+ /* REQ: Runner.Execute.firmware */
+ if(internal->firmware && internal->firmware->test()) {
+ internal->packages.insert("linux-firmware");
+ }
+#endif
+ output_step_end("pre-metadata");
+
+ /**************** NETWORK ****************/
+ output_step_start("net");
+
+ if(!this->internal->ssids.empty()) {
+ std::ofstream wpao("/tmp/horizon/wpa_supplicant.conf",
+ std::ios_base::trunc);
+ if(wpao) {
+ wpao << "# Enable the control interface for wpa_cli and wpa_gui"
+ << std::endl
+ << "ctrl_interface=/var/run/wpa_supplicant" << std::endl
+ << "ctrl_interface_group=wheel" << std::endl
+ << "update_config=1" << std::endl;
+ wpao.close();
+ } else {
+ output_error("internal",
+ "cannot write wireless networking configuration");
+ }
+
+ for(auto &ssid : internal->ssids) {
+ if(!ssid->execute(opts)) {
+ EXECUTE_FAILURE("ssid");
+ /* "Soft" error. Not fatal. */
+ }
+ }
+
+ if(opts.test(Simulate)) {
+ std::ifstream wpai("/tmp/horizon/wpa_supplicant.conf");
+ if(wpai) {
+ std::cout << "cat >/target/etc/wpa_supplicant/wpa_supplicant.conf "
+ << "<<- WPA_EOF" << std::endl
+ << wpai.rdbuf() << std::endl
+ << "WPA_EOF" << std::endl;
+ } else {
+ output_error("internal",
+ "cannot read wireless networking configuration");
+ }
+ } else {
+ if(!fs::exists("/target/etc/wpa_supplicant", ec)) {
+ fs::create_directory("/target/etc/wpa_supplicant", ec);
+ }
+ fs::copy_file("/tmp/horizon/wpa_supplicant.conf",
+ "/target/etc/wpa_supplicant/wpa_supplicant.conf",
+ fs_overwrite, ec);
+ if(ec) {
+ output_error("internal", "cannot save wireless networking "
+ "configuration to target", ec.message());
+ }
+ }
+ }
+
+ if(!internal->addresses.empty()) {
+ fs::path netifrc_dir("/tmp/horizon/netifrc");
+ if(!fs::exists(netifrc_dir) &&
+ !fs::create_directory(netifrc_dir, ec)) {
+ output_error("internal", "cannot create temporary directory",
+ ec.message());
+ }
+
+ for(auto &addr : internal->addresses) {
+ if(!addr->execute(opts)) {
+ EXECUTE_FAILURE("netaddress");
+ /* "Soft" error. Not fatal. */
+ }
+ }
+
+ std::ostringstream conf;
+ for(auto &&var_dent : fs::directory_iterator(netifrc_dir)) {
+ const std::string variable(var_dent.path().filename().string());
+ std::ifstream contents(var_dent.path().string());
+ if(!contents) {
+ output_error("internal", "cannot read network configuration");
+ EXECUTE_FAILURE("net-internal");
+ continue;
+ }
+ conf << variable << "=\"";
+ if(contents.rdbuf()->in_avail()) conf << contents.rdbuf();
+ conf << "\"" << std::endl;
+ }
+
+ if(opts.test(Simulate)) {
+ std::cout << "mkdir -p /target/etc/conf.d" << std::endl;
+ std::cout << "cat >>/target/etc/conf.d/net <<- NETCONF_EOF"
+ << std::endl << conf.str() << std::endl
+ << "NETCONF_EOF" << std::endl;
+ } else {
+ if(!fs::exists("/target/etc/conf.d")) {
+ fs::create_directory("/target/etc/conf.d", ec);
+ if(ec) {
+ output_error("internal", "could not create /etc/conf.d "
+ "directory", ec.message());
+ }
+ }
+ std::ofstream conf_file("/target/etc/conf.d/net",
+ std::ios_base::app);
+ if(!conf_file) {
+ output_error("internal", "cannot save network configuration "
+ "to target");
+ EXECUTE_FAILURE("net-internal");
+ } else {
+ conf_file << conf.str();
+ }
+ }
+ }
+
+ if(!internal->network->execute(opts)) {
+ EXECUTE_FAILURE("network");
+ return false;
+ }
+
+ if(internal->network->test()) {
+ bool do_wpa = !internal->ssids.empty();
+
+ if(opts.test(Simulate)) {
+ if(do_wpa) {
+ std::cout << "cp /target/etc/wpa_supplicant/wpa_supplicant.conf "
+ << "/etc/wpa_supplicant/wpa_supplicant.conf"
+ << std::endl;
+ }
+ std::cout << "cp /target/etc/conf.d/net /etc/conf.d/net"
+ << std::endl;
+ } else {
+ if(do_wpa) {
+ fs::copy_file("/target/etc/wpa_supplicant/wpa_supplicant.conf",
+ "/etc/wpa_supplicant/wpa_supplicant.conf",
+ fs_overwrite, ec);
+ if(ec) {
+ output_error("internal", "cannot use wireless configuration "
+ "during installation", ec.message());
+ EXECUTE_FAILURE("network");
+ }
+ }
+ fs::copy_file("/target/etc/conf.d/net", "/etc/conf.d/net",
+ fs_overwrite, ec);
+ if(ec) {
+ output_error("internal", "cannot use networking configuration "
+ "during installation", ec.message());
+ EXECUTE_FAILURE("network");
+ return false;
+ }
+ }
+ }
+ output_step_end("net");
+
+ /**************** PKGDB ****************/
+ output_step_start("pkgdb");
+
+ /* REQ: Runner.Execute.signingkey */
+ for(auto &key : internal->repo_keys) {
+ if(!key->execute(opts)) {
+ EXECUTE_FAILURE("signingkey");
+ return false;
+ }
+ }
+
+ /* REQ: Runner.Execute.pkginstall.APKDB */
+ output_info("internal", "initialising APK");
+ if(opts.test(Simulate)) {
+ std::cout << "apk --root /target --initdb add" << std::endl;
+ } else {
+ if(system("apk --root /target --initdb add") != 0) {
+ EXECUTE_FAILURE("pkginstall");
+ return false;
+ }
+ }
+
+ /* REQ: Runner.Execute.pkginstall */
+ output_info("internal", "installing packages to target");
+ std::ostringstream pkg_list;
+ for(auto &pkg : this->internal->packages) {
+ pkg_list << pkg << " ";
+ }
+ if(opts.test(Simulate)) {
+ std::cout << "apk --root /target update" << std::endl;
+ std::cout << "apk --root /target add " << pkg_list.str() << std::endl;
+ } else {
+ if(system("apk --root /target update") != 0) {
+ EXECUTE_FAILURE("pkginstall");
+ return false;
+ }
+ std::string apk_invoc = "apk --root /target add " + pkg_list.str();
+ if(system(apk_invoc.c_str()) != 0) {
+ EXECUTE_FAILURE("pkginstall");
+ return false;
+ }
+ }
+
+ output_step_end("pkgdb");
+
+ /**************** POST PACKAGE METADATA ****************/
+ output_step_start("post-metadata");
+
+ if(!internal->rootpw->execute(opts)) {
+ EXECUTE_FAILURE("rootpw");
+ return false;
+ }
+
+ if(internal->lang && !internal->lang->execute(opts)) {
+ EXECUTE_FAILURE("language");
+ return false;
+ }
+
+ /* keymap */
+ /* UserAccounts */
+
+ if(!internal->tzone->execute(opts)) {
+ EXECUTE_FAILURE("timezone");
+ return false;
+ }
+
+ output_step_end("post-metadata");
+ return true;
+}
+
+}