diff --git a/Makefile.in b/Makefile.in index a0e7aa4..8af54a0 100644 --- a/Makefile.in +++ b/Makefile.in @@ -91,6 +91,7 @@ SOURCE=\ thin-provisioning/restore_emitter.cc \ thin-provisioning/rmap_visitor.cc \ thin-provisioning/superblock.cc \ + thin-provisioning/shared_library_emitter.cc \ thin-provisioning/thin_check.cc \ thin-provisioning/thin_delta.cc \ thin-provisioning/thin_dump.cc \ @@ -123,9 +124,9 @@ STRIP:=@STRIP@ OBJECTS:=$(subst .cc,.o,$(SOURCE)) TOP_DIR:=@top_srcdir@ TOP_BUILDDIR:=@top_builddir@ -CFLAGS+=-g -Wall -O3 +CFLAGS+=-g -Wall -O3 -fPIC CFLAGS+=@LFS_FLAGS@ -CXXFLAGS+=-g -Wall -fno-strict-aliasing -std=c++11 +CXXFLAGS+=-g -Wall -fPIC -fno-strict-aliasing -std=c++11 ifeq ("@DEVTOOLS@", "yes") CXXFLAGS+=-DDEV_TOOLS @@ -260,6 +261,7 @@ endif ifeq ("@TESTING@", "yes") include unit-tests/Makefile +include contrib/Makefile .PHONEY: features diff --git a/configure.ac b/configure.ac index d9600b9..9b0b2ce 100644 --- a/configure.ac +++ b/configure.ac @@ -188,6 +188,7 @@ dnl -- First and last lines should not contain files to generate in order to dnl -- keep utility scripts running properly AC_CONFIG_FILES([ Makefile +contrib/Makefile unit-tests/Makefile version.h ]) diff --git a/contrib/Makefile.in b/contrib/Makefile.in new file mode 100644 index 0000000..a47c8e2 --- /dev/null +++ b/contrib/Makefile.in @@ -0,0 +1,7 @@ +contrib: contrib/thin_sexp_emitter.so + +contrib/thin_sexp_emitter.so: contrib/thin_sexp_emitter.o + $(V)echo " [LD] $@" + $(V)$(CC) -shared -Wl,-soname,thin_sexp_emitter.so -o contrib/thin_sexp_emitter.so contrib/thin_sexp_emitter.o + + diff --git a/contrib/thin_sexp_emitter.cc b/contrib/thin_sexp_emitter.cc new file mode 100644 index 0000000..281f0ec --- /dev/null +++ b/contrib/thin_sexp_emitter.cc @@ -0,0 +1,120 @@ +#include "base/indented_stream.h" +#include "thin-provisioning/emitter.h" + +using namespace std; +using namespace thin_provisioning; + +//---------------------------------------------------------------- + +namespace { + class sexp_emitter : public emitter { + public: + sexp_emitter(ostream &out) + : out_(out) { + } + + virtual void begin_superblock(std::string const &uuid, + uint64_t time, + uint64_t trans_id, + boost::optional flags, + boost::optional version, + uint32_t data_block_size, + uint64_t nr_data_blocks, + boost::optional metadata_snap) { + open("superblock"); + out_.indent(); + out_ << "((uuid \"" << uuid << "\")\n"; + kv("time", time); + kv("trans_id", trans_id); + kv("flags", flags); + kv("version", version); + kv("data_block_size", data_block_size); + kv("nr_data_blocks", nr_data_blocks); + kv("metadata_snap", metadata_snap); + out_.indent(); + out_ << ")\n"; + } + + virtual void end_superblock() { + close(); + } + + virtual void begin_device(uint32_t dev_id, + uint64_t mapped_blocks, + uint64_t trans_id, + uint64_t creation_time, + uint64_t snap_time) { + open("device"); + out_.indent(); + out_ << "((dev_id " << dev_id << ")\n"; + kv("mapped_blocks", mapped_blocks); + kv("trans_id", trans_id); + kv("creation_time", creation_time); + kv("snap_time", snap_time); + out_.indent(); + out_ << ")\n"; + } + + virtual void end_device() { + close(); + } + + virtual void begin_named_mapping(std::string const &name) { + + } + + virtual void end_named_mapping() { + + } + + virtual void identifier(std::string const &name) { + + } + + virtual void range_map(uint64_t origin_begin, uint64_t data_begin, uint32_t time, uint64_t len) { + out_.indent(); + out_ << "(range (origin_begin " << origin_begin + << ") (data_begin " << data_begin + << ") (time " << time + << ") (len " << len << "))\n"; + } + + virtual void single_map(uint64_t origin_block, uint64_t data_block, uint32_t time) { + out_.indent(); + out_ << "(single (origin_block " << origin_block + << ") (data_block " << data_block + << ") (time " << time << "))\n"; + } + + private: + void open(char const *tag) { + out_.indent(); + out_ << "(" << tag << "\n"; + out_.inc(); + } + + void close() { + out_.dec(); + out_.indent(); + out_ << ")\n"; + } + + template + void kv(char const *k, T const &v) { + out_.indent(); + out_ << " (" << k << " " << v << ")\n"; + } + + indented_stream out_; + }; +} + +//---------------------------------------------------------------- + +extern "C" { + emitter::ptr create_emitter(ostream &out) { + return emitter::ptr(new sexp_emitter(out)); + } +} + +//---------------------------------------------------------------- diff --git a/man8/thin_dump.8 b/man8/thin_dump.8 index 28b6d8b..eb81c29 100644 --- a/man8/thin_dump.8 +++ b/man8/thin_dump.8 @@ -26,8 +26,13 @@ in order to put it back onto a metadata This tool cannot be run on live metadata unless the \fB\-\-metadata\-snap\fP option is used. -.IP "\fB\-f, \-\-format\fP \fI{xml|human_readable}\fP". -Print output in XML or human readable format. +.IP "\fB\-f, \-\-format\fP \fI{xml|human_readable|custom}\fP". + +Print output in XML or human readable format. Custom formats are +supported via shared library plugins. They should be specified as in +this example: +.sp +.B thin_dump --format custom=mylib.so /dev/sda .IP "\fB\-r, \-\-repair\fP". Repair the metadata whilst dumping it. diff --git a/thin-provisioning/shared_library_emitter.cc b/thin-provisioning/shared_library_emitter.cc new file mode 100644 index 0000000..58f12d2 --- /dev/null +++ b/thin-provisioning/shared_library_emitter.cc @@ -0,0 +1,29 @@ +#include "thin-provisioning/shared_library_emitter.h" + +#include +#include + +using namespace std; +using namespace thin_provisioning; + +//---------------------------------------------------------------- + +emitter::ptr +thin_provisioning::create_custom_emitter(string const &shared_lib, ostream &out) +{ + emitter::ptr (*create_fn)(ostream &out); + void *handle = dlopen(shared_lib.c_str(), RTLD_LAZY); + if (!handle) + throw runtime_error(dlerror()); + + dlerror(); // Clear any existing error + create_fn = reinterpret_cast(dlsym(handle, "create_emitter")); + + char *error = dlerror(); + if (error) + throw runtime_error(error); + + return create_fn(out); +} + +//---------------------------------------------------------------- diff --git a/thin-provisioning/shared_library_emitter.h b/thin-provisioning/shared_library_emitter.h new file mode 100644 index 0000000..e0f8b95 --- /dev/null +++ b/thin-provisioning/shared_library_emitter.h @@ -0,0 +1,14 @@ +#ifndef THIN_PROVISIONING_SHARED_LIBRARY_EMITTER_H +#define THIN_PROVISIONING_SHARED_LIBRARY_EMITTER_H + +#include "thin-provisioning/emitter.h" + +//---------------------------------------------------------------- + +namespace thin_provisioning { + emitter::ptr create_custom_emitter(std::string const &shared_lib, std::ostream &out); +} + +//---------------------------------------------------------------- + +#endif diff --git a/thin-provisioning/thin_dump.cc b/thin-provisioning/thin_dump.cc index 87d78c0..c6a964a 100644 --- a/thin-provisioning/thin_dump.cc +++ b/thin-provisioning/thin_dump.cc @@ -21,13 +21,14 @@ #include #include -#include "human_readable_format.h" -#include "metadata_dumper.h" -#include "metadata.h" -#include "xml_format.h" -#include "version.h" -#include "thin-provisioning/commands.h" #include "persistent-data/file_utils.h" +#include "thin-provisioning/commands.h" +#include "thin-provisioning/human_readable_format.h" +#include "thin-provisioning/metadata.h" +#include "thin-provisioning/metadata_dumper.h" +#include "thin-provisioning/shared_library_emitter.h" +#include "thin-provisioning/xml_format.h" +#include "version.h" using namespace boost; using namespace persistent_data; @@ -56,6 +57,10 @@ namespace { return md; } + bool begins_with(string const &str, string const &prefix) { + return str.substr(0, prefix.length()) == prefix; + } + emitter::ptr create_emitter(string const &format, ostream &out) { emitter::ptr e; @@ -65,9 +70,13 @@ namespace { else if (format == "human_readable") e = create_human_readable_emitter(out); + else if (begins_with(format, "custom=")) + e = create_custom_emitter(format.substr(7), out); + else { - cerr << "unknown format '" << format << "'" << endl; - exit(1); + ostringstream msg; + msg << "unknown format '" << format << "'"; + throw runtime_error(msg.str()); } return e;