[lttng-dev] [MI RFC 1/3] Mi: mi backend + mi for command version

Jonathan Rajotte jonathan.r.julien at gmail.com
Sun Apr 6 15:30:32 EDT 2014


Hi,

These patches add machine interface functionality to command version and list.
At the same time they procure a base architecture of the machine
interface.

The first patch cover the version command and add the base for the
machine interface.(mostly the same as last week mi patch)
p.s: David if you want to pull this one [1].

The second patch provide full machine interface functionality to the list
command. You will find attached a little script that demonstrate the mi
functionality of list. The script need a root sessiond and need to be
at the base of lttng-tool directory. Outputs will be generated in
./mi_test_output/ .

The third patch provide an xsd to validate the mi output of list and
version command.

Voilà!

Jonathan

[1] https://github.com/PSRCode/lttng-tools-dev/commits/list_release
    commit: add4aa1a79247749cb823ec68ab1067beda937d0
--Patch--

The machine interface option is now available for version.
Other commands will shortly follow.
Mi backend is expected to grow with addition of mi support
to other commands.

How to use:
	lttng --mi <mi_output_type> <command>

This syntax will be the same for all commands.
Currently *only xml is supported as machine interface output.*

As for errors handling, if an error/warning occurs while using mi it will be
reported on stderr as normal. The integrity of the mi output cannot be
guaranteed if an error/warning occurs.
The error output format is consistent and easy for a machine client to parse.
Thus no mi output format is required for error output.

Example:
	lttng --mi xml version
Output:
<?xml version="1.0" encoding="UTF-8"?>
  <command>
    <name>version</name>
    <output>
      <string>2.4.0-rc2</string>
      <major>2</major>
      <minor>4</minor>
      <patchLevel>0</patchLevel>
      <name>Époque Opaque</name>
      <description>The Époque Opaque is a black IPA from Trou du Diable
      brewery. The nose is eerily reminiscent of the coffee crisp candy from
      of our childhood. These strong mocha accents are present on the palate,
      which are quickly subdued by Japanese and Australian hops leading to a
      final state of satisfaction enjoyable for any fan of bitter
      beer.</description>
      <url>http://lttng.org</url>
      <license>lttng is free software and under the GPL license and part
      LGPL</license>
  </output>
</command>

Signed-off-by: Olivier Cotte <olivier.cotte at polymtl.ca>
Signed-off-by: Jonathan Rajotte <jonathan.r.julien at gmail.com>
---
 include/lttng/lttng-error.h               |   6 +-
 include/lttng/lttng.h                     |   5 +
 src/bin/lttng-consumerd/lttng-consumerd.c |   2 +
 src/bin/lttng/commands/add_context.c      |   7 +
 src/bin/lttng/commands/calibrate.c        |   7 +
 src/bin/lttng/commands/create.c           |   7 +
 src/bin/lttng/commands/destroy.c          |   7 +
 src/bin/lttng/commands/disable_channels.c |   7 +
 src/bin/lttng/commands/disable_events.c   |   7 +
 src/bin/lttng/commands/enable_channels.c  |   7 +
 src/bin/lttng/commands/enable_events.c    |   7 +
 src/bin/lttng/commands/list.c             |   7 +
 src/bin/lttng/commands/load.c             |   7 +
 src/bin/lttng/commands/save.c             |   7 +
 src/bin/lttng/commands/set_session.c      |   7 +
 src/bin/lttng/commands/snapshot.c         |   7 +
 src/bin/lttng/commands/start.c            |   7 +
 src/bin/lttng/commands/stop.c             |   7 +
 src/bin/lttng/commands/version.c          |  88 ++++++++-
 src/bin/lttng/commands/view.c             |   4 +
 src/bin/lttng/lttng.c                     |  32 +++-
 src/common/Makefile.am                    |   5 +-
 src/common/error.h                        |  11 +-
 src/common/mi-lttng.c                     | 284 ++++++++++++++++++++++++++++++
 src/common/mi-lttng.h                     | 230 ++++++++++++++++++++++++
 src/lib/lttng-ctl/lttng-ctl.c             |   1 +
 tests/unit/ini_config/ini_config.c        |   1 +
 tests/unit/test_kernel_data.c             |   1 +
 tests/unit/test_session.c                 |   1 +
 tests/unit/test_uri.c                     |   1 +
 tests/unit/test_ust_data.c                |   1 +
 tests/unit/test_utils_expand_path.c       |   1 +
 tests/unit/test_utils_parse_size_suffix.c |   1 +
 33 files changed, 769 insertions(+), 11 deletions(-)
 create mode 100644 src/common/mi-lttng.c
 create mode 100644 src/common/mi-lttng.h

diff --git a/include/lttng/lttng-error.h b/include/lttng/lttng-error.h
index 06baa04..1594ba9 100644
--- a/include/lttng/lttng-error.h
+++ b/include/lttng/lttng-error.h
@@ -120,9 +120,9 @@ enum lttng_error_code {
 	LTTNG_ERR_LOAD_INVALID_CONFIG    = 87,  /* Invalid session configuration */
 	LTTNG_ERR_LOAD_IO_FAIL           = 88,  /* IO error while reading a session configuration */
 	LTTNG_ERR_LOAD_SESSION_NOENT     = 89,  /* Session file not found */
-	/* 90 */
-	/* 91 */
-	/* 92 */
+	LTTNG_ERR_MI_OUTPUT_TYPE         = 90, /* Invalid MI output format */
+	LTTNG_ERR_MI_IO_FAIL             = 91, /* IO error while writing machine interface output */
+	LTTNG_ERR_MI_NOT_IMPLEMENTED     = 92, /* Mi feature not implemented */
 	/* 93 */
 	/* 94 */
 	/* 95 */
diff --git a/include/lttng/lttng.h b/include/lttng/lttng.h
index f0be224..9449161 100644
--- a/include/lttng/lttng.h
+++ b/include/lttng/lttng.h
@@ -170,6 +170,11 @@ enum lttng_buffer_type {
 	LTTNG_BUFFER_GLOBAL,	/* Only supported by the Kernel. */
 };
 
+/* Machine interface output type */
+enum lttng_mi_output_type {
+	LTTNG_MI_XML                          = 1 /* XML output */
+};
+
 /*
  * The structures should be initialized to zero before use.
  */
diff --git a/src/bin/lttng-consumerd/lttng-consumerd.c b/src/bin/lttng-consumerd/lttng-consumerd.c
index e226ebc..0ce163e 100644
--- a/src/bin/lttng-consumerd/lttng-consumerd.c
+++ b/src/bin/lttng-consumerd/lttng-consumerd.c
@@ -65,6 +65,8 @@ static int sigintcount = 0;
 /* Argument variables */
 int lttng_opt_quiet;    /* not static in error.h */
 int lttng_opt_verbose;  /* not static in error.h */
+int lttng_opt_mi;       /* not static in error.h */
+
 static int opt_daemon;
 static const char *progname;
 static char command_sock_path[PATH_MAX]; /* Global command socket path */
diff --git a/src/bin/lttng/commands/add_context.c b/src/bin/lttng/commands/add_context.c
index 6483c98..2eb0e90 100644
--- a/src/bin/lttng/commands/add_context.c
+++ b/src/bin/lttng/commands/add_context.c
@@ -446,6 +446,13 @@ int cmd_add_context(int argc, const char **argv)
 		goto end;
 	}
 
+	/* TODO: mi support */
+	if (lttng_opt_mi) {
+		ret = -LTTNG_ERR_MI_NOT_IMPLEMENTED;
+		ERR("mi option not supported");
+		goto end;
+	}
+
 	pc = poptGetContext(NULL, argc, argv, long_options, 0);
 	poptReadDefaultConfig(pc, 0);
 
diff --git a/src/bin/lttng/commands/calibrate.c b/src/bin/lttng/commands/calibrate.c
index 092632c..7d4de29 100644
--- a/src/bin/lttng/commands/calibrate.c
+++ b/src/bin/lttng/commands/calibrate.c
@@ -133,6 +133,13 @@ static int calibrate_lttng(void)
 		goto error;
 	}
 
+	/* TODO: mi support */
+	if (lttng_opt_mi) {
+		ret = -LTTNG_ERR_MI_NOT_IMPLEMENTED;
+		ERR("mi option not supported");
+		goto error;
+	}
+
 	handle = lttng_create_handle(NULL, &dom);
 	if (handle == NULL) {
 		ret = CMD_ERROR;
diff --git a/src/bin/lttng/commands/create.c b/src/bin/lttng/commands/create.c
index e02b141..c43ce59 100644
--- a/src/bin/lttng/commands/create.c
+++ b/src/bin/lttng/commands/create.c
@@ -515,6 +515,13 @@ int cmd_create(int argc, const char **argv)
 		}
 	}
 
+	/* TODO: mi support */
+	if (lttng_opt_mi) {
+		ret = -LTTNG_ERR_MI_NOT_IMPLEMENTED;
+		ERR("mi option not supported");
+		goto end;
+	}
+
 	if (opt_no_consumer) {
 		MSG("The option --no-consumer is obsolete. Use --no-output now.");
 		ret = CMD_WARNING;
diff --git a/src/bin/lttng/commands/destroy.c b/src/bin/lttng/commands/destroy.c
index 3fb5fb2..26548b9 100644
--- a/src/bin/lttng/commands/destroy.c
+++ b/src/bin/lttng/commands/destroy.c
@@ -148,6 +148,13 @@ int cmd_destroy(int argc, const char **argv)
 		goto end;
 	}
 
+	/* TODO: mi support */
+	if (lttng_opt_mi) {
+		ret = -LTTNG_ERR_MI_NOT_IMPLEMENTED;
+		ERR("mi option not supported");
+		goto end;
+	}
+
 	/* Ignore session name in case all sessions are to be destroyed */
 	if (opt_destroy_all) {
 		ret = destroy_all_sessions();
diff --git a/src/bin/lttng/commands/disable_channels.c b/src/bin/lttng/commands/disable_channels.c
index 1aa3916..da53d25 100644
--- a/src/bin/lttng/commands/disable_channels.c
+++ b/src/bin/lttng/commands/disable_channels.c
@@ -167,6 +167,13 @@ int cmd_disable_channels(int argc, const char **argv)
 		}
 	}
 
+	/* TODO: mi support */
+	if (lttng_opt_mi) {
+		ret = -LTTNG_ERR_MI_NOT_IMPLEMENTED;
+		ERR("mi option not supported");
+		goto end;
+	}
+
 	opt_channels = (char*) poptGetArg(pc);
 	if (opt_channels == NULL) {
 		ERR("Missing channel name(s).\n");
diff --git a/src/bin/lttng/commands/disable_events.c b/src/bin/lttng/commands/disable_events.c
index e712343..fb96b80 100644
--- a/src/bin/lttng/commands/disable_events.c
+++ b/src/bin/lttng/commands/disable_events.c
@@ -212,6 +212,13 @@ int cmd_disable_events(int argc, const char **argv)
 		}
 	}
 
+	/* TODO: mi support */
+	if (lttng_opt_mi) {
+		ret = -LTTNG_ERR_MI_NOT_IMPLEMENTED;
+		ERR("mi option not supported");
+		goto end;
+	}
+
 	opt_event_list = (char*) poptGetArg(pc);
 	if (opt_event_list == NULL && opt_disable_all == 0) {
 		ERR("Missing event name(s).\n");
diff --git a/src/bin/lttng/commands/enable_channels.c b/src/bin/lttng/commands/enable_channels.c
index 55fbae6..8ea405e 100644
--- a/src/bin/lttng/commands/enable_channels.c
+++ b/src/bin/lttng/commands/enable_channels.c
@@ -327,6 +327,13 @@ int cmd_enable_channels(int argc, const char **argv)
 	pc = poptGetContext(NULL, argc, argv, long_options, 0);
 	poptReadDefaultConfig(pc, 0);
 
+	/* TODO: mi support */
+	if (lttng_opt_mi) {
+		ret = -LTTNG_ERR_MI_NOT_IMPLEMENTED;
+		ERR("mi option not supported");
+		goto end;
+	}
+
 	while ((opt = poptGetNextOpt(pc)) != -1) {
 		switch (opt) {
 		case OPT_HELP:
diff --git a/src/bin/lttng/commands/enable_events.c b/src/bin/lttng/commands/enable_events.c
index bd2d997..8c7a73e 100644
--- a/src/bin/lttng/commands/enable_events.c
+++ b/src/bin/lttng/commands/enable_events.c
@@ -968,6 +968,13 @@ int cmd_enable_events(int argc, const char **argv)
 	pc = poptGetContext(NULL, argc, argv, long_options, 0);
 	poptReadDefaultConfig(pc, 0);
 
+	/* TODO: mi support */
+	if (lttng_opt_mi) {
+		ret = -LTTNG_ERR_MI_NOT_IMPLEMENTED;
+		ERR("mi option not supported");
+		goto end;
+	}
+
 	/* Default event type */
 	opt_event_type = LTTNG_EVENT_ALL;
 
diff --git a/src/bin/lttng/commands/list.c b/src/bin/lttng/commands/list.c
index f69b3ba..bbd674c 100644
--- a/src/bin/lttng/commands/list.c
+++ b/src/bin/lttng/commands/list.c
@@ -850,6 +850,13 @@ int cmd_list(int argc, const char **argv)
 	pc = poptGetContext(NULL, argc, argv, long_options, 0);
 	poptReadDefaultConfig(pc, 0);
 
+	/* TODO: mi support */
+	if (lttng_opt_mi) {
+		ret = -LTTNG_ERR_MI_NOT_IMPLEMENTED;
+		ERR("mi option not supported");
+		goto end;
+	}
+
 	while ((opt = poptGetNextOpt(pc)) != -1) {
 		switch (opt) {
 		case OPT_HELP:
diff --git a/src/bin/lttng/commands/load.c b/src/bin/lttng/commands/load.c
index e732baa..2cfc698 100644
--- a/src/bin/lttng/commands/load.c
+++ b/src/bin/lttng/commands/load.c
@@ -77,6 +77,13 @@ int cmd_load(int argc, const char **argv)
 	pc = poptGetContext(NULL, argc, argv, load_opts, 0);
 	poptReadDefaultConfig(pc, 0);
 
+	/* TODO: mi support */
+	if (lttng_opt_mi) {
+		ret = -LTTNG_ERR_MI_NOT_IMPLEMENTED;
+		ERR("mi option not supported");
+		goto end;
+	}
+
 	while ((opt = poptGetNextOpt(pc)) != -1) {
 		switch (opt) {
 		case OPT_HELP:
diff --git a/src/bin/lttng/commands/save.c b/src/bin/lttng/commands/save.c
index e8a4565..1aeb9f2 100644
--- a/src/bin/lttng/commands/save.c
+++ b/src/bin/lttng/commands/save.c
@@ -73,6 +73,13 @@ int cmd_save(int argc, const char **argv)
 	pc = poptGetContext(NULL, argc, argv, save_opts, 0);
 	poptReadDefaultConfig(pc, 0);
 
+	/* TODO: mi support */
+	if (lttng_opt_mi) {
+		ret = -LTTNG_ERR_MI_NOT_IMPLEMENTED;
+		ERR("mi option not supported");
+		goto end;
+	}
+
 	while ((opt = poptGetNextOpt(pc)) != -1) {
 		switch (opt) {
 		case OPT_HELP:
diff --git a/src/bin/lttng/commands/set_session.c b/src/bin/lttng/commands/set_session.c
index 4238016..54320df 100644
--- a/src/bin/lttng/commands/set_session.c
+++ b/src/bin/lttng/commands/set_session.c
@@ -92,6 +92,13 @@ int cmd_set_session(int argc, const char **argv)
 	pc = poptGetContext(NULL, argc, argv, long_options, 0);
 	poptReadDefaultConfig(pc, 0);
 
+	/* TODO: mi support */
+	if (lttng_opt_mi) {
+		ret = -LTTNG_ERR_MI_NOT_IMPLEMENTED;
+		ERR("mi option not supported");
+		goto end;
+	}
+
 	while ((opt = poptGetNextOpt(pc)) != -1) {
 		switch (opt) {
 		case OPT_HELP:
diff --git a/src/bin/lttng/commands/snapshot.c b/src/bin/lttng/commands/snapshot.c
index c704eee..21b5a05 100644
--- a/src/bin/lttng/commands/snapshot.c
+++ b/src/bin/lttng/commands/snapshot.c
@@ -446,6 +446,13 @@ int cmd_snapshot(int argc, const char **argv)
 	pc = poptGetContext(NULL, argc, argv, snapshot_opts, 0);
 	poptReadDefaultConfig(pc, 0);
 
+	/* TODO: mi support */
+	if (lttng_opt_mi) {
+		ret = -LTTNG_ERR_MI_NOT_IMPLEMENTED;
+		ERR("mi option not supported");
+		goto end;
+	}
+
 	while ((opt = poptGetNextOpt(pc)) != -1) {
 		switch (opt) {
 		case OPT_HELP:
diff --git a/src/bin/lttng/commands/start.c b/src/bin/lttng/commands/start.c
index 82122cc..82b3f5f 100644
--- a/src/bin/lttng/commands/start.c
+++ b/src/bin/lttng/commands/start.c
@@ -118,6 +118,13 @@ int cmd_start(int argc, const char **argv)
 	pc = poptGetContext(NULL, argc, argv, long_options, 0);
 	poptReadDefaultConfig(pc, 0);
 
+	/* TODO: mi support */
+	if (lttng_opt_mi) {
+		ret = -LTTNG_ERR_MI_NOT_IMPLEMENTED;
+		ERR("mi option not supported");
+		goto end;
+	}
+
 	while ((opt = poptGetNextOpt(pc)) != -1) {
 		switch (opt) {
 		case OPT_HELP:
diff --git a/src/bin/lttng/commands/stop.c b/src/bin/lttng/commands/stop.c
index 60a1dac..5de96d8 100644
--- a/src/bin/lttng/commands/stop.c
+++ b/src/bin/lttng/commands/stop.c
@@ -122,6 +122,13 @@ int cmd_stop(int argc, const char **argv)
 	pc = poptGetContext(NULL, argc, argv, long_options, 0);
 	poptReadDefaultConfig(pc, 0);
 
+	/* TODO: mi support */
+	if (lttng_opt_mi) {
+		ret = -LTTNG_ERR_MI_NOT_IMPLEMENTED;
+		ERR("mi option not supported");
+		goto end;
+	}
+
 	while ((opt = poptGetNextOpt(pc)) != -1) {
 		switch (opt) {
 		case OPT_HELP:
diff --git a/src/bin/lttng/commands/version.c b/src/bin/lttng/commands/version.c
index 7f69de3..5c7ebbf 100644
--- a/src/bin/lttng/commands/version.c
+++ b/src/bin/lttng/commands/version.c
@@ -25,6 +25,8 @@
 #include <unistd.h>
 #include <config.h>
 
+#include <common/mi-lttng.h>
+
 #include "../command.h"
 
 enum {
@@ -32,6 +34,8 @@ enum {
 	OPT_LIST_OPTIONS,
 };
 
+static const char *lttng_license = "lttng is free software and under the GPL license and part LGPL";
+
 static struct poptOption long_options[] = {
 	/* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
 	{"help",      'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0},
@@ -53,6 +57,78 @@ static void usage(FILE *ofp)
 }
 
 /*
+ *  create_version
+ */
+static void create_version(struct mi_lttng_version *version)
+{
+	strncpy(version->version, VERSION, NAME_MAX);
+	version->version_major = VERSION_MAJOR;
+	version->version_minor = VERSION_MINOR;
+	version->version_patchlevel = VERSION_PATCHLEVEL;
+	strncpy(version->version_name, VERSION_NAME, NAME_MAX);
+	strncpy(version->package_url, PACKAGE_URL, NAME_MAX);
+}
+
+/*
+ *  print_mi
+ */
+static int print_mi()
+{
+	int ret;
+	struct mi_writer *writer = NULL;
+	struct mi_lttng_version version;
+
+	create_version(&version);
+
+	writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi);
+
+	if (!writer) {
+		ret = -LTTNG_ERR_NOMEM;
+		goto end;
+	}
+
+	/* Open the command element */
+	ret = mi_lttng_writer_command_open(writer,
+			mi_lttng_element_command_version);
+	if (ret) {
+		goto error;
+	}
+
+	/* Beginning of output */
+	ret = mi_lttng_writer_open_element(writer,
+			mi_lttng_element_command_output);
+	if (ret) {
+		goto error;
+	}
+
+	/* Print the machine interface of version */
+	ret = mi_lttng_version(writer, &version,
+			VERSION_DESCRIPTION, lttng_license);
+	if (ret) {
+		goto error;
+	}
+
+	/* Close the output element */
+	ret = mi_lttng_writer_close_element(writer);
+	if (ret) {
+		goto error;
+	}
+
+	/* Close the command  */
+	ret = mi_lttng_writer_command_close(writer);
+
+error:
+	/* Cleanup */
+	if (writer && mi_lttng_writer_destroy(writer)) {
+		/* Preserve original error code */
+		ret = ret ? ret : LTTNG_ERR_MI_IO_FAIL;
+	}
+
+end:
+	return ret;
+}
+
+/*
  *  cmd_version
  */
 int cmd_version(int argc, const char **argv)
@@ -78,10 +154,14 @@ int cmd_version(int argc, const char **argv)
 		}
 	}
 
-	MSG("lttng version " VERSION " - " VERSION_NAME);
-	MSG("\n" VERSION_DESCRIPTION "\n");
-	MSG("Web site: http://lttng.org");
-	MSG("\nlttng is free software and under the GPL license and part LGPL");
+	if (lttng_opt_mi) {
+		ret = print_mi();
+	} else {
+		MSG("lttng version " VERSION " - " VERSION_NAME);
+		MSG("\n" VERSION_DESCRIPTION "\n");
+		MSG("Web site: http://lttng.org");
+		MSG("\n%s", lttng_license);
+	}
 
 end:
 	poptFreeContext(pc);
diff --git a/src/bin/lttng/commands/view.c b/src/bin/lttng/commands/view.c
index adedf29..dcd4d66 100644
--- a/src/bin/lttng/commands/view.c
+++ b/src/bin/lttng/commands/view.c
@@ -445,6 +445,10 @@ int cmd_view(int argc, const char **argv)
 	pc = poptGetContext(NULL, argc, argv, long_options, 0);
 	poptReadDefaultConfig(pc, 0);
 
+	if (lttng_opt_mi) {
+		WARN("mi does not apply to view command");
+	}
+
 	while ((opt = poptGetNextOpt(pc)) != -1) {
 		switch (opt) {
 		case OPT_HELP:
diff --git a/src/bin/lttng/lttng.c b/src/bin/lttng/lttng.c
index bc7577d..4510057 100644
--- a/src/bin/lttng/lttng.c
+++ b/src/bin/lttng/lttng.c
@@ -55,6 +55,7 @@ static struct option long_options[] = {
 	{"group",            1, NULL, 'g'},
 	{"verbose",          0, NULL, 'v'},
 	{"quiet",            0, NULL, 'q'},
+	{"mi",               1, NULL, 'm'},
 	{"no-sessiond",      0, NULL, 'n'},
 	{"sessiond-path",    1, NULL, OPT_SESSION_PATH},
 	{"relayd-path",      1, NULL, OPT_RELAYD_PATH},
@@ -99,6 +100,8 @@ static void usage(FILE *ofp)
 	fprintf(ofp, "      --list-commands        Simple listing of lttng commands\n");
 	fprintf(ofp, "  -v, --verbose              Increase verbosity\n");
 	fprintf(ofp, "  -q, --quiet                Quiet mode\n");
+	fprintf(ofp, "  -m, --mi TYPE              Machine Interface mode.\n");
+	fprintf(ofp, "                                 Type: xml\n");
 	fprintf(ofp, "  -g, --group NAME           Unix tracing group name. (default: tracing)\n");
 	fprintf(ofp, "  -n, --no-sessiond          Don't spawn a session daemon\n");
 	fprintf(ofp, "      --sessiond-path PATH   Session daemon full path\n");
@@ -136,6 +139,26 @@ static void version(FILE *ofp)
 }
 
 /*
+ * mi_output_type
+ *
+ * Find the MI output type enum from a string
+ * This function is for the support of
+ * new output language.
+ */
+static int mi_output_type(const char *output_type)
+{
+	int ret = 0;
+	if (!strncasecmp("xml", output_type, 3)) {
+		ret = LTTNG_MI_XML;
+	} else {
+		/* Invalid output format */
+		ERR("MI output format not supported");
+		ret = -LTTNG_ERR_MI_OUTPUT_TYPE;
+	}
+	return ret;
+}
+
+/*
  *  list_options
  *
  *  List options line by line. This is mostly for bash auto completion and to
@@ -426,7 +449,7 @@ static int parse_args(int argc, char **argv)
 		clean_exit(EXIT_FAILURE);
 	}
 
-	while ((opt = getopt_long(argc, argv, "+Vhnvqg:", long_options, NULL)) != -1) {
+	while ((opt = getopt_long(argc, argv, "+Vhnvqg:m:", long_options, NULL)) != -1) {
 		switch (opt) {
 		case 'V':
 			version(stdout);
@@ -442,6 +465,13 @@ static int parse_args(int argc, char **argv)
 		case 'q':
 			lttng_opt_quiet = 1;
 			break;
+		case 'm':
+			lttng_opt_mi = mi_output_type(optarg);
+			if (lttng_opt_mi < 0) {
+				ret = lttng_opt_mi;
+				goto error;
+			}
+			break;
 		case 'g':
 			lttng_set_tracing_group(optarg);
 			break;
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 1a764ae..dffab06 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -16,8 +16,11 @@ noinst_LTLIBRARIES = libcommon.la
 libcommon_la_SOURCES = error.h error.c utils.c utils.h runas.c runas.h \
                        common.h futex.c futex.h uri.c uri.h defaults.c \
                        pipe.c pipe.h readwrite.c readwrite.h \
+                       mi-lttng.h mi-lttng.c \
                        daemonize.c daemonize.h
-libcommon_la_LIBADD = -luuid
+libcommon_la_LIBADD = \
+		-luuid \
+		$(top_builddir)/src/common/config/libconfig.la
 
 # Consumer library
 noinst_LTLIBRARIES += libconsumer.la
diff --git a/src/common/error.h b/src/common/error.h
index f8c0a1d..4e096b3 100644
--- a/src/common/error.h
+++ b/src/common/error.h
@@ -36,6 +36,7 @@
 
 extern int lttng_opt_quiet;
 extern int lttng_opt_verbose;
+extern int lttng_opt_mi;
 
 /* Error type. */
 #define PRINT_ERR   0x1
@@ -48,12 +49,18 @@ extern int lttng_opt_verbose;
 
 /*
  * Macro for printing message depending on command line option and verbosity.
+ *
+ * Machine interface:
+ * We use lttng_opt_mi to suppress all normal msg to stdout. We don't
+ * want any nested msg to show up when printing mi to stdout(if it's the case).
+ * All warnings and errors should be printed to stderr as normal.
  */
 #define __lttng_print(type, fmt, args...)                           \
 	do {                                                            \
-		if (lttng_opt_quiet == 0 && type == PRINT_MSG) {            \
+		if (lttng_opt_quiet == 0 && lttng_opt_mi == 0 &&            \
+				type == PRINT_MSG) {                                \
 			fprintf(stdout, fmt, ## args);                          \
-		} else if (lttng_opt_quiet == 0 &&                          \
+		} else if (lttng_opt_quiet == 0 && lttng_opt_mi == 0 &&     \
 				(((type & PRINT_DBG) && lttng_opt_verbose == 1) ||  \
 				((type & (PRINT_DBG | PRINT_DBG2)) &&               \
 					lttng_opt_verbose == 2) ||                      \
diff --git a/src/common/mi-lttng.c b/src/common/mi-lttng.c
new file mode 100644
index 0000000..bbec2c5
--- /dev/null
+++ b/src/common/mi-lttng.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2014   - Jonathan Rajotte <jonathan.r.julien at gmail.com>
+ *                      - Olivier Cotte <olivier.cotte at polymtl.ca>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <include/config.h>
+#include <common/config/config.h>
+#include "mi-lttng.h"
+
+/* Strings related to command */
+const char * const mi_lttng_element_command = "command";
+const char * const mi_lttng_element_command_version = "version";
+const char * const mi_lttng_element_command_list = "list";
+const char * const mi_lttng_element_command_name = "name";
+const char * const mi_lttng_element_command_output = "output";
+
+/* Strings related to command: version */
+const char * const mi_lttng_element_version = "version";
+const char * const mi_lttng_element_version_str = "string";
+const char * const mi_lttng_element_version_web = "url";
+const char * const mi_lttng_element_version_major = "major";
+const char * const mi_lttng_element_version_minor = "minor";
+const char * const mi_lttng_element_version_license = "license";
+const char * const mi_lttng_element_version_patch_level = "patchLevel";
+const char * const mi_lttng_element_version_description = "description";
+
+LTTNG_HIDDEN
+struct mi_writer *mi_lttng_writer_create(int fd_output, int mi_output_type)
+{
+	struct mi_writer *mi_writer;
+
+	mi_writer = zmalloc(sizeof(struct mi_writer));
+	if (!mi_writer) {
+		PERROR("zmalloc mi_writer_create");
+		goto end;
+	}
+	if (mi_output_type == LTTNG_MI_XML) {
+		mi_writer->writer = config_writer_create(fd_output);
+		if (!mi_writer->writer) {
+			goto err_destroy;
+		}
+		mi_writer->type = LTTNG_MI_XML;
+	} else {
+		goto err_destroy;
+	}
+
+end:
+	return mi_writer;
+
+err_destroy:
+	free(mi_writer);
+	return NULL;
+}
+
+LTTNG_HIDDEN
+int mi_lttng_writer_destroy(struct mi_writer *writer)
+{
+	int ret;
+
+	if (!writer) {
+		ret = -EINVAL;
+		goto end;
+	}
+
+	ret = config_writer_destroy(writer->writer);
+	if (ret < 0) {
+		goto end;
+	}
+
+	free(writer);
+end:
+	return ret;
+}
+
+LTTNG_HIDDEN
+int mi_lttng_writer_command_open(struct mi_writer *writer, const char *command)
+{
+	int ret;
+	ret = mi_lttng_writer_open_element(writer, mi_lttng_element_command);
+	if (ret) {
+		goto end;
+	}
+	ret = mi_lttng_writer_write_element_string(writer,
+			mi_lttng_element_command_name, command);
+end:
+	return ret;
+}
+
+LTTNG_HIDDEN
+int mi_lttng_writer_command_close(struct mi_writer *writer)
+{
+	return mi_lttng_writer_close_element(writer);
+}
+
+LTTNG_HIDDEN
+int mi_lttng_writer_open_element(struct mi_writer *writer,
+		const char *element_name)
+{
+	return config_writer_open_element(writer->writer, element_name);
+}
+
+LTTNG_HIDDEN
+int mi_lttng_writer_close_element(struct mi_writer *writer)
+{
+	return config_writer_close_element(writer->writer);
+}
+
+LTTNG_HIDDEN
+int mi_lttng_writer_write_element_unsigned_int(struct mi_writer *writer,
+		const char *element_name, uint64_t value)
+{
+	return config_writer_write_element_unsigned_int(writer->writer,
+			element_name, value);
+}
+
+LTTNG_HIDDEN
+int mi_lttng_writer_write_element_signed_int(struct mi_writer *writer,
+		const char *element_name, int64_t value)
+{
+	return config_writer_write_element_signed_int(writer->writer,
+			element_name, value);
+}
+
+LTTNG_HIDDEN
+int mi_lttng_writer_write_element_bool(struct mi_writer *writer,
+		const char *element_name, int value)
+{
+	return config_writer_write_element_bool(writer->writer,
+			element_name, value);
+}
+
+LTTNG_HIDDEN
+int mi_lttng_writer_write_element_string(struct mi_writer *writer,
+		const char *element_name, const char *value)
+{
+	return config_writer_write_element_string(writer->writer,
+			element_name, value);
+}
+
+LTTNG_HIDDEN
+int mi_lttng_version(struct mi_writer *writer, struct mi_lttng_version *version,
+	const char *lttng_description, const char *lttng_license)
+{
+	int ret;
+
+	/* Open version */
+	ret = mi_lttng_writer_open_element(writer, mi_lttng_element_version);
+	if (ret) {
+		goto end;
+	}
+
+	/* Version string (contain info like rc etc.) */
+	ret = mi_lttng_writer_write_element_string(writer,
+			mi_lttng_element_version_str, VERSION);
+	if (ret) {
+		goto end;
+	}
+
+	/* Major version number */
+	ret = mi_lttng_writer_write_element_unsigned_int(writer,
+			mi_lttng_element_version_major, version->version_major);
+	if (ret) {
+		goto end;
+	}
+
+	/* Minor version number */
+	ret = mi_lttng_writer_write_element_unsigned_int(writer,
+			mi_lttng_element_version_minor, version->version_minor);
+	if (ret) {
+		goto end;
+	}
+
+	/* Patch number */
+	ret = mi_lttng_writer_write_element_unsigned_int(writer,
+			mi_lttng_element_version_patch_level, version->version_patchlevel);
+	if (ret) {
+		goto end;
+	}
+
+	/* Name of the version */
+	ret = mi_lttng_writer_write_element_string(writer,
+			config_element_name, version->version_name);
+	if (ret) {
+		goto end;
+	}
+
+	/* Description mostly related to beer... */
+	ret = mi_lttng_writer_write_element_string(writer,
+			mi_lttng_element_version_description, lttng_description);
+	if (ret) {
+		goto end;
+	}
+
+	/* url */
+	ret = mi_lttng_writer_write_element_string(writer,
+			mi_lttng_element_version_web, version->package_url);
+	if (ret) {
+		goto end;
+	}
+
+	/* License: free as in free beer...no...*speech* */
+	ret = mi_lttng_writer_write_element_string(writer,
+			mi_lttng_element_version_license, lttng_license);
+	if (ret) {
+		goto end;
+	}
+
+	/* Close version element */
+	ret = mi_lttng_writer_close_element(writer);
+
+end:
+	return ret;
+}
+
+LTTNG_HIDDEN
+int mi_lttng_session(struct mi_writer *writer,
+		struct lttng_session *session, int is_open)
+{
+	int ret;
+
+	/* open sessions element */
+	ret = mi_lttng_writer_open_element(writer,
+			config_element_session);
+	if (ret) {
+		goto end;
+	}
+
+	/* Name of the session */
+	ret = mi_lttng_writer_write_element_string(writer,
+			config_element_name, session->name);
+	if (ret) {
+		goto end;
+	}
+
+	/* path */
+	ret = mi_lttng_writer_write_element_string(writer,
+			config_element_path, session->path);
+	if (ret) {
+		goto end;
+	}
+
+	/* enabled ? */
+	ret = mi_lttng_writer_write_element_unsigned_int(writer,
+			config_element_enabled, session->enabled);
+	if (ret) {
+		goto end;
+	}
+
+	/* snapshot mode */
+	ret = mi_lttng_writer_write_element_unsigned_int(writer,
+			config_element_snapshot_mode, session->snapshot_mode);
+	if (ret) {
+		goto end;
+	}
+
+	/* live timer interval in usec */
+	ret = mi_lttng_writer_write_element_unsigned_int(writer,
+			config_element_live_timer_interval,
+			session->live_timer_interval);
+	if (ret) {
+		goto end;
+	}
+
+	if (!is_open) {
+		/* Closing session element */
+		ret = mi_lttng_writer_close_element(writer);
+	}
+end:
+	return ret;
+
+}
diff --git a/src/common/mi-lttng.h b/src/common/mi-lttng.h
new file mode 100644
index 0000000..69ec0f5
--- /dev/null
+++ b/src/common/mi-lttng.h
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2014   - Jonathan Rajotte <jonathan.r.julien at gmail.com>
+ *                      - Olivier Cotte <olivier.cotte at polymtl.ca>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License, version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _MI_LTTNG_H
+#define _MI_LTTNG_H
+
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/config/config.h>
+
+#include <lttng/lttng.h>
+
+#include <stdint.h>
+
+
+/* Instance of a machine interface writer. */
+struct mi_writer {
+	struct config_writer *writer;
+	enum lttng_mi_output_type type;
+};
+
+/*
+ * Version information for the machine interface.
+ */
+struct mi_lttng_version {
+	char version[NAME_MAX]; /* Version number of package */
+	uint32_t version_major; /* LTTng-Tools major version number */
+	uint32_t version_minor; /* LTTng-Tools minor version number */
+	uint32_t version_patchlevel; /* LTTng-Tools patchlevel version number */
+	char version_name[NAME_MAX];
+	char package_url[NAME_MAX]; /* Define to the home page for this package. */
+};
+
+/* Strings related to command */
+const char * const mi_lttng_element_command;
+const char * const mi_lttng_element_command_version;
+const char * const mi_lttng_element_command_list;
+const char * const mi_lttng_element_command_name;
+const char * const mi_lttng_element_command_output;
+
+/* Strings related to command: version */
+const char * const mi_lttng_element_version;
+const char * const mi_lttng_element_version_str;
+const char * const mi_lttng_element_version_web;
+const char * const mi_lttng_element_version_major;
+const char * const mi_lttng_element_version_minor;
+const char * const mi_lttng_element_version_license;
+const char * const mi_lttng_element_version_patch_level;
+const char * const mi_lttng_element_version_description;
+
+/*
+ * Create an instance of a machine interface writer.
+ *
+ * fd_output File to which the XML content must be written. The file will be
+ * closed once the mi_writer has been destroyed.
+ *
+ * Returns an instance of a machine interface writer on success, NULL on
+ * error.
+ */
+struct mi_writer *mi_lttng_writer_create(int fd_output, int mi_output_type);
+
+/*
+ * Destroy an instance of a machine interface writer.
+ *
+ * writer An instance of a machine interface writer.
+ *
+ * Returns zero if the XML document could be closed cleanly. Negative values
+ * indicate an error.
+ */
+int mi_lttng_writer_destroy(struct mi_writer *writer);
+
+/*
+ * Open a command tag and add it's name node.
+ *
+ * writer An instance of a machine interface writer.
+ *
+ * command The command name.
+ *
+ * Returns zero if the XML document could be closed cleanly.
+ * Negative values indicate an error.
+ */
+int mi_lttng_writer_command_open(struct mi_writer *writer, const char *command);
+
+/*
+ * Close a command tag.
+ *
+ * writer An instance of a machine interface writer.
+ *
+ * Returns zero if the XML document could be closed cleanly.
+ * Negative values indicate an error.
+ */
+int mi_lttng_writer_command_close(struct mi_writer *writer);
+
+/*
+ * Open an element tag.
+ *
+ * writer An instance of a machine interface writer.
+ *
+ * element_name Element tag name.
+ *
+ * Returns zero if the XML document could be closed cleanly.
+ * Negative values indicate an error.
+ */
+int mi_lttng_writer_open_element(struct mi_writer *writer,
+		const char *element_name);
+
+/*
+ * Close the current element tag.
+ *
+ * writer An instance of a machine interface writer.
+ *
+ * Returns zero if the XML document could be closed cleanly.
+ * Negative values indicate an error.
+ */
+int mi_lttng_writer_close_element(struct mi_writer *writer);
+
+/*
+ * Write an element of type unsigned int.
+ *
+ * writer An instance of a machine interface writer.
+ *
+ * element_name Element name.
+ *
+ * value Unsigned int value of the element
+ *
+ * Returns zero if the element's value could be written.
+ * Negative values indicate an error.
+ */
+int mi_lttng_writer_write_element_unsigned_int(struct mi_writer *writer,
+		const char *element_name, uint64_t value);
+
+/*
+ * Write an element of type signed int.
+ *
+ * writer An instance of a machine interface writer.
+ *
+ * element_name Element name.
+ *
+ * value Signed int value of the element
+ *
+ * Returns zero if the element's value could be written.
+ * Negative values indicate an error.
+ */
+int mi_lttng_writer_write_element_signed_int(struct mi_writer *writer,
+		const char *element_name, int64_t value);
+
+/*
+ * Write an element of type boolean.
+ *
+ * writer An instance of a machine interface writer.
+ *
+ * element_name Element name.
+ *
+ * value Boolean value of the element
+ *
+ * Returns zero if the element's value could be written.
+ * Negative values indicate an error.
+ */
+int mi_lttng_writer_write_element_bool(struct mi_writer *writer,
+		const char *element_name, int value);
+
+/*
+ * Write an element of type string.
+ *
+ * writer An instance of a machine interface writer.
+ *
+ * element_name Element name.
+ *
+ * value String value of the element
+ *
+ * Returns zero if the element's value could be written.
+ * Negative values indicate an error.
+ */
+int mi_lttng_writer_write_element_string(struct mi_writer *writer,
+		const char *element_name, const char *value);
+
+/*
+ * Machine interface of struct version.
+ *
+ * writer An instance of a machine interface writer.
+ *
+ * version Version struct.
+ *
+ * lttng_description String value of the version description.
+ *
+ * lttng_license String value of the version license.
+ *
+ * Returns zero if the element's value could be written.
+ * Negative values indicate an error.
+ */
+int mi_lttng_version(struct mi_writer *writer, struct mi_lttng_version *version,
+		const char *lttng_description, const char *lttng_license);
+
+/*
+ * Machine interface of struct session.
+ *
+ * writer An instance of a machine interface writer
+ *
+ * session An instance of a session
+ *
+ * isOpen Define if we close the session element
+ *        This should be use carefully and the client
+ *        need to close the session element.
+ *        Use case: nested addition information on a session
+ *                  ex: domain,channel event.
+ *        0-> False
+ *        1-> True
+ *
+ * Returns zero if the element's value could be written.
+ * Negative values indicate an error.
+ */
+int mi_lttng_session(struct mi_writer *writer,
+		struct lttng_session *session, int isOpen);
+
+#endif /* _MI_LTTNG_H */
diff --git a/src/lib/lttng-ctl/lttng-ctl.c b/src/lib/lttng-ctl/lttng-ctl.c
index 100e587..7dd9828 100644
--- a/src/lib/lttng-ctl/lttng-ctl.c
+++ b/src/lib/lttng-ctl/lttng-ctl.c
@@ -74,6 +74,7 @@ static int connected;
  */
 int lttng_opt_quiet;
 int lttng_opt_verbose;
+int lttng_opt_mi;
 
 /*
  * Copy string from src to dst and enforce null terminated byte.
diff --git a/tests/unit/ini_config/ini_config.c b/tests/unit/ini_config/ini_config.c
index 38fe5f4..29935ef 100644
--- a/tests/unit/ini_config/ini_config.c
+++ b/tests/unit/ini_config/ini_config.c
@@ -31,6 +31,7 @@ struct state {
 
 int lttng_opt_quiet = 1;
 int lttng_opt_verbose = 0;
+int lttng_opt_mi;
 
 int entry_handler(const struct config_entry *entry,
 		struct state *state)
diff --git a/tests/unit/test_kernel_data.c b/tests/unit/test_kernel_data.c
index e2182e9..2639be4 100644
--- a/tests/unit/test_kernel_data.c
+++ b/tests/unit/test_kernel_data.c
@@ -38,6 +38,7 @@
 /* For error.h */
 int lttng_opt_quiet = 1;
 int lttng_opt_verbose;
+int lttng_opt_mi;
 
 int ust_consumerd32_fd;
 int ust_consumerd64_fd;
diff --git a/tests/unit/test_session.c b/tests/unit/test_session.c
index a0e84e9..4f90c33 100644
--- a/tests/unit/test_session.c
+++ b/tests/unit/test_session.c
@@ -46,6 +46,7 @@ static struct ltt_session_list *session_list;
 /* For error.h */
 int lttng_opt_quiet = 1;
 int lttng_opt_verbose = 0;
+int lttng_opt_mi;
 
 int ust_consumerd32_fd;
 int ust_consumerd64_fd;
diff --git a/tests/unit/test_uri.c b/tests/unit/test_uri.c
index 7cac95d..432e2b3 100644
--- a/tests/unit/test_uri.c
+++ b/tests/unit/test_uri.c
@@ -25,6 +25,7 @@
 /* For error.h */
 int lttng_opt_quiet = 1;
 int lttng_opt_verbose = 3;
+int lttng_opt_mi;
 
 /* Number of TAP tests in this file */
 #define NUM_TESTS 11
diff --git a/tests/unit/test_ust_data.c b/tests/unit/test_ust_data.c
index 150d5da..dd00089 100644
--- a/tests/unit/test_ust_data.c
+++ b/tests/unit/test_ust_data.c
@@ -44,6 +44,7 @@
 /* For error.h */
 int lttng_opt_quiet = 1;
 int lttng_opt_verbose;
+int lttng_opt_mi;
 
 int ust_consumerd32_fd;
 int ust_consumerd64_fd;
diff --git a/tests/unit/test_utils_expand_path.c b/tests/unit/test_utils_expand_path.c
index fe709ac..f863b5d 100644
--- a/tests/unit/test_utils_expand_path.c
+++ b/tests/unit/test_utils_expand_path.c
@@ -31,6 +31,7 @@
 /* For error.h */
 int lttng_opt_quiet = 1;
 int lttng_opt_verbose = 3;
+int lttng_opt_mi;
 
 struct valid_test_input {
 	char *input;
diff --git a/tests/unit/test_utils_parse_size_suffix.c b/tests/unit/test_utils_parse_size_suffix.c
index 990aa1b..f81278d 100644
--- a/tests/unit/test_utils_parse_size_suffix.c
+++ b/tests/unit/test_utils_parse_size_suffix.c
@@ -26,6 +26,7 @@
 /* For error.h */
 int lttng_opt_quiet = 1;
 int lttng_opt_verbose = 3;
+int lttng_opt_mi;
 
 struct valid_test_input {
 	char *input;
-- 
1.8.3.2

-------------- next part --------------
A non-text attachment was scrubbed...
Name: run_mi_test.sh
Type: application/x-sh
Size: 2453 bytes
Desc: not available
URL: <http://lists.lttng.org/pipermail/lttng-dev/attachments/20140406/24c88c30/attachment-0001.sh>


More information about the lttng-dev mailing list