aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Kummerlaender2015-12-23 21:43:51 +0100
committerAdrian Kummerlaender2015-12-23 21:43:51 +0100
commitcce63aca270c51fb3ce9790438e3c23864de0a87 (patch)
tree1dc5fe5357c7620345950189692130bd415bfd63
parentb02091f847b94e94db465469e723fcf2aee81a01 (diff)
downloadchange-cce63aca270c51fb3ce9790438e3c23864de0a87.tar
change-cce63aca270c51fb3ce9790438e3c23864de0a87.tar.gz
change-cce63aca270c51fb3ce9790438e3c23864de0a87.tar.bz2
change-cce63aca270c51fb3ce9790438e3c23864de0a87.tar.xz
change-cce63aca270c51fb3ce9790438e3c23864de0a87.zip
Implement file change tracking using `diff`
The newly introduced `ChangeTracker` class is now keeping track of all tracked file in addition to spawning and managing a corresponding `diff` instance that enables printing pretty _patch-style_ change summaries to the logging target. This commit introduces `boost-process` and `diff` as dependencies of this library.
-rw-r--r--CMakeLists.txt13
-rw-r--r--src/change_log.cc36
-rw-r--r--src/change_tracker.cc94
-rw-r--r--src/change_tracker.h32
-rw-r--r--src/utility.h26
5 files changed, 171 insertions, 30 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a9596fe..2be4520 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,4 +10,15 @@ include_directories(
src/
)
-add_library(ChangeLog SHARED src/change_log.cc)
+add_library(
+ ChangeLog
+ SHARED
+ src/change_log.cc
+ src/change_tracker.cc
+)
+
+target_link_libraries(
+ ChangeLog
+ boost_system
+ boost_filesystem
+)
diff --git a/src/change_log.cc b/src/change_log.cc
index a9e485f..e128383 100644
--- a/src/change_log.cc
+++ b/src/change_log.cc
@@ -4,20 +4,18 @@
#include <algorithm>
#include <functional>
-#include <unordered_set>
#include "io.h"
#include "utility.h"
#include "actual_function.h"
+#include "change_tracker.h"
-static std::unique_ptr<std::unordered_set<std::string>> tracked_files;
-static std::unique_ptr<io::FileDescriptorGuard> fd_guard;
-static std::unique_ptr<utility::Logger> logger;
+static std::unique_ptr<io::FileDescriptorGuard> fd_guard;
+static std::unique_ptr<utility::Logger> logger;
+static std::unique_ptr<utility::ChangeTracker> tracker;
void init() __attribute__ ((constructor));
void init() {
- tracked_files = std::make_unique<std::unordered_set<std::string>>();
-
if ( getenv("CHANGE_LOG_TARGET") != NULL ) {
fd_guard = std::make_unique<io::FileDescriptorGuard>(
getenv("CHANGE_LOG_TARGET")
@@ -26,6 +24,8 @@ void init() {
} else {
logger = std::make_unique<utility::Logger>(STDERR_FILENO);
}
+
+ tracker = std::make_unique<utility::ChangeTracker>(logger.get());
}
void exit(int status) {
@@ -39,22 +39,12 @@ void exit(int status) {
actual_exit(status);
}
-bool is_tracked_file(const std::string& file_name) {
- return tracked_files->find(file_name) != tracked_files->end();
-}
-
-bool track_file(const std::string& file_name) {
- return tracked_files->emplace(file_name).second;
-}
-
ssize_t write(int fd, const void* buffer, size_t count) {
if ( io::is_regular_file(fd) ) {
const std::string file_name{ io::get_file_name(fd) };
- if ( !is_tracked_file(file_name) ) {
- track_file(file_name);
-
- logger->append("wrote to '" + file_name + "'");
+ if ( !tracker->is_tracked(file_name) ) {
+ tracker->track(file_name);
}
}
@@ -62,8 +52,8 @@ ssize_t write(int fd, const void* buffer, size_t count) {
}
int rename(const char* old_path, const char* new_path) {
- if ( !is_tracked_file(old_path) ) {
- track_file(old_path);
+ if ( !tracker->is_tracked(old_path) ) {
+ tracker->track(old_path);
}
logger->append("renamed '" + std::string(old_path) + "' to '" + std::string(new_path) + "'");
@@ -99,10 +89,8 @@ void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset)
if ( ( prot & PROT_WRITE ) && io::is_regular_file(fd) ) {
const std::string file_name{ io::get_file_name(fd) };
- if ( !is_tracked_file(file_name) ) {
- track_file(file_name);
-
- logger->append("mmap '" + file_name + "'");
+ if ( !tracker->is_tracked(file_name) ) {
+ tracker->track(file_name);
}
}
diff --git a/src/change_tracker.cc b/src/change_tracker.cc
new file mode 100644
index 0000000..1373a8a
--- /dev/null
+++ b/src/change_tracker.cc
@@ -0,0 +1,94 @@
+#include "change_tracker.h"
+
+#include <sstream>
+
+#include <boost/optional.hpp>
+#include <boost/filesystem/fstream.hpp>
+
+namespace {
+
+// constants for increasing pair access readability
+constexpr unsigned int EMPLACE_SUCCESS = 1;
+constexpr unsigned int FILE_NAME = 0;
+constexpr unsigned int DIFF_PROCESS = 1;
+
+boost::process::context createContext() {
+ boost::process::context context;
+
+ context.environment = boost::process::self::get_environment();
+ context.stdout_behavior = boost::process::capture_stream();
+ context.stdin_behavior = boost::process::capture_stream();
+
+ return context;
+}
+
+}
+
+namespace utility {
+
+ChangeTracker::ChangeTracker(utility::Logger* logger):
+ logger_(logger),
+ children_() { }
+
+ChangeTracker::~ChangeTracker() {
+ for ( auto&& tracked : this->children_ ) {
+ std::get<DIFF_PROCESS>(tracked)->get_stdin().close();
+
+ this->logger_->forward(std::get<DIFF_PROCESS>(tracked)->get_stdout());
+
+ tracked.second->wait();
+ }
+}
+
+bool ChangeTracker::is_tracked(const std::string& file_path) const {
+ return this->children_.find(file_path) != this->children_.end();
+}
+
+// Begins tracking changes to a file reachable by a given path
+//
+// The actual tracking is performed by a `diff` instance that is
+// spawned by this method and managed by this class.
+// As `diff` is called as a new subprocess and because there is no
+// straight forward way for checking / enforcing if it has already
+// completed reading the initial contents of the file the command
+// is structured in the following sequence:
+//
+// diff -p - $file_path
+//
+// This means that reading the final file contents is delegated to
+// `diff` while the initial file contents are read by this method
+// and written to `diff`'s standard input.
+//
+// If the `-` and `$file_path` arguments were exchanged there would
+// be no way to:
+// - be sure the initial contents are read before the changing
+// syscall that triggered this tracking in the first place is
+// performed
+// - be sure that the initial file contents are read completly
+// as `diff` seemingly only reads the first block of the first
+// file provided if the second file argument is standard input
+//
+bool ChangeTracker::track(const std::string& file_path) {
+ auto result = this->children_.emplace(
+ file_path,
+ std::make_unique<boost::process::child>(
+ boost::process::launch_shell("diff -p - " + file_name, createContext())
+ )
+ );
+
+ if ( std::get<EMPLACE_SUCCESS>(result) ) {
+ boost::filesystem::ifstream file(
+ std::get<FILE_NAME>(*result.first)
+ );
+
+ if ( file.is_open() ) {
+ std::get<DIFF_PROCESS>(*result.first)->get_stdin() << file.rdbuf();
+ }
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+}
diff --git a/src/change_tracker.h b/src/change_tracker.h
new file mode 100644
index 0000000..e8ffa32
--- /dev/null
+++ b/src/change_tracker.h
@@ -0,0 +1,32 @@
+#ifndef CHANGE_SRC_CHANGE_TRACKER_H_
+#define CHANGE_SRC_CHANGE_TRACKER_H_
+
+#include "utility.h"
+
+#include <unordered_map>
+
+#include <boost/process.hpp>
+
+namespace utility {
+
+class ChangeTracker {
+ public:
+ ChangeTracker(utility::Logger*);
+ ~ChangeTracker();
+
+ bool is_tracked(const std::string&) const;
+
+ bool track(const std::string&);
+
+ private:
+ utility::Logger* const logger_;
+
+ std::unordered_map<
+ std::string, std::unique_ptr<boost::process::child>
+ > children_;
+
+};
+
+}
+
+#endif // CHANGE_SRC_CHANGE_TRACKER_H_
diff --git a/src/utility.h b/src/utility.h
index 96ee15c..1dc44ed 100644
--- a/src/utility.h
+++ b/src/utility.h
@@ -3,6 +3,8 @@
#include <ext/stdio_filebuf.h>
+#include <boost/process.hpp>
+
#include <iostream>
namespace utility {
@@ -10,16 +12,30 @@ namespace utility {
class Logger {
public:
Logger(const int target_fd):
- buffer(target_fd, std::ios::out),
- stream(&this->buffer) { }
+ buffer_(target_fd, std::ios::out),
+ stream_(&this->buffer) { }
void append(const std::string& msg) {
- this->stream << msg << std::endl;
+ this->stream_ << msg << std::endl;
+ }
+
+ // Forward the contents of a given standard output stream to the log target
+ //
+ // While `this->stream_ << stream.rdbuf()` would be more effective it sadly
+ // does not work with `boost::process::pistream` due to a broken pipe error
+ // in conjunction with the required `boost::process::capture_stream` context
+ // flag.
+ //
+ void forward(boost::process::pistream& stream) {
+ this->stream << std::string(
+ (std::istreambuf_iterator<char>(stream)),
+ (std::istreambuf_iterator<char>())
+ );
}
private:
- __gnu_cxx::stdio_filebuf<char> buffer;
- std::ostream stream;
+ __gnu_cxx::stdio_filebuf<char> buffer_;
+ std::ostream stream_;
};