👷 update ui

This commit is contained in:
Vladislav Nepogodin 2021-12-05 04:15:34 +04:00
parent c36ab03920
commit ef6fd02314
No known key found for this signature in database
GPG Key ID: B62C3D10C54D5DA9
7 changed files with 232 additions and 23 deletions

24
.clang-tidy Normal file
View File

@ -0,0 +1,24 @@
Checks: '-android-cloexec-fopen,
-cppcoreguidelines-*,
-fuchsia-default-arguments-calls,
-fuchsia-default-arguments-declarations,
-fuchsia-overloaded-operator,
-google-explicit-constructor,
-google-readability-function-size,
-google-runtime-int,
-google-runtime-references,
-hicpp-*,
-llvm-header-guard,
-llvm-include-order,
-llvmlibc-*,
-misc-*,
-modernize-*,
-readability-*,
-performance-*'
FormatStyle: 'file'
CheckOptions:
- key: hicpp-special-member-functions.AllowSoleDefaultDtor
value: 1
HeaderFilterRegex: '.*hpp$'

View File

@ -80,6 +80,12 @@ add_executable(${PROJECT_NAME}
src/main.cpp
)
add_executable(test-exec-interactive
src/config.cpp
src/utils.cpp
src/main_test.cpp
)
# Link this 'library' to use the warnings specified in CompilerWarnings.cmake
add_library(project_warnings INTERFACE)
set_project_warnings(project_warnings)
@ -93,6 +99,7 @@ enable_sanitizers(project_options)
include_directories(${CMAKE_SOURCE_DIR}/src)
target_link_libraries(${PROJECT_NAME} PRIVATE project_warnings project_options fmt::fmt ftxui::screen ftxui::dom ftxui::component nlohmann_json::nlohmann_json cpr::cpr)
target_link_libraries(test-exec-interactive PRIVATE project_warnings project_options fmt::fmt)
option(ENABLE_UNITY "Enable Unity builds of projects" OFF)
if(ENABLE_UNITY)

View File

@ -103,6 +103,13 @@ executable(
include_directories: [include_directories('src')],
install: true)
executable(
'test-exec-interactive',
files('src/config.cpp', 'src/utils.cpp', 'src/main_test.cpp'),
dependencies: [fmt],
include_directories: [include_directories('src')],
install: false)
summary(
{
'Build type': get_option('buildtype'),

8
src/main_test.cpp Normal file
View File

@ -0,0 +1,8 @@
#include "definitions.hpp"
#include "utils.hpp"
int main() {
output("\n\n------ TEST BASH LAUNCH BEGIN ------\n\n");
utils::exec("bash", true);
output("\n\n------ TEST BASH LAUNCH END ------\n\n");
}

View File

@ -38,12 +38,10 @@ ftxui::Element centered_widget(ftxui::Component& container, const std::string_vi
});
}
ftxui::Component controls_widget(const std::array<std::string_view, 2>&& titles, const std::array<std::function<void()>, 2>&& callbacks) {
ftxui::Component controls_widget(const std::array<std::string_view, 2>&& titles, const std::array<std::function<void()>, 2>&& callbacks, ftxui::ButtonOption* button_option) {
/* clang-format off */
auto button_option = ButtonOption();
button_option.border = false;
auto button_ok = Button(titles[0].data(), callbacks[0], &button_option);
auto button_quit = Button(titles[1].data(), callbacks[1], &button_option);
auto button_ok = Button(titles[0].data(), callbacks[0], button_option);
auto button_quit = Button(titles[1].data(), callbacks[1], button_option);
/* clang-format on */
auto container = Container::Horizontal({
@ -80,6 +78,63 @@ ftxui::Element multiline_text(const std::vector<std::string>& lines) {
return vbox(std::move(multiline)) | frame;
}
// BIOS and UEFI
void auto_partition() noexcept {
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
// Find existing partitions (if any) to remove
auto parts = utils::exec(fmt::format("parted -s {} print | {}", config_data["DEVICE"], "awk \'/^ / {print $1}\'"));
const auto& del_parts = utils::make_multiline(parts);
for (const auto& del_part : del_parts) {
#ifdef NDEVENV
utils::exec(fmt::format("parted -s {} rm {}", config_data["DEVICE"], del_part));
#else
output("{}\n", del_part);
#endif
}
#ifdef NDEVENV
// Identify the partition table
const auto& part_table = utils::exec(fmt::format("parted -s {} print | grep -i \'partition table\' | {}", config_data["DEVICE"], "awk \'{print $3}\'"));
// Create partition table if one does not already exist
if ((config_data["SYSTEM"] == "BIOS") && (part_table != "msdos"))
utils::exec(fmt::format("parted -s {} mklabel msdos", config_data["DEVICE"]));
if ((config_data["SYSTEM"] == "UEFI") && (part_table != "gpt"))
utils::exec(fmt::format("parted -s {} mklabel gpt", config_data["DEVICE"]));
// Create partitions (same basic partitioning scheme for BIOS and UEFI)
if (config_data["SYSTEM"] == "BIOS")
utils::exec(fmt::format("parted -s {} mkpart primary ext3 1MiB 513MiB", config_data["DEVICE"]));
else
utils::exec(fmt::format("parted -s {} mkpart ESP fat32 1MiB 513MiB", config_data["DEVICE"]));
utils::exec(fmt::format("parted -s {} set 1 boot on", config_data["DEVICE"]));
utils::exec(fmt::format("parted -s {} mkpart primary ext3 513MiB 100%", config_data["DEVICE"]));
#endif
// Show created partitions
auto disklist = utils::exec(fmt::format("lsblk {} -o NAME,TYPE,FSTYPE,SIZE", config_data["DEVICE"]));
auto& screen = tui::screen_service::instance()->data();
/* clang-format off */
auto button_option = ButtonOption();
button_option.border = false;
auto button_back = Button("Back", screen.ExitLoopClosure(), &button_option);
/* clang-format on */
auto container = Container::Horizontal({
button_back,
});
auto renderer = Renderer(container, [&] {
return tui::centered_widget(container, "New CLI Installer", multiline_text(utils::make_multiline(disklist)) | size(HEIGHT, GREATER_THAN, 5));
});
screen.Loop(renderer);
}
// Simple code to show devices / partitions.
void show_devices() noexcept {
auto& screen = tui::screen_service::instance()->data();
@ -122,7 +177,70 @@ void select_device() noexcept {
const auto& lines = utils::make_multiline(src, " ");
config_data["DEVICE"] = lines[0];
};
auto controls_container = controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()});
ButtonOption button_option{.border = false};
auto controls_container = controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
auto controls = Renderer(controls_container, [&] {
return controls_container->Render() | hcenter | size(HEIGHT, LESS_THAN, 3) | size(WIDTH, GREATER_THAN, 25);
});
auto global = Container::Vertical({
content,
Renderer([] { return separator(); }),
controls,
});
auto renderer = Renderer(global, [&] {
return tui::centered_interative_multi("New CLI Installer", global);
});
screen.Loop(renderer);
}
void create_partitions() noexcept {
static constexpr std::string_view optwipe = "Securely Wipe Device (optional)";
static constexpr std::string_view optauto = "Automatic Partitioning";
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
std::vector<std::string> menu_entries = {
optwipe.data(),
optauto.data(),
"cfdisk",
"cgdisk",
"fdisk",
"gdisk",
"parted",
};
auto& screen = tui::screen_service::instance()->data();
std::int32_t selected{};
auto menu = Menu(&menu_entries, &selected);
auto content = Renderer(menu, [&] {
return menu->Render() | center | size(HEIGHT, GREATER_THAN, 10) | size(WIDTH, GREATER_THAN, 40);
});
auto ok_callback = [&] {
const auto& answer = menu_entries[static_cast<std::size_t>(selected)];
if (answer != optwipe && answer != optauto) {
utils::exec(fmt::format("{} {}", answer, config_data["DEVICE"]), true);
return;
}
if (answer == optwipe) {
utils::secure_wipe();
return;
}
if (answer == optauto) {
auto_partition();
return;
}
};
ButtonOption button_option{.border = false};
auto controls_container = controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
auto controls = Renderer(controls_container, [&] {
return controls_container->Render() | hcenter | size(HEIGHT, LESS_THAN, 3) | size(WIDTH, GREATER_THAN, 25);
@ -144,7 +262,8 @@ void select_device() noexcept {
void init() noexcept {
auto& screen = tui::screen_service::instance()->data();
auto ok_callback = [=] { info("ok\n"); };
auto container = controls_widget({"OK", "Quit"}, {ok_callback, screen.ExitLoopClosure()});
ButtonOption button_option{.border = false};
auto container = controls_widget({"OK", "Quit"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
auto renderer = Renderer(container, [&] {
return tui::centered_widget(container, "New CLI Installer", text("TODO!!") | size(HEIGHT, GREATER_THAN, 5));

View File

@ -2,6 +2,7 @@
#include "config.hpp"
#include "definitions.hpp"
#include <algorithm> // for transform
#include <array> // for array
#include <chrono> // for filesystem, seconds
#include <cstdint> // for int32_t
@ -9,6 +10,7 @@
#include <cstdlib> // for exit, WIFEXITED, WIFSIGNALED
#include <filesystem> // for exists, is_directory
#include <iostream> // for basic_istream, cin
#include <regex> // for regex_search, match_results<>::_Base_type
#include <string> // for operator==, string, basic_string, allocator
#include <sys/mount.h> // for mount
#include <sys/wait.h> // for waitpid
@ -16,10 +18,12 @@
#include <unistd.h> // for execvp, fork
#include <unordered_map> // for unordered_map
#ifdef NDEVENV
#include <cpr/api.h>
#include <cpr/response.h>
#include <cpr/status_codes.h>
#include <cpr/timeout.h>
#endif
namespace fs = std::filesystem;
@ -51,22 +55,35 @@ void clear_screen() noexcept {
output("{}", CLEAR_SCREEN_ANSI);
}
std::string exec(const std::string_view& command, bool capture_output) noexcept {
if (!capture_output) {
std::int32_t status{};
auto pid = fork();
if (pid == 0) {
/* clang-format off */
char* args[2] = { const_cast<char*>(command.data()), nullptr };
/* clang-format on */
execvp(args[0], args);
} else {
do {
waitpid(pid, &status, 0);
} while ((!WIFEXITED(status)) && (!WIFSIGNALED(status)));
}
void exec(const std::vector<std::string>& vec) noexcept {
std::int32_t status{};
auto pid = fork();
if (pid == 0) {
std::vector<char*> args;
std::transform(vec.cbegin(), vec.cend(), std::back_inserter(args),
[=](const std::string& arg) -> char* { return const_cast<char*>(arg.data()); });
args.push_back(nullptr);
char** command = args.data();
execvp(command[0], command);
} else {
do {
waitpid(pid, &status, 0);
} while ((!WIFEXITED(status)) && (!WIFSIGNALED(status)));
}
}
// https://github.com/sheredom/subprocess.h
// https://gist.github.com/konstantint/d49ab683b978b3d74172
// https://github.com/arun11299/cpp-subprocess/blob/master/subprocess.hpp#L1218
// https://stackoverflow.com/questions/11342868/c-interface-for-interactive-bash
// https://github.com/hniksic/rust-subprocess
std::string exec(const std::string_view& command, const bool& interactive) noexcept {
if (interactive) {
system(command.data());
return {};
}
auto* pipe = popen(command.data(), "r");
if (!pipe) {
return "popen failed!";
@ -125,6 +142,17 @@ auto make_multiline(std::string& str, const std::string_view&& delim) noexcept -
return lines;
}
// install a pkg in the live session if not installed
void inst_needed(const std::string_view& pkg) {
const auto& pkg_info = utils::exec(fmt::format("pacman -Q {}", pkg));
const std::regex pkg_regex("/error/");
if (!std::regex_search(pkg_info, pkg_regex)) {
std::this_thread::sleep_for(std::chrono::seconds(2));
utils::clear_screen();
utils::exec(fmt::format("pacman -Sy --noconfirm {}", pkg));
}
}
// Unmount partitions.
void umount_partitions() noexcept {
auto* config_instance = Config::instance();
@ -144,6 +172,20 @@ void umount_partitions() noexcept {
}
}
// Securely destroy all data on a given device.
void secure_wipe() noexcept {
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
#ifdef NDEVENV
utils::inst_needed("wipe");
utils::exec(fmt::format("wipe -Ifre {}", config_data["DEVICE"]));
#else
utils::inst_needed("bash");
output("{}\n", config_data["DEVICE"]);
#endif
}
void id_system() noexcept {
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
@ -223,7 +265,7 @@ void show_iwctl() noexcept {
info("6 - type `exit`\n");
while (utils::prompt_char("Press a key to continue...", CYAN)) {
utils::exec("iwctl", false);
utils::exec("iwctl", true);
break;
}
}

View File

@ -13,8 +13,10 @@ void print_banner() noexcept;
bool prompt_char(const char* prompt, const char* color = RESET, char* read = nullptr) noexcept;
void clear_screen() noexcept;
[[nodiscard]] auto make_multiline(std::string& str, const std::string_view&& delim = "\n") noexcept -> std::vector<std::string>;
void secure_wipe() noexcept;
auto exec(const std::string_view& command, bool capture_output = true) noexcept -> std::string;
void exec(const std::vector<std::string>& vec) noexcept;
auto exec(const std::string_view& command, const bool& interactive = false) noexcept -> std::string;
[[nodiscard]] bool check_root() noexcept;
void id_system() noexcept;
[[nodiscard]] bool handle_connection() noexcept;