pipedal_kconfig (GRUB bootloader)

This commit is contained in:
Robin E. R. Davies
2024-11-29 10:45:46 -05:00
parent 245a47fe81
commit efe8775713
7 changed files with 226 additions and 49 deletions
+1 -1
View File
@@ -22,7 +22,7 @@
// Resolved by CMake Tools: // Resolved by CMake Tools:
"program": "${command:cmake.launchTargetPath}", "program": "${command:cmake.launchTargetPath}",
"args": [ "args": [
//"-h" "--no-sudo"
], ],
"stopAtEntry": false, "stopAtEntry": false,
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
+183 -47
View File
@@ -25,7 +25,7 @@
#include "SysExec.hpp" #include "SysExec.hpp"
#include "util.hpp" #include "util.hpp"
#include <set> #include <set>
#include <ranges> #include "SystemConfigFile.hpp"
namespace fs = std::filesystem; namespace fs = std::filesystem;
@@ -84,9 +84,9 @@ std::string ReadFileLine(const fs::path &path)
{ {
if (!fs::exists(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); std::ifstream f(path);
if (!f.is_open()) if (!f.is_open())
{ {
@@ -97,11 +97,11 @@ std::string ReadFileLine(const fs::path &path)
std::getline(f, line); std::getline(f, line);
return line; return line;
} }
std::vector<std::string> ReadFileArgs(const fs::path&path) std::vector<std::string> ReadFileArgs(const fs::path &path)
{ {
std::string line = ReadFileLine(path); std::string line = ReadFileLine(path);
return split(line,' '); return split(line, ' ');
} }
using namespace pipedal; using namespace pipedal;
@@ -179,7 +179,8 @@ void BootConfig::WriteConfiguration(std::function<void(bool success, std::string
this->thread = nullptr; // wait for any previous operations to complete. this->thread = nullptr; // wait for any previous operations to complete.
if (!changed) if (!changed)
{ {
if (onComplete) onComplete(true, ""); if (onComplete)
onComplete(true, "");
} }
else else
{ {
@@ -193,7 +194,8 @@ void BootConfig::WriteConfiguration(std::function<void(bool success, std::string
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
if (onComplete) onComplete(false, e.what()); if (onComplete)
onComplete(false, e.what());
} }
} }
else if (this->bootLoader == BootLoaderT::Grub) else if (this->bootLoader == BootLoaderT::Grub)
@@ -202,7 +204,8 @@ void BootConfig::WriteConfiguration(std::function<void(bool success, std::string
} }
else else
{ {
if (onComplete) onComplete(false, "Unsupported bootloader."); if (onComplete)
onComplete(false, "Unsupported bootloader.");
} }
} }
} }
@@ -211,15 +214,14 @@ static std::string commandLineOption(BootConfig::DynamicSchedulerT dynamicSchedu
{ {
switch (dynamicScheduler) switch (dynamicScheduler)
{ {
case BootConfig::DynamicSchedulerT::None: case BootConfig::DynamicSchedulerT::None:
return "preempt=none"; return "preempt=none";
case BootConfig::DynamicSchedulerT::Voluntary: case BootConfig::DynamicSchedulerT::Voluntary:
return "preempt=voluntary"; return "preempt=voluntary";
case BootConfig::DynamicSchedulerT::Full: case BootConfig::DynamicSchedulerT::Full:
return "preempt=full"; return "preempt=full";
default: default:
throw std::range_error("Invalid value."); throw std::range_error("Invalid value.");
} }
} }
void BootConfig::WriteUBootConfiguration(std::function<void(bool success, std::string errorMessage)> onComplete) void BootConfig::WriteUBootConfiguration(std::function<void(bool success, std::string errorMessage)> onComplete)
@@ -229,40 +231,24 @@ void BootConfig::WriteUBootConfiguration(std::function<void(bool success, std::s
this->thread = std::make_unique<std::jthread>( this->thread = std::make_unique<std::jthread>(
[this, onComplete]() [this, onComplete]()
{ {
try try
{ {
fs::path cmdlineFile = "/boot/firmware/cmdline.txt"; fs::path cmdlineFile = "/boot/firmware/cmdline.txt";
std::vector<std::string> bootArgs = ReadFileArgs(cmdlineFile);
std::vector<std::string> newBootArgs; fs::path backupFile = "/boot/firmware/cmdline.txt.bak";
for (const auto&value: bootArgs) 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) std::string line = ReadFileLine(cmdlineFile);
{
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 newCmdLine = ss.str(); std::string newLine = SetCmdlineArgs(line);
{ {
std::ofstream f(cmdlineFile); std::ofstream f(cmdlineFile);
@@ -270,7 +256,7 @@ void BootConfig::WriteUBootConfiguration(std::function<void(bool success, std::s
{ {
throw std::runtime_error(SS("Can't write to " << cmdlineFile)); throw std::runtime_error(SS("Can't write to " << cmdlineFile));
} }
f << newCmdLine; f << newLine << '\n'; // trailing \n is REQUIRED
} }
if (onComplete) if (onComplete)
@@ -287,6 +273,107 @@ void BootConfig::WriteUBootConfiguration(std::function<void(bool success, std::s
} }
}); });
} }
static std::string QuoteGrubArgument(const std::string &value) {
std::ostringstream ss;
ss << '\"';
for (char c : value) {
if (c == '\\' || c == '\"')
{
ss << '\\' << c;
} else {
ss << c;
}
}
ss << '\"';
return ss.str();
}
static std::string UnquoteGrubArgument(const std::string &value) {
if (value.length() == 0) return value;
std::istringstream ss(value);
if (ss.peek() == '\"')
{
// c-like; \ is escape character.
if (ss.peek() != '\"')
{
throw std::runtime_error("Invalid string in GRUB configuration file.");
}
ss.get();
std::ostringstream result;
while (ss.peek() != '\"' && ss.peek() != -1)
{
int c = ss.get();
if (c == -1 || c == '\n') {
throw std::runtime_error("Invalid string in GRUB configuration file.");
}
if (c == '\\')
{
int c2 = ss.get();
if (c2 == '\n' || c2 == -1) {
throw std::runtime_error("Invalid string in GRUB configuration file.");
}
result << (char)c2;
} else {
result << (char)c;
}
}
return result.str();
} else if (ss.peek() == '\'')
{
ss.get();
std::ostringstream result;
while (true)
{
int c = ss.get();
if (c == -1 || c == '\n') {
throw std::runtime_error("Invalid string in GRUB configuration file.");
}
if (c == '\'')
{
break;
}
result << (char)c;
}
return result.str();
} else {
throw std::runtime_error("Invalid string in GRUB configuration file.");
}
}
std::string BootConfig::SetCmdlineArgs(const std::string &cmdLine)
{
std::vector<std::string> bootArgs = split(cmdLine,' ');
std::vector<std::string> 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<void(bool success, std::string errorMessage)> onComplete) void BootConfig::WriteGrubConfiguration(std::function<void(bool success, std::string errorMessage)> onComplete)
{ {
this->thread = nullptr; // blocks if already running. this->thread = nullptr; // blocks if already running.
@@ -294,11 +381,60 @@ void BootConfig::WriteGrubConfiguration(std::function<void(bool success, std::st
this->thread = std::make_unique<std::jthread>( this->thread = std::make_unique<std::jthread>(
[this, onComplete]() [this, onComplete]()
{ {
sleep(1); try
if (onComplete)
{ {
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());
}
} }
}); });
} }
+1
View File
@@ -67,6 +67,7 @@ namespace pipedal {
bool operator==(BootConfig&other) const; bool operator==(BootConfig&other) const;
private: private:
std::unique_ptr<std::jthread> thread; std::unique_ptr<std::jthread> thread;
std::string SetCmdlineArgs(const std::string &args);
void WriteUBootConfiguration(std::function<void(bool success,std::string errorMessage)> onComplete); void WriteUBootConfiguration(std::function<void(bool success,std::string errorMessage)> onComplete);
void WriteGrubConfiguration(std::function<void(bool success,std::string errorMessage)> onComplete); void WriteGrubConfiguration(std::function<void(bool success,std::string errorMessage)> onComplete);
+4 -1
View File
@@ -333,6 +333,8 @@ endif()
add_executable(pipedal_kconfig add_executable(pipedal_kconfig
kconfigMain.cpp kconfigMain.cpp
BootConfig.cpp BootConfig.hpp BootConfig.cpp BootConfig.hpp
SystemConfigFile.hpp SystemConfigFile.cpp
) )
@@ -687,6 +689,7 @@ add_executable(pipedalconfig
ConfigMain.cpp ConfigMain.cpp
alsaCheck.cpp alsaCheck.cpp
alsaCheck.hpp alsaCheck.hpp
BootConfig.cpp BootConfig.hpp
ModFileTypes.cpp ModFileTypes.hpp ModFileTypes.cpp ModFileTypes.hpp
PiPedalConfiguration.hpp PiPedalConfiguration.cpp PiPedalConfiguration.hpp PiPedalConfiguration.cpp
JackServerSettings.hpp JackServerSettings.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) EXPORT pipedalTargets)
install (TARGETS pipedald pipedaladmind pipedal_update DESTINATION ${CMAKE_INSTALL_PREFIX}/sbin install (TARGETS pipedald pipedaladmind pipedal_update DESTINATION ${CMAKE_INSTALL_PREFIX}/sbin
+25
View File
@@ -28,6 +28,7 @@
#include "RegDb.hpp" #include "RegDb.hpp"
#include "Locale.hpp" #include "Locale.hpp"
#include "Finally.hpp" #include "Finally.hpp"
#include "BootConfig.hpp"
#include <filesystem> #include <filesystem>
#include <stdlib.h> #include <stdlib.h>
@@ -1026,6 +1027,28 @@ bool SetWebServerPort(std::string portOption)
return true; 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) void Install(const fs::path &programPrefix, const std::string endpointAddress)
{ {
cout << "Configuring pipedal" << endl; cout << "Configuring pipedal" << endl;
@@ -1257,6 +1280,8 @@ void Install(const fs::path &programPrefix, const std::string endpointAddress)
// Restart Wi-Fi Direct if neccessary. // Restart Wi-Fi Direct if neccessary.
OnWifiReinstall(); OnWifiReinstall();
CheckPreemptDynamicConfig();
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
+10
View File
@@ -109,6 +109,16 @@ static inline std::string ValuePart(const std::string &line)
return line.substr(pos + 1); 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 std::string SystemConfigFile::Get(const std::string &key) const
{ {
int64_t lineIndex = GetLine(key); int64_t lineIndex = GetLine(key);
+2
View File
@@ -50,6 +50,8 @@ public:
const std::string& Get(int position) const { return lines[position]; } 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);
void Set(const std::string&key,const std::string &value, const std::string&comment); 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);
void SetDefault(const std::string&key,const std::string &value, const std::string &comment); void SetDefault(const std::string&key,const std::string &value, const std::string &comment);