Skip to content

Commit f6cab3d

Browse files
authored
Merge pull request #652 from imwints/config-read-only
2 parents 27c6f11 + 6637485 commit f6cab3d

File tree

4 files changed

+83
-20
lines changed

4 files changed

+83
-20
lines changed

src/btop.cpp

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -832,29 +832,23 @@ int main(int argc, char **argv) {
832832
//? Call argument parser if launched with arguments
833833
if (argc > 1) argumentParser(argc, argv);
834834

835-
//? Setup paths for config, log and user themes
836-
for (const auto& env : {"XDG_CONFIG_HOME", "HOME"}) {
837-
if (std::getenv(env) != nullptr and access(std::getenv(env), W_OK) != -1) {
838-
Config::conf_dir = fs::path(std::getenv(env)) / (((string)env == "HOME") ? ".config/btop" : "btop");
839-
break;
840-
}
841-
}
842-
if (Config::conf_dir.empty()) {
843-
fmt::println("WARNING: Could not get path user HOME folder.\n"
844-
"Make sure $XDG_CONFIG_HOME or $HOME environment variables is correctly set to fix this.");
845-
}
846-
else {
847-
if (std::error_code ec; not fs::is_directory(Config::conf_dir) and not fs::create_directories(Config::conf_dir, ec)) {
848-
fmt::println("WARNING: Could not create or access btop config directory. Logging and config saving disabled.\n"
849-
"Make sure $XDG_CONFIG_HOME or $HOME environment variables is correctly set to fix this.");
850-
}
851-
else {
835+
{
836+
const auto config_dir = Config::get_config_dir();
837+
if (config_dir.has_value()) {
838+
Config::conf_dir = config_dir.value();
852839
Config::conf_file = Config::conf_dir / "btop.conf";
853840
Logger::logfile = Config::conf_dir / "btop.log";
854841
Theme::user_theme_dir = Config::conf_dir / "themes";
855-
if (not fs::exists(Theme::user_theme_dir) and not fs::create_directory(Theme::user_theme_dir, ec)) Theme::user_theme_dir.clear();
842+
843+
// If necessary create the user theme directory
844+
std::error_code error;
845+
if (not fs::exists(Theme::user_theme_dir, error) and not fs::create_directories(Theme::user_theme_dir, error)) {
846+
Theme::user_theme_dir.clear();
847+
Logger::warning("Failed to create user theme directory: " + error.message());
848+
}
856849
}
857850
}
851+
858852
//? Try to find global btop theme path relative to binary path
859853
#ifdef __linux__
860854
{ std::error_code ec;

src/btop_config.cpp

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ tab-size = 4
2424
#include <utility>
2525

2626
#include <fmt/core.h>
27+
#include <sys/statvfs.h>
2728

2829
#include "btop_config.hpp"
2930
#include "btop_shared.hpp"
@@ -320,6 +321,65 @@ namespace Config {
320321
};
321322
std::unordered_map<std::string_view, int> intsTmp;
322323

324+
// Returns a valid config dir or an empty optional
325+
// The config dir might be read only, a warning is printed, but a path is returned anyway
326+
[[nodiscard]] std::optional<fs::path> get_config_dir() noexcept {
327+
fs::path config_dir;
328+
{
329+
std::error_code error;
330+
if (const auto xdg_config_home = std::getenv("XDG_CONFIG_HOME"); xdg_config_home != nullptr) {
331+
if (fs::exists(xdg_config_home, error)) {
332+
config_dir = fs::path(xdg_config_home) / "btop";
333+
}
334+
} else if (const auto home = std::getenv("HOME"); home != nullptr) {
335+
error.clear();
336+
if (fs::exists(home, error)) {
337+
config_dir = fs::path(home) / ".config" / "btop";
338+
}
339+
if (error) {
340+
fmt::print(stderr, "\033[0;31mWarning: \033[0m{} could not be accessed: {}\n", config_dir.string(), error.message());
341+
config_dir = "";
342+
}
343+
}
344+
}
345+
346+
// FIXME: This warnings can be noisy if the user deliberately has a non-writable config dir
347+
// offer an alternative | disable messages by default | disable messages if config dir is not writable | disable messages with a flag
348+
// FIXME: Make happy path not branch
349+
if (not config_dir.empty()) {
350+
std::error_code error;
351+
if (fs::exists(config_dir, error)) {
352+
if (fs::is_directory(config_dir, error)) {
353+
struct statvfs stats {};
354+
if ((fs::status(config_dir, error).permissions() & fs::perms::owner_write) == fs::perms::owner_write and
355+
statvfs(config_dir.c_str(), &stats) == 0 and (stats.f_flag & ST_RDONLY) == 0) {
356+
return config_dir;
357+
} else {
358+
fmt::print(stderr, "\033[0;31mWarning: \033[0m`{}` is not writable\n", fs::absolute(config_dir).string());
359+
// If the config is readable we can still use the provided config, but changes will not be persistent
360+
if ((fs::status(config_dir, error).permissions() & fs::perms::owner_read) == fs::perms::owner_read) {
361+
fmt::print(stderr, "\033[0;31mWarning: \033[0mLogging is disabled, config changes are not persistent\n");
362+
return config_dir;
363+
}
364+
}
365+
} else {
366+
fmt::print(stderr, "\033[0;31mWarning: \033[0m`{}` is not a directory\n", fs::absolute(config_dir).string());
367+
}
368+
} else {
369+
// Doesn't exist
370+
if (fs::create_directories(config_dir, error)) {
371+
return config_dir;
372+
} else {
373+
fmt::print(stderr, "\033[0;31mWarning: \033[0m`{}` could not be created: {}\n", fs::absolute(config_dir).string(), error.message());
374+
}
375+
}
376+
} else {
377+
fmt::print(stderr, "\033[0;31mWarning: \033[0mCould not determine config path: Make sure `$XDG_CONFIG_HOME` or `$HOME` is set\n");
378+
}
379+
fmt::print(stderr, "\033[0;31mWarning: \033[0mLogging is disabled, config changes are not persistent\n");
380+
return {};
381+
}
382+
323383
bool _locked(const std::string_view name) {
324384
atomic_wait(writelock, true);
325385
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 {
594654
}
595655

596656
void load(const fs::path& conf_file, vector<string>& load_warnings) {
657+
std::error_code error;
597658
if (conf_file.empty())
598659
return;
599-
else if (not fs::exists(conf_file)) {
660+
else if (not fs::exists(conf_file, error)) {
600661
write_new = true;
601662
return;
602663
}
664+
if (error) {
665+
return;
666+
}
667+
603668
std::ifstream cread(conf_file);
604669
if (cread.good()) {
605670
vector<string> valid_names;

src/btop_config.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ tab-size = 4
1818

1919
#pragma once
2020

21+
#include <filesystem>
22+
#include <optional>
2123
#include <string>
2224
#include <vector>
23-
#include <filesystem>
2425

2526
#include <unordered_map>
2627

@@ -57,6 +58,8 @@ namespace Config {
5758
extern vector<string> available_batteries;
5859
extern int current_preset;
5960

61+
[[nodiscard]] std::optional<std::filesystem::path> get_config_dir() noexcept;
62+
6063
//* Check if string only contains space separated valid names for boxes
6164
bool check_boxes(const string& boxes);
6265

src/btop_tools.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,7 @@ namespace Logger {
670670
lose_priv neutered{};
671671
std::error_code ec;
672672
try {
673+
// NOTE: `exist()` could throw but since we return with an empty logfile we don't care
673674
if (fs::exists(logfile) and fs::file_size(logfile, ec) > 1024 << 10 and not ec) {
674675
auto old_log = logfile;
675676
old_log += ".1";

0 commit comments

Comments
 (0)