From efe8775713098d89ca00a2b594161229e6a3506c Mon Sep 17 00:00:00 2001 From: "Robin E. R. Davies" Date: Fri, 29 Nov 2024 10:45:46 -0500 Subject: [PATCH] pipedal_kconfig (GRUB bootloader) --- .vscode/launch.json | 2 +- src/BootConfig.cpp | 230 +++++++++++++++++++++++++++++++-------- src/BootConfig.hpp | 1 + src/CMakeLists.txt | 5 +- src/ConfigMain.cpp | 25 +++++ src/SystemConfigFile.cpp | 10 ++ src/SystemConfigFile.hpp | 2 + 7 files changed, 226 insertions(+), 49 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 536ea90..7c3ac1c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -22,7 +22,7 @@ // Resolved by CMake Tools: "program": "${command:cmake.launchTargetPath}", "args": [ - //"-h" + "--no-sudo" ], "stopAtEntry": false, "cwd": "${workspaceFolder}", diff --git a/src/BootConfig.cpp b/src/BootConfig.cpp index d70d0c2..ba7adc2 100644 --- a/src/BootConfig.cpp +++ b/src/BootConfig.cpp @@ -25,7 +25,7 @@ #include "SysExec.hpp" #include "util.hpp" #include -#include +#include "SystemConfigFile.hpp" namespace fs = std::filesystem; @@ -84,9 +84,9 @@ std::string ReadFileLine(const fs::path &path) { if (!fs::exists(path)) { - throw std::runtime_error(SS("File does not exist: " << path )); + throw std::runtime_error(SS("File does not exist: " << path)); } - + std::ifstream f(path); if (!f.is_open()) { @@ -97,11 +97,11 @@ std::string ReadFileLine(const fs::path &path) std::getline(f, line); return line; } -std::vector ReadFileArgs(const fs::path&path) +std::vector ReadFileArgs(const fs::path &path) { std::string line = ReadFileLine(path); - return split(line,' '); + return split(line, ' '); } using namespace pipedal; @@ -179,7 +179,8 @@ void BootConfig::WriteConfiguration(std::functionthread = nullptr; // wait for any previous operations to complete. if (!changed) { - if (onComplete) onComplete(true, ""); + if (onComplete) + onComplete(true, ""); } else { @@ -193,7 +194,8 @@ void BootConfig::WriteConfiguration(std::functionbootLoader == BootLoaderT::Grub) @@ -202,7 +204,8 @@ void BootConfig::WriteConfiguration(std::function onComplete) @@ -229,40 +231,24 @@ void BootConfig::WriteUBootConfiguration(std::functionthread = std::make_unique( [this, onComplete]() { - try { + fs::path cmdlineFile = "/boot/firmware/cmdline.txt"; - std::vector bootArgs = ReadFileArgs(cmdlineFile); - std::vector newBootArgs; - for (const auto&value: bootArgs) + fs::path backupFile = "/boot/firmware/cmdline.txt.bak"; + if (!fs::exists(backupFile)) { - if (value != "threadirqs" && !value.starts_with("preempt=")) + try { + fs::copy_file(cmdlineFile,backupFile); + } catch(...) { - newBootArgs.push_back(value); + } } - if (this->canSetThreadIrqs && this->threadedIrqs) - { - newBootArgs.push_back("threadirqs"); - } - if (this->dynamicScheduler != DynamicSchedulerT::NotApplicable) - { - newBootArgs.push_back(commandLineOption(this->dynamicScheduler)); - } - std::stringstream ss; - bool firstArg = true; - for (const std::string&arg: newBootArgs) { - if (!firstArg) { - ss << ' '; - } - firstArg = false; - ss << arg; - } - ss << '\n'; + std::string line = ReadFileLine(cmdlineFile); - std::string newCmdLine = ss.str(); + std::string newLine = SetCmdlineArgs(line); { std::ofstream f(cmdlineFile); @@ -270,7 +256,7 @@ void BootConfig::WriteUBootConfiguration(std::function bootArgs = split(cmdLine,' '); + std::vector newBootArgs; + for (const auto &value : bootArgs) + { + if (value != "threadirqs" && !value.starts_with("preempt=")) + { + newBootArgs.push_back(value); + } + } + if (this->canSetThreadIrqs && this->threadedIrqs) + { + newBootArgs.push_back("threadirqs"); + } + if (this->dynamicScheduler != DynamicSchedulerT::NotApplicable) + { + newBootArgs.push_back(commandLineOption(this->dynamicScheduler)); + } + std::stringstream ss; + bool firstArg = true; + for (const std::string &arg : newBootArgs) + { + if (!firstArg) + { + ss << ' '; + } + firstArg = false; + ss << arg; + } + return ss.str(); +} + void BootConfig::WriteGrubConfiguration(std::function onComplete) { this->thread = nullptr; // blocks if already running. @@ -294,11 +381,60 @@ void BootConfig::WriteGrubConfiguration(std::functionthread = std::make_unique( [this, onComplete]() { - sleep(1); - - if (onComplete) + try { - onComplete(false, "Grub handler not implemeneted."); + ////////// rewrite /etc/default/grub //////////// + fs::path configPath = "/etc/default/grub"; + fs::path backupPath = "/etc/default/grub.bak"; + if (!fs::exists(backupPath)) + { + try { + fs::copy_file(configPath,backupPath); + } catch(...) + { + + } + } + SystemConfigFile grubFile(configPath); + + std::string linePrefix = "GRUB_CMDLINE_LINUX_DEFAULT="; + auto line = grubFile.GetLineNumberStartingWith(linePrefix); + if (line < 0) + { + throw std::runtime_error(SS("Unexpected file format: " << configPath)); + } + std::string cmdlineDefault = grubFile.Get(line); + + std::string cmdlineArgs = UnquoteGrubArgument(cmdlineDefault.substr(linePrefix.length())); + + std::string newArgs = SetCmdlineArgs(cmdlineArgs); + + std::string newLine = linePrefix + QuoteGrubArgument(newArgs); + + grubFile.Set(line,newLine); + + grubFile.Save(configPath); + + /////// regenerate actual GRUB boot files ///////////// + + auto result = sysExecForOutput("update-grub",""); + if (result.exitCode != EXIT_SUCCESS) + { + throw std::runtime_error(SS("update-grub failed. (" << result.output.substr(0,std::min((size_t)60,result.output.length())) << ")")); + } + //// signal completion ////// + + if (onComplete) + { + onComplete(true,""); + } + } + catch (const std::exception &e) + { + if (onComplete) + { + onComplete(false, e.what()); + } } }); } diff --git a/src/BootConfig.hpp b/src/BootConfig.hpp index c6834b0..5e40329 100644 --- a/src/BootConfig.hpp +++ b/src/BootConfig.hpp @@ -67,6 +67,7 @@ namespace pipedal { bool operator==(BootConfig&other) const; private: std::unique_ptr thread; + std::string SetCmdlineArgs(const std::string &args); void WriteUBootConfiguration(std::function onComplete); void WriteGrubConfiguration(std::function onComplete); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 23ab568..58a1acc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -333,6 +333,8 @@ endif() add_executable(pipedal_kconfig kconfigMain.cpp BootConfig.cpp BootConfig.hpp + SystemConfigFile.hpp SystemConfigFile.cpp + ) @@ -687,6 +689,7 @@ add_executable(pipedalconfig ConfigMain.cpp alsaCheck.cpp alsaCheck.hpp + BootConfig.cpp BootConfig.hpp ModFileTypes.cpp ModFileTypes.hpp PiPedalConfiguration.hpp PiPedalConfiguration.cpp JackServerSettings.hpp JackServerSettings.cpp @@ -816,7 +819,7 @@ add_custom_target ( ) -install (TARGETS pipedalconfig pipedal_latency_test DESTINATION ${CMAKE_INSTALL_PREFIX}/bin +install (TARGETS pipedalconfig pipedal_kconfig pipedal_latency_test DESTINATION ${CMAKE_INSTALL_PREFIX}/bin EXPORT pipedalTargets) install (TARGETS pipedald pipedaladmind pipedal_update DESTINATION ${CMAKE_INSTALL_PREFIX}/sbin diff --git a/src/ConfigMain.cpp b/src/ConfigMain.cpp index 11b9e69..0c6c42a 100644 --- a/src/ConfigMain.cpp +++ b/src/ConfigMain.cpp @@ -28,6 +28,7 @@ #include "RegDb.hpp" #include "Locale.hpp" #include "Finally.hpp" +#include "BootConfig.hpp" #include #include @@ -1026,6 +1027,28 @@ bool SetWebServerPort(std::string portOption) return true; } +void CheckPreemptDynamicConfig() +{ + try { + BootConfig bootConfig; + + if (bootConfig.KernelType() == "PREEMPT_DYNAMIC") + { + if (bootConfig.DynamicScheduler() != BootConfig::DynamicSchedulerT::Full) + { + cout << endl; + cout << "Warning: PREEMPT_DYNAMIC kernel has not been configured for 'preempt=full'" << endl; + cout << "Run pipedal_kconfig to configure the kernel for real-time audio." << endl; + cout << endl; + } + } + + } catch(const std::exception& /* ignore */) + { + } +} + + void Install(const fs::path &programPrefix, const std::string endpointAddress) { cout << "Configuring pipedal" << endl; @@ -1257,6 +1280,8 @@ void Install(const fs::path &programPrefix, const std::string endpointAddress) // Restart Wi-Fi Direct if neccessary. OnWifiReinstall(); + + CheckPreemptDynamicConfig(); } catch (const std::exception &e) { diff --git a/src/SystemConfigFile.cpp b/src/SystemConfigFile.cpp index 8bea50b..cc56266 100644 --- a/src/SystemConfigFile.cpp +++ b/src/SystemConfigFile.cpp @@ -109,6 +109,16 @@ static inline std::string ValuePart(const std::string &line) return line.substr(pos + 1); } +void SystemConfigFile::Set(int position,const std::string&line) +{ + if ((size_t)position == lines.size()) + { + lines.push_back(line); + } else { + lines[position] = line; + } +} + std::string SystemConfigFile::Get(const std::string &key) const { int64_t lineIndex = GetLine(key); diff --git a/src/SystemConfigFile.hpp b/src/SystemConfigFile.hpp index a8622cb..ab9aeb4 100644 --- a/src/SystemConfigFile.hpp +++ b/src/SystemConfigFile.hpp @@ -50,6 +50,8 @@ public: const std::string& Get(int position) const { return lines[position]; } void Set(const std::string&key,const std::string &value); void Set(const std::string&key,const std::string &value, const std::string&comment); + void Set(int position,const std::string&line); + void SetDefault(const std::string&key, const std::string &value); void SetDefault(const std::string&key,const std::string &value, const std::string &comment);