summaryrefslogtreecommitdiff
path: root/image
diff options
context:
space:
mode:
authorA. Wilcox <AWilcox@Wilcox-Tech.com>2020-05-28 10:05:23 -0500
committerA. Wilcox <AWilcox@Wilcox-Tech.com>2020-05-28 10:05:23 -0500
commit30b9f32d7a544f50f5e06ef930525d620a028db1 (patch)
tree1c5ba88d16c0fb8d8801e5329251700583754e4e /image
parenta52fcf56f737c6fc962c12efad1b637521cc5685 (diff)
downloadhorizon-30b9f32d7a544f50f5e06ef930525d620a028db1.tar.gz
horizon-30b9f32d7a544f50f5e06ef930525d620a028db1.tar.bz2
horizon-30b9f32d7a544f50f5e06ef930525d620a028db1.tar.xz
horizon-30b9f32d7a544f50f5e06ef930525d620a028db1.zip
image: Put target inside ir_dir, add WIP ISO backend
ir_dir used to be the target; now the target is inside ir_dir so backends have the choice of making other directories inside ir_dir if needed.
Diffstat (limited to 'image')
-rw-r--r--image/CMakeLists.txt13
-rw-r--r--image/backends/CMakeLists.txt1
-rw-r--r--image/backends/iso.cc170
-rw-r--r--image/backends/tar.cc6
-rw-r--r--image/creator.cc50
5 files changed, 231 insertions, 9 deletions
diff --git a/image/CMakeLists.txt b/image/CMakeLists.txt
index ea0a3ed..58b5eef 100644
--- a/image/CMakeLists.txt
+++ b/image/CMakeLists.txt
@@ -1,12 +1,19 @@
-find_package(Boost REQUIRED COMPONENTS program_options)
-include_directories(${Boost_INCLUDE_DIR})
-
add_subdirectory(backends)
set(IMG_SRCS
creator.cc
)
add_executable(hscript-image ${IMG_SRCS})
+
+if("cxx_std_17" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
+ find_package(Boost REQUIRED COMPONENTS program_options)
+ set_property(TARGET hscript-image PROPERTY CXX_STANDARD 17)
+else()
+ find_package(Boost REQUIRED COMPONENTS filesystem program_options)
+endif()
+
+include_directories(${Boost_INCLUDE_DIR})
+
target_link_libraries(hscript-image hscript hi-backends ${Boost_LIBRARIES})
install(TARGETS hscript-image DESTINATION bin)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/creator.1 DESTINATION share/man/man1 RENAME hscript-image.1)
diff --git a/image/backends/CMakeLists.txt b/image/backends/CMakeLists.txt
index 2c79c8e..325fddf 100644
--- a/image/backends/CMakeLists.txt
+++ b/image/backends/CMakeLists.txt
@@ -1,5 +1,6 @@
set(BACKEND_SRCS
basic.cc
+ iso.cc
)
set(BACKEND_LIBS "")
diff --git a/image/backends/iso.cc b/image/backends/iso.cc
new file mode 100644
index 0000000..b1d4c43
--- /dev/null
+++ b/image/backends/iso.cc
@@ -0,0 +1,170 @@
+/*
+ * iso.cc - Implementation of the CD image Horizon Image Creation backend
+ * image, the image processing utilities for
+ * Project Horizon
+ *
+ * Copyright (c) 2020 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 <fstream>
+#include "basic.hh"
+#include "hscript/util.hh"
+#include "util/filesystem.hh"
+#include "util/output.hh"
+
+namespace Horizon {
+namespace Image {
+
+class CDBackend : public BasicBackend {
+public:
+ enum CDError {
+ COMMAND_MISSING = 1,
+ FS_ERROR,
+ COMMAND_ERROR
+ };
+
+ explicit CDBackend(std::string ir, std::string out)
+ : BasicBackend(ir, out) {};
+
+ int prepare() override {
+ error_code ec;
+
+ output_info("CD backend", "probing SquashFS tools version...");
+ if(run_command("mksquashfs", {"-version"}) != 0) {
+ output_error("CD backend", "SquashFS tools are not present");
+ return COMMAND_MISSING;
+ }
+
+ /* REQ: ISO.1 */
+ if(fs::exists(this->ir_dir, ec)) {
+ output_info("CD backend", "removing old IR tree", this->ir_dir);
+ fs::remove_all(this->ir_dir, ec);
+ if(ec) {
+ output_warning("CD backend", "could not remove IR tree",
+ ec.message());
+ /* we can _try_ to proceed anyway... */
+ }
+ }
+
+ /* REQ: ISO.2 */
+ fs::create_directory(this->ir_dir, ec);
+ if(ec && ec.value() != EEXIST) {
+ output_error("CD backend", "could not create IR directory",
+ ec.message());
+ return FS_ERROR;
+ }
+
+ /* REQ: ISO.2 */
+ fs::create_directory(this->ir_dir + "/cdroot", ec);
+ if(ec && ec.value() != EEXIST) {
+ output_error("CD backend", "could not create ISO directory",
+ ec.message());
+ return FS_ERROR;
+ }
+
+ /* REQ: ISO.2 */
+ fs::create_directory(this->ir_dir + "/target", ec);
+ if(ec && ec.value() != EEXIST) {
+ output_error("CD backend", "could not create target directory",
+ ec.message());
+ return FS_ERROR;
+ }
+
+ /* REQ: ISO.4 */
+ fs::create_directories(this->ir_dir + "/target/etc/default", ec);
+ if(ec && ec.value() != EEXIST) {
+ output_error("CD backend", "could not create target config dir",
+ ec.message());
+ return FS_ERROR;
+ }
+
+ /* REQ: ISO.4 */
+ std::ofstream grub(this->ir_dir + "/target/etc/default/grub");
+ grub << "ADELIE_MANUAL_CONFIG=1" << std::endl;
+ if(grub.fail() || grub.bad()) {
+ output_error("CD backend", "failed to configure GRUB");
+ return FS_ERROR;
+ }
+ grub.close();
+
+ return 0;
+ }
+
+ int create() override {
+ std::string my_arch;
+ std::ifstream archstream(this->ir_dir + "/target/etc/apk/arch");
+ const std::string target = this->ir_dir + "/target";
+ const std::string cdpath = this->ir_dir + "/cdroot";
+ archstream >> my_arch;
+
+ /* REQ: ISO.7 */
+ fs::create_directory(target + "/target");
+ fs::create_directories(target + "/media/live");
+
+ /* REQ: ISO.9 */
+ std::ofstream mtab(target + "/etc/conf.d/mtab");
+ if(!mtab) {
+ output_error("CD backend", "failed to open mtab configuration");
+ return FS_ERROR;
+ }
+ mtab << "mtab_is_file=no" << std::endl;
+ if(mtab.fail() || mtab.bad()) {
+ output_error("CD backend", "failed to write mtab configuration");
+ return FS_ERROR;
+ }
+ mtab.close();
+
+ /* REQ: ISO.10 */
+ const std::string targetsi = target + "/etc/runlevels/sysinit/";
+ fs::create_symlink("/etc/init.d/udev", targetsi + "udev");
+ fs::create_symlink("/etc/init.d/udev-trigger",
+ targetsi + "udev-trigger");
+ fs::create_symlink("/etc/init.d/lvmetad", targetsi + "lvmetad");
+
+ const std::string squashpath = cdpath + "/" + my_arch + ".squashfs";
+
+ /* REQ: ISO.22 */
+ {
+ std::ofstream exclude(this->ir_dir + "/exclude.list");
+ exclude << "dev/*" << std::endl
+ << "proc/*" << std::endl
+ << "sys/*" << std::endl;
+ if(exclude.fail() || exclude.bad()) {
+ output_error("CD backend", "failed to write exclusion list");
+ return FS_ERROR;
+ }
+ exclude.flush();
+ exclude.close();
+ }
+
+ /* REQ: ISO.22 */
+ if(run_command("mksquashfs", {target, squashpath, "-noappend",
+ "-wildcards", "-ef",
+ this->ir_dir + "/exclude.list"}) != 0) {
+ output_error("CD backend", "failed to create SquashFS");
+ return COMMAND_ERROR;
+ }
+ return 0;
+ }
+
+ int finalise() override {
+ return 0;
+ }
+};
+
+__attribute__((constructor(400)))
+void register_cd_backend() {
+ BackendManager::register_backend(
+ {"iso", "Create a CD image (.iso)",
+ [](std::string ir_dir, std::string out_path) {
+ return new CDBackend(ir_dir, out_path);
+ }
+ });
+}
+
+}
+}
diff --git a/image/backends/tar.cc b/image/backends/tar.cc
index 279a11c..f9309a1 100644
--- a/image/backends/tar.cc
+++ b/image/backends/tar.cc
@@ -81,10 +81,10 @@ public:
int fd, r, code = 0;
struct stat s;
void *buff;
+ std::string target = this->ir_dir + "/target";
- for(const auto& dent : fs::recursive_directory_iterator(this->ir_dir,
- ec)) {
- fs::path relpath = dent.path().lexically_relative(this->ir_dir);
+ for(const auto& dent : fs::recursive_directory_iterator(target, ec)) {
+ fs::path relpath = dent.path().lexically_relative(target);
#define OUTPUT_FAILURE(x) \
output_error("tar backend", "failed to " x " '" + std::string(dent.path()) + "'",\
strerror(errno));
diff --git a/image/creator.cc b/image/creator.cc
index 8a6cc73..0934f08 100644
--- a/image/creator.cc
+++ b/image/creator.cc
@@ -13,13 +13,24 @@
#include <boost/program_options.hpp>
#include <cstdlib> /* EXIT_* */
#include <string>
+#include <sys/mount.h>
+
#include "backends/basic.hh"
+#include "hscript/meta.hh"
#include "hscript/script.hh"
+#include "util/filesystem.hh"
#include "util/output.hh"
bool pretty = true; /*! Controls ASCII colour output */
+const std::string arch_xlate(const std::string &arch) {
+ if(arch == "pmmx") return "i386";
+ if(arch == "armv7") return "arm";
+ return arch;
+}
+
+
#define DESCR_TEXT "Usage: hscript-image [OPTION]... [INSTALLFILE]\n"\
"Write an operating system image configured per INSTALLFILE"
/*! Text used at the top of usage output */
@@ -32,7 +43,7 @@ int main(int argc, char *argv[]) {
bool needs_help{}, disable_pretty{}, version_only{};
int exit_code = EXIT_SUCCESS;
- std::string if_path{"/etc/horizon/installfile"}, ir_dir{"/target"},
+ std::string if_path{"/etc/horizon/installfile"}, ir_dir{"/tmp/horizon-image"},
output_path{"image.tar"}, type_code{"tar"};
BasicBackend *backend = nullptr;
Horizon::ScriptOptions opts;
@@ -49,7 +60,7 @@ int main(int argc, char *argv[]) {
target.add_options()
("output,o", value<std::string>()->default_value("image.tar"), "Desired filename for the output file.")
("type,t", value<std::string>()->default_value("tar"), "Type of output file to generate. Use 'list' for a list of supported types.")
- ("ir-dir,i", value<std::string>()->default_value("/target"), "Where to store intermediate files.")
+ ("ir-dir,i", value<std::string>()->default_value("/tmp/horizon-image"), "Where to store intermediate files.")
;
ui.add(general).add(target);
@@ -109,6 +120,10 @@ int main(int argc, char *argv[]) {
ir_dir = vm["ir-dir"].as<std::string>();
}
+ if(fs::path(ir_dir).is_relative()) {
+ ir_dir = fs::absolute(ir_dir).string();
+ }
+
if(!vm["output"].empty()) {
output_path = vm["output"].as<std::string>();
}
@@ -169,18 +184,47 @@ int main(int argc, char *argv[]) {
RUN_PHASE_OR_TROUBLE(prepare, "preparation");
- my_script->setTargetDirectory(ir_dir);
+ /* Attempt to make images work cross-architecture.
+ * This requires binfmt_misc to be configured properly.
+ * It also requires qemu-user to be installed to /usr/bin on the host.
+ * It may not work all the time, so we ignore errors.
+ * But we try anyway, because it can work and make things easier.
+ */
+ const Horizon::Keys::Key *archkey = my_script->getOneValue("arch");
+ std::string qpath;
+ if(archkey) {
+ const Horizon::Keys::Arch *arch =
+ dynamic_cast<const Horizon::Keys::Arch *>(archkey);
+ qpath = "/usr/bin/qemu-" + arch_xlate(arch->value());
+ error_code ec;
+ if(fs::exists(qpath, ec)) {
+ fs::create_directories(ir_dir + "/target/usr/bin", ec);
+ if(!ec) fs::copy_file(qpath, ir_dir + "/target/" + qpath, ec);
+ }
+ }
+
+ my_script->setTargetDirectory(ir_dir + "/target");
if(!my_script->execute()) {
exit_code = EXIT_FAILURE;
goto trouble;
}
+ if(!qpath.empty() && fs::exists(ir_dir + "/target" + qpath)) {
+ error_code ec;
+ fs::remove(ir_dir + "/target" + qpath, ec);
+ }
+
RUN_PHASE_OR_TROUBLE(create, "creation");
RUN_PHASE_OR_TROUBLE(finalise, "finalisation");
}
trouble: /* delete the Script and exit */
+ /* ensure that our target mounts are unmounted */
+ umount((ir_dir + "/target/sys").c_str());
+ umount((ir_dir + "/target/proc").c_str());
+ umount((ir_dir + "/target/dev").c_str());
+
delete my_script;
early_trouble: /* no script yet */
delete backend;