summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA. Wilcox <AWilcox@Wilcox-Tech.com>2020-06-03 00:06:49 -0500
committerA. Wilcox <AWilcox@Wilcox-Tech.com>2020-06-03 00:06:49 -0500
commit419e2951caca02ba77a89b6a75c9317c6ff02300 (patch)
tree8406b2d205ce891a5b57e9a2e067bc0ee2c2a4e8
parent48b38926b6b21ac82da5beeb61586048d48d23f7 (diff)
downloadhorizon-419e2951caca02ba77a89b6a75c9317c6ff02300.tar.gz
horizon-419e2951caca02ba77a89b6a75c9317c6ff02300.tar.bz2
horizon-419e2951caca02ba77a89b6a75c9317c6ff02300.tar.xz
horizon-419e2951caca02ba77a89b6a75c9317c6ff02300.zip
image: Finish implementing ISO backend
-rw-r--r--image/backends/initrd.sh.cpp82
-rw-r--r--image/backends/iso.cc322
-rw-r--r--image/creator.cc4
3 files changed, 384 insertions, 24 deletions
diff --git a/image/backends/initrd.sh.cpp b/image/backends/initrd.sh.cpp
new file mode 100644
index 0000000..6608481
--- /dev/null
+++ b/image/backends/initrd.sh.cpp
@@ -0,0 +1,82 @@
+const char *initrd = "#!/bin/sh -e\n\
+ # \n\
+ # This file is part of the Horizon image creation system.\n\
+ # SPDX-License-Identifier: AGPL-3.0-only\n\
+ # \n\
+ # Portions of this file are derived from adelie-build-cd.\n\
+ # \n\
+ \n\
+ log() {\n\
+ # $1 - Type of log message (info, warning, error)\n\
+ # $2 - The message.\n\
+ printf \"%s\tlog\tinitrd: %s: %s\n\" `date -u +%Y-%m-%dT%H:%M:%S.000` \"$1\" \"$2\"\n\
+ }\n\
+ \n\
+ log info 'Creating initrd structure...'\n\
+ \n\
+ ARCH=$1\n\
+ LDARCH=$1\n\
+ case $ARCH in\n\
+ ppc) LDARCH=powerpc;;\n\
+ ppc64) LDARCH=powerpc64;;\n\
+ pmmx) LDARCH=i386;;\n\
+ esac\n\
+ TARGET_DIR=$2\n\
+ CDROOT_DIR=$3\n\
+ CDINIT_DIR=$4\n\
+ INITRD_DIR=`mktemp -d -t 'hinitrd-XXXXXX'`\n\
+ if [ $? -ne 0 ]; then\n\
+ log error 'cannot create temporary directory'\n\
+ exit 1\n\
+ fi\n\
+ \n\
+ # mount points\n\
+ mkdir ${INITRD_DIR}/dev\n\
+ mkdir ${INITRD_DIR}/media\n\
+ for _rootdir in newroot lowerroot upperroot; do\n\
+ mkdir ${INITRD_DIR}/$_rootdir\n\
+ chmod 755 ${INITRD_DIR}/$_rootdir\n\
+ done\n\
+ mkdir ${INITRD_DIR}/proc\n\
+ mkdir ${INITRD_DIR}/sys\n\
+ \n\
+ # manual /dev nodes for initial udev startup\n\
+ mknod -m 600 ${INITRD_DIR}/dev/console c 5 1\n\
+ mknod -m 666 ${INITRD_DIR}/dev/null c 1 3\n\
+ mknod -m 666 ${INITRD_DIR}/dev/ptmx c 5 2\n\
+ mknod -m 666 ${INITRD_DIR}/dev/random c 1 8\n\
+ mknod -m 666 ${INITRD_DIR}/dev/tty c 5 0\n\
+ mknod -m 620 ${INITRD_DIR}/dev/tty1 c 4 1\n\
+ mknod -m 666 ${INITRD_DIR}/dev/urandom c 1 9\n\
+ mknod -m 666 ${INITRD_DIR}/dev/zero c 1 5\n\
+ \n\
+ # base\n\
+ mkdir ${INITRD_DIR}/lib\n\
+ cp ${TARGET_DIR}/lib/ld-musl-$LDARCH.so.1 ${INITRD_DIR}/lib/\n\
+ cp ${TARGET_DIR}/lib/libblkid.so.1 ${INITRD_DIR}/lib/\n\
+ cp ${TARGET_DIR}/lib/libuuid.so.1 ${INITRD_DIR}/lib/\n\
+ \n\
+ # udev\n\
+ mkdir -p ${INITRD_DIR}/etc/udev\n\
+ mkdir ${INITRD_DIR}/run\n\
+ mkdir ${INITRD_DIR}/sbin\n\
+ cp ${TARGET_DIR}/bin/udevadm ${INITRD_DIR}/sbin/\n\
+ cp ${TARGET_DIR}/sbin/udevd ${INITRD_DIR}/sbin/\n\
+ cp ${TARGET_DIR}/lib/libkmod.so.2 ${INITRD_DIR}/lib/\n\
+ cp ${TARGET_DIR}/lib/libcrypto.so.1.1 ${INITRD_DIR}/lib/\n\
+ cp ${TARGET_DIR}/lib/liblzma.so.5 ${INITRD_DIR}/lib/\n\
+ cp ${TARGET_DIR}/lib/libudev.so.1 ${INITRD_DIR}/lib/\n\
+ cp ${TARGET_DIR}/lib/libz.so.1 ${INITRD_DIR}/lib/\n\
+ \n\
+ if [ -n ${CDINIT_DIR} ]; then\n\
+ cp ${CDINIT_DIR}/cdinit-$ARCH ${INITRD_DIR}/init\n\
+ else\n\
+ log error 'No cdinit binary found, and compilation is not yet supported'\n\
+ exit 2\n\
+ fi\n\
+ \n\
+ log info 'Compressing initrd...'\n\
+ \n\
+ (cd ${INITRD_DIR}; find . | cpio -H newc -o) > ${CDROOT_DIR}/initrd-$ARCH\n\
+ gzip -9 ${CDROOT_DIR}/initrd-$ARCH\n\
+ mv ${CDROOT_DIR}/initrd-$ARCH.gz ${CDROOT_DIR}/initrd-$ARCH";
diff --git a/image/backends/iso.cc b/image/backends/iso.cc
index c3f316e..a7e2dd7 100644
--- a/image/backends/iso.cc
+++ b/image/backends/iso.cc
@@ -10,12 +10,151 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-#include <fstream>
+#include <cstdlib> /* getenv */
+#include <cstring> /* strlen, strtok */
+#include <fstream> /* ifstream, ofstream */
+#include <boost/algorithm/string.hpp>
+
#include "basic.hh"
#include "hscript/util.hh"
#include "util/filesystem.hh"
#include "util/output.hh"
+using namespace boost::algorithm;
+
+const std::vector<std::string> data_dirs() {
+ std::vector<std::string> dirs;
+
+ char *home = std::getenv("XDG_DATA_HOME");
+ if(home != nullptr && std::strlen(home) > 0) {
+ dirs.push_back(home);
+ } else {
+ home = std::getenv("HOME");
+ if(home != nullptr && std::strlen(home) > 0) {
+ dirs.push_back(std::string(home) + "/.local/share");
+ } else {
+ home = std::getenv("APPDATA");
+ if(home != nullptr) {
+ dirs.push_back(home);
+ } else {
+ /* Give up. */
+ }
+ }
+ }
+
+ const char *sys_c = std::getenv("XDG_DATA_DIRS");
+ if(sys_c == nullptr || std::strlen(sys_c) == 0) {
+ sys_c = "/usr/local/share:/usr/share";
+ }
+
+ const std::string sys{sys_c};
+ std::vector<std::string> temp;
+ boost::split(temp, sys, is_any_of(":"));
+ std::move(temp.begin(), temp.end(), std::back_inserter(dirs));
+
+ return dirs;
+}
+
+const fs::path find_data_file(std::string name) {
+ error_code ec;
+ for(const auto &p : data_dirs()) {
+ fs::path src = fs::path(p).append("horizon").append("iso").append(name);
+
+ if(fs::exists(src, ec)) {
+ return src;
+ }
+ }
+ return fs::path();
+}
+
+bool copy_volume_icon_to(fs::path ir_dir) {
+ error_code ec;
+
+ const fs::path dest = ir_dir.append("cdroot").append("VolumeIcon.icns");
+ const fs::path src = find_data_file("VolumeIcon.icns");
+
+ /* No volume icon. */
+ if(!src.has_filename()) return false;
+
+ fs::copy(src, dest, ec);
+ if(ec && ec.value() != EEXIST) {
+ output_error("CD backend", "could not copy volume icon", ec.message());
+ return false;
+ }
+ return true;
+}
+
+bool write_etc_mtab_to(fs::path target) {
+ std::ofstream mtab(target.append("/etc/conf.d/mtab"));
+ if(!mtab) {
+ output_error("CD backend", "failed to open mtab configuration");
+ return false;
+ }
+ mtab << "mtab_is_file=no" << std::endl;
+ if(mtab.fail() || mtab.bad()) {
+ output_error("CD backend", "failed to write mtab configuration");
+ return false;
+ }
+ mtab.flush();
+ mtab.close();
+ return true;
+}
+
+bool write_fstab_to(fs::path target) {
+ std::ofstream fstab{target.append("/etc/fstab")};
+ if(!fstab) {
+ output_error("CD backend", "failed to open fstab");
+ return false;
+ }
+
+ fstab << "# Welcome to Adélie Linux." << std::endl
+ << "# This fstab(5) is for the live media only. "
+ << "Do not edit or use for your installation." << std::endl
+ << std::endl
+ << "tmpfs /tmp tmpfs defaults 0 1"
+ << std::endl
+ << "proc /proc proc defaults 0 1"
+ << std::endl;
+
+ if(fstab.fail() || fstab.bad()) {
+ output_error("CD backend", "failed to write fstab");
+ return false;
+ }
+ fstab.flush();
+ fstab.close();
+ return true;
+}
+
+bool write_etc_issue_to(fs::path target) {
+ error_code ec;
+ const fs::path dest{target.append("/etc/issue")};
+
+ const fs::path src{find_data_file("issue")};
+ if(src.has_filename()) {
+ fs::copy(src, dest, ec);
+ return !ec;
+ }
+
+ /* We don't have a file, so write out our default. */
+ std::ofstream issue(dest);
+ if(!issue) {
+ output_error("CD backend", "failed to open issue file");
+ return false;
+ }
+
+ issue << "Welcome to Adélie Linux!" << std::endl
+ << "You may log in as 'root' to install, or 'live' to play around."
+ << std::endl << std::endl << "Have fun." << std::endl;
+
+ if(issue.fail() || issue.bad()) {
+ output_error("CD backend", "failed to write issue file");
+ return false;
+ }
+ issue.flush();
+ issue.close();
+ return true;
+}
+
namespace Horizon {
namespace Image {
@@ -41,15 +180,17 @@ public:
}
/* REQ: ISO.1 */
- if(fs::exists(this->ir_dir, ec)) {
+ /*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());
+ ec.message());*/
/* we can _try_ to proceed anyway... */
- }
- }
+ //}
+ //}
+
+ output_info("CD backend", "creating directory tree");
/* REQ: ISO.2 */
fs::create_directory(this->ir_dir, ec);
@@ -84,6 +225,7 @@ public:
}
/* REQ: ISO.4 */
+ output_info("CD backend", "configuring boot loader");
std::ofstream grub(this->ir_dir + "/target/etc/default/grub");
grub << "ADELIE_MANUAL_CONFIG=1" << std::endl;
if(grub.fail() || grub.bad()) {
@@ -96,39 +238,70 @@ public:
}
int create() override {
+ error_code ec;
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");
+ fs::current_path(this->ir_dir);
- /* 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;
+ /* REQ: ISO.7 */
+ output_info("CD backend", "creating live environment directories");
+ fs::create_directory(target + "/target", ec);
+ if(ec && ec.value() != EEXIST) {
+ output_error("CD backend", "could not create directory",
+ ec.message());
}
- mtab << "mtab_is_file=no" << std::endl;
- if(mtab.fail() || mtab.bad()) {
- output_error("CD backend", "failed to write mtab configuration");
- return FS_ERROR;
+ fs::create_directories(target + "/media/live", ec);
+ if(ec && ec.value() != EEXIST) {
+ output_error("CD backend", "could not create directory",
+ ec.message());
}
- mtab.close();
+
+ /* REQ: ISO.9 */
+ output_info("CD backend", "configuring mtab");
+ write_etc_mtab_to(target);
/* REQ: ISO.10 */
+ output_info("CD backend", "enabling required services");
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");
+ for(const std::string &svc : {"udev", "udev-trigger", "lvmetad"}) {
+ fs::create_symlink("/etc/init.d/" + svc, targetsi + svc, ec);
+ if(ec && ec.value() != EEXIST) {
+ output_error("CD backend", "could not enable service " + svc,
+ ec.message());
+ return FS_ERROR;
+ }
+ }
- const std::string squashpath = cdpath + "/" + my_arch + ".squashfs";
+ /* REQ: ISO.12 */
+ output_info("CD backend", "creating live environment /etc/fstab");
+ if(!write_fstab_to(target)) return FS_ERROR;
+
+ /* REQ: ISO.13 */
+ output_info("CD backend", "setting root shell");
+ run_command("sed", {"-i", "s#/root:/bin/sh$#/root:/bin/zsh#",
+ target + "/etc/passwd"});
+
+ /* REQ: ISO.15 */
+ output_info("CD backend", "configuring login services");
+ run_command("sed", {"-i", "s/pam_unix.so$/pam_unix.so nullok_secure/",
+ target + "/etc/pam.d/base-auth"});
+
+ /* REQ: ISO.19 */
+ output_info("CD backend", "creating live /etc/issue");
+ if(opts.find("issue-path") != opts.end() &&
+ fs::exists(opts.at("issue-path"))) {
+ fs::path dest = fs::path(cdpath).append("etc/issue");
+ fs::copy(opts.at("issue-path"), dest, ec);
+ if(ec) output_error("CD backend", "could not copy /etc/issue",
+ ec.message());
+ } else if(!write_etc_issue_to(target)) return FS_ERROR;
/* REQ: ISO.22 */
+ output_info("CD backend", "generating file list");
{
std::ofstream exclude(this->ir_dir + "/exclude.list");
exclude << "dev/*" << std::endl
@@ -143,16 +316,117 @@ public:
}
/* REQ: ISO.22 */
+ output_info("CD backend", "creating SquashFS");
+ const std::string squashpath = cdpath + "/" + my_arch + ".squashfs";
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;
+
+ /* REQ: ISO.21 */
+ if(opts.find("icon-path") != opts.end() &&
+ fs::exists(opts.at("icon-path"))) {
+ fs::path dest = fs::path(cdpath).append("VolumeIcon.icns");
+ fs::copy(opts.at("icon-path"), dest, ec);
+ if(ec) output_error("CD backend", "could not copy volume icon",
+ ec.message());
+ } else if(!copy_volume_icon_to(this->ir_dir)) {
+ output_warning("CD backend", "No volume icon could be found.");
+ }
+
+ /* REQ: ISO.23 */
+ output_info("CD backend", "creating initrd");
+ std::string cdinit_path{""};
+ for(const auto &path : data_dirs()) {
+ fs::path candidate{fs::path{path}.append("horizon").append("iso")
+ .append("cdinits")};
+ if(fs::exists(candidate, ec)) {
+ cdinit_path = candidate;
+ }
+ }
+#include "initrd.sh.cpp"
+ if(run_command("/bin/sh", {"-ec", initrd, "mkinitrd", my_arch, target,
+ cdpath, cdinit_path}) != 0) {
+ output_error("CD backend", "failed to create initrd");
+ return COMMAND_ERROR;
+ }
+
+ /* REQ: ISO.24 */
+ std::string postscript;
+ if(opts.find("post-script") != opts.end() &&
+ fs::exists(opts.at("post-script"))) {
+ postscript = opts.at("post-script");
+ } else {
+ for(const auto &path : data_dirs()) {
+ fs::path candidate{fs::path{path}.append("horizon")
+ .append("iso").append("post-" + my_arch + ".sh")};
+ if(fs::exists(candidate, ec)) {
+ postscript = candidate;
+ break;
+ }
+ }
+ }
+ if(postscript.length() > 0) {
+ output_info("CD backend", "running architecture-specific script",
+ postscript);
+ if(run_command("/bin/sh", {"-e", postscript}) != 0) {
+ output_error("CD backend", "architecture-specific script failed");
+ return COMMAND_ERROR;
+ }
+ }
+
+ /* REQ: ISO.25 */
+ output_info("CD backend", "installing kernel");
+ for(const auto &candidate : fs::directory_iterator(target + "/boot")) {
+ auto name = candidate.path().filename().string();
+ if(name.length() > 6 && name.substr(0, 6) == "vmlinu") {
+ fs::copy(candidate.path(), cdpath + "/kernel-" + my_arch, ec);
+ if(ec) {
+ output_error("CD backend", "failed to copy kernel",
+ ec.message());
+ return FS_ERROR;
+ }
+ fs::remove(candidate.path(), ec);
+ break;
+ }
+ }
+
+ /* REQ: ISO.26 */
+ output_info("CD backend", "creating ISO");
+ std::vector<std::string> iso_args = {"-as", "mkisofs", "-o", out_path,
+ "-joliet", "-rational-rock", "-V",
+ "Adelie "+my_arch};
+ std::vector<std::string> arch_args;
+ std::string raw_arch;
+ {
+ const fs::path param_file = find_data_file("iso-params-" + my_arch);
+ if(param_file.has_filename()) {
+ std::ifstream params{param_file};
+ if(!params) {
+ output_warning("CD backend", "couldn't read ISO params");
+ } else {
+ params >> raw_arch;
+ }
+ }
+ }
+ if(raw_arch.length() > 0) {
+ boost::split(arch_args, raw_arch, is_any_of(" "));
+ std::move(arch_args.begin(), arch_args.end(),
+ std::back_inserter(iso_args));
+ }
+ if(opts.find("iso-params") != opts.end()) {
+ boost::split(arch_args, opts.at("iso-params"), is_any_of(" "));
+ std::move(arch_args.begin(), arch_args.end(),
+ std::back_inserter(iso_args));
+ }
+ iso_args.push_back(cdpath);
+ return run_command("xorriso", iso_args);
}
int finalise() override {
+ output_info("CD backend", "Live image created successfully", out_path);
return 0;
}
};
diff --git a/image/creator.cc b/image/creator.cc
index 046027a..1a064f1 100644
--- a/image/creator.cc
+++ b/image/creator.cc
@@ -137,6 +137,10 @@ int main(int argc, char *argv[]) {
output_path = vm["output"].as<std::string>();
}
+ if(fs::path(output_path).is_relative()) {
+ output_path = fs::absolute(output_path).string();
+ }
+
if(!vm["backconfig"].empty()) {
for(const auto &confpart :
vm["backconfig"].as<std::vector<std::string>>()) {