From ed47b70d7db4b9bf9a1e0b38324d822f1017d835 Mon Sep 17 00:00:00 2001
From: Dmitry Simonenko <pmwkaa@gmail.com>
Date: Thu, 14 Jun 2012 18:09:31 +0400
Subject: [PATCH] connector-c-librpl: console client refactoring.

---
 CMakeLists.txt                                |   2 +-
 client/tarantool/tnt.c                        | 506 ------------------
 client/{tarantool => tc}/CMakeLists.txt       |  16 +-
 client/tc/tc.c                                | 129 +++++
 client/tc/tc.h                                |  37 ++
 .../{tarantool/tnt_admin.c => tc/tc_admin.c}  |  42 +-
 .../{tarantool/tnt_admin.h => tc/tc_admin.h}  |  22 +-
 client/tc/tc_cli.c                            | 176 ++++++
 client/tc/tc_cli.h                            |  32 ++
 client/tc/tc_opt.c                            | 121 +++++
 client/tc/tc_opt.h                            |  54 ++
 client/tc/tc_print.c                          |  75 +++
 client/tc/tc_print.h                          |  32 ++
 client/tc/tc_query.c                          | 157 ++++++
 client/tc/tc_query.h                          |  41 ++
 client/tc/tc_wal.c                            | 175 ++++++
 client/tc/tc_wal.h                            |  33 ++
 connector/c/include/tarantool/tnt_iter.h      |   1 +
 connector/c/include/tarantool/tnt_rpl.h       |   2 +-
 connector/c/tntrpl/tnt_rpl.c                  |  10 +-
 connector/c/tntsql/tnt_sql.c                  |   2 +-
 test/connector_c/rpl.c                        |  15 +-
 22 files changed, 1110 insertions(+), 570 deletions(-)
 delete mode 100644 client/tarantool/tnt.c
 rename client/{tarantool => tc}/CMakeLists.txt (73%)
 create mode 100644 client/tc/tc.c
 create mode 100644 client/tc/tc.h
 rename client/{tarantool/tnt_admin.c => tc/tc_admin.c} (82%)
 rename client/{tarantool/tnt_admin.h => tc/tc_admin.h} (75%)
 create mode 100644 client/tc/tc_cli.c
 create mode 100644 client/tc/tc_cli.h
 create mode 100644 client/tc/tc_opt.c
 create mode 100644 client/tc/tc_opt.h
 create mode 100644 client/tc/tc_print.c
 create mode 100644 client/tc/tc_print.h
 create mode 100644 client/tc/tc_query.c
 create mode 100644 client/tc/tc_query.h
 create mode 100644 client/tc/tc_wal.c
 create mode 100644 client/tc/tc_wal.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 341a488035..c3f20af588 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -222,7 +222,7 @@ option(ENABLE_DOC "Enable building of documentation" OFF)
 
 option(ENABLE_CLIENT "Enable building of console client" OFF)
 if (ENABLE_CLIENT)
-    set (TARANTOOL_CLIENTS ${TARANTOOL_CLIENTS} "tarantool")
+    set (TARANTOOL_CLIENTS ${TARANTOOL_CLIENTS} "tc")
 endif()
 
 option(ENABLE_GCOV "Enable integration with gcov, a code coverage program" OFF)
diff --git a/client/tarantool/tnt.c b/client/tarantool/tnt.c
deleted file mode 100644
index 2fd57aa339..0000000000
--- a/client/tarantool/tnt.c
+++ /dev/null
@@ -1,506 +0,0 @@
-
-/*
- * Copyright (C) 2011 Mail.RU
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <inttypes.h>
-#include <limits.h>
-
-#include <signal.h>
-#include <errno.h>
-
-#include <readline/readline.h>
-#include <readline/history.h>
-
-#include <errcode.h>
-#include <third_party/gopt/gopt.h>
-
-#include <connector/c/include/tarantool/tnt.h>
-#include <connector/c/include/tarantool/tnt_sql.h>
-#include <connector/c/include/tarantool/tnt_net.h>
-#include <connector/c/include/tarantool/tnt_xlog.h>
-#include <connector/c/include/tarantool/tnt_rpl.h>
-
-#include <client/tarantool/tnt_admin.h>
-
-#define DEFAULT_HOST "localhost"
-#define DEFAULT_PORT 33013
-#define DEFAULT_PORT_ADMIN 33015
-#define HISTORY_FILE ".tarantool_history"
-
-static char *query_op_type(uint32_t type) {
-	switch (type) {
-	case TNT_OP_PING:   return "Ping";
-	case TNT_OP_INSERT: return "Insert";
-	case TNT_OP_DELETE: return "Delete";
-	case TNT_OP_UPDATE: return "Update";
-	case TNT_OP_SELECT: return "Select";
-	case TNT_OP_CALL:   return "Call";
-	}
-	return "Unknown";
-}
-
-static char *query_op(struct tnt_reply *r) {
-	return query_op_type(r->op);
-}
-
-static void print_tuple(struct tnt_tuple *tu) {
-	struct tnt_iter ifl;
-	tnt_iter(&ifl, tu);
-	printf("[");
-	while (tnt_next(&ifl)) {
-		if (TNT_IFIELD_IDX(&ifl) != 0)
-			printf(", ");
-		char *data = TNT_IFIELD_DATA(&ifl);
-		uint32_t size = TNT_IFIELD_SIZE(&ifl);
-		if (!isprint(data[0]) && (size == 4 || size == 8)) {
-			if (size == 4) {
-				uint32_t i = *((uint32_t*)data);
-				printf("%"PRIu32, i);
-			} else {
-				uint64_t i = *((uint64_t*)data);
-				printf("%"PRIu64, i);
-			}
-		} else {
-			printf("'%-.*s'", size, data);
-		}
-	}
-	if (ifl.status == TNT_ITER_FAIL)
-		printf("<parsing error>");
-	printf("]\n");
-}
-
-static void print_tuple_list(struct tnt_list *l) {
-	struct tnt_iter it;
-	tnt_iter_list(&it, l);
-	while (tnt_next(&it)) {
-		struct tnt_tuple *tu = TNT_ILIST_TUPLE(&it);
-		print_tuple(tu);
-	}
-}
-
-static void
-query_reply_show(struct tnt_reply *r)
-{
-	printf("%s OK, %d rows affected\n", query_op(r), r->count);
-	print_tuple_list(TNT_REPLY_LIST(r));
-}
-
-static int
-query_reply(struct tnt_stream *t, int show_reply)
-{
-	int rc = -1;
-	struct tnt_iter i;
-	tnt_iter_reply(&i, t);
-	while (tnt_next(&i)) {
-		struct tnt_reply *r = TNT_IREPLY_PTR(&i);
-		if (tnt_error(t) != TNT_EOK) {
-			printf("%s ERROR, %s\n", query_op(r),
-			       tnt_strerror(t));
-			goto error;
-		} else if (r->code != 0) {
-			printf("%s ERROR, %s (%s)\n", query_op(r),
-			       ((r->error) ? r->error : ""), tnt_errcode_str(r->code >> 8));
-			goto error;
-		}
-		if (show_reply)
-			query_reply_show(r);
-
-	}
-	rc = (i.status == TNT_ITER_FAIL) ? -1 : 0;
-error:
-	tnt_iter_free(&i);
-	return rc;
-}
-
-static int
-query(struct tnt_stream *t, char *q)
-{
-	char *e = NULL;
-	int rc = tnt_query(t, q, strlen(q), &e);
-	if (rc == -1) {
-		if (e) {
-			printf("error: %s", e);
-			free(e);
-		}
-		return -1;
-	}
-	rc = tnt_flush(t);
-	if (rc < 0) {
-		printf("error: %s\n", tnt_strerror(t));
-		return -1;
-	}
-	if (query_reply(t, 1) == -1)
-		return -1;
-	return 0;
-}
-
-static int
-query_admin(struct tnt_admin *a, char *q, int reply)
-{
-	if (tnt_admin_query(a, q) == -1) {
-		printf("error: failed to send admin query\n");
-		return -1;
-	}
-	if (!reply)
-		return 0;
-	char *rep = NULL;
-	size_t reps = 0;
-	if (tnt_admin_reply(a, &rep, &reps) == -1) {
-		printf("error: failed to recv admin reply\n");
-		return -1;
-	}
-	if (rep) {
-		printf("%s", rep);
-		free(rep);
-	}
-	return 0;
-}
-
-static int
-run_cmdline(struct tnt_stream *t, struct tnt_admin *a, int argc, char **argv) 
-{
-	int i, rc = 0;
-	for (i = 1 ; i < argc ; i++) {
-		if (tnt_query_is(argv[i], strlen(argv[i]))) {
-			if (query(t, argv[i]) == -1)
-				rc = 1;
-		} else {
-			int reply = strcmp(argv[i], "exit") && strcmp(argv[i], "quit");
-			if (query_admin(a, argv[i], reply) == -1)
-				rc = 1;
-			if (!reply)
-				break;
-		}
-	}
-	return rc;
-}
-
-static int reconnect_do(struct tnt_stream *t, struct tnt_admin *a) {
-	if (tnt_connect(t) == -1) {
-		printf("reconnect: %s\n", tnt_strerror(t));
-		return 0;
-	}
-	if (tnt_admin_reconnect(a) == -1) {
-		printf("reconnect: admin console connection failed\n");
-		return 0;
-	}
-	printf("reconnected\n");
-	return 1;
-}
-
-static int
-run_interactive(struct tnt_stream *t, struct tnt_admin *a, char *host) 
-{
-	/* ignoring SIGPIPE */
-	struct sigaction sa;
-	memset(&sa, 0, sizeof(sa));
-	sigemptyset(&sa.sa_mask);
-	sa.sa_handler = SIG_IGN;
-	if (sigaction(SIGPIPE, &sa, NULL) == -1) {
-		printf("signal initialization failed\n");
-		return 1;
-	}
-
-	char *home = getenv("HOME");
-	char history[1024];
-	snprintf(history, sizeof(history), "%s/%s", home, HISTORY_FILE);
-	read_history(history);
-
-	char prompt[128];
-	snprintf(prompt, sizeof(prompt), "%s> ", host);
-
-	/* interactive mode */
-	int reconnect = 0;
-	char *cmd;
-	while ((cmd = readline(prompt))) {
-		if (!cmd[0])
-			goto next;
-		if (reconnect) {
-reconnect: 		if (reconnect_do(t, a))
-				reconnect = 0;
-			else 
-				goto next;
-		}
-		if (tnt_query_is(cmd, strlen(cmd))) {
-			if (query(t, cmd) == -1) {
-				/* broken pipe or recv() == 0 */
-				int e = tnt_errno(t) == EPIPE || tnt_errno(t) == 0;
-				if (tnt_error(t) == TNT_ESYSTEM && e)
-					reconnect = 1;
-			}
-		} else {
-			int reply = strcmp(cmd, "exit") && strcmp(cmd, "quit");
-			if (query_admin(a, cmd, reply) == -1)
-				reconnect = 1;
-			if (!reply) {
-				free(cmd);
-				break;
-			}
-		}
-		add_history(cmd);
-		if (reconnect)
-			goto reconnect;
-next:
-		free(cmd);
-	}
-
-	write_history(history);
-	clear_history();
-	return 0;
-}
-
-static int
-run_wal_cat(const char *file)
-{
-	struct tnt_stream s;
-	tnt_xlog(&s);
-	if (tnt_xlog_open(&s, (char*)file) == -1) {
-		tnt_stream_free(&s);
-		return -1;
-	}
-	struct tnt_iter i;
-	tnt_iter_request(&i, &s);
-	while (tnt_next(&i)) {
-		struct tnt_request *r = TNT_IREQUEST_PTR(&i);
-		struct tnt_stream_xlog *sx = TNT_SXLOG_CAST(&s);
-		printf("%s lsn: %"PRIu64", time: %f, len: %"PRIu32"\n",
-		       query_op_type(r->h.type),
-		       sx->hdr.lsn,
-		       sx->hdr.tm, sx->hdr.len);
-		switch (r->h.type) {
-		case TNT_OP_INSERT:
-			print_tuple(&r->r.insert.t);
-			break;
-		case TNT_OP_DELETE:
-			print_tuple(&r->r.delete.t);
-			break;
-		case TNT_OP_UPDATE:
-			print_tuple(&r->r.update.t);
-			break;
-		case TNT_OP_CALL:
-			print_tuple(&r->r.call.t);
-			break;
-		}
-	}
-	int rc = 0;
-	if (i.status == TNT_ITER_FAIL) {
-		printf("parsing failed: %s\n", tnt_xlog_strerror(&s));
-		rc = 1;
-	}
-	tnt_iter_free(&i);
-	tnt_stream_free(&s);
-	return rc;
-}
-
-static int
-run_wal_play(struct tnt_stream *t, const char *file)
-{
-	struct tnt_stream s;
-	tnt_xlog(&s);
-	if (tnt_xlog_open(&s, (char*)file) == -1) {
-		tnt_stream_free(&s);
-		return -1;
-	}
-	struct tnt_iter i;
-	tnt_iter_request(&i, &s);
-	while (tnt_next(&i)) {
-		struct tnt_request *r = TNT_IREQUEST_PTR(&i);
-		if (t->write_request(t, r) == -1) {
-			printf("failed to write request\n");
-			goto error;
-		}
-		if (query_reply(t, 0) == -1)
-			goto error;
-	}
-	if (i.status == TNT_ITER_FAIL) {
-		printf("parsing failed: %s\n", tnt_xlog_strerror(&s));
-		goto error;
-	}
-	return 0;
-error:
-	tnt_iter_free(&i);
-	tnt_stream_free(&s);
-	return 1;
-}
-
-static int
-run_replica(char *host, int port, uint64_t lsn)
-{
-	struct tnt_stream s;
-	tnt_rpl(&s);
-
-	struct tnt_stream *sn = tnt_rpl_net(&s);
-	tnt_set(sn, TNT_OPT_HOSTNAME, host);
-	tnt_set(sn, TNT_OPT_PORT, port);
-	tnt_set(sn, TNT_OPT_SEND_BUF, 0);
-	tnt_set(sn, TNT_OPT_RECV_BUF, 0);
-	if (tnt_rpl_open(&s, lsn) == -1)
-		return 1;
-
-	struct tnt_iter i;
-	tnt_iter_request(&i, &s);
-
-	while (tnt_next(&i)) {
-		struct tnt_request *r = TNT_IREQUEST_PTR(&i);
-		struct tnt_stream_rpl *sr = TNT_RPL_CAST(&s);
-		printf("%s lsn: %"PRIu64"\n",
-		       query_op_type(r->h.type),
-		       sr->hdr.lsn);
-		switch (r->h.type) {
-		case TNT_OP_INSERT:
-			print_tuple(&r->r.insert.t);
-			break;
-		case TNT_OP_DELETE:
-			print_tuple(&r->r.delete.t);
-			break;
-		case TNT_OP_UPDATE:
-			print_tuple(&r->r.update.t);
-			break;
-		case TNT_OP_CALL:
-			print_tuple(&r->r.call.t);
-			break;
-		}
-	}
-	if (i.status == TNT_ITER_FAIL)
-		printf("parsing failed\n");
-
-	tnt_iter_free(&i);
-	tnt_stream_free(&s);
-	return 0;
-}
-
-int
-main(int argc, char *argv[])
-{
-	const void *opt_def =
-		gopt_start(gopt_option('a', GOPT_ARG, gopt_shorts('a'),
-				       gopt_longs("host"), " <host>", "server address"),
-			   gopt_option('p', GOPT_ARG, gopt_shorts('p'),
-				       gopt_longs("port"), " <port>", "server port"),
-			   gopt_option('m', GOPT_ARG, gopt_shorts('m'),
-				       gopt_longs("port-admin"), " <port>", "server admin port"),
-			   gopt_option('C', GOPT_ARG, gopt_shorts('C'),
-				       gopt_longs("wal-cat"), " <file>", "print xlog file content"),
-			   gopt_option('P', GOPT_ARG, gopt_shorts('P'),
-				       gopt_longs("wal-play"), " <file>", "replay xlog file to the specified server"),
-			   gopt_option('R', GOPT_ARG, gopt_shorts('R'),
-				       gopt_longs("rpl"), " <lsn>", "act as replica for to the specified server"),
-			   gopt_option('h', 0, gopt_shorts('h', '?'), gopt_longs("help"),
-				       NULL, "display this help and exit"));
-	void *opt = gopt_sort(&argc, (const char**)argv, opt_def);
-	if (gopt(opt, 'h')) {
-		printf("usage: tarantool [options] [query]\n\n");
-		printf("tarantool sql client.\n");
-		gopt_help(opt_def);
-		gopt_free(opt);
-		return 0;
-	}
-
-	/* wal-cat */
-	const char *arg = NULL;
-	if (gopt_arg(opt, 'C', &arg))
-		return run_wal_cat(arg);
-
-	/* server host */
-	gopt_arg(opt, 'a', &arg);
-
-	char host[128];
-	snprintf(host, sizeof(host), "%s", (arg) ? arg : DEFAULT_HOST);
-
-	/* server port */
-	int port = DEFAULT_PORT;
-	if (gopt_arg(opt, 'p', &arg))
-		port = atoi(arg);
-
-	/* replica mode */
-	if (gopt_arg(opt, 'R', &arg)) {
-		uint64_t lsn = strtoll(arg, NULL, 10);
-		if (lsn == LLONG_MIN || lsn == LLONG_MAX) {
-			printf("bad lsn number\n");
-			return 1;
-		}
-		return run_replica(host, port, lsn);
-	}
-
-	/* server admin port */
-	int admin_port = DEFAULT_PORT_ADMIN;
-	if (gopt_arg(opt, 'm', &arg))
-		admin_port = atoi(arg);
-
-	/* wal-player mode */
-	const char *wal_player_file = NULL;
-	gopt_arg(opt, 'P', &wal_player_file);
-	gopt_free(opt);
-
-	/* creating and initializing tarantool network stream */
-	struct tnt_stream *t = tnt_net(NULL);
-	if (t == NULL)
-		return 1;
-	tnt_set(t, TNT_OPT_HOSTNAME, host);
-	tnt_set(t, TNT_OPT_PORT, port);
-	tnt_set(t, TNT_OPT_SEND_BUF, 0);
-	tnt_set(t, TNT_OPT_RECV_BUF, 0);
-	if (tnt_init(t) == -1) {
-		printf("error: %s\n", tnt_strerror(t));
-		tnt_stream_free(t);
-		return 1;
-	}
-
-	/* connecting to server */
-	if (tnt_connect(t) == -1) {
-		printf("error: %s\n", tnt_strerror(t));
-		tnt_stream_free(t);
-		return 1;
-	}
-
-	/* wal-player mode */
-	if (wal_player_file) {
-		int rc = run_wal_play(t, wal_player_file);
-		tnt_stream_free(t);
-		return rc;
-	}
-
-	/* creating tarantool admin handler */
-	struct tnt_admin a;
-	if (tnt_admin_init(&a, host, admin_port) == -1) {
-		printf("error: admin console initialization failed\n");
-		tnt_stream_free(t);
-		return 1;
-	}
-
-	/* main */
-	int rc = 0;
-	if (argc >= 2) 
-		rc = run_cmdline(t, &a, argc, argv);
-	else
-		rc = run_interactive(t, &a, host);
-	tnt_admin_free(&a);
-	tnt_stream_free(t);
-	return rc;
-}
diff --git a/client/tarantool/CMakeLists.txt b/client/tc/CMakeLists.txt
similarity index 73%
rename from client/tarantool/CMakeLists.txt
rename to client/tc/CMakeLists.txt
index 9379d4f0f9..f866ac8288 100644
--- a/client/tarantool/CMakeLists.txt
+++ b/client/tc/CMakeLists.txt
@@ -1,4 +1,3 @@
-
 project(tnt_cli)
 
 include(FindCurses)
@@ -19,18 +18,15 @@ set (cli_deps ${READLINE_LIB} ${READLINE_HISTORY_LIB})
 if (CURSES_FOUND)
     set(cli_deps ${cli_deps} ${CURSES_LIBRARIES})
 else()
-    message(FATAL_ERROR "curses library not found.")
-endif()
-
-find_library(TERMCAP_LIBRARY NAMES "termcap")
-if (TERMCAP_LIBRARY)
-    set(cli_deps ${cli_deps} ${TERMCAP_LIBRARY})
-else()
-    message(FATAL_ERROR "termcap library not found.")
+    # readline could be compiled with termcap
+    find_library(TERMCAP_LIB NAMES "termcap")
+	if (TERMCAP_LIB)
+	set(cli_deps ${cli_deps} ${TERMCAP_LIB})
+    endif()
 endif()
 
 set (cli "tarantool")
-set (cli_sources tnt.c tnt_admin.c)
+set (cli_sources tc.c tc_opt.c tc_admin.c tc_query.c tc_print.c tc_cli.c tc_wal.c)
 set (cli_libs tntrpl tntnet tntsql tnt gopt ${cli_deps})
 
 add_executable(${cli} ${cli_sources} ${CMAKE_SOURCE_DIR}/src/errcode.c)
diff --git a/client/tc/tc.c b/client/tc/tc.c
new file mode 100644
index 0000000000..ad491d5bed
--- /dev/null
+++ b/client/tc/tc.c
@@ -0,0 +1,129 @@
+
+/*
+ * Copyright (C) 2012 Mail.RU
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <connector/c/include/tarantool/tnt.h>
+#include <connector/c/include/tarantool/tnt_net.h>
+#include <connector/c/include/tarantool/tnt_sql.h>
+
+#include "client/tc/tc_opt.h"
+#include "client/tc/tc_admin.h"
+#include "client/tc/tc.h"
+#include "client/tc/tc_cli.h"
+#include "client/tc/tc_wal.h"
+
+struct tc tc;
+
+static void tc_init(void) {
+	memset(&tc, 0, sizeof(tc));
+}
+
+static void tc_free(void) {
+	if (tc.net) {
+		tnt_stream_free(tc.net);
+	}
+	tc_admin_close(&tc.admin);
+}
+
+void tc_error(char *fmt, ...) {
+	char msg[256];
+	va_list args;
+	tc_free();
+	/* - - - - */
+	va_start(args, fmt);
+	vsnprintf(msg, sizeof(msg), fmt, args);
+	va_end(args);
+	printf("error: %s\n", msg);
+	exit(1);
+}
+
+static void tc_connect(void)
+{
+	/* allocating stream */
+	tc.net = tnt_net(NULL);
+	if (tc.net == NULL)
+		tc_error("stream allocation error");
+	/* initializing network stream */
+	tnt_set(tc.net, TNT_OPT_HOSTNAME, tc.opt.host);
+	tnt_set(tc.net, TNT_OPT_PORT, tc.opt.port);
+	tnt_set(tc.net, TNT_OPT_SEND_BUF, 0);
+	tnt_set(tc.net, TNT_OPT_RECV_BUF, 0);
+	if (tnt_init(tc.net) == -1)
+		tc_error("%s", tnt_strerror(tc.net));
+	/* connecting to server */
+	if (tnt_connect(tc.net) == -1)
+		tc_error("%s", tnt_strerror(tc.net));
+}
+
+static void tc_connect_admin(void)
+{
+	if (tc_admin_connect(&tc.admin,
+			     tc.opt.host,
+			     tc.opt.port_admin) == -1)
+		tc_error("admin console connection failed");
+}
+
+int main(int argc, char *argv[])
+{
+	tc_init();
+
+	int rc;
+	enum tc_opt_mode mode = tc_opt_init(&tc.opt, argc, argv);
+	switch (mode) {
+	case TC_OPT_USAGE:
+		tc_opt_usage();
+		break;
+	case TC_OPT_RPL:
+		tc_connect();
+		rc = tc_wal_remote();
+		break;
+	case TC_OPT_WAL_CAT:
+		rc = tc_wal_cat();
+		break;
+	case TC_OPT_WAL_PLAY:
+		tc_connect();
+		rc = tc_wal_play();
+		break;
+	case TC_OPT_CMD:
+		tc_connect();
+		tc_connect_admin();
+		rc = tc_cli_cmdv();
+		break;
+	case TC_OPT_INTERACTIVE:
+		tc_connect();
+		tc_connect_admin();
+		rc = tc_cli();
+		break;
+	}
+
+	tc_free();
+	return rc;
+}
diff --git a/client/tc/tc.h b/client/tc/tc.h
new file mode 100644
index 0000000000..49166f1389
--- /dev/null
+++ b/client/tc/tc.h
@@ -0,0 +1,37 @@
+#ifndef TC_H_INCLUDED
+#define TC_H_INCLUDED
+
+/*
+ * Copyright (C) 2012 Mail.RU
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+struct tc {
+	struct tc_opt opt;
+	struct tc_admin admin;
+	struct tnt_stream *net;
+};
+
+void tc_error(char *fmt, ...);
+
+#endif /* TC_H_INCLUDED */
diff --git a/client/tarantool/tnt_admin.c b/client/tc/tc_admin.c
similarity index 82%
rename from client/tarantool/tnt_admin.c
rename to client/tc/tc_admin.c
index b5d7b16c29..ad5fdb739a 100644
--- a/client/tarantool/tnt_admin.c
+++ b/client/tc/tc_admin.c
@@ -1,6 +1,6 @@
 
 /*
- * Copyright (C) 2011 Mail.RU
+ * Copyright (C) 2012 Mail.RU
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -41,11 +41,12 @@
 #include <errno.h>
 #include <limits.h>
 
-#include <client/tarantool/tnt_admin.h>
+#include "client/tc/tc_admin.h"
 
-static int
-tnt_admin_connect(struct tnt_admin *a)
+int tc_admin_connect(struct tc_admin *a, const char *host, int port)
 {
+	a->host = host;
+	a->port = port;
 	a->fd = socket(AF_INET, SOCK_STREAM, 0);
 	if (a->fd < 0)
 		return -1;
@@ -69,32 +70,21 @@ tnt_admin_connect(struct tnt_admin *a)
 	return -1;
 }
 
-int
-tnt_admin_init(struct tnt_admin *a, char *host, int port)
+int tc_admin_reconnect(struct tc_admin *a)
 {
-	a->host = host;
-	a->port = port;
-	return tnt_admin_connect(a);
+	tc_admin_close(a);
+	return tc_admin_connect(a, a->host, a->port);
 }
 
-int
-tnt_admin_reconnect(struct tnt_admin *a)
-{
-	tnt_admin_free(a);
-	return tnt_admin_connect(a);
-}
-
-void
-tnt_admin_free(struct tnt_admin *a)
+void tc_admin_close(struct tc_admin *a)
 {
 	if (a->fd > 0)
 		close(a->fd);
-	a->fd = -1;
+	a->fd = 0;
 }
 
 static int
-tnt_admin_send(struct tnt_admin *a, char *buf, size_t size) 
-{
+tc_admin_send(struct tc_admin *a, char *buf, size_t size) {
 	ssize_t rc, off = 0;
 	do {
 		rc = send(a->fd, buf + off, size - off, 0);
@@ -105,18 +95,16 @@ tnt_admin_send(struct tnt_admin *a, char *buf, size_t size)
 	return 0;
 }
 
-int
-tnt_admin_query(struct tnt_admin *a, char *q)
+int tc_admin_query(struct tc_admin *a, char *q)
 {
-	if (tnt_admin_send(a, q, strlen(q)) == -1)
+	if (tc_admin_send(a, q, strlen(q)) == -1)
 		return -1;
-	if (tnt_admin_send(a, "\n", 1) == -1)
+	if (tc_admin_send(a, "\n", 1) == -1)
 		return -1;
 	return 0;
 }
 
-int
-tnt_admin_reply(struct tnt_admin *a, char **r, size_t *size)
+int tc_admin_reply(struct tc_admin *a, char **r, size_t *size)
 {
 	char *buf = NULL;
 	size_t off = 0;
diff --git a/client/tarantool/tnt_admin.h b/client/tc/tc_admin.h
similarity index 75%
rename from client/tarantool/tnt_admin.h
rename to client/tc/tc_admin.h
index 43e34cb20f..e981611977 100644
--- a/client/tarantool/tnt_admin.h
+++ b/client/tc/tc_admin.h
@@ -1,8 +1,8 @@
-#ifndef TNT_ADMIN_H_INCLUDED
-#define TNT_ADMIN_H_INCLUDED
+#ifndef TC_ADMIN_H_INCLUDED
+#define TC_ADMIN_H_INCLUDED
 
 /*
- * Copyright (C) 2011 Mail.RU
+ * Copyright (C) 2012 Mail.RU
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -26,18 +26,18 @@
  * SUCH DAMAGE.
  */
 
-struct tnt_admin {
-	char *host;
+struct tc_admin {
+	const char *host;
 	int port;
 	int fd;
 };
 
-int tnt_admin_init(struct tnt_admin *a, char *host, int port);
-void tnt_admin_free(struct tnt_admin *a);
+int tc_admin_connect(struct tc_admin *a, const char *host, int port);
+int tc_admin_reconnect(struct tc_admin *a);
 
-int tnt_admin_reconnect(struct tnt_admin *a);
+void tc_admin_close(struct tc_admin *a);
 
-int tnt_admin_query(struct tnt_admin *a, char *q);
-int tnt_admin_reply(struct tnt_admin *a, char **r, size_t *size);
+int tc_admin_query(struct tc_admin *a, char *q);
+int tc_admin_reply(struct tc_admin *a, char **r, size_t *size);
 
-#endif /* TNT_ADMIN_H_INCLUDED */
+#endif /* TC_ADMIN_H_INCLUDED */
diff --git a/client/tc/tc_cli.c b/client/tc/tc_cli.c
new file mode 100644
index 0000000000..7b4a88e86d
--- /dev/null
+++ b/client/tc/tc_cli.c
@@ -0,0 +1,176 @@
+
+/*
+ * Copyright (C) 2012 Mail.RU
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <signal.h>
+#include <errno.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include <connector/c/include/tarantool/tnt.h>
+#include <connector/c/include/tarantool/tnt_net.h>
+#include <connector/c/include/tarantool/tnt_sql.h>
+
+#include "client/tc/tc_opt.h"
+#include "client/tc/tc_admin.h"
+#include "client/tc/tc.h"
+#include "client/tc/tc_query.h"
+#include "client/tc/tc_cli.h"
+
+#define TC_DEFAULT_HISTORY_FILE ".tarantool_history"
+
+extern struct tc tc;
+
+static inline int tc_cli_error(char *e) {
+	if (e) {
+		printf("%s\n", e);
+		free(e);
+	}
+	return 1;
+}
+
+static int tc_cli_reconnect(void) {
+	if (tnt_connect(tc.net) == -1) {
+		printf("reconnect: %s\n", tnt_strerror(tc.net));
+		return 1;
+	}
+	if (tc_admin_reconnect(&tc.admin) == -1) {
+		printf("reconnect: admin console connection failed\n");
+		return 1;
+	}
+	printf("reconnected\n");
+	return 0;
+}
+
+enum tc_cli_cmd_ret {
+	TC_CLI_OK,
+	TC_CLI_ERROR,
+	TC_CLI_EXIT
+};
+
+static enum tc_cli_cmd_ret tc_cli_cmd(char *cmd, size_t size)
+{
+	int reconnect = 0;
+	do {
+		if (reconnect) {
+			reconnect = tc_cli_reconnect();
+			if (reconnect)
+				return TC_CLI_ERROR;
+		}
+		char *e;
+		if (tnt_query_is(cmd, size)) {
+			if (tc_query(cmd, &e) == 0) {
+				if (tc_query_foreach(tc_query_printer, NULL, &e) == -1)
+					reconnect = tc_cli_error(e);
+			} else {
+				reconnect = tc_cli_error(e);
+			}
+			/* reconnect only for network errors */
+			if (reconnect && tnt_error(tc.net) != TNT_ESYSTEM)
+				reconnect = 0;
+		} else {
+			int reply = strcmp(cmd, "exit") &&
+				    strcmp(cmd, "quit");
+			tc_query_admin_t cb = tc_query_admin_printer;
+			if (!reply)
+				cb = NULL;
+			if (tc_query_admin(cmd, cb, &e) == -1)
+				reconnect = tc_cli_error(e);
+			if (!reply)
+				return TC_CLI_EXIT;
+		}
+	} while (reconnect);
+
+	return TC_CLI_OK;
+}
+
+int tc_cli_cmdv(void)
+{
+	int i, rc = 0;
+	for (i = 0 ; i < tc.opt.cmdc ; i++) {
+		enum tc_cli_cmd_ret ret =
+			tc_cli_cmd(tc.opt.cmdv[i], strlen(tc.opt.cmdv[i]));
+		if (ret == TC_CLI_EXIT)
+			break;
+		if (ret == TC_CLI_ERROR) {
+			rc = 1;
+			break;
+		}
+	}
+	return rc;
+}
+
+static void tc_cli_init(void) {
+	/* ignoring SIGPIPE for reconnection handling */
+	struct sigaction sa;
+	memset(&sa, 0, sizeof(sa));
+	sigemptyset(&sa.sa_mask);
+	sa.sa_handler = SIG_IGN;
+	if (sigaction(SIGPIPE, &sa, NULL) == -1)
+		tc_error("signal initialization failed\n");
+}
+
+int tc_cli(void)
+{
+	/* initializing cli */
+	tc_cli_init();
+
+	/* loading history file */
+	char *home = getenv("HOME");
+	char history[1024];
+	snprintf(history, sizeof(history), "%s/%s", home,
+		 TC_DEFAULT_HISTORY_FILE);
+	read_history(history);
+
+	/* setting prompt */
+	char prompt[128];
+	snprintf(prompt, sizeof(prompt), "%s> ", tc.opt.host);
+
+	/* interactive mode */
+	char *cmd;
+	while ((cmd = readline(prompt))) {
+		if (!cmd[0])
+			goto next;
+		enum tc_cli_cmd_ret ret =
+			tc_cli_cmd(cmd, strlen(cmd));
+		if (ret == TC_CLI_EXIT)
+			break;
+		add_history(cmd);
+next:
+		free(cmd);
+	}
+
+	/* updating history file */
+	write_history(history);
+	clear_history();
+	return 0;
+}
diff --git a/client/tc/tc_cli.h b/client/tc/tc_cli.h
new file mode 100644
index 0000000000..03eba7f7a7
--- /dev/null
+++ b/client/tc/tc_cli.h
@@ -0,0 +1,32 @@
+#ifndef TC_CLI_H_INCLUDED
+#define TC_CLI_H_INCLUDED
+
+/*
+ * Copyright (C) 2012 Mail.RU
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+int tc_cli_cmdv(void);
+int tc_cli(void);
+
+#endif /* TC_CLI_H_INCLUDED */
diff --git a/client/tc/tc_opt.c b/client/tc/tc_opt.c
new file mode 100644
index 0000000000..9e9e1903e1
--- /dev/null
+++ b/client/tc/tc_opt.c
@@ -0,0 +1,121 @@
+
+/*
+ * Copyright (C) 2012 Mail.RU
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <third_party/gopt/gopt.h>
+
+#include "client/tc/tc_opt.h"
+
+#define TC_DEFAULT_HOST "localhost"
+#define TC_DEFAULT_PORT 33013
+#define TC_DEFAULT_PORT_ADMIN 33015
+
+/* supported cli options */
+static const void *tc_options_def = gopt_start(
+	gopt_option('a', GOPT_ARG, gopt_shorts('a'),
+		    gopt_longs("host"), " <host>", "server address"),
+	gopt_option('p', GOPT_ARG, gopt_shorts('p'),
+		    gopt_longs("port"), " <port>", "server port"),
+	gopt_option('m', GOPT_ARG, gopt_shorts('m'),
+		    gopt_longs("port-admin"), " <port>", "server admin port"),
+	gopt_option('C', GOPT_ARG, gopt_shorts('C'),
+		    gopt_longs("wal-cat"), " <file>", "print xlog file content"),
+	gopt_option('P', GOPT_ARG, gopt_shorts('P'),
+		    gopt_longs("wal-play"), " <file>", "replay xlog file to the specified server"),
+	gopt_option('R', GOPT_ARG, gopt_shorts('R'),
+		    gopt_longs("rpl"), " <lsn>", "act as replica for the specified server"),
+	gopt_option('h', 0, gopt_shorts('h', '?'), gopt_longs("help"),
+		    NULL, "display this help and exit")
+);
+
+void tc_opt_usage(void)
+{
+	printf("usage: tarantool [options] [query]\n\n");
+	printf("tarantool cli.\n");
+	gopt_help(tc_options_def);
+	exit(0);
+}
+
+enum tc_opt_mode tc_opt_init(struct tc_opt *opt, int argc, char **argv)
+{
+	/* usage */
+	void *tc_options = gopt_sort(&argc, (const char**)argv, tc_options_def);
+	if (gopt(tc_options, 'h')) {
+		opt->mode = TC_OPT_USAGE;
+		goto done;
+	}
+
+	/* server host */
+	gopt_arg(tc_options, 'a', &opt->host);
+	if (opt->host == NULL)
+		opt->host = TC_DEFAULT_HOST;
+
+	/* server port */
+	const char *arg = NULL;
+	opt->port = TC_DEFAULT_PORT;
+	if (gopt_arg(tc_options, 'p', &arg))
+		opt->port = atoi(arg);
+
+	/* server admin port */
+	opt->port_admin = TC_DEFAULT_PORT_ADMIN;
+	if (gopt_arg(tc_options, 'm', &arg))
+		opt->port_admin = atoi(arg);
+
+	/* replica mode */
+	if (gopt_arg(tc_options, 'R', &arg)) {
+		opt->mode = TC_OPT_RPL;
+		opt->lsn = strtoll(arg, NULL, 10);
+		goto done;
+	}
+
+	/* wal-cat mode */
+	if (gopt_arg(tc_options, 'C', &opt->xlog)) {
+		opt->mode = TC_OPT_WAL_CAT;
+		goto done;
+	}
+
+	/* wal-play mode */
+	if (gopt_arg(tc_options, 'P', &opt->xlog)) {
+		opt->mode = TC_OPT_WAL_PLAY;
+		goto done;
+	}
+
+	/* default */
+	if (argc >= 2) {
+		opt->cmdv = argv + 1;
+		opt->cmdc = argc - 1;
+		opt->mode = TC_OPT_CMD;
+	} else {
+		opt->mode = TC_OPT_INTERACTIVE;
+	}
+done:
+	gopt_free(tc_options);
+	return opt->mode;
+}
diff --git a/client/tc/tc_opt.h b/client/tc/tc_opt.h
new file mode 100644
index 0000000000..2e7ff605c7
--- /dev/null
+++ b/client/tc/tc_opt.h
@@ -0,0 +1,54 @@
+#ifndef TC_OPT_H_INCLUDED
+#define TC_OPT_H_INCLUDED
+
+/*
+ * Copyright (C) 2012 Mail.RU
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+enum tc_opt_mode {
+	TC_OPT_USAGE,
+	TC_OPT_RPL,
+	TC_OPT_WAL_CAT,
+	TC_OPT_WAL_PLAY,
+	TC_OPT_CMD,
+	TC_OPT_INTERACTIVE
+};
+
+struct tc_opt {
+	enum tc_opt_mode mode;
+	const char *host;
+	int port;
+	int port_admin;
+	uint64_t lsn;
+	const char *xlog;
+	char **cmdv;
+	int cmdc;
+};
+
+void tc_opt_usage(void);
+
+enum tc_opt_mode
+tc_opt_init(struct tc_opt *opt, int argc, char **argv);
+
+#endif /* TC_OPT_H_INCLUDED */
diff --git a/client/tc/tc_print.c b/client/tc/tc_print.c
new file mode 100644
index 0000000000..7457e0eb23
--- /dev/null
+++ b/client/tc/tc_print.c
@@ -0,0 +1,75 @@
+
+/*
+ * Copyright (C) 2012 Mail.RU
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <connector/c/include/tarantool/tnt.h>
+
+#include "client/tc/tc_print.h"
+
+void tc_print_tuple(struct tnt_tuple *tu)
+{
+	struct tnt_iter ifl;
+	tnt_iter(&ifl, tu);
+	printf("[");
+	while (tnt_next(&ifl)) {
+		if (TNT_IFIELD_IDX(&ifl) != 0)
+			printf(", ");
+		char *data = TNT_IFIELD_DATA(&ifl);
+		uint32_t size = TNT_IFIELD_SIZE(&ifl);
+		if (!isprint(data[0]) && (size == 4 || size == 8)) {
+			if (size == 4) {
+				uint32_t i = *((uint32_t*)data);
+				printf("%"PRIu32, i);
+			} else {
+				uint64_t i = *((uint64_t*)data);
+				printf("%"PRIu64, i);
+			}
+		} else {
+			printf("'%-.*s'", size, data);
+		}
+	}
+	if (ifl.status == TNT_ITER_FAIL)
+		printf("<parsing error>");
+	printf("]\n");
+	tnt_iter_free(&ifl);
+}
+
+void tc_print_list(struct tnt_list *l)
+{
+	struct tnt_iter it;
+	tnt_iter_list(&it, l);
+	while (tnt_next(&it)) {
+		struct tnt_tuple *tu = TNT_ILIST_TUPLE(&it);
+		tc_print_tuple(tu);
+	}
+	tnt_iter_free(&it);
+}
diff --git a/client/tc/tc_print.h b/client/tc/tc_print.h
new file mode 100644
index 0000000000..cefe8a1e69
--- /dev/null
+++ b/client/tc/tc_print.h
@@ -0,0 +1,32 @@
+#ifndef TC_PRINT_H_INCLUDED
+#define TC_PRINT_H_INCLUDED
+
+/*
+ * Copyright (C) 2012 Mail.RU
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+void tc_print_tuple(struct tnt_tuple *tu);
+void tc_print_list(struct tnt_list *l);
+
+#endif /* TC_PRINT_H_INCLUDED */
diff --git a/client/tc/tc_query.c b/client/tc/tc_query.c
new file mode 100644
index 0000000000..709ca41eb1
--- /dev/null
+++ b/client/tc/tc_query.c
@@ -0,0 +1,157 @@
+
+/*
+ * Copyright (C) 2012 Mail.RU
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <include/errcode.h>
+
+#include <connector/c/include/tarantool/tnt.h>
+#include <connector/c/include/tarantool/tnt_net.h>
+#include <connector/c/include/tarantool/tnt_sql.h>
+
+#include "client/tc/tc_opt.h"
+#include "client/tc/tc_admin.h"
+#include "client/tc/tc.h"
+#include "client/tc/tc_print.h"
+#include "client/tc/tc_query.h"
+
+extern struct tc tc;
+
+char *tc_query_type(uint32_t type) {
+	switch (type) {
+	case TNT_OP_PING:   return "Ping";
+	case TNT_OP_INSERT: return "Insert";
+	case TNT_OP_DELETE: return "Delete";
+	case TNT_OP_UPDATE: return "Update";
+	case TNT_OP_SELECT: return "Select";
+	case TNT_OP_CALL:   return "Call";
+	}
+	return "Unknown";
+}
+
+static char *tc_query_op(struct tnt_reply *r) {
+	return tc_query_type(r->op);
+}
+
+int tc_query_printer(struct tnt_reply *r, void *ptr, char **e) {
+	(void)ptr;
+	(void)e;
+	printf("%s OK, %d rows affected\n", tc_query_op(r),
+	       r->count);
+	tc_print_list(TNT_REPLY_LIST(r));
+	return 0;
+}
+
+static char *tc_query_error(char *fmt, ...) {
+	char msg[256];
+	va_list args;
+	va_start(args, fmt);
+	vsnprintf(msg, sizeof(msg), fmt, args);
+	va_end(args);
+	char *ptr = strdup(msg);
+	if (ptr == NULL)
+		tc_error("memory allocation failed");
+	return ptr;
+}
+
+int tc_query_foreach(tc_query_t cb, void *cba, char **e)
+{
+	int rc = -1;
+	struct tnt_iter i;
+	tnt_iter_reply(&i, tc.net);
+	while (tnt_next(&i)) {
+		struct tnt_reply *r = TNT_IREPLY_PTR(&i);
+		if (tnt_error(tc.net) != TNT_EOK) {
+			*e = tc_query_error("%s ERROR, %s",
+					    tc_query_op(r),
+					    tnt_strerror(tc.net));
+			goto error;
+		} else if (r->code != 0) {
+			*e = tc_query_error("%s ERROR, %s (%s)",
+					    tc_query_op(r), ((r->error) ? r->error : ""),
+					    tnt_errcode_str(r->code >> 8));
+			goto error;
+		}
+		/* invoking callback if supplied */
+		if (cb) {
+			if (cb(r, cba, e) == -1)
+				goto error;
+		}
+	}
+	rc = (i.status == TNT_ITER_FAIL) ? -1 : 0;
+error:
+	tnt_iter_free(&i);
+	return rc;
+}
+
+int tc_query(char *q, char **e) {
+	int rc = tnt_query(tc.net, q, strlen(q), e);
+	if (rc == -1)
+		return -1;
+	rc = tnt_flush(tc.net);
+	if (rc < 0) {
+		char *ee = tnt_strerror(tc.net);
+		if (ee) {
+			*e = tc_query_error("%s", ee);
+		}
+		return -1;
+	}
+	return 0;
+}
+
+int tc_query_admin_printer(char *r, char **e) {
+	(void)e;
+	printf("%s", r);
+	return 0;
+}
+
+int tc_query_admin(char *q, tc_query_admin_t cb, char **e)
+{
+	if (tc_admin_query(&tc.admin, q) == -1) {
+		*e = tc_query_error("failed to send admin query");
+		return -1;
+	}
+	if (cb == NULL)
+		return 0;
+	char *reply = NULL;
+	size_t reply_size = 0;
+	if (tc_admin_reply(&tc.admin, &reply, &reply_size) == -1) {
+		*e = tc_query_error("failed to recv admin reply");
+		return -1;
+	}
+	if (cb && reply) {
+		if (cb(reply, e) == -1) {
+			free(reply);
+			return -1;
+		}
+	}
+	free(reply);
+	return 0;
+}
diff --git a/client/tc/tc_query.h b/client/tc/tc_query.h
new file mode 100644
index 0000000000..b672e1556b
--- /dev/null
+++ b/client/tc/tc_query.h
@@ -0,0 +1,41 @@
+#ifndef TC_QUERY_H_INCLUDED
+#define TC_QUERY_H_INCLUDED
+
+/*
+ * Copyright (C) 2012 Mail.RU
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+typedef int (*tc_query_t)(struct tnt_reply *r, void *ptr, char **e);
+typedef int (*tc_query_admin_t)(char *r, char **e);
+
+char *tc_query_type(uint32_t type);
+
+int tc_query_printer(struct tnt_reply *r, void *ptr, char **e);
+int tc_query_foreach(tc_query_t cb, void *cba, char **e);
+int tc_query(char *q, char **e);
+
+int tc_query_admin_printer(char *r, char **e);
+int tc_query_admin(char *q, tc_query_admin_t cb, char **e);
+
+#endif /* TC_QUERY_H_INCLUDED */
diff --git a/client/tc/tc_wal.c b/client/tc/tc_wal.c
new file mode 100644
index 0000000000..ef4e207a9a
--- /dev/null
+++ b/client/tc/tc_wal.c
@@ -0,0 +1,175 @@
+
+/*
+ * Copyright (C) 2012 Mail.RU
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdint.h>
+
+#include <connector/c/include/tarantool/tnt.h>
+#include <connector/c/include/tarantool/tnt_net.h>
+#include <connector/c/include/tarantool/tnt_sql.h>
+#include <connector/c/include/tarantool/tnt_xlog.h>
+#include <connector/c/include/tarantool/tnt_rpl.h>
+
+#include "client/tc/tc_opt.h"
+#include "client/tc/tc_admin.h"
+#include "client/tc/tc.h"
+#include "client/tc/tc_print.h"
+#include "client/tc/tc_query.h"
+#include "client/tc/tc_wal.h"
+
+extern struct tc tc;
+
+typedef int (*tc_wal_t)(struct tnt_iter *i);
+
+static int tc_wal_error(char *fmt, ...) {
+	char msg[256];
+	va_list args;
+	va_start(args, fmt);
+	vsnprintf(msg, sizeof(msg), fmt, args);
+	va_end(args);
+	printf("error: %s\n", msg);
+	return -1;
+}
+
+static int tc_wal_foreach(struct tnt_stream *s, tc_wal_t cb) {
+	struct tnt_iter i;
+	tnt_iter_request(&i, s);
+	while (tnt_next(&i)) {
+		if (cb(&i) == -1) {
+			tnt_iter_free(&i);
+			return -1;
+		}
+	}
+	int rc = 0;
+	if (i.status == TNT_ITER_FAIL)
+		rc = tc_wal_error("parsing failed");
+	tnt_iter_free(&i);
+	return rc;
+}
+
+static void tc_wal_print(struct tnt_xlog_header_v11 *hdr,
+		         struct tnt_request *r)
+{
+	printf("%s lsn: %"PRIu64", time: %f, len: %"PRIu32"\n",
+	       tc_query_type(r->h.type),
+	       hdr->lsn,
+	       hdr->tm,
+	       hdr->len);
+	switch (r->h.type) {
+	case TNT_OP_INSERT:
+		tc_print_tuple(&r->r.insert.t);
+		break;
+	case TNT_OP_DELETE:
+		tc_print_tuple(&r->r.delete.t);
+		break;
+	case TNT_OP_UPDATE:
+		tc_print_tuple(&r->r.update.t);
+		break;
+	case TNT_OP_CALL:
+		tc_print_tuple(&r->r.call.t);
+		break;
+	}
+}
+
+static int tc_wal_printer(struct tnt_iter *i) {
+	struct tnt_request *r = TNT_IREQUEST_PTR(i);
+	struct tnt_stream_xlog *s =
+		TNT_SXLOG_CAST(TNT_IREQUEST_STREAM(i));
+	tc_wal_print(&s->hdr, r);
+	return 0;
+}
+
+static int tc_wal_foreach_xlog(tc_wal_t cb) {
+	struct tnt_stream s;
+	tnt_xlog(&s);
+	if (tnt_xlog_open(&s, (char*)tc.opt.xlog) == -1) {
+		tnt_stream_free(&s);
+		return 1;
+	}
+	if (tc_wal_foreach(&s, cb) == -1) {
+		tnt_stream_free(&s);
+		return 1;
+	}
+	tnt_stream_free(&s);
+	return 0;
+}
+
+int tc_wal_cat(void)
+{
+	return tc_wal_foreach_xlog(tc_wal_printer);
+}
+
+static int tc_wal_resender(struct tnt_iter *i) {
+	struct tnt_request *r = TNT_IREQUEST_PTR(i);
+	if (tc.net->write_request(tc.net, r) == -1)
+		return tc_wal_error("failed to write request");
+	char *e = NULL;
+	if (tc_query_foreach(NULL, NULL, &e) == -1) {
+		tc_wal_error("%s", e);
+		free(e);
+		return -1;
+	}
+	return 0;
+}
+
+int tc_wal_play(void)
+{
+	return tc_wal_foreach_xlog(tc_wal_resender);
+}
+
+static int tc_wal_printer_from_rpl(struct tnt_iter *i) {
+	struct tnt_request *r = TNT_IREQUEST_PTR(i);
+	struct tnt_stream_rpl *s =
+		TNT_RPL_CAST(TNT_IREQUEST_STREAM(i));
+	tc_wal_print(&s->hdr, r);
+	return 0;
+}
+
+int tc_wal_remote(void)
+{
+	if (tc.opt.lsn == LLONG_MAX ||
+	    tc.opt.lsn == LLONG_MIN) {
+		tc_wal_error("bad lsn number");
+		return 1;
+	}
+	struct tnt_stream s;
+	tnt_rpl(&s);
+	tnt_rpl_attach(&s, tc.net);
+	int rc = 0;
+	if (tnt_rpl_open(&s, tc.opt.lsn) == -1) {
+		rc = 1;
+		goto done;
+	}
+	if (tc_wal_foreach(&s, tc_wal_printer_from_rpl) == -1)
+		rc = 1;
+done:
+	tnt_stream_free(&s);
+	return rc;
+}
diff --git a/client/tc/tc_wal.h b/client/tc/tc_wal.h
new file mode 100644
index 0000000000..c49cd59f62
--- /dev/null
+++ b/client/tc/tc_wal.h
@@ -0,0 +1,33 @@
+#ifndef TC_WAL_H_INCLUDED
+#define TC_WAL_H_INCLUDED
+
+/*
+ * Copyright (C) 2012 Mail.RU
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+int tc_wal_cat(void);
+int tc_wal_play(void);
+int tc_wal_remote(void);
+
+#endif /* TC_WAL_H_INCLUDED */
diff --git a/connector/c/include/tarantool/tnt_iter.h b/connector/c/include/tarantool/tnt_iter.h
index 79f93d159b..950a87caab 100644
--- a/connector/c/include/tarantool/tnt_iter.h
+++ b/connector/c/include/tarantool/tnt_iter.h
@@ -80,6 +80,7 @@ struct tnt_iter_request {
 
 #define TNT_IREQUEST(I) (&(I)->data.request)
 #define TNT_IREQUEST_PTR(I) &TNT_IREQUEST(I)->r
+#define TNT_IREQUEST_STREAM(I) TNT_IREQUEST(I)->s
 
 /* reply iterator */
 
diff --git a/connector/c/include/tarantool/tnt_rpl.h b/connector/c/include/tarantool/tnt_rpl.h
index 1420df5cac..f5b1713850 100644
--- a/connector/c/include/tarantool/tnt_rpl.h
+++ b/connector/c/include/tarantool/tnt_rpl.h
@@ -35,7 +35,7 @@ struct tnt_stream_rpl {
 #define TNT_RPL_CAST(S) ((struct tnt_stream_rpl*)(S)->data)
 
 struct tnt_stream *tnt_rpl(struct tnt_stream *s);
-struct tnt_stream *tnt_rpl_net(struct tnt_stream *s);
+void tnt_rpl_attach(struct tnt_stream *s, struct tnt_stream *net);
 
 int tnt_rpl_open(struct tnt_stream *s, uint64_t lsn);
 void tnt_rpl_close(struct tnt_stream *s);
diff --git a/connector/c/tntrpl/tnt_rpl.c b/connector/c/tntrpl/tnt_rpl.c
index 4ae6b8ea96..4ed9a1ccef 100644
--- a/connector/c/tntrpl/tnt_rpl.c
+++ b/connector/c/tntrpl/tnt_rpl.c
@@ -44,7 +44,7 @@ static const uint32_t tnt_rpl_version = 11;
 static void tnt_rpl_free(struct tnt_stream *s) {
 	struct tnt_stream_rpl *sr = TNT_RPL_CAST(s);
 	if (sr->net) {
-		tnt_stream_free(sr->net);
+		/* network stream should not be free'd here */
 		sr->net = NULL;
 	}
 	tnt_mem_free(s->data);
@@ -111,9 +111,7 @@ struct tnt_stream *tnt_rpl(struct tnt_stream *s)
 	s->free = tnt_rpl_free;
 	/* initializing internal data */
 	struct tnt_stream_rpl *sr = TNT_RPL_CAST(s);
-	sr->net = tnt_net(NULL);
-	if (sr->net == NULL)
-		goto error;
+	sr->net = NULL;
 	return s;
 error:
 	if (s->data) {
@@ -181,6 +179,6 @@ void tnt_rpl_close(struct tnt_stream *s) {
  *
  * s - replication stream pointer
 */
-struct tnt_stream *tnt_rpl_net(struct tnt_stream *s) {
-	return TNT_RPL_CAST(s)->net;
+void tnt_rpl_attach(struct tnt_stream *s, struct tnt_stream *net) {
+	TNT_RPL_CAST(s)->net = net;
 }
diff --git a/connector/c/tntsql/tnt_sql.c b/connector/c/tntsql/tnt_sql.c
index b8b80c279a..b6220052f0 100644
--- a/connector/c/tntsql/tnt_sql.c
+++ b/connector/c/tntsql/tnt_sql.c
@@ -55,7 +55,7 @@ tnt_sql_error(struct tnt_sql *sql, struct tnt_tk *last, char *fmt, ...)
 	int line = (last) ? last->line : sql->l->line;
 	int col = (last) ? last->col : sql->l->col;
 	char msg[256];
-	snprintf(msg, sizeof(msg), "%d:%d %s\n", line, col, msgu);
+	snprintf(msg, sizeof(msg), "%d:%d %s", line, col, msgu);
 	if (sql->error == NULL)
 		sql->error = tnt_mem_dup(msg);
 	return false;
diff --git a/test/connector_c/rpl.c b/test/connector_c/rpl.c
index e9e6492fc8..d57112f317 100644
--- a/test/connector_c/rpl.c
+++ b/test/connector_c/rpl.c
@@ -55,15 +55,15 @@ main(int argc, char * argv[])
 		printf("usage %s: host port limit\n", argv[0]);
 		return 1;
 	}
-
 	struct tnt_stream s;
 	tnt_rpl(&s);
-
-	struct tnt_stream *sn = tnt_rpl_net(&s);
-	tnt_set(sn, TNT_OPT_HOSTNAME, argv[1]);
-	tnt_set(sn, TNT_OPT_PORT, atoi(argv[2]));
-	tnt_set(sn, TNT_OPT_SEND_BUF, 0);
-	tnt_set(sn, TNT_OPT_RECV_BUF, 0);
+	struct tnt_stream sn;
+	tnt_net(&sn);
+	tnt_set(&sn, TNT_OPT_HOSTNAME, argv[1]);
+	tnt_set(&sn, TNT_OPT_PORT, atoi(argv[2]));
+	tnt_set(&sn, TNT_OPT_SEND_BUF, 0);
+	tnt_set(&sn, TNT_OPT_RECV_BUF, 0);
+	tnt_rpl_attach(&s, &sn);
 	if (tnt_rpl_open(&s, 2) == -1)
 		return 1;
 
@@ -83,5 +83,6 @@ main(int argc, char * argv[])
 
 	tnt_iter_free(&i);
 	tnt_stream_free(&s);
+	tnt_stream_free(&sn);
 	return 0;
 }
-- 
GitLab