Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
PACKAGE = nullinitrd
VERSION = 1.0.0
VERSION = 1.1
-include .config
CXX ?= g++
CXXFLAGS = -std=c++17 -Wall -Wextra -O2 -DVERSION=\"$(VERSION)\"
Expand All @@ -20,7 +20,7 @@ endif
SRCDIR = src
OBJDIR = obj
BINDIR = bin
GEN_SOURCES = $(SRCDIR)/main.cpp $(SRCDIR)/config.cpp $(SRCDIR)/generator.cpp $(SRCDIR)/hooks.cpp $(SRCDIR)/utils.cpp
GEN_SOURCES = $(SRCDIR)/main.cpp $(SRCDIR)/config.cpp $(SRCDIR)/generator.cpp $(SRCDIR)/hooks.cpp $(SRCDIR)/utils.cpp $(SRCDIR)/logger/log.cpp
GEN_OBJECTS = $(GEN_SOURCES:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o)
GEN_TARGET = $(BINDIR)/$(PACKAGE)
INIT_SOURCE = $(SRCDIR)/init.cpp
Expand All @@ -42,12 +42,10 @@ $(INIT_TARGET): $(INIT_OBJECT) | $(BINDIR)
$(CXX) $(INIT_OBJECT) -o $@ -static
strip $@

$(OBJDIR)/%.o: $(SRCDIR)/%.cpp | $(OBJDIR)
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
@mkdir -p $(dir $@)
$(CXX) $(CXXFLAGS) -c $< -o $@

$(OBJDIR):
mkdir -p $(OBJDIR)

$(BINDIR):
mkdir -p $(BINDIR)

Expand Down
28 changes: 26 additions & 2 deletions src/config.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include "config.hpp"
#include <fstream>
#include <sstream>
#include <stdexcept>
#include <sys/stat.h>

Config::Config(const std::string& path)
: compression("zstd"),
Expand All @@ -11,10 +11,24 @@ Config::Config(const std::string& path)
parse_file(path);
}

/*
* Parse a config file, write the defaults to it if not found.
*/
void Config::parse_file(const std::string& path) {
std::ifstream file(path);
if (!file.is_open()) {
throw std::runtime_error(":: [!] cannot open config file: " + path);
// create the default config in const std::string& path
mkdir("/etc/nullinitrd", 0755);
std::ofstream configfile;
configfile.open(path);
configfile << "CONFIG_STATIC=y\n";
configfile << "CONFIG_DEBUG=n\n";
configfile << "CONFIG_LTO=y\n";
configfile << "CONFIG_FEATURE_LVM=n\n";
configfile << "CONFIG_FEATURE_LUKS=n\n";
configfile << "CONFIG_FEATURE_MDADM=n\n";
configfile << "CONFIG_FEATURE_BTRFS=n\n";
configfile << "CONFIG_FEATURE_ZFS=n\n";
}

std::string line;
Expand Down Expand Up @@ -56,6 +70,12 @@ std::string Config::get(const std::string& key, const std::string& default_val)
return it != config_map.end() ? it->second : default_val;
}

/*
* Get a boolean value from the config, return a bool.
* Arguments:
* const std::string& key - key to check
* bool default_val - use this value if empty
*/
bool Config::get_bool(const std::string& key, bool default_val) const {
auto val = get(key, "");
if (val.empty()) return default_val;
Expand All @@ -74,6 +94,10 @@ std::vector<std::string> Config::get_list(const std::string& key) const {
return result;
}

/* Check if a feature is enabled, return a boolean.
* Arguments:
* const std::string& feature - the feat to check for
*/
bool Config::is_enabled(const std::string& feature) const {
return features.count(feature) > 0;
}
75 changes: 59 additions & 16 deletions src/generator.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "generator.hpp"
#include "hooks.hpp"
#include "logger/log.hpp"
#include <filesystem>
#include <iostream>
#include <algorithm>
Expand All @@ -12,7 +13,7 @@ Generator::Generator(const Config& cfg, const std::string& kernel_ver, bool v)
char tmpl[] = "/tmp/nullinitrd.XXXXXX";
char* tmp = mkdtemp(tmpl);
if (!tmp) {
throw std::runtime_error(":: [!] failed to create temp directory");
throw std::runtime_error("failed to create temp directory");
}
chmod(tmp, 0755);
work_dir = tmp;
Expand All @@ -26,15 +27,23 @@ Generator::Generator(const Config& cfg, const std::string& kernel_ver, bool v)
};
}

/*
* Helper to create a directory.
* Arguments:
* const fs::path& path - directory to create
*/
void Generator::create_directory(const fs::path& path) {
if (verbose) {
std::cout << ":: creating dir: " << path << std::endl;
log_info("creating dir: " + path.string());
}
fs::create_directories(path);
}

/*
* Creates the strucutre of the initramfs.
*/
void Generator::create_structure() {
std::cout << ":: creating structure..." << std::endl;
log_info("creating structure...");
create_directory(work_dir / "usr/bin");
create_directory(work_dir / "usr/lib");
create_directory(work_dir / "usr/lib64");
Expand All @@ -53,8 +62,11 @@ void Generator::create_structure() {
}
}

/*
* Create UsrMerge symlinks.
*/
void Generator::create_symlinks() {
std::cout << ":: creating symlinks..." << std::endl;
log_info("creating symlinks...");
auto safe_symlink = [this](const char* target, const fs::path& link) {
if (fs::exists(link) || fs::is_symlink(link)) fs::remove(link);
fs::create_symlink(target, link);
Expand All @@ -65,9 +77,15 @@ void Generator::create_symlinks() {
safe_symlink("usr/lib64", work_dir / "lib64");
}

/*
* Helper to copy a file.
* Arguments:
* const fs::path& src - source path
* const fs::path& dst - destination path
*/
void Generator::copy_file(const fs::path& src, const fs::path& dst) {
if (verbose) {
std::cout << ":: copying " << src << " -> " << dst << std::endl;
log_info("copying " + src.string() + " -> " + dst.string());
}
fs::create_directories(dst.parent_path());
fs::copy_file(src, dst, fs::copy_options::overwrite_existing);
Expand All @@ -77,6 +95,12 @@ void Generator::copy_file(const fs::path& src, const fs::path& dst) {
}
}

/*
* Helper to find a program.
* Returns a std::string (path of the found binary), "" if not found.
* Arguments:
* const std::string& name - program to find
*/
std::string Generator::find_binary(const std::string& name) {
std::vector<std::string> paths = {
"/usr/local/sbin", "/usr/local/bin",
Expand Down Expand Up @@ -135,7 +159,7 @@ void Generator::copy_binary_with_deps(const std::string& binary) {
std::string bin_path = find_binary(binary);
if (bin_path.empty()) {
if (verbose) {
std::cerr << ":: [?] binary not found: " << binary << std::endl;
log_warning("binary not found: " + binary);
}
return;
}
Expand Down Expand Up @@ -164,7 +188,7 @@ void Generator::copy_binary_with_deps(const std::string& binary) {
}

void Generator::copy_binaries() {
std::cout << ":: copying binaries..." << std::endl;
log_info("copying binaries...");

copy_binary_with_deps("kmod");
fs::path kmod_dst = work_dir / "usr/bin/kmod";
Expand Down Expand Up @@ -254,7 +278,7 @@ void Generator::copy_module(const std::string& module) {
fs::create_directories(dst.parent_path());
if (needs_decompress) {
if (verbose) {
std::cout << ":: decompressing " << src << " -> " << dst << std::endl;
log_info("decompressing " + src.string() + " -> " + dst.string());
}
std::string full_cmd = decompress_cmd + " '" + mod_file + "' > '" + dst.string() + "'";
system(full_cmd.c_str());
Expand All @@ -267,8 +291,11 @@ void Generator::copy_module(const std::string& module) {
pclose(pipe);
}

/*
* Copy kernel modules.
*/
void Generator::copy_modules() {
std::cout << ":: copying kernel modules..." << std::endl;
log_info("copying kernel modules...");
create_directory(work_dir / "usr/lib/modules" / kernel_version);

std::vector<std::string> modules_to_copy;
Expand All @@ -285,10 +312,10 @@ void Generator::copy_modules() {
copy_module(mod);
}

std::cout << ":: generating module dependencies..." << std::endl;
log_info("generating module dependencies...");
std::string depmod_cmd = "depmod -b " + work_dir.string() + " " + kernel_version;
if (system(depmod_cmd.c_str()) != 0) {
std::cerr << ":: [!] depmod failed, falling back to copying modules.dep" << std::endl;
log_warning("depmod failed, falling back to copying modules.dep");
std::string dep_src = "/usr/lib/modules/" + kernel_version + "/modules.dep";
if (fs::exists(dep_src)) {
copy_file(dep_src, work_dir / "usr/lib/modules" / kernel_version / "modules.dep");
Expand All @@ -301,7 +328,7 @@ void Generator::copy_modules() {
}

void Generator::create_init() {
std::cout << ":: installing init..." << std::endl;
log_info("installing init...");

std::vector<std::string> init_paths = {
"/usr/share/nullinitrd/init",
Expand All @@ -318,22 +345,30 @@ void Generator::create_init() {
}

if (init_src.empty()) {
throw std::runtime_error(":: [!] init binary not found");
throw std::runtime_error("init binary not found");
}

fs::path init_dst = work_dir / "init";
copy_file(init_src, init_dst);
chmod(init_dst.c_str(), 0755);
}

/*
* Run hooks.
*/
void Generator::run_hooks() {
std::cout << ":: running hooks..." << std::endl;
log_info("running hooks...");
HookManager hook_mgr(config, work_dir, kernel_version, verbose);
for (const auto& hook : config.hooks) {
hook_mgr.run_hook(hook);
}
}

/*
* Find the compression command to use.
* Return the command to use, ofc.
* Defaults to zstd -19 -T0.
*/
std::string Generator::get_compression_cmd() {
if (config.compression == "gzip") return "gzip -9";
if (config.compression == "bzip2") return "bzip2 -9";
Expand All @@ -345,14 +380,22 @@ std::string Generator::get_compression_cmd() {
return "zstd -19 -T0";
}

/*
* Pack the initramfs.
* Arguments:
* const std::string& output - output file
*/
void Generator::pack(const std::string& output) {
std::cout << ":: packing initramfs..." << std::endl;
log_info("packing initramfs...");
std::string cpio_cmd = "cd " + work_dir.string() + " && find . | cpio -o -H newc 2>/dev/null";
std::string compress_cmd = get_compression_cmd();
std::string full_cmd = cpio_cmd + " | " + compress_cmd + " > " + output;
int ret = system(full_cmd.c_str());
if (ret != 0) {
throw std::runtime_error(":: [!] failed to pack initramfs");
throw std::runtime_error("failed to pack initramfs");
}
fs::remove_all(work_dir);
if (fs::exists(output)) {
throw std::runtime_error("failed to pack initramfs");
}
}
28 changes: 19 additions & 9 deletions src/hooks.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "hooks.hpp"
#include "logger/log.hpp"
#include <iostream>
#include <cstdlib>
#include <sys/stat.h>
Expand All @@ -7,24 +8,30 @@ HookManager::HookManager(const Config& cfg, const fs::path& work,
: config(cfg), work_dir(work), kernel_version(kver), verbose(v) {}

void HookManager::run_script(const fs::path& script) {
std::cout << ":: [#] " << script << std::endl;
std::string cmd = "NULLINITRD_WORKDIR=" + work_dir.string() +
log_job(std::string("[#] ") + script.string());
std::string cmd = "NULLINITRD_WORKDIR=" + work_dir.string() +
" NULLINITRD_KERNEL=" + kernel_version +
" " + script.string();

int ret = system(cmd.c_str());
if (ret != 0) {
std::cerr << ":: [?] hook " << script << " exited with code " << ret << std::endl;
log_warning(std::string("hook ") + script.string() + " exited with code " + std::to_string(ret));
}
}

/*
* Find a hook in /usr/[local]/share/nullinitrd/hooks or /etc/nullinitrd/hooks,
* return false if not found or true if found.
* Arguments:
* const std::string& hook_name - the hook to find
*/
bool HookManager::find_and_run(const std::string& hook_name) {
std::vector<std::string> search_paths = {
"/etc/nullinitrd/hooks",
"/usr/share/nullinitrd/hooks",
"/usr/local/share/nullinitrd/hooks"
};

for (const auto& path : search_paths) {
fs::path hook_path = fs::path(path) / hook_name;
if (fs::exists(hook_path)) {
Expand All @@ -35,14 +42,17 @@ bool HookManager::find_and_run(const std::string& hook_name) {
}
}
}

return false;
}

/*
* Run a hook.
* Arguments:
* const std::string& hook_name - name of the hook
*/
void HookManager::run_hook(const std::string& hook_name) {
if (!find_and_run(hook_name)) {
if (verbose) {
std::cerr << ":: [?] hook not found: " << hook_name << std::endl;
}
log_warning("hook not found: " + hook_name);
}
}
Loading