mirror of
https://github.com/aristocratos/btop.git
synced 2024-06-02 10:35:14 +12:00
Improved error handling when determining the config directory
More verbose error printing when a directory is e.g. not readable Also allow the use of read only configurations with disabled logging and persistent configuration
This commit is contained in:
parent
32b6622cec
commit
ff8352fdcd
30
src/btop.cpp
30
src/btop.cpp
|
@ -829,29 +829,23 @@ int main(int argc, char **argv) {
|
||||||
//? Call argument parser if launched with arguments
|
//? Call argument parser if launched with arguments
|
||||||
if (argc > 1) argumentParser(argc, argv);
|
if (argc > 1) argumentParser(argc, argv);
|
||||||
|
|
||||||
//? Setup paths for config, log and user themes
|
{
|
||||||
for (const auto& env : {"XDG_CONFIG_HOME", "HOME"}) {
|
const auto config_dir = Config::get_config_dir();
|
||||||
if (std::getenv(env) != nullptr and access(std::getenv(env), W_OK) != -1) {
|
if (config_dir.has_value()) {
|
||||||
Config::conf_dir = fs::path(std::getenv(env)) / (((string)env == "HOME") ? ".config/btop" : "btop");
|
Config::conf_dir = config_dir.value();
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Config::conf_dir.empty()) {
|
|
||||||
fmt::println("WARNING: Could not get path user HOME folder.\n"
|
|
||||||
"Make sure $XDG_CONFIG_HOME or $HOME environment variables is correctly set to fix this.");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (std::error_code ec; not fs::is_directory(Config::conf_dir) and not fs::create_directories(Config::conf_dir, ec)) {
|
|
||||||
fmt::println("WARNING: Could not create or access btop config directory. Logging and config saving disabled.\n"
|
|
||||||
"Make sure $XDG_CONFIG_HOME or $HOME environment variables is correctly set to fix this.");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Config::conf_file = Config::conf_dir / "btop.conf";
|
Config::conf_file = Config::conf_dir / "btop.conf";
|
||||||
Logger::logfile = Config::conf_dir / "btop.log";
|
Logger::logfile = Config::conf_dir / "btop.log";
|
||||||
Theme::user_theme_dir = Config::conf_dir / "themes";
|
Theme::user_theme_dir = Config::conf_dir / "themes";
|
||||||
if (not fs::exists(Theme::user_theme_dir) and not fs::create_directory(Theme::user_theme_dir, ec)) Theme::user_theme_dir.clear();
|
|
||||||
|
// If necessary create the user theme directory
|
||||||
|
std::error_code error;
|
||||||
|
if (not fs::exists(Theme::user_theme_dir, error) and not fs::create_directories(Theme::user_theme_dir, error)) {
|
||||||
|
Theme::user_theme_dir.clear();
|
||||||
|
Logger::warning("Failed to create user theme directory: " + error.message());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//? Try to find global btop theme path relative to binary path
|
//? Try to find global btop theme path relative to binary path
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
{ std::error_code ec;
|
{ std::error_code ec;
|
||||||
|
|
|
@ -24,6 +24,7 @@ tab-size = 4
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
|
#include <sys/statvfs.h>
|
||||||
|
|
||||||
#include "btop_config.hpp"
|
#include "btop_config.hpp"
|
||||||
#include "btop_shared.hpp"
|
#include "btop_shared.hpp"
|
||||||
|
@ -320,6 +321,65 @@ namespace Config {
|
||||||
};
|
};
|
||||||
std::unordered_map<std::string_view, int> intsTmp;
|
std::unordered_map<std::string_view, int> intsTmp;
|
||||||
|
|
||||||
|
// Returns a valid config dir or an empty optional
|
||||||
|
// The config dir might be read only, a warning is printed, but a path is returned anyway
|
||||||
|
[[nodiscard]] std::optional<fs::path> get_config_dir() noexcept {
|
||||||
|
fs::path config_dir;
|
||||||
|
{
|
||||||
|
std::error_code error;
|
||||||
|
if (const auto xdg_config_home = std::getenv("XDG_CONFIG_HOME"); xdg_config_home != nullptr) {
|
||||||
|
if (fs::exists(xdg_config_home, error)) {
|
||||||
|
config_dir = fs::path(xdg_config_home) / "btop";
|
||||||
|
}
|
||||||
|
} else if (const auto home = std::getenv("HOME"); home != nullptr) {
|
||||||
|
error.clear();
|
||||||
|
if (fs::exists(home, error)) {
|
||||||
|
config_dir = fs::path(home) / ".config" / "btop";
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
fmt::print(stderr, "\033[0;31mWarning: \033[0m{} could not be accessed: {}\n", config_dir.string(), error.message());
|
||||||
|
config_dir = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: This warnings can be noisy if the user deliberately has a non-writable config dir
|
||||||
|
// offer an alternative | disable messages by default | disable messages if config dir is not writable | disable messages with a flag
|
||||||
|
// FIXME: Make happy path not branch
|
||||||
|
if (not config_dir.empty()) {
|
||||||
|
std::error_code error;
|
||||||
|
if (fs::exists(config_dir, error)) {
|
||||||
|
if (fs::is_directory(config_dir, error)) {
|
||||||
|
struct statvfs stats {};
|
||||||
|
if ((fs::status(config_dir, error).permissions() & fs::perms::owner_write) == fs::perms::owner_write and
|
||||||
|
statvfs(config_dir.c_str(), &stats) == 0 and (stats.f_flag & ST_RDONLY) == 0) {
|
||||||
|
return config_dir;
|
||||||
|
} else {
|
||||||
|
fmt::print(stderr, "\033[0;31mWarning: \033[0m`{}` is not writable\n", fs::absolute(config_dir).string());
|
||||||
|
// If the config is readable we can still use the provided config, but changes will not be persistent
|
||||||
|
if ((fs::status(config_dir, error).permissions() & fs::perms::owner_read) == fs::perms::owner_read) {
|
||||||
|
fmt::print(stderr, "\033[0;31mWarning: \033[0mLogging is disabled, config changes are not persistent\n");
|
||||||
|
return config_dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt::print(stderr, "\033[0;31mWarning: \033[0m`{}` is not a directory\n", fs::absolute(config_dir).string());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Doesn't exist
|
||||||
|
if (fs::create_directories(config_dir, error)) {
|
||||||
|
return config_dir;
|
||||||
|
} else {
|
||||||
|
fmt::print(stderr, "\033[0;31mWarning: \033[0m`{}` could not be created: {}\n", fs::absolute(config_dir).string(), error.message());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt::print(stderr, "\033[0;31mWarning: \033[0mCould not determine config path: Make sure `$XDG_CONFIG_HOME` or `$HOME` is set\n");
|
||||||
|
}
|
||||||
|
fmt::print(stderr, "\033[0;31mWarning: \033[0mLogging is disabled, config changes are not persistent\n");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
bool _locked(const std::string_view name) {
|
bool _locked(const std::string_view name) {
|
||||||
atomic_wait(writelock, true);
|
atomic_wait(writelock, true);
|
||||||
if (not write_new and rng::find_if(descriptions, [&name](const auto& a) { return a.at(0) == name; }) != descriptions.end())
|
if (not write_new and rng::find_if(descriptions, [&name](const auto& a) { return a.at(0) == name; }) != descriptions.end())
|
||||||
|
@ -594,12 +654,17 @@ namespace Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
void load(const fs::path& conf_file, vector<string>& load_warnings) {
|
void load(const fs::path& conf_file, vector<string>& load_warnings) {
|
||||||
|
std::error_code error;
|
||||||
if (conf_file.empty())
|
if (conf_file.empty())
|
||||||
return;
|
return;
|
||||||
else if (not fs::exists(conf_file)) {
|
else if (not fs::exists(conf_file, error)) {
|
||||||
write_new = true;
|
write_new = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::ifstream cread(conf_file);
|
std::ifstream cread(conf_file);
|
||||||
if (cread.good()) {
|
if (cread.good()) {
|
||||||
vector<string> valid_names;
|
vector<string> valid_names;
|
||||||
|
|
|
@ -18,9 +18,10 @@ tab-size = 4
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <filesystem>
|
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
@ -57,6 +58,8 @@ namespace Config {
|
||||||
extern vector<string> available_batteries;
|
extern vector<string> available_batteries;
|
||||||
extern int current_preset;
|
extern int current_preset;
|
||||||
|
|
||||||
|
[[nodiscard]] std::optional<std::filesystem::path> get_config_dir() noexcept;
|
||||||
|
|
||||||
//* Check if string only contains space separated valid names for boxes
|
//* Check if string only contains space separated valid names for boxes
|
||||||
bool check_boxes(const string& boxes);
|
bool check_boxes(const string& boxes);
|
||||||
|
|
||||||
|
|
|
@ -669,6 +669,7 @@ namespace Logger {
|
||||||
lose_priv neutered{};
|
lose_priv neutered{};
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
try {
|
try {
|
||||||
|
// NOTE: `exist()` could throw but since we return with an empty logfile we don't care
|
||||||
if (fs::exists(logfile) and fs::file_size(logfile, ec) > 1024 << 10 and not ec) {
|
if (fs::exists(logfile) and fs::file_size(logfile, ec) > 1024 << 10 and not ec) {
|
||||||
auto old_log = logfile;
|
auto old_log = logfile;
|
||||||
old_log += ".1";
|
old_log += ".1";
|
||||||
|
|
Loading…
Reference in a new issue