diff options
author | Thomas Habets <thomas@habets.se> | 2021-04-06 23:04:11 +0100 |
---|---|---|
committer | mormj <34754695+mormj@users.noreply.github.com> | 2021-05-02 19:00:31 -0400 |
commit | 79d294331ca941cd16c42ddc07278c5ce749b938 (patch) | |
tree | 6175523684e78c51a6becc1bcba37925f708c111 | |
parent | cefe09329bbbec6256e5a1d0efa669f1c5111404 (diff) |
runtime: Save prefs safely by atomic rename
Signed-off-by: Thomas Habets <thomas@habets.se>
-rw-r--r-- | gnuradio-runtime/lib/prefs.cc | 31 |
1 files changed, 24 insertions, 7 deletions
diff --git a/gnuradio-runtime/lib/prefs.cc b/gnuradio-runtime/lib/prefs.cc index c1d9f9d5cd..af6c35f0b9 100644 --- a/gnuradio-runtime/lib/prefs.cc +++ b/gnuradio-runtime/lib/prefs.cc @@ -138,14 +138,31 @@ std::string prefs::to_string() void prefs::save() { - // TODO: write the settings in an error-safe way, by writing to a tempfile and - // atomically renaming. - std::string conf = to_string(); - fs::path userconf = fs::path(gr::userconf_path()) / "config.conf"; - std::ofstream fout(userconf); + const std::string conf = to_string(); + + // Write temp file. + const fs::path tmp = fs::path(gr::userconf_path()) / "config.conf.tmp"; + std::ofstream fout(tmp); fout << conf; fout.close(); - // TODO: throw on error? + if (!fout.good()) { + const std::string write_err = strerror(errno); + { + std::error_code err; + fs::remove(tmp, err); + if (err) { + std::cerr << "Failed to remove temp file: " << err << std::endl; + } + } + throw std::runtime_error("failed to write updated config: " + write_err); + } + + // Atomic rename. + const fs::path userconf = fs::path(gr::userconf_path()) / "config.conf"; + fs::rename(tmp, userconf); + // If fs::rename() fails, we'll leak the tempfile and throw. That's fine. + // If the user wants it (it was written successfully) they can have it. + // Or it'll be overwritten on the next save. } char* prefs::option_to_env(std::string section, std::string option) @@ -161,7 +178,7 @@ bool prefs::has_section(const std::string& section) std::string s = section; stolower(s); std::lock_guard<std::mutex> lk(d_mutex); - return d_config_map.find(s) != d_config_map.end(); + return d_config_map.count(s) > 0; } bool prefs::has_option(const std::string& section, const std::string& option) |