Skip to content

Commit

Permalink
Introduce AtomicFile
Browse files Browse the repository at this point in the history
  • Loading branch information
Al2Klimov committed Jul 25, 2022
1 parent a8185c4 commit 86d7f15
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/base/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ set(base_SOURCES
application.cpp application.hpp application-ti.hpp application-version.cpp application-environment.cpp
array.cpp array.hpp array-script.cpp
atomic.hpp
atomic-file.cpp atomic-file.hpp
base64.cpp base64.hpp
boolean.cpp boolean.hpp boolean-script.cpp
bulker.hpp
Expand Down
64 changes: 64 additions & 0 deletions lib/base/atomic-file.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* Icinga 2 | (c) 2022 Icinga GmbH | GPLv2+ */

#include "base/atomic-file.hpp"
#include "base/exception.hpp"
#include "base/utility.hpp"
#include <utility>

#ifdef _WIN32
# include <windows.h>
#else /* _WIN32 */
# include <errno.h>
# include <unistd.h>
#endif /* _WIN32 */

using namespace icinga;

AtomicFile::AtomicFile(String path, int mode) : m_Path(std::move(path))
{
exceptions(failbit | badbit);
m_TempFilename = Utility::CreateTempFile(m_Path + ".tmp.XXXXXX", mode, *this);
}

AtomicFile::~AtomicFile()
{
if (!m_TempFilename.IsEmpty()) {
try {
Utility::Remove(m_TempFilename);
} catch (...) {
// A destructor must not throw
}
}
}

void AtomicFile::Commit()
{
flush();

auto h ((*this)->handle());

#ifdef _WIN32
if (!FlushFileBuffers(h)) {
auto err (GetLastError());

BOOST_THROW_EXCEPTION(win32_error()
<< boost::errinfo_api_function("FlushFileBuffers")
<< errinfo_win32_error(err)
<< boost::errinfo_file_name(m_TempFilename));
}
#else /* _WIN32 */
if (fsync(h)) {
auto err (errno);

BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("fsync")
<< boost::errinfo_errno(err)
<< boost::errinfo_file_name(m_TempFilename));
}
#endif /* _WIN32 */

close();

Utility::RenameFile(m_TempFilename, m_Path);
m_TempFilename = "";
}
33 changes: 33 additions & 0 deletions lib/base/atomic-file.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* Icinga 2 | (c) 2022 Icinga GmbH | GPLv2+ */

#ifndef ATOMIC_FILE_H
#define ATOMIC_FILE_H

#include "base/string.hpp"
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>

namespace icinga
{

/**
* Atomically replaces a file's content.
*
* @ingroup base
*/
class AtomicFile : public boost::iostreams::stream<boost::iostreams::file_descriptor>
{
public:
AtomicFile(String path, int mode);
~AtomicFile();

void Commit();

private:
String m_Path;
String m_TempFilename;
};

}

#endif /* ATOMIC_FILE_H */

0 comments on commit 86d7f15

Please sign in to comment.