~nabijaczleweli/klapki

0c5db6f80b18bbd93683b19df0d6148dec31fa0f — наб 3 months ago bfaaf3b
General cleanup
M .build.yml => .build.yml +2 -0
@@ 5,6 5,8 @@ packages:
  - libefiboot-dev
  - libssl-dev
tasks:
  - cmdline: |
      cat /proc/cmdline
  - build-gcc: |
      cd klapki
      make

M Makefile => Makefile +3 -46
@@ 34,7 34,7 @@ TEST_SOURCES := $(sort $(wildcard $(TSTDIR)*.cpp $(TSTDIR)**/*.cpp $(TSTDIR)**/*
LIBFMT := $(patsubst ext/fmt/src/%.cc,$(BLDDIR)fmt/obj/%$(OBJ),$(wildcard ext/fmt/src/*.cc))


.PHONY : all clean build build-test audiere cpp-localiser cpr fmt seed11 semver zstd whereami-cpp
.PHONY : all clean build build-test fmt


all : fmt build build-test test


@@ 45,8 45,8 @@ test: build-test
clean :
	rm -rf $(OUTDIR)

build : audiere cpp-localiser cpr seed11 fmt seed11 semver whereami-cpp zstd $(OUTDIR)klapki$(EXE)
build-test : audiere cpp-localiser cpr seed11 fmt seed11 semver whereami-cpp zstd $(OUTDIR)klapki-test$(EXE)
build : fmt $(OUTDIR)klapki$(EXE)
build-test : fmt $(OUTDIR)klapki-test$(EXE)
fmt : $(LIBFMT)




@@ 57,37 57,6 @@ $(OUTDIR)klapki$(EXE) : $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,$(OBJ),$(SOURCE
$(OUTDIR)klapki-test$(EXE) : $(subst $(TSTDIR),$(BLDDIR)test/,$(subst .cpp,$(OBJ),$(TEST_SOURCES))) $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,$(OBJ),$(filter-out $(SRCDIR)main.cpp,$(SOURCES)))) $(patsubst ext/fmt/src/%.cc,$(BLDDIR)fmt/obj/%$(OBJ),$(wildcard ext/fmt/src/*.cc))
	$(CXX) $(CXXAR) -o$@ $^ $(PIC) $(LDAR)

$(BLDDIR)audiere/lib/libaudiere$(DLL) : ext/audiere/CMakeLists.txt
	@mkdir -p $(abspath $(dir $@)../build)
	# FLAC doesn't seem to work on Travis by default so v0v
	cd $(abspath $(dir $@)../build) && $(INCCMAKEAR) $(LNCMAKEAR) $(CMAKE) -DUSE_FLAC=OFF -DCMAKE_INSTALL_PREFIX:PATH="$(abspath $(dir $@)..)" $(abspath $(dir $^)) -GNinja
	cd $(abspath $(dir $@)../build) && $(NINJA) install
	$(if $(OS) | grep Windows_NT,cp $@ $(OUTDIR))

$(BLDDIR)cpp-localiser/libcpp-localiser$(ARCH) : ext/cpp-localiser/Makefile
	$(MAKE) -C$(dir $^) BUILD=$(abspath $(dir $@)) stlib

$(BLDDIR)seed11/libseed11$(ARCH) : $(foreach src,seed11_system_agnostic seed11_$(SEED11_SYSTEM_TYPE) deterministic_unsafe_seed_device,$(BLDDIR)seed11/obj/$(src)$(OBJ))
	$(AR) crs $@ $^

$(BLDDIR)semver/libsemver$(ARCH) : $(patsubst ext/semver/src/%.cpp,$(BLDDIR)semver/obj/%$(OBJ),$(wildcard ext/semver/src/*.cpp))
	$(AR) crs $@ $^

$(BLDDIR)semver/include/semver/semver200.h : $(wildcard ext/semver/include/*.h ext/semver/include/*.inl)
	@mkdir -p $(dir $@)
	cp $^ $(dir $@)

$(BLDDIR)whereami-cpp/libwhereami++$(ARCH) : ext/whereami-cpp/Makefile
	$(MAKE) -C$(dir $^) BUILD=$(abspath $(dir $@)) stlib

$(BLDDIR)zstd/libzstd$(ARCH) : $(subst ext/zstd/lib,$(BLDDIR)zstd/obj,$(subst .c,$(OBJ),$(wildcard ext/zstd/lib/common/*.c ext/zstd/lib/compress/*.c ext/zstd/lib/decompress/*.c)))
	@mkdir -p $(dir $@)
	$(AR) crs $@ $^

$(BLDDIR)zstd/include/zstd/zstd.h : $(wildcard ext/zstd/lib/*.h ext/zstd/lib/common/*.h ext/zstd/lib/compress/*.h ext/zstd/lib/decompress/*.h)
	@mkdir -p $(foreach incfile,$(subst ext/zstd/lib,$(BLDDIR)zstd/include/zstd,$^),$(abspath $(dir $(incfile))))
	$(foreach incfile,$^,cp $(incfile) $(subst ext/zstd/lib,$(BLDDIR)zstd/include/zstd,$(incfile));)


$(OBJDIR)%$(OBJ) : $(SRCDIR)%.cpp
	@mkdir -p $(dir $@)


@@ 100,15 69,3 @@ $(BLDDIR)test/%$(OBJ) : $(TSTDIR)%.cpp
$(BLDDIR)fmt/obj/%$(OBJ) : ext/fmt/src/%.cc
	@mkdir -p $(dir $@)
	$(CXX) $(CXXAR) -Iext/fmt/include -c -o$@ $^

#$(BLDDIR)semver/obj/%$(OBJ) : ext/semver/src/%.cpp
#	@mkdir -p $(dir $@)
#	$(CXX) $(CXXAR) -Iext/semver/include -c -o$@ $^
#
#$(BLDDIR)seed11/obj/%$(OBJ) : ext/seed11/src/%.cpp
#	@mkdir -p $(dir $@)
#	$(CXX) $(CXXAR) -isystemext/seed11/include -c -o$@ $^
#
#$(BLDDIR)zstd/obj/%$(OBJ) : ext/zstd/lib/%.c
#	@mkdir -p $(dir $@)
#	$(CC) $(CCAR) -Iext/zstd/lib -Iext/zstd/lib/common -c -o$@ $^

M configMakefile => configMakefile +1 -2
@@ 46,7 46,7 @@ else
	LNCXXAR :=
endif

#KLAPKI_VERSION := "$(patsubst v%,%,$(shell git describe --tags --abbrev=0))"
#KLAPKI_VERSION := "$(patsubst v%,%,$(shell git describe))"
KLAPKI_VERSION := "0.0.0-$(shell git rev-list HEAD --count)"

INCCMAKEAR := CXXFLAGS="$(INCCXXAR)"


@@ 56,7 56,6 @@ OBJ := .o
ARCH := .a
AR := ar
CXXAR := -O3 -std=c++17 -Wall -Wextra $(PEDANTIC) -pipe $(INCCXXAR) $(PIC)
CCAR := -O3 -fomit-frame-pointer -std=c11 -pipe $(PIC)
STRIP := strip
STRIPAR := --strip-all --remove-section=.comment --remove-section=.note


M src/config.cpp => src/config.cpp +27 -8
@@ 24,6 24,7 @@
#include "util.hpp"
#include <cstdlib>
#include <fmt/format.h>
#include <fstream>
#include <limits.h>
#include <sys/stat.h>
#include <sys/utsname.h>


@@ 42,11 43,18 @@ extern "C" {
	})


static std::string readline(const char * from) {
	std::ifstream in(from);
	std::string ret;
	std::getline(in, ret);
	return ret;
}

static std::string get_host() {
	if(auto host = std::getenv("KLAPKI_HOST"))
		return host;
	else {
		auto mid = klapki::readline("/etc/machine-id");
		auto mid = readline("/etc/machine-id");
		if(!mid.empty())
			return mid;



@@ 112,8 120,9 @@ std::variant<klapki::config, std::string> klapki::config::read(const char ** arg
	const auto argv0 = argv[0];
	++argv;

	bool verbose = false;
	bool commit  = true;
	bool verbose  = false;
	bool verybose = false;
	bool commit   = true;
	for(; *argv && argv[0][0] == '-'; ++argv) {
		for(auto opts = argv[0] + 1; *opts; ++opts)
			switch(opts[0]) {


@@ 121,6 130,10 @@ std::variant<klapki::config, std::string> klapki::config::read(const char ** arg
					verbose = true;
					break;

				case 'V':
					verybose = true;
					break;

				case 'E':
					efi_set_verbose(efi_get_verbose() + 1, nullptr);
					break;


@@ 131,14 144,20 @@ std::variant<klapki::config, std::string> klapki::config::read(const char ** arg

				case 'h':
					return fmt::format("klapki {}\n"
					                   "Usage: {} [-vEnh]… [op [arg…]]…\n"
					                   "Usage: {} [-nvVEh]… [op [arg…]]…\n"
					                   "\n"
					                   "Flags:\n"
					                   "  -n\tDon't commit\n"
					                   "  -v\tVerbose operation\n"
					                   "  -V\tAdd {{dump}}s around all ops\n"
					                   "  -E\tIncrease libefivar verbosity level\n"
					                   "  -n\tDon't commit\n"
					                   "  -h\tShow this help\n"
					                   "\n"
					                   "Environment:\n"
					                   "  KLAPKI_HOST=    \tReplaces contents of /etc/machine-id or hostname\n"
					                   "  KLAPKI_WISDOM=  \tReplaces /etc/klapki\n"
					                   "  KLAPKI_EFI_ROOT=\tReplaces \\klapki\n"
					                   "\n"
					                   "Recognised ops:\n"
					                   "\tdump\n"
					                   "\tbootpos <position>\n"


@@ 154,7 173,7 @@ std::variant<klapki::config, std::string> klapki::config::read(const char ** arg
	}

	std::vector<ops::op_t> ops;
	if(verbose)
	if(verybose)
		ops.emplace_back(ops::dump{});
	while(*argv) {
		auto pop = op::from_cmdline(argv0, argv);


@@ 162,10 181,10 @@ std::variant<klapki::config, std::string> klapki::config::read(const char ** arg
			return std::move(*err);
		else {
			ops.emplace_back(std::move(std::get<ops::op_t>(pop)));
			if(verbose)
			if(verybose)
				ops.emplace_back(ops::dump{});
		}
	}

	return config{argv0, get_host(), verbose, commit, TRY(find_esp()), std::move(ops), get_wisom_root()};
	return config{get_host(), verbose, commit, TRY(find_esp()), std::move(ops), get_wisom_root()};
}

M src/config.hpp => src/config.hpp +0 -1
@@ 32,7 32,6 @@

namespace klapki {
	struct config {
		const char * argv0;
		std::string host;
		constexpr std::string_view wisdom_root() const noexcept;
		bool verbose;

M src/context_derive.cpp => src/context_derive.cpp +11 -0
@@ 32,6 32,17 @@ extern "C" {
}


namespace {
	/// unique_ptr deleter that uses free(3)
	struct free_deleter {
		template <class T>
		void operator()(T * ptr) const noexcept {
			std::free(ptr);
		}
	};
}


klapki::context::context klapki::context::context::derive(const config & cfg, state::state & output_state) {
	context ret{};


M src/main.cpp => src/main.cpp +1 -1
@@ 51,7 51,7 @@ namespace klapki {
			fmt::print("{}\n", cfg);


		const auto input_state = TRY(2, state::state::load(cfg.argv0, cfg.host));
		const auto input_state = TRY(2, state::state::load(cfg.host));


		auto output_state = TRY(3, context::resolve_state_context(cfg, input_state));

M src/state.cpp => src/state.cpp +12 -11
@@ 95,15 95,16 @@ static int get_boot_entry(std::map<std::uint16_t, klapki::state::boot_entry> & b
}

static int get_our_config(klapki::state::stated_config & statecfg, std::string_view us) {
	return get_efi_data(klapki::efi_guid_klapki, us.data(),
	                    // TODO: error recovery; flag to ignore? force?
	                    [&](auto && data, auto size, auto) { return klapki::state::stated_config::parse(statecfg, data.get(), size); });
	return get_efi_data(klapki::efi_guid_klapki, us.data(), [&](auto && data, auto size, auto) {
		klapki::state::stated_config::parse(statecfg, data.get(), size);
		return 0;
	});
}


std::variant<klapki::state::state, std::string> klapki::state::state::load(const char * argv0, std::string_view us) {
std::variant<klapki::state::state, std::string> klapki::state::state::load(std::string_view us) {
	if(!efi_variables_supported())
		return fmt::format("{}: EFI not supported?", argv0);
		return fmt::format("EFI not supported?");


	std::vector<std::uint16_t> boot_entries;


@@ 118,29 119,29 @@ std::variant<klapki::state::state, std::string> klapki::state::state::load(const
		   else if(is_our_config(guid, name, us))
			   have_our_config = true;
	   }))
		return fmt::format("{}: klapki::state::state::load(): iteration: {}", argv0, strerror(res));  // No threads here, and we avoid a strerror_r() clusterfuck
		return fmt::format("EFI load: klapki::state::state::load(): iteration: {}", strerror(res));  // No threads here, and we avoid a strerror_r() clusterfuck


	boot_order_flat bord{};
	if(have_boot_order) {
		if(get_boot_order(bord) < 0)
			return fmt::format("{}: klapki::state::state::load(): getting BootOrder: {}", argv0, strerror(errno));
			return fmt::format("EFI load: getting BootOrder: {}", strerror(errno));
	} else
		fmt::print(stderr, "{}: no BootOrder?\n", argv0);
		fmt::print(stderr, "EFI: no BootOrder?\n");


	std::map<std::uint16_t, boot_entry> entries;
	for(auto bent : boot_entries)
		if(get_boot_entry(entries, bent) < 0)
			return fmt::format("{}: klapki::state::state::load(): getting Boot{:04X}: {}", argv0, bent, strerror(errno));
			return fmt::format("EFI load: getting Boot{:04X}: {}", bent, strerror(errno));


	stated_config statecfg{};
	if(have_our_config) {
		if(get_our_config(statecfg, us) < 0)
			return fmt::format("{}: klapki::state::state::load(): getting {}-{}: {}", argv0, efi_guid_klapki_s, us, strerror(errno));
			return fmt::format("EFI load: getting {}-{}: {}", efi_guid_klapki_s, us, strerror(errno));
	} else
		fmt::print(stderr, "{}: no config for this host ({}) found; going to the top\n", argv0, us);
		fmt::print(stderr, "EFI load: no config for this host ({}) found; going to the top\n", us);

	return state{std::move(bord), std::move(entries), std::move(statecfg)};
}

M src/state.hpp => src/state.hpp +2 -3
@@ 79,8 79,7 @@ namespace klapki::state {
		std::vector<std::string> variants;  // entries NUL-terminated; list empty-terminated; kept unique, order-preserving
		std::vector<stated_config_entry> wanted_entries;

		/// 0 on OK, errno on error
		static int parse(stated_config & into, const void * data, std::size_t size);
		static void parse(stated_config & into, const void * data, std::size_t size);

		std::vector<std::uint8_t> serialise() const;



@@ 96,7 95,7 @@ namespace klapki::state {

		std::optional<std::string> commit(std::string_view us, const state & original_state) const;

		static std::variant<state, std::string> load(const char * argv0, std::string_view us);
		static std::variant<state, std::string> load(std::string_view us);
	};
}


M src/state_config.cpp => src/state_config.cpp +13 -15
@@ 29,10 29,10 @@
#define SUB "\x1A"


int klapki::state::stated_config::parse(klapki::state::stated_config & into, const void * _data, std::size_t size) {
void klapki::state::stated_config::parse(klapki::state::stated_config & into, const void * _data, std::size_t size) {
	if(size < 2) {
		errno = ENODATA;
		return -1;
		fmt::print(stderr, "Parsing state: cut off before boot position\n");
		return;
	}

	const char * data = reinterpret_cast<const char *>(_data);


@@ 44,7 44,7 @@ int klapki::state::stated_config::parse(klapki::state::stated_config & into, con
	for(;;) {
		const auto variant_end = std::find_if(data, data + size, [](auto b) { return b == '\0'; });
		if(variant_end == data + size) {
			fmt::print(stderr, "extraneous data; -1\n");
			fmt::print(stderr, "Parsing state: cut off after {} variant{}\n", into.variants.size(), into.variants.size() == 1 ? "" : "s");
			data = data + size;
			size = 0;
			break;


@@ 68,7 68,7 @@ int klapki::state::stated_config::parse(klapki::state::stated_config & into, con
	while(size != 0) {
		stated_config_entry new_entry{};
		if(size <= sizeof(new_entry.bootnum_hint) + sizeof(new_entry.load_option_sha)) {
			fmt::print(stderr, "extraneous data; 0\n");
			fmt::print(stderr, "Parsing state: cut off after {} entr{}\n", into.wanted_entries.size(), into.wanted_entries.size() == 1 ? "y" : "ies");
			break;
		}



@@ 83,36 83,33 @@ int klapki::state::stated_config::parse(klapki::state::stated_config & into, con

		const auto kver_end = std::find_if(data, data + size, [](auto b) { return b == '\0'; });
		if(kver_end == data + size) {
			fmt::print(stderr, "extraneous data; 0.5; have: {:04X}\n", new_entry.bootnum_hint);
			fmt::print(stderr, "Parsing state: cut off reading kernel version for entry {:04X}\n", new_entry.bootnum_hint);
			break;
		}
		new_entry.version.assign(data, kver_end - data);
		data += new_entry.version.size() + 1;  // NUL
		size -= new_entry.version.size() + 1;  // NUL
		// fmt::print(stderr, "version: {} ({})\n", new_entry.version, new_entry.version.size());

		const auto kvar_end = std::find_if(data, data + size, [](auto b) { return b == '\0'; });
		if(kvar_end == data + size) {
			fmt::print(stderr, "extraneous data; 0.75; have: {:04X}\n", new_entry.bootnum_hint);
			fmt::print(stderr, "Parsing state: cut off reading kernel variant for entry {:04X}\n", new_entry.bootnum_hint);
			break;
		}
		new_entry.variant.assign(data, kvar_end - data);
		data += new_entry.variant.size() + 1;  // NUL
		size -= new_entry.variant.size() + 1;  // NUL
		// fmt::print(stderr, "kernel: {} ({})\n", new_entry.variant, new_entry.variant.size());

		const auto kdir_end = std::find_if(data, data + size, [](auto b) { return b == '\0'; });
		if(kdir_end == data + size) {
			fmt::print(stderr, "extraneous data; 1; have: {:04X}\n", new_entry.bootnum_hint);
			fmt::print(stderr, "Parsing state: cut off reading kernel image directory for entry {:04X}\n", new_entry.bootnum_hint);
			break;
		}
		new_entry.kernel_dirname.assign(data, kdir_end - data);
		data += new_entry.kernel_dirname.size() + 1;  // NUL
		size -= new_entry.kernel_dirname.size() + 1;  // NUL
		// fmt::print(stderr, "variant: {} ({})\n", new_entry.kernel_dirname, new_entry.kernel_dirname.size());

		if(size <= sizeof(new_entry.kernel_image_sha)) {
			fmt::print(stderr, "extraneous data; 1.5\n");
			fmt::print(stderr, "Parsing state: cut off reading kernel image SHA for entry {:04X}\n", new_entry.bootnum_hint);
			break;
		}
		memcpy(&new_entry.kernel_image_sha, data, sizeof(new_entry.kernel_image_sha));


@@ 122,7 119,8 @@ int klapki::state::stated_config::parse(klapki::state::stated_config & into, con
		for(;;) {
			const auto idir_end = std::find_if(data, data + size, [](auto b) { return b == '\0'; });
			if(idir_end == data + size) {
				fmt::print(stderr, "extraneous data; 2\n");
				fmt::print(stderr, "Parsing state: cut off after {} initrd{} for entry {:04X}\n", new_entry.bootnum_hint, new_entry.initrd_dirnames.size(),
				           new_entry.initrd_dirnames.size() == 1 ? "" : "s");
				goto end;  // break outer loop; 2020 and C++ does not have this
			}



@@ 135,7 133,7 @@ int klapki::state::stated_config::parse(klapki::state::stated_config & into, con

			shaa_t idir_sha;
			if(size <= idir_sha.size()) {
				fmt::print(stderr, "extraneous data; 2.5\n");
				fmt::print(stderr, "Parsing state: cut off reading SHA for initrd {} for entry {:04X}\n", new_entry.bootnum_hint, new_entry.initrd_dirnames.size());
				break;
			}
			memcpy(&idir_sha[0], data, idir_sha.size());


@@ 152,7 150,7 @@ int klapki::state::stated_config::parse(klapki::state::stated_config & into, con
	}
end:

	return 0;
	return;
}



D src/util.cpp => src/util.cpp +0 -32
@@ 1,32 0,0 @@
// The MIT License (MIT)

// Copyright (c) 2020 наб <nabijaczleweli@nabijaczleweli.xyz>

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


#include "util.hpp"
#include <fstream>


std::string klapki::readline(const char * from) {
	std::ifstream in(from);
	std::string ret;
	std::getline(in, ret);
	return ret;
}

M src/util.hpp => src/util.hpp +0 -14
@@ 23,13 23,7 @@
#pragma once


#include <cstdlib>
#include <string>


namespace klapki {
	std::string readline(const char * from);

	/// Stolen from https://en.cppreference.com/w/cpp/utility/variant/visit
	template <class... Ts>
	struct overload : Ts... {


@@ 37,12 31,4 @@ namespace klapki {
	};
	template <class... Ts>
	overload(Ts...)->overload<Ts...>;

	/// unique_ptr deleter that uses free(3)
	struct free_deleter {
		template <class T>
		void operator()(T * ptr) const noexcept {
			std::free(ptr);
		}
	};
}

M test/state_parse.cpp => test/state_parse.cpp +7 -7
@@ 94,14 94,14 @@ static std::unordered_map<const char *, klapki::state::stated_config> states = {

TEST_CASE("klapki::state::stated_config::parse() ENODATA", "[klapki::state::stated_config::parse]") {
	klapki::state::stated_config clean{};
	clean.boot_position = __LINE__;
	auto cclean         = clean;

	REQUIRE(klapki::state::stated_config::parse(clean, nullptr, 0) == -1);
	REQUIRE(errno == ENODATA);
  REQUIRE(clean == klapki::state::stated_config{});
	klapki::state::stated_config::parse(clean, nullptr, 0);
	REQUIRE(clean == cclean);

	REQUIRE(klapki::state::stated_config::parse(clean, nullptr, 1) == -1);
	REQUIRE(errno == ENODATA);
  REQUIRE(clean == klapki::state::stated_config{});
	klapki::state::stated_config::parse(clean, nullptr, 1);
	REQUIRE(clean == cclean);
}

TEST_CASE("klapki::state::stated_config::parse() okay", "[klapki::state::stated_config::parse]") {


@@ 110,7 110,7 @@ TEST_CASE("klapki::state::stated_config::parse() okay", "[klapki::state::stated_
			const auto indata = klapki::test::read_file(std::string{"test-data/state_parse/"} + name);

			klapki::state::stated_config incfg{};
			REQUIRE(klapki::state::stated_config::parse(incfg, indata.data(), indata.size()) == 0);
			klapki::state::stated_config::parse(incfg, indata.data(), indata.size());
			// for(auto && ent : incfg.wanted_entries)
			// 	fmt::print(stderr,
			// 	           "\tbootnum_hint: {}\n"