diff options
author | Steffen Winter <steffen.winter@proton.me> | 2023-10-23 15:18:28 +0200 |
---|---|---|
committer | Steffen Winter <steffen.winter@proton.me> | 2024-01-03 16:50:15 +0100 |
commit | ff8352fdcdc08d32494b5873bb85164f2054825a (patch) | |
tree | 0c44203561c4245f9f2f157e96121396c325744e /src/btop_config.cpp | |
parent | 32b6622cec38aa6caa02111d76186195dcfbe95f (diff) |
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
Diffstat (limited to 'src/btop_config.cpp')
-rw-r--r-- | src/btop_config.cpp | 67 |
1 files changed, 66 insertions, 1 deletions
diff --git a/src/btop_config.cpp b/src/btop_config.cpp index 6ddfd43..535d8c8 100644 --- a/src/btop_config.cpp +++ b/src/btop_config.cpp @@ -24,6 +24,7 @@ tab-size = 4 #include <utility> #include <fmt/core.h> +#include <sys/statvfs.h> #include "btop_config.hpp" #include "btop_shared.hpp" @@ -320,6 +321,65 @@ namespace Config { }; 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) { atomic_wait(writelock, true); 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) { + std::error_code error; if (conf_file.empty()) return; - else if (not fs::exists(conf_file)) { + else if (not fs::exists(conf_file, error)) { write_new = true; return; } + if (error) { + return; + } + std::ifstream cread(conf_file); if (cread.good()) { vector<string> valid_names; |