Skip to content

Commit 24a250d

Browse files
committed
👷 gucc: add grub config gen from provided struct
1 parent 9c8f8f2 commit 24a250d

File tree

5 files changed

+435
-0
lines changed

5 files changed

+435
-0
lines changed

‎.github/workflows/build.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ jobs:
4040
./gucc/tests/test-pacmanconf
4141
./gucc/tests/test-fstab_gen
4242
./gucc/tests/test-crypttab_gen
43+
./gucc/tests/test-grub_config_gen
4344
shell: bash
4445
build-cmake_withoutdev:
4546
name: Build with CMake (DEVENV OFF)
@@ -86,6 +87,7 @@ jobs:
8687
./gucc/tests/test-pacmanconf
8788
./gucc/tests/test-fstab_gen
8889
./gucc/tests/test-crypttab_gen
90+
./gucc/tests/test-grub_config_gen
8991
shell: bash
9092
build-meson_withoutdev:
9193
name: Build with Meson (DEVENV OFF)

‎gucc/include/gucc/bootloader.hpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,64 @@
11
#ifndef BOOTLOADER_HPP
22
#define BOOTLOADER_HPP
33

4+
#include <cinttypes>
5+
6+
#include <optional> // for optional
7+
#include <string> // for string
48
#include <string_view> // for string_view
59

610
namespace gucc::bootloader {
711

12+
struct GrubConfig final {
13+
// e.g GRUB_DEFAULT=0
14+
std::string default_entry{"0"};
15+
// e.g GRUB_TIMEOUT=5
16+
std::int32_t grub_timeout{5};
17+
// e.g GRUB_DISTRIBUTOR="Arch"
18+
std::string grub_distributor{"Arch"};
19+
// e.g GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet"
20+
std::string cmdline_linux_default{"loglevel=3 quiet"};
21+
// e.g GRUB_CMDLINE_LINUX=""
22+
std::string cmdline_linux{};
23+
// e.g GRUB_PRELOAD_MODULES="part_gpt part_msdos"
24+
std::string preload_modules{"part_gpt part_msdos"};
25+
// e.g #GRUB_ENABLE_CRYPTODISK=y
26+
std::optional<bool> enable_cryptodisk{};
27+
// e.g GRUB_TIMEOUT_STYLE=menu
28+
std::string timeout_style{"menu"};
29+
// e.g GRUB_TERMINAL_INPUT=console
30+
std::string terminal_input{"console"};
31+
// e.g #GRUB_TERMINAL_OUTPUT=console
32+
std::optional<std::string> terminal_output{};
33+
// e.g GRUB_GFXMODE=auto
34+
std::string gfxmode{"auto"};
35+
// e.g GRUB_GFXPAYLOAD_LINUX=keep
36+
std::string gfxpayload_linux{"keep"};
37+
// e.g #GRUB_DISABLE_LINUX_UUID=true
38+
std::optional<bool> disable_linux_uuid{};
39+
// e.g GRUB_DISABLE_RECOVERY=true
40+
bool disable_recovery{true};
41+
// e.g #GRUB_COLOR_NORMAL="light-blue/black"
42+
std::optional<std::string> color_normal{};
43+
// e.g #GRUB_COLOR_HIGHLIGHT="light-cyan/blue"
44+
std::optional<std::string> color_highlight{};
45+
// e.g #GRUB_BACKGROUND="/path/to/wallpaper"
46+
std::optional<std::string> background{};
47+
// e.g #GRUB_THEME="/path/to/gfxtheme"
48+
std::optional<std::string> theme{};
49+
// e.g #GRUB_INIT_TUNE="480 440 1"
50+
std::optional<std::string> init_tune{};
51+
// e.g #GRUB_SAVEDEFAULT=true
52+
std::optional<bool> savedefault{};
53+
// e.g #GRUB_DISABLE_SUBMENU=y
54+
std::optional<bool> disable_submenu{};
55+
// e.g #GRUB_DISABLE_OS_PROBER=false
56+
std::optional<bool> disable_os_prober{};
57+
};
58+
59+
// Generate grub_config into string
60+
auto gen_grub_config(const GrubConfig& grub_config) noexcept -> std::string;
61+
862
// Installs & configures systemd-boot on system
963
auto install_systemd_boot(std::string_view root_mountpoint, std::string_view efi_directory, bool is_volume_removable) noexcept -> bool;
1064

‎gucc/src/bootloader.cpp

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,204 @@
77

88
#include <spdlog/spdlog.h>
99

10+
#if defined(__clang__)
11+
#pragma clang diagnostic push
12+
#pragma clang diagnostic ignored "-Wold-style-cast"
13+
#elif defined(__GNUC__)
14+
#pragma GCC diagnostic push
15+
#pragma GCC diagnostic ignored "-Wnull-dereference"
16+
#pragma GCC diagnostic ignored "-Wuseless-cast"
17+
#pragma GCC diagnostic ignored "-Wold-style-cast"
18+
#endif
19+
20+
#include <range/v3/range/conversion.hpp>
21+
#include <range/v3/view/join.hpp>
22+
#include <range/v3/view/split.hpp>
23+
#include <range/v3/view/transform.hpp>
24+
25+
#if defined(__clang__)
26+
#pragma clang diagnostic pop
27+
#elif defined(__GNUC__)
28+
#pragma GCC diagnostic pop
29+
#endif
30+
1031
using namespace std::string_view_literals;
1132

33+
#define CONV_REQ_F(needle, f) \
34+
if (line.starts_with(needle)) { \
35+
return fmt::format(needle "{}", f); \
36+
}
37+
#define CONV_REQ_F_S(needle, f) \
38+
if (line.starts_with(needle)) { \
39+
return fmt::format(needle "\"{}\"", f); \
40+
}
41+
#define CONV_OPT_F(needle, f, default_val) \
42+
if (line.starts_with(needle)) { \
43+
if (f) { \
44+
return fmt::format(needle "{}", *f); \
45+
} \
46+
return {"#" needle default_val}; \
47+
}
48+
#define CONV_OPT_F_S(needle, f, default_val) \
49+
if (line.starts_with(needle)) { \
50+
if (f) { \
51+
return fmt::format(needle "\"{}\"", *f); \
52+
} \
53+
return {"#" needle "\"" default_val "\""}; \
54+
}
55+
#define CONV_REQ_B(needle, f) \
56+
if (line.starts_with(needle)) { \
57+
return fmt::format(needle "{}", convert_boolean_val(needle, f)); \
58+
}
59+
#define CONV_OPT_B(needle, f, default_val) \
60+
if (line.starts_with(needle)) { \
61+
if (f) { \
62+
return fmt::format(needle "{}", convert_boolean_val(needle, *f)); \
63+
} \
64+
return {"#" needle default_val}; \
65+
}
66+
67+
namespace {
68+
69+
// NOLINTNEXTLINE
70+
static constexpr auto GRUB_DEFAULT_CONFIG = R"(# GRUB boot loader configuration
71+
72+
GRUB_DEFAULT=0
73+
GRUB_TIMEOUT=5
74+
GRUB_DISTRIBUTOR="Arch"
75+
GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet"
76+
GRUB_CMDLINE_LINUX=""
77+
78+
# Preload both GPT and MBR modules so that they are not missed
79+
GRUB_PRELOAD_MODULES="part_gpt part_msdos"
80+
81+
# Uncomment to enable booting from LUKS encrypted devices
82+
#GRUB_ENABLE_CRYPTODISK=y
83+
84+
# Set to 'countdown' or 'hidden' to change timeout behavior,
85+
# press ESC key to display menu.
86+
GRUB_TIMEOUT_STYLE=menu
87+
88+
# Uncomment to use basic console
89+
GRUB_TERMINAL_INPUT=console
90+
91+
# Uncomment to disable graphical terminal
92+
#GRUB_TERMINAL_OUTPUT=console
93+
94+
# The resolution used on graphical terminal
95+
# note that you can use only modes which your graphic card supports via VBE
96+
# you can see them in real GRUB with the command `videoinfo'
97+
GRUB_GFXMODE=auto
98+
99+
# Uncomment to allow the kernel use the same resolution used by grub
100+
GRUB_GFXPAYLOAD_LINUX=keep
101+
102+
# Uncomment if you want GRUB to pass to the Linux kernel the old parameter
103+
# format "root=/dev/xxx" instead of "root=/dev/disk/by-uuid/xxx"
104+
#GRUB_DISABLE_LINUX_UUID=true
105+
106+
# Uncomment to disable generation of recovery mode menu entries
107+
GRUB_DISABLE_RECOVERY=true
108+
109+
# Uncomment and set to the desired menu colors. Used by normal and wallpaper
110+
# modes only. Entries specified as foreground/background.
111+
#GRUB_COLOR_NORMAL="light-blue/black"
112+
#GRUB_COLOR_HIGHLIGHT="light-cyan/blue"
113+
114+
# Uncomment one of them for the gfx desired, a image background or a gfxtheme
115+
#GRUB_BACKGROUND="/path/to/wallpaper"
116+
#GRUB_THEME="/path/to/gfxtheme"
117+
118+
# Uncomment to get a beep at GRUB start
119+
#GRUB_INIT_TUNE="480 440 1"
120+
121+
# Uncomment to make GRUB remember the last selection. This requires
122+
# setting 'GRUB_DEFAULT=saved' above.
123+
#GRUB_SAVEDEFAULT=true
124+
125+
# Uncomment to disable submenus in boot menu
126+
#GRUB_DISABLE_SUBMENU=y
127+
128+
# Probing for other operating systems is disabled for security reasons. Read
129+
# documentation on GRUB_DISABLE_OS_PROBER, if still want to enable this
130+
# functionality install os-prober and uncomment to detect and include other
131+
# operating systems.
132+
#GRUB_DISABLE_OS_PROBER=false
133+
)"sv;
134+
135+
template <typename T, typename npos_type = std::remove_cvref_t<decltype(T::npos)>>
136+
concept string_findable = requires(T value) {
137+
// check that type of T::npos is T::size_type
138+
{ npos_type{} } -> std::same_as<typename T::size_type>;
139+
// check that type of T::find is T::size_type
140+
{ value.find(std::string_view{""}) } -> std::same_as<typename T::size_type>;
141+
};
142+
143+
// simple helper function to check if string contains a string
144+
constexpr auto contains(string_findable auto const& str, std::string_view needle) noexcept -> bool {
145+
using str_type = std::remove_reference_t<decltype(str)>;
146+
return str.find(needle) != str_type::npos;
147+
}
148+
149+
constexpr auto convert_boolean_val(std::string_view needle, bool value) noexcept -> std::string_view {
150+
if (needle == "GRUB_ENABLE_CRYPTODISK="sv || needle == "GRUB_DISABLE_SUBMENU="sv) {
151+
return value ? "y"sv : "n"sv;
152+
}
153+
return value ? "true"sv : "false"sv;
154+
}
155+
156+
auto parse_grub_line(const gucc::bootloader::GrubConfig& grub_config, std::string_view line) noexcept -> std::string {
157+
if (line.starts_with("#GRUB_")) {
158+
// uncomment grub setting
159+
line.remove_prefix(1);
160+
}
161+
162+
if (line.starts_with("GRUB_")) {
163+
CONV_REQ_F("GRUB_DEFAULT=", grub_config.default_entry);
164+
CONV_REQ_F("GRUB_TIMEOUT=", grub_config.grub_timeout);
165+
CONV_REQ_F_S("GRUB_DISTRIBUTOR=", grub_config.grub_distributor);
166+
CONV_REQ_F_S("GRUB_CMDLINE_LINUX_DEFAULT=", grub_config.cmdline_linux_default);
167+
CONV_REQ_F_S("GRUB_CMDLINE_LINUX=", grub_config.cmdline_linux);
168+
CONV_REQ_F_S("GRUB_PRELOAD_MODULES=", grub_config.preload_modules);
169+
CONV_REQ_F("GRUB_TIMEOUT_STYLE=", grub_config.timeout_style);
170+
CONV_REQ_F("GRUB_TERMINAL_INPUT=", grub_config.terminal_input);
171+
CONV_OPT_F("GRUB_TERMINAL_OUTPUT=", grub_config.terminal_output, "console");
172+
CONV_REQ_F("GRUB_GFXMODE=", grub_config.gfxmode);
173+
CONV_REQ_F("GRUB_GFXPAYLOAD_LINUX=", grub_config.gfxpayload_linux);
174+
CONV_OPT_F_S("GRUB_COLOR_NORMAL=", grub_config.color_normal, "light-blue/black");
175+
CONV_OPT_F_S("GRUB_COLOR_HIGHLIGHT=", grub_config.color_highlight, "light-cyan/blue");
176+
CONV_OPT_F_S("GRUB_BACKGROUND=", grub_config.background, "/path/to/wallpaper");
177+
CONV_OPT_F_S("GRUB_THEME=", grub_config.theme, "/path/to/gfxtheme");
178+
CONV_OPT_F_S("GRUB_INIT_TUNE=", grub_config.init_tune, "480 440 1");
179+
if (contains(line, "=y") || contains(line, "=true") || contains(line, "=false")) {
180+
// booleans
181+
CONV_OPT_B("GRUB_ENABLE_CRYPTODISK=", grub_config.enable_cryptodisk, "y");
182+
CONV_OPT_B("GRUB_DISABLE_LINUX_UUID=", grub_config.disable_linux_uuid, "true");
183+
CONV_REQ_B("GRUB_DISABLE_RECOVERY=", grub_config.disable_recovery);
184+
CONV_OPT_B("GRUB_SAVEDEFAULT=", grub_config.savedefault, "true");
185+
CONV_OPT_B("GRUB_DISABLE_SUBMENU=", grub_config.disable_submenu, "y");
186+
CONV_OPT_B("GRUB_DISABLE_OS_PROBER=", grub_config.disable_os_prober, "false");
187+
}
188+
}
189+
return std::string{line.data(), line.size()};
190+
}
191+
192+
} // namespace
193+
12194
namespace gucc::bootloader {
13195

196+
auto gen_grub_config(const GrubConfig& grub_config) noexcept -> std::string {
197+
std::string result = GRUB_DEFAULT_CONFIG | ranges::views::split('\n')
198+
| ranges::views::transform([&](auto&& rng) {
199+
auto&& line = std::string_view(&*rng.begin(), static_cast<size_t>(ranges::distance(rng)));
200+
return parse_grub_line(grub_config, line);
201+
})
202+
| ranges::views::join('\n')
203+
| ranges::to<std::string>();
204+
result += '\n';
205+
return result;
206+
}
207+
14208
auto install_systemd_boot(std::string_view root_mountpoint, std::string_view efi_directory, bool is_volume_removable) noexcept -> bool {
15209
// Install systemd-boot onto EFI
16210
const auto& bootctl_cmd = fmt::format(FMT_COMPILE("bootctl --path={} install"), efi_directory);

‎gucc/tests/meson.build

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,11 @@ executable(
2929
link_with: [gucc_lib],
3030
include_directories: [include_directories('../include')],
3131
install: false)
32+
33+
executable(
34+
'test-grub_config_gen',
35+
files('unit-grub_config_gen.cpp'),
36+
dependencies: deps,
37+
link_with: [gucc_lib],
38+
include_directories: [include_directories('../include')],
39+
install: false)

0 commit comments

Comments
 (0)