[stgt] [PATCH] mgmt and concat_buf

Alexander Nezhinsky alexandern at mellanox.com
Tue Feb 14 18:10:40 CET 2012


This patch introduces concat_buf mechanism (based on open_memstream)
to dynamically produce response buffers for management tasks in tgtd and
request buffers for management tasks in tgtadm. This eliminates the need
in special macros like shprintf() and spares explicit checkguards against
buffer overflow.

concat_buf api is defined in util.h, as inline functions to avoid
duplication in tgtadm.c. Should consider separation into a separate file
in the future.

concat_printf() is based on FILE* produced by open_memstream(). This mechanism
takes care of the dynamic allocation and possible re-allocation of memory on
the as-needed basis. File descriptor is stored within struct concat_buf,
and is used transparently to write to a file through concat_write().

Management task buffers data segments are split into request and response
which makes the code more readable and less error-prone. 
In tgtd the response buffer is implemented using concat_buf, while in tgtadm
it's the request buffer that uses concat_buf.

Thus the changes influence mainly the SHOW commands of tgtd (those producing
some output). Upon concat_buf finalization the dynamically allociated buffer
is stored directly to the mtask response descriptor. No need to worry
about memory re-allocation anymore, it is taken care of by concat_buf
mechanism. Concatenated buffer descriptor is stored within struct concat_buf,
and is used transparently to write to a file through concat_write().

Using struct concat_buf and its associated concat_printf() variant
of snprintf(), replacing buffer_check() and _buffer_check() macros.
This spares explicit checkguards against buffer overflow, and by
explicit passing of concat_buf as the function argument allows
nested calls to functions which append more lines to the buffer.

Using concat_buf api highlights a problem pre-existing in the mgmt code:
a mix of the regular linux error codes (like ENOMEM) and enum tgtadm_errno
(like TGTADM_NOMEM). Sometimes positive values returned correspond to
tgtadm_errno, sometimes to the number of bytes written. Thus another
step to increase reliability is making the usage of "enum tgtadm_errno"
explicit throughout the code and avoiding mixup with other error types. 
This also uncovers many small glitches in the error-handling flow
(some already present in the patch, some are to be added yet).

enum tgtadm_errno is typedef'ed to tgtadm_err for brevity.
As C does not provide for strict type checking for enums and mixes them
freely with integers, some extra effort should be made to separate between
these two types of error codes.

These changes also make the code more readable by explicitly defining some
functions as acting on behalf of the management tasks by making them return
tgtadm_err.

Signed-off-by: Alexander Nezhinsky <alexandern at mellanox.com>
---
 usr/bs.c           |    6 +-
 usr/bs_aio.c       |    4 +-
 usr/bs_rdwr.c      |    2 +-
 usr/bs_sg.c        |    8 +-
 usr/bs_ssc.c       |    2 +-
 usr/bs_thread.h    |    4 +-
 usr/driver.c       |    2 +-
 usr/driver.h       |    6 +-
 usr/iscsi/conn.c   |    2 +-
 usr/iscsi/iscsid.h |   32 ++--
 usr/iscsi/isns.c   |   22 +--
 usr/iscsi/target.c |  154 ++++++----------
 usr/mgmt.c         |  498 ++++++++++++++++++++++++++++------------------------
 usr/mmc.c          |   10 +-
 usr/osd.c          |    4 +-
 usr/sbc.c          |    4 +-
 usr/scc.c          |    4 +-
 usr/smc.c          |   61 ++++---
 usr/spc.c          |   43 +++--
 usr/spc.h          |   12 +-
 usr/ssc.c          |    9 +-
 usr/target.c       |  261 +++++++++++++--------------
 usr/tgtadm.c       |  105 ++++++------
 usr/tgtadm_error.h |    4 +
 usr/tgtd.h         |   53 +++---
 usr/util.h         |   88 ++++++++--
 26 files changed, 727 insertions(+), 673 deletions(-)

diff --git a/usr/bs.c b/usr/bs.c
index e56e450..e2faf30 100644
--- a/usr/bs.c
+++ b/usr/bs.c
@@ -323,8 +323,8 @@ int bs_init(void)
 	return 1;
 }
 
-int bs_thread_open(struct bs_thread_info *info, request_func_t *rfn,
-		   int nr_threads)
+tgtadm_err bs_thread_open(struct bs_thread_info *info, request_func_t *rfn,
+			  int nr_threads)
 {
 	int i, ret;
 
@@ -356,7 +356,7 @@ int bs_thread_open(struct bs_thread_info *info, request_func_t *rfn,
 	pthread_mutex_unlock(&info->startup_lock);
 	info->nr_worker_threads = nr_threads;
 
-	return 0;
+	return TGTADM_SUCCESS;
 destroy_threads:
 	info->stop = 1;
 
diff --git a/usr/bs_aio.c b/usr/bs_aio.c
index 8d07df1..cbd91ba 100644
--- a/usr/bs_aio.c
+++ b/usr/bs_aio.c
@@ -366,7 +366,7 @@ static void bs_aio_close(struct scsi_lu *lu)
 	close(lu->fd);
 }
 
-static int bs_aio_init(struct scsi_lu *lu)
+static tgtadm_err bs_aio_init(struct scsi_lu *lu)
 {
 	struct bs_aio_info *info = BS_AIO_I(lu);
 	int i;
@@ -379,7 +379,7 @@ static int bs_aio_init(struct scsi_lu *lu)
 	for (i=0; i < ARRAY_SIZE(info->iocb_arr); i++)
 		info->piocb_arr[i] = &info->iocb_arr[i];
 
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
 static void bs_aio_exit(struct scsi_lu *lu)
diff --git a/usr/bs_rdwr.c b/usr/bs_rdwr.c
index c6f6845..276bff3 100644
--- a/usr/bs_rdwr.c
+++ b/usr/bs_rdwr.c
@@ -184,7 +184,7 @@ static void bs_rdwr_close(struct scsi_lu *lu)
 
 int nr_iothreads = 16;
 
-static int bs_rdwr_init(struct scsi_lu *lu)
+static tgtadm_err bs_rdwr_init(struct scsi_lu *lu)
 {
 	struct bs_thread_info *info = BS_THREAD_I(lu);
 
diff --git a/usr/bs_sg.c b/usr/bs_sg.c
index f0f037d..4f78def 100644
--- a/usr/bs_sg.c
+++ b/usr/bs_sg.c
@@ -400,7 +400,7 @@ static int init_sg_device(int fd)
 	return 0;
 }
 
-static int bs_sg_init(struct scsi_lu *lu)
+static tgtadm_err bs_sg_init(struct scsi_lu *lu)
 {
 	/*
 	 * Setup struct scsi_lu->cmd_perform() passthrough pointer
@@ -413,7 +413,7 @@ static int bs_sg_init(struct scsi_lu *lu)
 	 * usr/target.c:__cmd_done_passthrough().
 	 */
 	lu->cmd_done = &__cmd_done_passthrough;
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
 static int bs_sg_open(struct scsi_lu *lu, char *path, int *fd, uint64_t *size)
@@ -461,12 +461,12 @@ static void bs_sg_close(struct scsi_lu *lu)
 	close(lu->fd);
 }
 
-static int bs_sg_lu_init(struct scsi_lu *lu)
+static tgtadm_err bs_sg_lu_init(struct scsi_lu *lu)
 {
 	if (spc_lu_init(lu))
 		return TGTADM_NOMEM;
 
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
 static struct backingstore_template sg_bst = {
diff --git a/usr/bs_ssc.c b/usr/bs_ssc.c
index 4efc482..193a9ed 100644
--- a/usr/bs_ssc.c
+++ b/usr/bs_ssc.c
@@ -603,7 +603,7 @@ static void tape_rdwr_request(struct scsi_cmd *cmd)
 			cmd, cmd->scb[0], ret, length, cmd->offset);
 }
 
-static int bs_ssc_init(struct scsi_lu *lu)
+static tgtadm_err bs_ssc_init(struct scsi_lu *lu)
 {
 	struct bs_thread_info *info = BS_THREAD_I(lu);
 	return bs_thread_open(info, tape_rdwr_request, 1);
diff --git a/usr/bs_thread.h b/usr/bs_thread.h
index 50dd1c3..beb4c3f 100644
--- a/usr/bs_thread.h
+++ b/usr/bs_thread.h
@@ -23,8 +23,8 @@ static inline struct bs_thread_info *BS_THREAD_I(struct scsi_lu *lu)
 	return (struct bs_thread_info *) ((char *)lu + sizeof(*lu));
 }
 
-extern int bs_thread_open(struct bs_thread_info *info, request_func_t *rfn,
-			  int nr_threads);
+extern tgtadm_err bs_thread_open(struct bs_thread_info *info, request_func_t *rfn,
+				 int nr_threads);
 extern void bs_thread_close(struct bs_thread_info *info);
 extern int bs_thread_cmd_submit(struct scsi_cmd *cmd);
 
diff --git a/usr/driver.c b/usr/driver.c
index bf612b9..a564cf7 100644
--- a/usr/driver.c
+++ b/usr/driver.c
@@ -24,9 +24,9 @@
 #include <inttypes.h>
 
 #include "list.h"
+#include "util.h"
 #include "tgtd.h"
 #include "driver.h"
-#include "util.h"
 
 #define MAX_NR_DRIVERS	32
 
diff --git a/usr/driver.h b/usr/driver.h
index 091c3b0..53c19b6 100644
--- a/usr/driver.h
+++ b/usr/driver.h
@@ -1,6 +1,8 @@
 #ifndef __DRIVER_H__
 #define __DRIVER_H__
 
+#include "tgtadm_error.h"
+
 enum tgt_driver_state {
 	DRIVER_REGD = 0, /* just registered */
 	DRIVER_INIT, /* initialized ok */
@@ -23,8 +25,8 @@ struct tgt_driver {
 
 	int (*lu_create)(struct scsi_lu *);
 
-	int (*update)(int, int, int ,uint64_t, uint64_t, uint32_t, char *);
-	int (*show)(int, int, uint64_t, uint32_t, uint64_t, char *, int);
+	tgtadm_err (*update)(int, int, int ,uint64_t, uint64_t, uint32_t, char *);
+	tgtadm_err (*show)(int, int, uint64_t, uint32_t, uint64_t, struct concat_buf *);
 
 	uint64_t (*scsi_get_lun)(uint8_t *);
 
diff --git a/usr/iscsi/conn.c b/usr/iscsi/conn.c
index 53e719e..9015610 100644
--- a/usr/iscsi/conn.c
+++ b/usr/iscsi/conn.c
@@ -216,7 +216,7 @@ int conn_take_fd(struct iscsi_connection *conn)
 }
 
 /* called by tgtadm */
-int conn_close_admin(uint32_t tid, uint64_t sid, uint32_t cid)
+tgtadm_err conn_close_admin(uint32_t tid, uint64_t sid, uint32_t cid)
 {
 	struct iscsi_target* target = NULL;
 	struct iscsi_session *session;
diff --git a/usr/iscsi/iscsid.h b/usr/iscsi/iscsid.h
index c17ed13..bed83c0 100644
--- a/usr/iscsi/iscsid.h
+++ b/usr/iscsi/iscsid.h
@@ -28,6 +28,7 @@
 #include "param.h"
 #include "log.h"
 #include "tgtd.h"
+#include "util.h"
 
 #include "iscsi_proto.h"
 #include "iscsi_if.h"
@@ -301,7 +302,7 @@ extern int conn_get(struct iscsi_connection *conn);
 extern struct iscsi_connection * conn_find(struct iscsi_session *session, uint32_t cid);
 extern int conn_take_fd(struct iscsi_connection *conn);
 extern void conn_add_to_session(struct iscsi_connection *conn, struct iscsi_session *session);
-extern int conn_close_admin(uint32_t tid, uint64_t sid, uint32_t cid);
+extern tgtadm_err conn_close_admin(uint32_t tid, uint64_t sid, uint32_t cid);
 
 /* iscsid.c */
 extern char *text_key_find(struct iscsi_connection *conn, char *searchKey);
@@ -327,22 +328,22 @@ extern void session_get(struct iscsi_session *session);
 extern void session_put(struct iscsi_session *session);
 
 /* target.c */
-struct iscsi_target * target_find_by_name(const char *name);
-struct iscsi_target * target_find_by_id(int tid);
+extern struct iscsi_target * target_find_by_name(const char *name);
+extern struct iscsi_target * target_find_by_id(int tid);
 extern void target_list_build(struct iscsi_connection *, char *, char *);
 extern int ip_acl(int tid, struct iscsi_connection *conn);
 extern int iqn_acl(int tid, struct iscsi_connection *conn);
 extern int iscsi_target_create(struct target *);
 extern void iscsi_target_destroy(int tid, int force);
-extern int iscsi_target_show(int mode, int tid, uint64_t sid, uint32_t cid,
-			     uint64_t lun, char *buf, int rest);
-int iscsi_target_update(int mode, int op, int tid, uint64_t sid, uint64_t lun,
-			uint32_t cid, char *name);
-int target_redirected(struct iscsi_target *target,
-	struct iscsi_connection *conn, char *buf, int *reason);
+extern tgtadm_err iscsi_target_show(int mode, int tid, uint64_t sid, uint32_t cid,
+				    uint64_t lun, struct concat_buf *b);
+extern tgtadm_err iscsi_target_update(int mode, int op, int tid, uint64_t sid, uint64_t lun,
+				      uint32_t cid, char *name);
+extern int target_redirected(struct iscsi_target *target,
+			     struct iscsi_connection *conn, char *buf, int *reason);
 
 /* param.c */
-int param_index_by_name(char *name, struct iscsi_key *keys);
+extern int param_index_by_name(char *name, struct iscsi_key *keys);
 
 /* transport.c */
 extern int iscsi_init(int, char *);
@@ -351,19 +352,10 @@ extern void iscsi_exit(void);
 /* isns.c */
 extern int isns_init(void);
 extern void isns_exit(void);
-extern int isns_show(char *buf, int rest);
+extern int isns_show(struct concat_buf *b);
 extern int isns_update(char *params);
 extern int isns_scn_access(int tid, char *name);
 extern int isns_target_register(char *name);
 extern int isns_target_deregister(char *name);
 
-#define buffer_check(buf, total, len, rest)	\
-({						\
-	buf += len;				\
-	total += len;				\
-	rest -= len;				\
-	if (!rest)				\
-		break;				\
-})
-
 #endif	/* ISCSID_H */
diff --git a/usr/iscsi/isns.c b/usr/iscsi/isns.c
index 0afd251..1f1852c 100644
--- a/usr/iscsi/isns.c
+++ b/usr/iscsi/isns.c
@@ -1068,20 +1068,16 @@ void isns_exit(void)
 	free(rxbuf);
 }
 
-int isns_show(char *buf, int rest)
+int isns_show(struct concat_buf *b)
 {
-	int total = 0, max = rest;
-
-	shprintf(total, buf, rest, "iSNS:\n");
-	shprintf(total, buf, rest, _TAB1 "iSNS=%s\n",
-		 use_isns ? "On" : "Off");
-	shprintf(total, buf, rest, _TAB1 "iSNSServerIP=%s\n", isns_addr);
-	shprintf(total, buf, rest, _TAB1 "iSNSServerPort=%d\n", isns_port);
-	shprintf(total, buf, rest, _TAB1 "iSNSAccessControl=%s\n",
-		 use_isns_ac ? "On" : "Off");
-	return total;
-overflow:
-	return max;
+	concat_printf(b, "iSNS:\n");
+	concat_printf(b, _TAB1 "iSNS=%s\n", use_isns ? "On" : "Off");
+	concat_printf(b, _TAB1 "iSNSServerIP=%s\n", isns_addr);
+	concat_printf(b, _TAB1 "iSNSServerPort=%d\n", isns_port);
+	concat_printf(b, _TAB1 "iSNSAccessControl=%s\n",
+		      use_isns_ac ? "On" : "Off");
+
+	return TGTADM_SUCCESS;
 }
 
 enum {
diff --git a/usr/iscsi/target.c b/usr/iscsi/target.c
index 9546035..d266c62 100644
--- a/usr/iscsi/target.c
+++ b/usr/iscsi/target.c
@@ -36,6 +36,7 @@
 #include "tgtadm.h"
 #include "tgtd.h"
 #include "target.h"
+#include "util.h"
 
 LIST_HEAD(iscsi_targets_list);
 
@@ -495,16 +496,17 @@ static int iscsi_session_param_update(struct iscsi_target* target, int idx, char
 	return 0;
 }
 
-int iscsi_target_update(int mode, int op, int tid, uint64_t sid, uint64_t lun,
-			uint32_t cid, char *name)
+tgtadm_err iscsi_target_update(int mode, int op, int tid, uint64_t sid, uint64_t lun,
+			       uint32_t cid, char *name)
 {
-	int idx, err = TGTADM_INVALID_REQUEST;
+	tgtadm_err adm_err = TGTADM_INVALID_REQUEST;
+	int idx, err;
 	char *str;
 	struct iscsi_target* target;
 
 	switch (mode) {
 	case MODE_SYSTEM:
-		err = isns_update(name);
+		adm_err = isns_update(name);
 		break;
 	case MODE_TARGET:
 		target = target_find_by_id(tid);
@@ -518,12 +520,12 @@ int iscsi_target_update(int mode, int op, int tid, uint64_t sid, uint64_t lun,
 		if (!strncmp(name, "RedirectAddress", 15)) {
 			snprintf(target->redirect_info.addr,
 				 sizeof(target->redirect_info.addr), "%s", str);
-			err = TGTADM_SUCCESS;
+			adm_err = TGTADM_SUCCESS;
 			break;
 		} else if (!strncmp(name, "RedirectPort", 12)) {
 			snprintf(target->redirect_info.port,
 				 sizeof(target->redirect_info.port), "%s", str);
-			err = TGTADM_SUCCESS;
+			adm_err = TGTADM_SUCCESS;
 			break;
 		} else if (!strncmp(name, "RedirectReason", 14)) {
 			if (!strncmp(str, "Temporary", 9)) {
@@ -533,59 +535,48 @@ int iscsi_target_update(int mode, int op, int tid, uint64_t sid, uint64_t lun,
 			} else if (!strncmp(str, "Permanent", 9)) {
 				target->redirect_info.reason =
 					ISCSI_LOGIN_STATUS_TGT_MOVED_PERM;
-				err = TGTADM_SUCCESS;
+				adm_err = TGTADM_SUCCESS;
 			} else
 				break;
 		} else if (!strncmp(name, "RedirectCallback", 16)) {
 			target->redirect_info.callback = strdup(str);
 			if (!target->redirect_info.callback) {
-				err = TGTADM_NOMEM;
+				adm_err = TGTADM_NOMEM;
 				break;
 			}
-			err = TGTADM_SUCCESS;
+			adm_err = TGTADM_SUCCESS;
 		}
 
 		idx = param_index_by_name(name, session_keys);
 		if (idx >= 0) {
 			err = iscsi_session_param_update(target, idx, str);
-			if (err < 0)
-				err = TGTADM_INVALID_REQUEST;
+			adm_err = !err ? TGTADM_SUCCESS : TGTADM_INVALID_REQUEST;
 		}
 		break;
 	case MODE_CONNECTION:
 		if (op == OP_DELETE)
-			err = conn_close_admin(tid, sid, cid);
+			adm_err = conn_close_admin(tid, sid, cid);
 		break;
 	default:
 		break;
 	}
-	return err;
+	return adm_err;
 }
 
-static int show_iscsi_param(char *buf, struct param *param, int rest)
+static int show_iscsi_param(struct param *param, struct concat_buf *b)
 {
-	int i, len, total;
-	char value[64];
 	struct iscsi_key *keys = session_keys;
+	int i;
+	char value[64];
 
-	for (i = total = 0; session_keys[i].name; i++) {
+	for (i = 0; session_keys[i].name; i++) {
 		param_val_to_str(keys, i, param[i].val, value);
-		len = snprintf(buf, rest, "%s=%s\n", keys[i].name, value);
-		buffer_check(buf, total, len, rest);
+		concat_printf(b, "%s=%s\n", keys[i].name, value);
 	}
 
-	return total;
+	return TGTADM_SUCCESS;
 }
 
-#define __buffer_check(buf, total, len, rest)	\
-({						\
-	buf += len;				\
-	total += len;				\
-	rest -= len;				\
-	if (!rest)				\
-		return total;			\
-})
-
 static struct iscsi_session *iscsi_target_find_session(
 	struct iscsi_target *target,
 	uint64_t sid)
@@ -602,26 +593,21 @@ static struct iscsi_session *iscsi_target_find_session(
 }
 
 static int iscsi_target_show_session(struct iscsi_target *target, uint64_t sid,
-				     char *buf, int rest)
+				     struct concat_buf *b)
 {
-	int len = 0, total = 0;
 	struct iscsi_session *session;
 
 	session = iscsi_target_find_session(target, sid);
 
-	if (session) {
-		len = show_iscsi_param(buf, session->session_param, rest);
-		__buffer_check(buf, total, len, rest);
-
-	}
+	if (session)
+		show_iscsi_param(session->session_param, b);
 
-	return total;
+	return TGTADM_SUCCESS;
 }
 
 static int iscsi_target_show_stats(struct iscsi_target *target, uint64_t sid,
-				   char *buf, int rest)
+				   struct concat_buf *b)
 {
-	int len = 0, total = 0;
 	struct iscsi_session *session;
 	struct iscsi_connection *conn;
 
@@ -629,8 +615,7 @@ static int iscsi_target_show_stats(struct iscsi_target *target, uint64_t sid,
 
 	if (session) {
 		list_for_each_entry(conn, &session->conn_list, clist) {
-			len = snprintf(buf, rest,
-				       "rxdata_octets: %" PRIu64 "\n"
+			concat_printf(b, "rxdata_octets: %" PRIu64 "\n"
 				       "txdata_octets: %" PRIu64 "\n"
 				       "dataout_pdus:  %d\n"
 				       "datain_pdus:   %d\n"
@@ -642,30 +627,27 @@ static int iscsi_target_show_stats(struct iscsi_target *target, uint64_t sid,
 				       conn->stats.datain_pdus,
 				       conn->stats.scsicmd_pdus,
 				       conn->stats.scsirsp_pdus);
-			__buffer_check(buf, total, len, rest);
 		}
 	}
 
-	return total;
+	return TGTADM_SUCCESS;
 
 }
 
 static int iscsi_target_show_connections(struct iscsi_target *target,
 					 uint64_t sid,
-					 char *buf, int rest)
+					 struct concat_buf *b)
 {
 	struct iscsi_session *session;
 	struct iscsi_connection *conn;
 	char addr[128];
-	int len = 0, total = 0;
 
 	list_for_each_entry(session, &target->sessions_list, slist) {
 		list_for_each_entry(conn, &session->conn_list, clist) {
 			memset(addr, 0, sizeof(addr));
 			conn->tp->ep_show(conn, addr, sizeof(addr));
 
-
-			len = snprintf(buf, rest, "Session: %u\n"
+			concat_printf(b, "Session: %u\n"
 				_TAB1 "Connection: %u\n"
 				_TAB2 "Initiator: %s\n"
 				_TAB2 "%s\n",
@@ -673,16 +655,14 @@ static int iscsi_target_show_connections(struct iscsi_target *target,
 				conn->cid,
 				session->initiator,
 				addr);
-			__buffer_check(buf, total, len, rest);
 		}
 	}
-	return total;
+	return TGTADM_SUCCESS;
 }
 
 static int iscsi_target_show_portals(struct iscsi_target *target, uint64_t sid,
-				   char *buf, int rest)
+				     struct concat_buf *b)
 {
-	int len = 0, total = 0;
 	struct iscsi_portal *portal;
 
 	list_for_each_entry(portal, &iscsi_portals_list,
@@ -690,57 +670,42 @@ static int iscsi_target_show_portals(struct iscsi_target *target, uint64_t sid,
 		int is_ipv6;
 
 		is_ipv6 = strchr(portal->addr, ':') != NULL;
-		len = snprintf(buf, rest,
-			       "Portal: %s%s%s:%d,%d\n",
+		concat_printf(b, "Portal: %s%s%s:%d,%d\n",
 			       is_ipv6 ? "[" : "",
 			       portal->addr,
 			       is_ipv6 ? "]" : "",
 			       portal->port ? portal->port : ISCSI_LISTEN_PORT,
 			       portal->tpgt);
-		__buffer_check(buf, total, len, rest);
 	}
 
-	return total;
-
+	return TGTADM_SUCCESS;
 }
 
-static int show_redirect_info(struct iscsi_target* target, char *buf, int rest)
+static int show_redirect_info(struct iscsi_target* target, struct concat_buf *b)
 {
-	int len, total = 0;
-
-	len = snprintf(buf, rest, "RedirectAddress=%s\n", target->redirect_info.addr);
-	__buffer_check(buf, total, len, rest);
-	len = snprintf(buf, rest, "RedirectPort=%s\n", target->redirect_info.port);
-	__buffer_check(buf, total, len, rest);
-	if (target->redirect_info.reason == ISCSI_LOGIN_STATUS_TGT_MOVED_TEMP) {
-		len = snprintf(buf, rest, "RedirectReason=Temporary\n");
-		__buffer_check(buf, total, len, rest);
-	} else if (target->redirect_info.reason == ISCSI_LOGIN_STATUS_TGT_MOVED_PERM) {
-		len = snprintf(buf, rest, "RedirectReason=Permanent\n");
-		__buffer_check(buf, total, len, rest);
-	} else {
-		len = snprintf(buf, rest, "RedirectReason=Unknown\n");
-		__buffer_check(buf, total, len, rest);
-	}
+	concat_printf(b, "RedirectAddress=%s\n", target->redirect_info.addr);
+	concat_printf(b, "RedirectPort=%s\n", target->redirect_info.port);
+	if (target->redirect_info.reason == ISCSI_LOGIN_STATUS_TGT_MOVED_TEMP)
+		concat_printf(b, "RedirectReason=Temporary\n");
+	else if (target->redirect_info.reason == ISCSI_LOGIN_STATUS_TGT_MOVED_PERM)
+		concat_printf(b, "RedirectReason=Permanent\n");
+	else
+		concat_printf(b, "RedirectReason=Unknown\n");
 
-	return total;
+	return TGTADM_SUCCESS;
 }
 
-static int show_redirect_callback(struct iscsi_target *target, char *buf, int rest)
+static int show_redirect_callback(struct iscsi_target *target, struct concat_buf *b)
 {
-	int len, total = 0;
-
-	len = snprintf(buf, rest, "RedirectCallback=%s\n", target->redirect_info.callback);
-	__buffer_check(buf, total, len, rest);
+	concat_printf(b, "RedirectCallback=%s\n", target->redirect_info.callback);
 
-	return total;
+	return TGTADM_SUCCESS;
 }
 
-int iscsi_target_show(int mode, int tid, uint64_t sid, uint32_t cid, uint64_t lun,
-		      char *buf, int rest)
+tgtadm_err iscsi_target_show(int mode, int tid, uint64_t sid, uint32_t cid, uint64_t lun,
+			     struct concat_buf *b)
 {
 	struct iscsi_target* target = NULL;
-	int len, total = 0;
 
 	if (mode != MODE_SYSTEM && mode != MODE_PORTAL) {
 	    target = target_find_by_id(tid);
@@ -750,36 +715,31 @@ int iscsi_target_show(int mode, int tid, uint64_t sid, uint32_t cid, uint64_t lu
 
 	switch (mode) {
 	case MODE_SYSTEM:
-		total = isns_show(buf, rest);
+		isns_show(b);
 		break;
 	case MODE_TARGET:
 		if (target->redirect_info.callback)
-			len = show_redirect_callback(target, buf, rest);
+			show_redirect_callback(target, b);
 		else if (strlen(target->redirect_info.addr))
-			len = show_redirect_info(target, buf, rest);
+			show_redirect_info(target, b);
 		else
-			len = show_iscsi_param(buf, target->session_param, rest);
-		total += len;
+			show_iscsi_param(target->session_param, b);
 		break;
 	case MODE_SESSION:
-		len = iscsi_target_show_session(target, sid, buf, rest);
-		total += len;
+		iscsi_target_show_session(target, sid, b);
 		break;
 	case MODE_PORTAL:
-		len = iscsi_target_show_portals(target, sid, buf, rest);
-		total += len;
+		iscsi_target_show_portals(target, sid, b);
 		break;
 	case MODE_CONNECTION:
-		len = iscsi_target_show_connections(target, sid, buf, rest);
-		total += len;
+		iscsi_target_show_connections(target, sid, b);
 		break;
 	case MODE_STATS:
-		len = iscsi_target_show_stats(target, sid, buf, rest);
-		total += len;
+		iscsi_target_show_stats(target, sid, b);
 		break;
 	default:
 		break;
 	}
 
-	return total;
+	return TGTADM_SUCCESS;
 }
diff --git a/usr/mgmt.c b/usr/mgmt.c
index 26a360d..8b62d13 100644
--- a/usr/mgmt.c
+++ b/usr/mgmt.c
@@ -44,208 +44,229 @@
 enum mgmt_task_state {
 	MTASK_STATE_HDR_RECV,
 	MTASK_STATE_PDU_RECV,
-	MTASK_STATE_RSP_SEND,
+	MTASK_STATE_HDR_SEND,
+	MTASK_STATE_PDU_SEND,
 };
 
 struct mgmt_task {
 	enum mgmt_task_state mtask_state;
 	int retry;
 	int done;
-	char *buf;
-	int bsize;
 	struct tgtadm_req req;
+	char *req_buf;
+	int req_bsize;
 	struct tgtadm_rsp rsp;
+	struct concat_buf rsp_concat;
 /* 	struct tgt_work work; */
 };
 
+#define MAX_MGT_BUFSIZE	(8*1024) /* limit incoming mgmt request data size */
+
 static int ipc_fd;
 char mgmt_path[256];
 
-static void set_show_results(struct tgtadm_rsp *rsp, int *err)
+static struct mgmt_task *mtask_alloc(void);
+static void mtask_free(struct mgmt_task *mtask);
+
+static tgtadm_err errno2tgtadm(int err)
 {
-	if (*err < 0)
-		rsp->err = -*err;
-	else {
-		rsp->err = 0;
-		rsp->len = *err + sizeof(*rsp);
-		*err = 0;
+	if (err >= 0)
+		return TGTADM_SUCCESS;
+	else if (err == -ENOMEM)
+		return TGTADM_NOMEM;
+	else
+		return TGTADM_UNKNOWN_ERR;
+}
+
+static void set_mtask_result(struct mgmt_task *mtask, tgtadm_err adm_err)
+{
+	if (adm_err == TGTADM_SUCCESS && mtask->rsp_concat.err)
+		adm_err = errno2tgtadm(mtask->rsp_concat.err);
+
+	mtask->rsp.len = sizeof(mtask->rsp);
+	if (adm_err == TGTADM_SUCCESS) {
+		mtask->rsp.len += mtask->rsp_concat.size;
+		mtask->rsp.err = 0;
 	}
+	else
+		mtask->rsp.err = (uint32_t)adm_err;
 }
 
-static int target_mgmt(int lld_no, struct mgmt_task *mtask)
+static tgtadm_err target_mgmt(int lld_no, struct mgmt_task *mtask)
 {
 	struct tgtadm_req *req = &mtask->req;
-	struct tgtadm_rsp *rsp = &mtask->rsp;
-	int err = TGTADM_INVALID_REQUEST;
+	tgtadm_err adm_err = TGTADM_INVALID_REQUEST;
 
 	switch (req->op) {
 	case OP_NEW:
-		err = tgt_target_create(lld_no, req->tid, mtask->buf);
+		adm_err = tgt_target_create(lld_no, req->tid, mtask->req_buf);
 		break;
 	case OP_DELETE:
-		err = tgt_target_destroy(lld_no, req->tid, req->force);
+		adm_err = tgt_target_destroy(lld_no, req->tid, req->force);
 		break;
 	case OP_BIND:
 		/* FIXME */
 		if (req->len == sizeof(*req))
-			err = tgt_bind_host_to_target(req->tid, req->host_no);
+			adm_err = tgt_bind_host_to_target(req->tid, req->host_no);
 		else {
 			char *p;
 
-			p = strstr(mtask->buf, "initiator-address=");
-			if (p)
-				err = acl_add(req->tid, p + strlen("initiator-address="));
+			p = strstr(mtask->req_buf, "initiator-address=");
+			if (p) {
+				p += strlen("initiator-address=");
+				adm_err = acl_add(req->tid, p);
+				if (adm_err != TGTADM_SUCCESS) {
+					eprintf("Failed to bind by address: %s\n", p);
+					break;
+				}
+			}
 
-			p = strstr(mtask->buf, "initiator-name=");
-			if (p)
-				err = iqn_acl_add(req->tid, p + strlen("initiator-name="));
+			p = strstr(mtask->req_buf, "initiator-name=");
+			if (p) {
+				p += strlen("initiator-name=");
+				adm_err = iqn_acl_add(req->tid, p);
+				if (adm_err != TGTADM_SUCCESS) {
+					eprintf("Failed to bind by name: %s\n", p);
+					break;
+				}
+			}
 		}
 		break;
 	case OP_UNBIND:
 		if (req->len == sizeof(*req))
-			err = tgt_unbind_host_to_target(req->tid, req->host_no);
+			adm_err = tgt_unbind_host_to_target(req->tid, req->host_no);
 		else {
 			char *p;
 
-			p = strstr(mtask->buf, "initiator-address=");
+			p = strstr(mtask->req_buf, "initiator-address=");
 			if (p) {
-				err = acl_del(req->tid, p + strlen("initiator-address="));
+				p += strlen("initiator-address=");
+				adm_err = acl_del(req->tid, p);
+				if (adm_err != TGTADM_SUCCESS) {
+					eprintf("Failed to unbind by address: %s\n", p);
+					break;
+				}
 			}
 
-			p = strstr(mtask->buf, "initiator-name=");
+			p = strstr(mtask->req_buf, "initiator-name=");
 			if (p) {
-				err = iqn_acl_del(req->tid, p + strlen("initiator-name="));
+				p += strlen("initiator-name=");
+				adm_err = iqn_acl_del(req->tid, p + strlen("initiator-name="));
+				if (adm_err != TGTADM_SUCCESS) {
+					eprintf("Failed to unbind by name: %s\n", p);
+					break;
+				}
 			}
 		}
 		break;
 	case OP_UPDATE:
 	{
 		char *p;
-		err = TGTADM_UNSUPPORTED_OPERATION;
+		adm_err = TGTADM_UNSUPPORTED_OPERATION;
 
-		p = strchr(mtask->buf, '=');
+		p = strchr(mtask->req_buf, '=');
 		if (!p)
 			break;
 		*p++ = '\0';
 
-		if (!strcmp(mtask->buf, "state"))
-			err = tgt_set_target_state(req->tid, p);
-		else if (tgt_drivers[lld_no]->update)
-			err = tgt_drivers[lld_no]->update(req->mode, req->op, req->tid,
+		if (!strcmp(mtask->req_buf, "state")) {
+			adm_err = tgt_set_target_state(req->tid, p);
+		} else if (tgt_drivers[lld_no]->update)
+			adm_err = tgt_drivers[lld_no]->update(req->mode, req->op, req->tid,
 							  req->sid, req->lun,
-							  req->cid, mtask->buf);
+							  req->cid, mtask->req_buf);
 		break;
 	}
 	case OP_SHOW:
-		if (req->tid < 0) {
-			retry:
-			err = tgt_target_show_all(mtask->buf, mtask->bsize);
-			if (err == mtask->bsize) {
-				char *p;
-				mtask->bsize <<= 1;
-				p = realloc(mtask->buf, mtask->bsize);
-				if (p) {
-					mtask->buf = p;
-					goto retry;
-				} else {
-					eprintf("out of memory\n");
-					err = TGTADM_NOMEM;
-				}
-			}
-		} else if (tgt_drivers[lld_no]->show)
-			err = tgt_drivers[lld_no]->show(req->mode,
+	{
+		concat_buf_init(&mtask->rsp_concat);
+		if (req->tid < 0)
+			adm_err = tgt_target_show_all(&mtask->rsp_concat);
+		else if (tgt_drivers[lld_no]->show)
+			adm_err = tgt_drivers[lld_no]->show(req->mode,
 							req->tid,
 							req->sid,
 							req->cid, req->lun,
-							mtask->buf, mtask->bsize);
+							&mtask->rsp_concat);
+		concat_buf_finish(&mtask->rsp_concat);
 		break;
+	}
 	default:
 		break;
 	}
 
-	if (req->op == OP_SHOW)
-		set_show_results(rsp, &err);
-	else {
-		rsp->err = err;
-		rsp->len = sizeof(*rsp);
-	}
-	return err;
+	set_mtask_result(mtask, adm_err);
+	return adm_err;
 }
 
-static int portal_mgmt(int lld_no, struct mgmt_task *mtask,
-		       struct tgtadm_req *req,
-		       struct tgtadm_rsp *rsp)
+static tgtadm_err portal_mgmt(int lld_no, struct mgmt_task *mtask)
 {
-	int err = TGTADM_INVALID_REQUEST;
+	struct tgtadm_req *req = &mtask->req;
+	tgtadm_err adm_err = TGTADM_INVALID_REQUEST;
 
 	switch (req->op) {
 	case OP_SHOW:
 		if (tgt_drivers[lld_no]->show) {
-			err = tgt_drivers[lld_no]->show(req->mode,
+			concat_buf_init(&mtask->rsp_concat);
+			adm_err = tgt_drivers[lld_no]->show(req->mode,
 							req->tid, req->sid,
 							req->cid, req->lun,
-							mtask->buf,
-							mtask->bsize);
-
-			set_show_results(rsp, &err);
-			return err;
+							&mtask->rsp_concat);
+			concat_buf_finish(&mtask->rsp_concat);
 		}
 		break;
 	case OP_NEW:
-		err = tgt_portal_create(lld_no, mtask->buf);
+		adm_err = tgt_portal_create(lld_no, mtask->req_buf);
 		break;
 	case OP_DELETE:
-		err = tgt_portal_destroy(lld_no, mtask->buf);
+		adm_err = tgt_portal_destroy(lld_no, mtask->req_buf);
 		break;
 	default:
 		break;
 	}
 
-	rsp->err = err;
-	rsp->len = sizeof(*rsp);
-
-	return err;
+	set_mtask_result(mtask, adm_err);
+	return adm_err;
 }
 
-static int device_mgmt(int lld_no, struct tgtadm_req *req, char *params,
-		       struct tgtadm_rsp *rsp, int *rlen)
+static tgtadm_err device_mgmt(int lld_no, struct mgmt_task *mtask)
 {
-	int err = TGTADM_UNSUPPORTED_OPERATION;
+	struct tgtadm_req *req = &mtask->req;
+	char *params = mtask->req_buf;
+	tgtadm_err adm_err = TGTADM_UNSUPPORTED_OPERATION;
 
 	switch (req->op) {
 	case OP_NEW:
-		err = tgt_device_create(req->tid, req->device_type, req->lun,
-					params, 1);
+		eprintf("sz:%d params:%s\n",mtask->req_bsize,params);
+		adm_err = tgt_device_create(req->tid, req->device_type, req->lun,
+					    params, 1);
 		break;
 	case OP_DELETE:
-		err = tgt_device_destroy(req->tid, req->lun, 0);
+		adm_err = tgt_device_destroy(req->tid, req->lun, 0);
 		break;
 	case OP_UPDATE:
-		err = tgt_device_update(req->tid, req->lun, params);
+		adm_err = tgt_device_update(req->tid, req->lun, params);
 		break;
 	default:
 		break;
 	}
 
-	rsp->err = err;
-	rsp->len = sizeof(*rsp);
-
-	return err;
+	set_mtask_result(mtask, adm_err);
+	return adm_err;
 }
 
-static int account_mgmt(int lld_no,  struct mgmt_task *mtask)
+static tgtadm_err account_mgmt(int lld_no,  struct mgmt_task *mtask)
 {
 	struct tgtadm_req *req = &mtask->req;
-	struct tgtadm_rsp *rsp = &mtask->rsp;
-	int err = TGTADM_UNSUPPORTED_OPERATION;
 	char *user, *password;
+	tgtadm_err adm_err = TGTADM_UNSUPPORTED_OPERATION;
 
 	switch (req->op) {
 	case OP_NEW:
 	case OP_DELETE:
 	case OP_BIND:
 	case OP_UNBIND:
-		user = strstr(mtask->buf, "user=");
+		user = strstr(mtask->req_buf, "user=");
 		if (!user)
 			goto out;
 		user += 5;
@@ -258,130 +279,109 @@ static int account_mgmt(int lld_no,  struct mgmt_task *mtask)
 			*password++ = '\0';
 			password += strlen("password=");
 
-			err = account_add(user, password);
+			adm_err = account_add(user, password);
 		} else {
 			if (req->op == OP_DELETE) {
-				err = account_del(user);
+				adm_err = account_del(user);
 			} else
-				err = account_ctl(req->tid, req->ac_dir,
-						  user, req->op == OP_BIND);
+				adm_err = account_ctl(req->tid, req->ac_dir,
+						      user, req->op == OP_BIND);
 		}
 		break;
 	case OP_SHOW:
-	retry:
-		err = account_show(mtask->buf, mtask->bsize);
-		if (err == mtask->bsize) {
-			char *p;
-			mtask->bsize <<= 1;
-			p = realloc(mtask->buf, mtask->bsize);
-			if (p) {
-				mtask->buf = p;
-				goto retry;
-			} else
-				err = TGTADM_NOMEM;
-		}
+		concat_buf_init(&mtask->rsp_concat);
+		adm_err = account_show(&mtask->rsp_concat);
+		concat_buf_finish(&mtask->rsp_concat);
 		break;
 	default:
 		break;
 	}
 out:
-	if (req->op == OP_SHOW)
-		set_show_results(rsp, &err);
-	else {
-		rsp->err = err;
-		rsp->len = sizeof(*rsp);
-	}
-	return err;
+	set_mtask_result(mtask, adm_err);
+	return adm_err;
 }
 
-static int sys_mgmt(int lld_no, struct mgmt_task *mtask)
+static tgtadm_err sys_mgmt(int lld_no, struct mgmt_task *mtask)
 {
 	struct tgtadm_req *req = &mtask->req;
-	struct tgtadm_rsp *rsp = &mtask->rsp;
-	int err = TGTADM_INVALID_REQUEST, len = mtask->bsize;
+	tgtadm_err adm_err = TGTADM_INVALID_REQUEST;
 
 	switch (req->op) {
 	case OP_UPDATE:
-		if (!strncmp(mtask->buf, "debug=", 6)) {
-			if (!strncmp(mtask->buf+6, "on", 2)) {
+		if (!strncmp(mtask->req_buf, "debug=", 6)) {
+			if (!strncmp(mtask->req_buf+6, "on", 2)) {
 				is_debug = 1;
-				err = 0;
-			} else if (!strncmp(mtask->buf+6, "off", 3)) {
+				adm_err = TGTADM_SUCCESS;
+			} else if (!strncmp(mtask->req_buf+6, "off", 3)) {
 				is_debug = 0;
-				err = 0;
+				adm_err = TGTADM_SUCCESS;
 			}
-			if (!err)
+			if (adm_err == TGTADM_SUCCESS)
 				eprintf("set debug to: %d\n", is_debug);
 		} else if (tgt_drivers[lld_no]->update)
-			err = tgt_drivers[lld_no]->update(req->mode, req->op,
+			adm_err = tgt_drivers[lld_no]->update(req->mode, req->op,
 							  req->tid,
 							  req->sid, req->lun,
-							  req->cid, mtask->buf);
+							  req->cid, mtask->req_buf);
 
-		rsp->err = err;
-		rsp->len = sizeof(*rsp);
 		break;
 	case OP_SHOW:
-		err = system_show(req->mode, mtask->buf, len);
-		if (err >= 0 && tgt_drivers[lld_no]->show) {
-			err += tgt_drivers[lld_no]->show(req->mode,
-							 req->tid, req->sid,
-							 req->cid, req->lun,
-							 mtask->buf + err, len - err);
-		}
-		set_show_results(rsp, &err);
+		concat_buf_init(&mtask->rsp_concat);
+		adm_err = system_show(req->mode, &mtask->rsp_concat);
+		if (tgt_drivers[lld_no]->show)
+			adm_err = tgt_drivers[lld_no]->show(req->mode,
+							req->tid, req->sid,
+							req->cid, req->lun,
+							&mtask->rsp_concat);
+		concat_buf_finish(&mtask->rsp_concat);
 		break;
 	case OP_DELETE:
 		if (is_system_inactive())
-			err = 0;
-
-		rsp->err = err;
-		rsp->len = sizeof(*rsp);
+			adm_err = TGTADM_SUCCESS;
 		break;
 	default:
 		break;
 	}
 
-	return err;
+	set_mtask_result(mtask, adm_err);
+	return adm_err;
 }
 
-static int connection_mgmt(int lld_no, struct mgmt_task *mtask,
-			   struct tgtadm_req *req,
-			   struct tgtadm_rsp *rsp)
+static tgtadm_err connection_mgmt(int lld_no, struct mgmt_task *mtask)
 {
-	int err = TGTADM_INVALID_REQUEST;
+	struct tgtadm_req *req = &mtask->req;
+	tgtadm_err adm_err = TGTADM_INVALID_REQUEST;
 
 	switch (req->op) {
 	case OP_SHOW:
 		if (tgt_drivers[lld_no]->show) {
-			err = tgt_drivers[lld_no]->show(req->mode,
-							req->tid, req->sid,
-							req->cid, req->lun,
-							mtask->buf,
-							mtask->bsize);
-			set_show_results(rsp, &err);
-			return err;
+			concat_buf_init(&mtask->rsp_concat);
+			adm_err = tgt_drivers[lld_no]->show(req->mode,
+							    req->tid, req->sid,
+							    req->cid, req->lun,
+							    &mtask->rsp_concat);
+			concat_buf_finish(&mtask->rsp_concat);
+			break;
 		}
 		break;
 	default:
 		if (tgt_drivers[lld_no]->update)
-			err = tgt_drivers[lld_no]->update(req->mode, req->op,
-							  req->tid,
-							  req->sid, req->lun,
-							  req->cid, mtask->buf);
-		rsp->err = err;
-		rsp->len = sizeof(*rsp);
+			adm_err = tgt_drivers[lld_no]->update(req->mode, req->op,
+							      req->tid,
+							      req->sid, req->lun,
+							      req->cid, mtask->req_buf);
 		break;
 	}
 
-	return err;
+	set_mtask_result(mtask, adm_err);
+	return adm_err;
 }
 
-static int tgt_mgmt(struct mgmt_task *mtask)
+static tgtadm_err mtask_execute(struct mgmt_task *mtask)
 {
 	struct tgtadm_req *req = &mtask->req;
-	struct tgtadm_rsp *rsp = &mtask->rsp;
-	int lld_no, err = TGTADM_INVALID_REQUEST, len = mtask->bsize;
+	int lld_no;
+	tgtadm_err adm_err = TGTADM_INVALID_REQUEST;
 
 	if (!strlen(req->lld))
 		lld_no = 0;
@@ -393,51 +393,48 @@ static int tgt_mgmt(struct mgmt_task *mtask)
 			else
 				eprintf("driver %s is in state: %s\n",
 					req->lld, driver_state_name(tgt_drivers[lld_no]));
-			rsp->err = TGTADM_NO_DRIVER;
-			rsp->len = sizeof(*rsp);
+			set_mtask_result(mtask, TGTADM_NO_DRIVER);
 			return 0;
 		}
 	}
 
 	dprintf("%d %d %d %d %d %" PRIx64 " %" PRIx64 " %s %d\n",
 		req->len, lld_no, req->mode, req->op,
-		req->tid, req->sid, req->lun, mtask->buf, getpid());
+		req->tid, req->sid, req->lun, mtask->req_buf, getpid());
 
 	switch (req->mode) {
 	case MODE_SYSTEM:
-		err = sys_mgmt(lld_no, mtask);
+		adm_err = sys_mgmt(lld_no, mtask);
 		break;
 	case MODE_TARGET:
-		err = target_mgmt(lld_no, mtask);
+		adm_err = target_mgmt(lld_no, mtask);
 		break;
 	case MODE_PORTAL:
-		err = portal_mgmt(lld_no, mtask, req, rsp);
+		adm_err = portal_mgmt(lld_no, mtask);
 		break;
 	case MODE_DEVICE:
-		err = device_mgmt(lld_no, req, mtask->buf, rsp, &len);
+		adm_err = device_mgmt(lld_no, mtask);
 		break;
 	case MODE_ACCOUNT:
-		err = account_mgmt(lld_no, mtask);
+		adm_err = account_mgmt(lld_no, mtask);
 		break;
 	case MODE_CONNECTION:
-		err = connection_mgmt(lld_no, mtask, req, rsp);
+		adm_err = connection_mgmt(lld_no, mtask);
 		break;
 	default:
 		if (req->op == OP_SHOW && tgt_drivers[lld_no]->show) {
-			err = tgt_drivers[lld_no]->show(req->mode,
-							req->tid, req->sid,
-							req->cid, req->lun,
-							mtask->buf, len);
-
-			set_show_results(rsp, &err);
-		} else {
-			rsp->err = err;
-			rsp->len = sizeof(*rsp);
+			concat_buf_init(&mtask->rsp_concat);
+			adm_err = tgt_drivers[lld_no]->show(req->mode,
+							    req->tid, req->sid,
+							    req->cid, req->lun,
+							    &mtask->rsp_concat);
+			concat_buf_finish(&mtask->rsp_concat);
 		}
 		break;
 	}
 
-	return err;
+	set_mtask_result(mtask, adm_err);
+	return adm_err;
 }
 
 static int ipc_accept(int accept_fd)
@@ -472,7 +469,49 @@ static int ipc_perm(int fd)
 	return 0;
 }
 
-static void mtask_handler(int fd, int events, void *data)
+static struct mgmt_task *mtask_alloc(void)
+{
+	struct mgmt_task *mtask;
+
+	mtask = zalloc(sizeof(*mtask));
+	if (!mtask) {
+		eprintf("can't allocate mtask\n");
+		return NULL;
+	}
+	mtask->mtask_state = MTASK_STATE_HDR_RECV;
+
+	dprintf("mtask:%p\n", mtask);
+	return mtask;
+}
+
+static void mtask_free(struct mgmt_task *mtask)
+{
+	dprintf("mtask:%p\n", mtask);
+
+	if (mtask->req_buf)
+		free(mtask->req_buf);
+	concat_buf_release(&mtask->rsp_concat);
+	free(mtask);
+}
+
+static int mtask_received(struct mgmt_task *mtask, int fd)
+{
+	tgtadm_err adm_err;
+	int err;
+
+	adm_err = mtask_execute(mtask);
+	if (adm_err != TGTADM_SUCCESS)
+		eprintf("mgmt task processing failed, err: %d\n", adm_err);
+
+	mtask->mtask_state = MTASK_STATE_HDR_SEND;
+	mtask->done = 0;
+	err = tgt_event_modify(fd, EPOLLOUT);
+	if (err)
+		eprintf("failed to modify mgmt task event out\n");
+	return err;
+}
+
+static void mtask_recv_send_handler(int fd, int events, void *data)
 {
 	int err, len;
 	char *p;
@@ -487,24 +526,28 @@ static void mtask_handler(int fd, int events, void *data)
 		if (err > 0) {
 			mtask->done += err;
 			if (mtask->done == sizeof(*req)) {
-				if (req->len == sizeof(*req)) {
-					tgt_mgmt(mtask);
-					mtask->mtask_state =
-						MTASK_STATE_RSP_SEND;
-					if (tgt_event_modify(fd, EPOLLOUT))
-						eprintf("failed to modify\n");
-
-					mtask->done = 0;
+				mtask->req_bsize = req->len - sizeof(*req);
+				if (!mtask->req_bsize) {
+					err = mtask_received(mtask, fd);
+					if (err)
+						goto out;
 				} else {
 					/* the pdu exists */
-					mtask->done = 0;
-					mtask->mtask_state =
-						MTASK_STATE_PDU_RECV;
-
-					if (mtask->bsize < req->len) {
-						eprintf("FIXME: %d\n", req->len);
+					if (mtask->req_bsize > MAX_MGT_BUFSIZE) {
+						eprintf("mtask buffer len: %d too large\n",
+							mtask->req_bsize);
+						mtask->req_bsize = 0;
+						goto out;
+					}
+					mtask->req_buf = zalloc(mtask->req_bsize);
+					if (!mtask->req_buf) {
+						eprintf("can't allocate mtask buffer len: %d\n",
+							mtask->req_bsize);
+						mtask->req_bsize = 0;
 						goto out;
 					}
+					mtask->mtask_state = MTASK_STATE_PDU_RECV;
+					mtask->done = 0;
 				}
 			}
 		} else
@@ -513,47 +556,53 @@ static void mtask_handler(int fd, int events, void *data)
 
 		break;
 	case MTASK_STATE_PDU_RECV:
-		len = req->len - (sizeof(*req) + mtask->done);
-		err = read(fd, mtask->buf + mtask->done, len);
+		len = mtask->req_bsize - mtask->done;
+		err = read(fd, mtask->req_buf + mtask->done, len);
 		if (err > 0) {
 			mtask->done += err;
-			if (mtask->done == req->len - (sizeof(*req))) {
-				tgt_mgmt(mtask);
-				mtask->mtask_state = MTASK_STATE_RSP_SEND;
-				if (tgt_event_modify(fd, EPOLLOUT))
-					eprintf("failed to modify\n");
-
-				mtask->done = 0;
+			if (mtask->done == mtask->req_bsize) {
+				err = mtask_received(mtask, fd);
+				if (err)
+					goto out;
 			}
 		} else
 			if (errno != EAGAIN)
 				goto out;
 
 		break;
-	case MTASK_STATE_RSP_SEND:
-		if (mtask->done < sizeof(*rsp)) {
-			p = (char *)rsp + mtask->done;
-			len = sizeof(*rsp) - mtask->done;
-		} else {
-			p = mtask->buf + (mtask->done - sizeof(*rsp));
-			len = rsp->len - mtask->done;
-		}
+	case MTASK_STATE_HDR_SEND:
+		p = (char *)rsp + mtask->done;
+		len = sizeof(*rsp) - mtask->done;
 
 		err = write(fd, p, len);
 		if (err > 0) {
 			mtask->done += err;
+			if (mtask->done == sizeof(*rsp)) {
+				if (rsp->len == sizeof(*rsp))
+					goto out;
+				mtask->done = 0;
+				mtask->mtask_state = MTASK_STATE_PDU_SEND;
+			}
+		} else
+			if (errno != EAGAIN)
+				goto out;
 
+		break;
+	case MTASK_STATE_PDU_SEND:
+		err = concat_write(&mtask->rsp_concat, fd, mtask->done);
+		if (err > 0) {
+			mtask->done += err;
 			if (mtask->done == rsp->len) {
 				if (req->mode == MODE_SYSTEM &&
 				    req->op == OP_DELETE &&
 				    !rsp->err)
 					system_active = 0;
-
 				goto out;
 			}
 		} else
 			if (errno != EAGAIN)
 				goto out;
+
 		break;
 	default:
 		eprintf("unknown state %d\n", mtask->mtask_state);
@@ -562,13 +611,10 @@ static void mtask_handler(int fd, int events, void *data)
 	return;
 out:
 	tgt_event_del(fd);
-	free(mtask->buf);
-	free(mtask);
 	close(fd);
+	mtask_free(mtask);
 }
 
-#define BUFSIZE 1024
-
 static void mgmt_event_handler(int accept_fd, int events, void *data)
 {
 	int fd, err;
@@ -592,26 +638,14 @@ static void mgmt_event_handler(int accept_fd, int events, void *data)
 		goto out;
 	}
 
-	mtask = zalloc(sizeof(*mtask));
-	if (!mtask) {
-		eprintf("can't allocate mtask\n");
-		goto out;
-	}
-
-	mtask->buf = zalloc(BUFSIZE);
-	if (!mtask->buf) {
-		eprintf("can't allocate mtask buffer\n");
-		free(mtask);
+	mtask = mtask_alloc();
+	if (!mtask)
 		goto out;
-	}
 
-	mtask->bsize = BUFSIZE;
-	mtask->mtask_state = MTASK_STATE_HDR_RECV;
-	err = tgt_event_add(fd, EPOLLIN, mtask_handler, mtask);
+	err = tgt_event_add(fd, EPOLLIN, mtask_recv_send_handler, mtask);
 	if (err) {
 		eprintf("failed to add a socket to epoll %d\n", fd);
-		free(mtask->buf);
-		free(mtask);
+		mtask_free(mtask);
 		goto out;
 	}
 
diff --git a/usr/mmc.c b/usr/mmc.c
index c50c3c0..4d81426 100644
--- a/usr/mmc.c
+++ b/usr/mmc.c
@@ -2193,14 +2193,14 @@ static int mmc_mode_sense(int host_no, struct scsi_cmd *cmd)
 	return spc_mode_sense(host_no, cmd);
 }
 
-static int mmc_lu_init(struct scsi_lu *lu)
+static tgtadm_err mmc_lu_init(struct scsi_lu *lu)
 {
 	struct backingstore_template *bst;
 	struct mmc_info *mmc;
 
 	mmc = zalloc(sizeof(struct mmc_info));
 	if (!mmc)
-		return -ENOMEM;
+		return TGTADM_NOMEM;
 
 	lu->xxc_p = mmc;
 
@@ -2265,10 +2265,10 @@ static int mmc_lu_init(struct scsi_lu *lu)
 			  "0x10:0:0x96:0:0:0:0:0:0:0:0:0:0:0:0:0:0:"
 			  "0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:"
 			  "0:0");
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
-static int mmc_lu_online(struct scsi_lu *lu)
+static tgtadm_err mmc_lu_online(struct scsi_lu *lu)
 {
 	struct mmc_info *mmc = dtype_priv(lu);
 	struct stat st;
@@ -2291,7 +2291,7 @@ static int mmc_lu_online(struct scsi_lu *lu)
 			mmc->current_profile = PROFILE_DVD_ROM;
 	}
 
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
 static struct device_type_template mmc_template = {
diff --git a/usr/osd.c b/usr/osd.c
index 2ab29ac..984dcad 100644
--- a/usr/osd.c
+++ b/usr/osd.c
@@ -38,7 +38,7 @@ static int osd_varlen_cdb(int host_no, struct scsi_cmd *cmd)
  * XXX: missing support for b0 and b1, in page 0 and in inquiry code.
  * Figure out how to make spc_inquiry handle extra mode pages.
  */
-static int osd_lu_init(struct scsi_lu *lu)
+static tgtadm_err osd_lu_init(struct scsi_lu *lu)
 {
 	if (spc_lu_init(lu))
 		return TGTADM_NOMEM;
@@ -49,7 +49,7 @@ static int osd_lu_init(struct scsi_lu *lu)
 	lu->attrs.version_desc[1] = 0x0960; /* iSCSI */
 	lu->attrs.version_desc[2] = 0x0300; /* SPC-3 */
 
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
 static struct device_type_template osd_template = {
diff --git a/usr/sbc.c b/usr/sbc.c
index cf37b3a..0c3681e 100644
--- a/usr/sbc.c
+++ b/usr/sbc.c
@@ -420,7 +420,7 @@ sense:
 	return SAM_STAT_CHECK_CONDITION;
 }
 
-static int sbc_lu_init(struct scsi_lu *lu)
+static tgtadm_err sbc_lu_init(struct scsi_lu *lu)
 {
 	uint64_t size;
 	uint8_t *data;
@@ -463,7 +463,7 @@ static int sbc_lu_init(struct scsi_lu *lu)
 	/* Informational Exceptions Control page */
 	add_mode_page(lu, "0x1c:0:10:8:0:0:0:0:0:0:0:0:0");
 
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
 static struct device_type_template sbc_template = {
diff --git a/usr/scc.c b/usr/scc.c
index 27bc07b..ec67ca8 100644
--- a/usr/scc.c
+++ b/usr/scc.c
@@ -36,7 +36,7 @@
 #include "tgtadm_error.h"
 #include "spc.h"
 
-static int scc_lu_init(struct scsi_lu *lu)
+static tgtadm_err scc_lu_init(struct scsi_lu *lu)
 {
 	if (spc_lu_init(lu))
 		return TGTADM_NOMEM;
@@ -49,7 +49,7 @@ static int scc_lu_init(struct scsi_lu *lu)
 
 	lu->dev_type_template.lu_online(lu);
 
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
 static struct device_type_template scc_template = {
diff --git a/usr/smc.c b/usr/smc.c
index 197afd9..f327f14 100644
--- a/usr/smc.c
+++ b/usr/smc.c
@@ -490,15 +490,16 @@ sense:
 	return SAM_STAT_CHECK_CONDITION;
 }
 
-static int smc_lu_init(struct scsi_lu *lu)
+static tgtadm_err smc_lu_init(struct scsi_lu *lu)
 {
 	struct smc_info *smc;
+	tgtadm_err adm_err;
 
 	smc = zalloc(sizeof(struct smc_info));
 	if (smc)
 		dtype_priv(lu) = smc;
 	else
-		return -ENOMEM;
+		return TGTADM_NOMEM;
 
 	if (spc_lu_init(lu))
 		return TGTADM_NOMEM;
@@ -526,10 +527,10 @@ static int smc_lu_init(struct scsi_lu *lu)
 
 	INIT_LIST_HEAD(&smc->slots);
 
-	lu->dev_type_template.lu_online(lu); /* Library will now report as Online */
+	adm_err = lu->dev_type_template.lu_online(lu); /* Library will now report as Online */
 	lu->attrs.removable = 1; /* Default to removable media */
 
-	return 0;
+	return adm_err;
 }
 
 static void smc_lu_exit(struct scsi_lu *lu)
@@ -606,10 +607,10 @@ static void slot_dump(struct list_head *head)
 		}
 }
 
-static int add_slt(struct scsi_lu *lu, struct tmp_param *tmp)
+static tgtadm_err add_slt(struct scsi_lu *lu, struct tmp_param *tmp)
 {
 	struct smc_info *smc = dtype_priv(lu);
-	int ret = TGTADM_INVALID_REQUEST;
+	tgtadm_err adm_err = TGTADM_INVALID_REQUEST;
 	struct mode_pg *pg;
 	struct slot *s;
 	uint16_t *element;
@@ -655,22 +656,22 @@ static int add_slt(struct scsi_lu *lu, struct tmp_param *tmp)
 		if (s)	// Opps... Found a slot at this address..
 			goto dont_do_slots;
 
-		ret = TGTADM_SUCCESS;
+		adm_err = TGTADM_SUCCESS;
 		for(i = tmp->start_addr; i < (tmp->start_addr + tmp->quantity); i++)
 			if (slot_insert(&smc->slots, tmp->element_type, i))
-				ret = TGTADM_INVALID_REQUEST;
+				adm_err = TGTADM_INVALID_REQUEST;
 	}
 
 dont_do_slots:
-	return ret;
+	return adm_err;
 }
 
-static int config_slot(struct scsi_lu *lu, struct tmp_param *tmp)
+static tgtadm_err config_slot(struct scsi_lu *lu, struct tmp_param *tmp)
 {
 	struct smc_info *smc = dtype_priv(lu);
 	struct mode_pg *m = NULL;
 	struct slot *s = NULL;
-	int ret = TGTADM_INVALID_REQUEST;
+	int adm_err = TGTADM_INVALID_REQUEST;
 
 	switch(tmp->element_type) {
 	case ELEMENT_MEDIUM_TRANSPORT:
@@ -678,7 +679,7 @@ static int config_slot(struct scsi_lu *lu, struct tmp_param *tmp)
 		m = lu->mode_pgs[0x1e];
 		if (m) {
 			m->mode_data[0] = (tmp->sides > 1) ? 1 : 0;
-			ret = TGTADM_SUCCESS;
+			adm_err = TGTADM_SUCCESS;
 		}
 		break;
 	case ELEMENT_STORAGE:
@@ -688,12 +689,12 @@ static int config_slot(struct scsi_lu *lu, struct tmp_param *tmp)
 			break;	// Slot not found..
 		if (tmp->clear_slot) {
 			set_slot_empty(s);
-			ret = TGTADM_SUCCESS;
+			adm_err = TGTADM_SUCCESS;
 			break;
 		}
 		strncpy(s->barcode, tmp->barcode, sizeof(s->barcode));
 		set_slot_full(s, 0, NULL);
-		ret = TGTADM_SUCCESS;
+		adm_err = TGTADM_SUCCESS;
 		break;
 	case ELEMENT_DATA_TRANSFER:
 		if (!tmp->tid)
@@ -704,23 +705,23 @@ static int config_slot(struct scsi_lu *lu, struct tmp_param *tmp)
 		s->asc  = NO_ADDITIONAL_SENSE;
 		s->drive_tid = tmp->tid;
 		s->drive_lun = tmp->lun;
-		ret = TGTADM_SUCCESS;
+		adm_err = TGTADM_SUCCESS;
 		break;
 	}
-	return ret;
+	return adm_err;
 }
 
 #define ADD	1
 #define CONFIGURE 2
 
-static int __smc_lu_config(struct scsi_lu *lu, char *params)
+static tgtadm_err __smc_lu_config(struct scsi_lu *lu, char *params)
 {
 	struct smc_info *smc = dtype_priv(lu);
-	int err = TGTADM_SUCCESS;
+	tgtadm_err adm_err = TGTADM_SUCCESS;
 	char *p;
 	char buf[256];
 
-	while ((p = strsep(&params, ",")) != NULL) {
+	while (adm_err == TGTADM_SUCCESS && (p = strsep(&params, ",")) != NULL) {
 		substring_t args[MAX_OPT_ARGS];
 		int token;
 		if (!*p)
@@ -774,33 +775,35 @@ static int __smc_lu_config(struct scsi_lu *lu, char *params)
 			match_strncpy(buf, &args[0], sizeof(buf));
 			smc->media_home = strdup(buf);
 			if (!smc->media_home)
-				return TGTADM_NOMEM;
+				adm_err = TGTADM_NOMEM;
 			break;
 		default:
-			err = TGTADM_UNKNOWN_PARAM;
+			adm_err = TGTADM_UNKNOWN_PARAM;
+			break;
 		}
 	}
-	return err;
+	return adm_err;
 }
 
-static int smc_lu_config(struct scsi_lu *lu, char *params)
+static tgtadm_err smc_lu_config(struct scsi_lu *lu, char *params)
 {
-	int ret = TGTADM_SUCCESS;
+	tgtadm_err adm_err = TGTADM_SUCCESS;
 
 	memset(&sv_param, 0, sizeof(struct tmp_param));
 
-	if ((ret = lu_config(lu, params, __smc_lu_config)))
-		return TGTADM_UNKNOWN_PARAM;
+	adm_err = lu_config(lu, params, __smc_lu_config);
+	if (adm_err != TGTADM_SUCCESS)
+		return adm_err;
 
 	switch(sv_param.operation) {
 		case ADD:
-			ret = add_slt(lu, &sv_param);
+			adm_err = add_slt(lu, &sv_param);
 			break;
 		case CONFIGURE:
-			ret = config_slot(lu, &sv_param);
+			adm_err = config_slot(lu, &sv_param);
 			break;
 	}
-	return ret;
+	return adm_err;
 }
 
 struct device_type_template smc_template = {
diff --git a/usr/spc.c b/usr/spc.c
index 7ff7834..44cd193 100644
--- a/usr/spc.c
+++ b/usr/spc.c
@@ -1552,9 +1552,10 @@ static struct mode_pg *alloc_mode_pg(uint8_t pcode, uint8_t subpcode,
 	return pg;
 }
 
-int add_mode_page(struct scsi_lu *lu, char *p)
+tgtadm_err add_mode_page(struct scsi_lu *lu, char *p)
 {
-	int i, tmp, ret = TGTADM_SUCCESS;
+	tgtadm_err adm_err = TGTADM_SUCCESS;
+	int i, tmp;
 	uint8_t pcode, subpcode, *data;
 	uint16_t size;
 	struct mode_pg *pg;
@@ -1578,7 +1579,7 @@ int add_mode_page(struct scsi_lu *lu, char *p)
 
 			pg = alloc_mode_pg(pcode, subpcode, size);
 			if (!pg) {
-				ret = TGTADM_NOMEM;
+				adm_err = TGTADM_NOMEM;
 				goto exit;
 			}
 
@@ -1603,12 +1604,12 @@ int add_mode_page(struct scsi_lu *lu, char *p)
 	}
 
 	if (i != size + 3) {
-		ret = TGTADM_INVALID_REQUEST;
+		adm_err = TGTADM_INVALID_REQUEST;
 		eprintf("Mode Page %d (0x%02x): param_count %d != "
 			"MODE PAGE size : %d\n", pcode, subpcode, i, size + 3);
 	}
 exit:
-	return ret;
+	return adm_err;
 }
 
 void dump_cdb(struct scsi_cmd *cmd)
@@ -1684,24 +1685,24 @@ static match_table_t tokens = {
 	{Opt_err, NULL},
 };
 
-int spc_lu_online(struct scsi_lu *lu)
+tgtadm_err spc_lu_online(struct scsi_lu *lu)
 {
 	lu->attrs.online = 1;
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
-int spc_lu_offline(struct scsi_lu *lu)
+tgtadm_err spc_lu_offline(struct scsi_lu *lu)
 {
 	if (lu_prevent_removal(lu))
 		return TGTADM_PREVENT_REMOVAL;
 
 	lu->attrs.online = 0;
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
-int lu_config(struct scsi_lu *lu, char *params, match_fn_t *fn)
+tgtadm_err lu_config(struct scsi_lu *lu, char *params, match_fn_t *fn)
 {
-	int err = TGTADM_SUCCESS;
+	tgtadm_err adm_err = TGTADM_SUCCESS;
 	char *p;
 	char buf[1024];
 	struct lu_phy_attr *attrs;
@@ -1774,26 +1775,26 @@ int lu_config(struct scsi_lu *lu, char *params, match_fn_t *fn)
 		case Opt_online:
 			match_strncpy(buf, &args[0], sizeof(buf));
 			if (atoi(buf))
-				err |= lu->dev_type_template.lu_online(lu);
+				adm_err = lu->dev_type_template.lu_online(lu);
 			else
-				err |= lu->dev_type_template.lu_offline(lu);
+				adm_err = lu->dev_type_template.lu_offline(lu);
 			break;
 		case Opt_mode_page:
 			match_strncpy(buf, &args[0], sizeof(buf));
-			err = add_mode_page(lu, buf);
+			adm_err = add_mode_page(lu, buf);
 			break;
 		case Opt_path:
 			match_strncpy(buf, &args[0], sizeof(buf));
-			err = tgt_device_path_update(lu->tgt, lu, buf);
+			adm_err = tgt_device_path_update(lu->tgt, lu, buf);
 			break;
 		default:
-			err |= fn ? fn(lu, p) : TGTADM_INVALID_REQUEST;
+			adm_err = fn ? fn(lu, p) : TGTADM_INVALID_REQUEST;
 		}
 	}
-	return err;
+	return adm_err;
 }
 
-int spc_lu_config(struct scsi_lu *lu, char *params)
+tgtadm_err spc_lu_config(struct scsi_lu *lu, char *params)
 {
 	return lu_config(lu, params, NULL);
 }
@@ -1819,18 +1820,24 @@ int spc_lu_init(struct scsi_lu *lu)
 	/* VPD page 0x80 */
 	pg = PCODE_OFFSET(0x80);
 	lu_vpd[pg] = alloc_vpd(SCSI_SN_LEN);
+	if (!lu_vpd[pg])
+		return -ENOMEM;
 	lu_vpd[pg]->vpd_update = update_vpd_80;
 	lu_vpd[pg]->vpd_update(lu, lu->attrs.scsi_sn);
 
 	/* VPD page 0x83 */
 	pg = PCODE_OFFSET(0x83);
 	lu_vpd[pg] = alloc_vpd(SCSI_ID_LEN + 4);
+	if (!lu_vpd[pg])
+		return -ENOMEM;
 	lu_vpd[pg]->vpd_update = update_vpd_83;
 	lu_vpd[pg]->vpd_update(lu, lu->attrs.scsi_id);
 
 	/* VPD page 0xb0 */
 	pg = PCODE_OFFSET(0xb0);
 	lu_vpd[pg] = alloc_vpd(BLOCK_LIMITS_VPD_LEN);
+	if (!lu_vpd[pg])
+		return -ENOMEM;
 
 	lu->attrs.removable = 0;
 	lu->attrs.readonly = 0;
diff --git a/usr/spc.h b/usr/spc.h
index 3c486b8..5b88542 100644
--- a/usr/spc.h
+++ b/usr/spc.h
@@ -15,20 +15,20 @@ extern int spc_illegal_op(int host_no, struct scsi_cmd *cmd);
 extern int spc_lu_init(struct scsi_lu *lu);
 extern int spc_send_diagnostics(int host_no, struct scsi_cmd *cmd);
 
-typedef int (match_fn_t)(struct scsi_lu *lu, char *params);
-extern int lu_config(struct scsi_lu *lu, char *params, match_fn_t *);
-extern int spc_lu_config(struct scsi_lu *lu, char *params);
+typedef tgtadm_err (match_fn_t)(struct scsi_lu *lu, char *params);
+extern tgtadm_err lu_config(struct scsi_lu *lu, char *params, match_fn_t *);
+extern tgtadm_err spc_lu_config(struct scsi_lu *lu, char *params);
 extern void spc_lu_exit(struct scsi_lu *lu);
 extern void dump_cdb(struct scsi_cmd *cmd);
 extern int spc_mode_sense(int host_no, struct scsi_cmd *cmd);
-extern int add_mode_page(struct scsi_lu *lu, char *params);
+extern tgtadm_err add_mode_page(struct scsi_lu *lu, char *params);
 extern int set_mode_page_changeable_mask(struct scsi_lu *lu, uint8_t pcode,
 					 uint8_t *mask);
 extern int spc_mode_select(int host_no, struct scsi_cmd *cmd,
 			   int (*update)(struct scsi_cmd *, uint8_t *, int *));
 extern struct vpd *alloc_vpd(uint16_t size);
-extern int spc_lu_online(struct scsi_lu *lu);
-extern int spc_lu_offline(struct scsi_lu *lu);
+extern tgtadm_err spc_lu_online(struct scsi_lu *lu);
+extern tgtadm_err spc_lu_offline(struct scsi_lu *lu);
 
 extern int spc_access_check(struct scsi_cmd *cmd);
 #endif
diff --git a/usr/ssc.c b/usr/ssc.c
index 92d6b2c..59d2e15 100644
--- a/usr/ssc.c
+++ b/usr/ssc.c
@@ -155,16 +155,15 @@ static int ssc_read_block_limit(int host_no, struct scsi_cmd *cmd)
 	return SAM_STAT_GOOD;
 }
 
-static int ssc_lu_init(struct scsi_lu *lu)
+static tgtadm_err ssc_lu_init(struct scsi_lu *lu)
 {
 	uint8_t *data;
 	struct ssc_info *ssc;
 
 	ssc = zalloc(sizeof(struct ssc_info));
-	if (ssc)
-		dtype_priv(lu) = ssc;
-	else
+	if (!ssc)
 		return TGTADM_NOMEM;
+	dtype_priv(lu) = ssc;
 
 	if (spc_lu_init(lu))
 		return TGTADM_NOMEM;
@@ -204,7 +203,7 @@ static int ssc_lu_init(struct scsi_lu *lu)
 	/* Medium Configuration - Mandatory - SSC3 8.3.7 */
 	add_mode_page(lu, "0x1d:0:0x1e:1:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0"
 				":0:0:0:0:0:0:0:0:0:0:0:0:0");
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
 static struct device_type_template ssc_template = {
diff --git a/usr/target.c b/usr/target.c
index 42a3a09..0b6be13 100644
--- a/usr/target.c
+++ b/usr/target.c
@@ -386,10 +386,11 @@ static void tgt_cmd_queue_init(struct tgt_cmd_queue *q)
 	INIT_LIST_HEAD(&q->queue);
 }
 
-int tgt_device_path_update(struct target *target, struct scsi_lu *lu, char *path)
+tgtadm_err tgt_device_path_update(struct target *target, struct scsi_lu *lu, char *path)
 {
-	int err, dev_fd;
+	int dev_fd;
 	uint64_t size;
+	int err;
 
 	if (lu->path) {
 		int ret;
@@ -423,9 +424,7 @@ int tgt_device_path_update(struct target *target, struct scsi_lu *lu, char *path
 	lu->addr = 0;
 	lu->size = size;
 	lu->path = path;
-	lu->dev_type_template.lu_online(lu);
-
-	return 0;
+	return lu->dev_type_template.lu_online(lu);
 }
 
 static struct scsi_lu *
@@ -462,12 +461,13 @@ static match_table_t device_tokens = {
 
 static void __cmd_done(struct target *, struct scsi_cmd *);
 
-int tgt_device_create(int tid, int dev_type, uint64_t lun, char *params,
+tgtadm_err tgt_device_create(int tid, int dev_type, uint64_t lun, char *params,
 		      int backing)
 {
 	char *p, *path = NULL, *bstype = NULL;
 	char *bsoflags = NULL, *blocksize = NULL;
-	int ret = 0, lu_bsoflags = 0;
+	int lu_bsoflags = 0;
+	tgtadm_err adm_err = TGTADM_SUCCESS;
 	struct target *target;
 	struct scsi_lu *lu, *pos;
 	struct device_type_template *t;
@@ -504,14 +504,14 @@ int tgt_device_create(int tid, int dev_type, uint64_t lun, char *params,
 
 	target = target_lookup(tid);
 	if (!target) {
-		ret = TGTADM_NO_TARGET;
+		adm_err = TGTADM_NO_TARGET;
 		goto out;
 	}
 
 	lu = device_lookup(target, lun);
 	if (lu) {
 		eprintf("device %" PRIu64 " already exists\n", lun);
-		ret = TGTADM_LUN_EXIST;
+		adm_err = TGTADM_LUN_EXIST;
 		goto out;
 	}
 
@@ -521,7 +521,7 @@ int tgt_device_create(int tid, int dev_type, uint64_t lun, char *params,
 			bst = get_backingstore_template(bstype);
 			if (!bst) {
 				eprintf("failed to find bstype, %s\n", bstype);
-				ret = TGTADM_INVALID_REQUEST;
+				adm_err = TGTADM_INVALID_REQUEST;
 				goto out;
 			}
 		}
@@ -531,14 +531,14 @@ int tgt_device_create(int tid, int dev_type, uint64_t lun, char *params,
 	if ((!strncmp(bst->bs_name, "bsg", 3) ||
 	     !strncmp(bst->bs_name, "sg", 2)) &&
 	    dev_type != TYPE_PT) {
-		ret = TGTADM_INVALID_REQUEST;
+		adm_err = TGTADM_INVALID_REQUEST;
 		goto out;
 	}
 
 	if (bsoflags) {
 		lu_bsoflags = str_to_open_flags(bsoflags);
 		if (lu_bsoflags == -1) {
-			ret = TGTADM_INVALID_REQUEST;
+			adm_err = TGTADM_INVALID_REQUEST;
 			goto out;
 		}
 	}
@@ -548,20 +548,20 @@ int tgt_device_create(int tid, int dev_type, uint64_t lun, char *params,
 			open_flags_to_str(strflags,
 			(bst->bs_oflags_supported & lu_bsoflags) ^ lu_bsoflags),
 			bst->bs_name);
-		ret = TGTADM_INVALID_REQUEST;
+		adm_err = TGTADM_INVALID_REQUEST;
 		goto out;
 	}
 
 	t = device_type_lookup(dev_type);
 	if (!t) {
 		eprintf("Unknown device type %d\n", dev_type);
-		ret = TGTADM_INVALID_REQUEST;
+		adm_err = TGTADM_INVALID_REQUEST;
 		goto out;
 	}
 
 	lu = zalloc(sizeof(*lu) + bst->bs_datasize);
 	if (!lu) {
-		ret = TGTADM_NOMEM;
+		adm_err = TGTADM_NOMEM;
 		goto out;
 	}
 
@@ -599,14 +599,14 @@ int tgt_device_create(int tid, int dev_type, uint64_t lun, char *params,
 	}
 
 	if (lu->dev_type_template.lu_init) {
-		ret = lu->dev_type_template.lu_init(lu);
-		if (ret)
+		adm_err = lu->dev_type_template.lu_init(lu);
+		if (adm_err)
 			goto fail_lu_init;
 	}
 
 	if (lu->bst->bs_init) {
-		ret = lu->bst->bs_init(lu);
-		if (ret)
+		adm_err = lu->bst->bs_init(lu);
+		if (adm_err)
 			goto fail_lu_init;
 	}
 
@@ -616,8 +616,8 @@ int tgt_device_create(int tid, int dev_type, uint64_t lun, char *params,
 	}
 
 	if (backing && path) {
-		ret = tgt_device_path_update(target, lu, path);
-		if (ret)
+		adm_err = tgt_device_path_update(target, lu, path);
+		if (adm_err)
 			goto fail_bs_init;
 	}
 
@@ -645,8 +645,10 @@ int tgt_device_create(int tid, int dev_type, uint64_t lun, char *params,
 		list_for_each_entry(itn_lu, &itn->it_nexus_lu_info_list,
 				    lu_info_siblings) {
 
-			ret = ua_sense_add(itn_lu,
-					   ASC_REPORTED_LUNS_DATA_HAS_CHANGED);
+			if (ua_sense_add(itn_lu, ASC_REPORTED_LUNS_DATA_HAS_CHANGED)) {
+				adm_err = TGTADM_NOMEM;
+				goto fail_bs_init;
+			}
 		}
 	}
 
@@ -663,7 +665,8 @@ out:
 		free(path);
 	if (bsoflags)
 		free(bsoflags);
-	return ret;
+	return adm_err;
+
 fail_bs_init:
 	if (lu->bst->bs_exit)
 		lu->bst->bs_exit(lu);
@@ -672,7 +675,7 @@ fail_lu_init:
 	goto out;
 }
 
-int tgt_device_destroy(int tid, uint64_t lun, int force)
+tgtadm_err tgt_device_destroy(int tid, uint64_t lun, int force)
 {
 	struct target *target;
 	struct scsi_lu *lu;
@@ -735,7 +738,7 @@ int tgt_device_destroy(int tid, uint64_t lun, int force)
 		}
 	}
 
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
 struct lu_phy_attr *lu_attr_lookup(int tid, uint64_t lun)
@@ -782,11 +785,11 @@ int dtd_check_removable(int tid, uint64_t lun)
  *
  * load/unload media from the DATA TRANSFER DEVICE.
  */
-int dtd_load_unload(int tid, uint64_t lun, int load, char *file)
+tgtadm_err dtd_load_unload(int tid, uint64_t lun, int load, char *file)
 {
 	struct target *target;
 	struct scsi_lu *lu;
-	int err = TGTADM_SUCCESS;
+	tgtadm_err adm_err = TGTADM_SUCCESS;
 
 	lu = __device_lookup(tid, lun, &target);
 	if (!lu)
@@ -807,9 +810,9 @@ int dtd_load_unload(int tid, uint64_t lun, int load, char *file)
 	lu->size = 0;
 	lu->fd = 0;
 
-	err = lu->dev_type_template.lu_offline(lu);
-	if (err)
-		return err;
+	adm_err = lu->dev_type_template.lu_offline(lu);
+	if (adm_err)
+		return adm_err;
 
 	if (load) {
 		lu->path = strdup(file);
@@ -821,9 +824,9 @@ int dtd_load_unload(int tid, uint64_t lun, int load, char *file)
 			lu->path = NULL;
 			return TGTADM_UNSUPPORTED_OPERATION;
 		}
-		lu->dev_type_template.lu_online(lu);
+		adm_err = lu->dev_type_template.lu_online(lu);
 	}
-	return err;
+	return adm_err;
 }
 
 int device_reserve(struct scsi_cmd *cmd)
@@ -876,9 +879,9 @@ int device_reserved(struct scsi_cmd *cmd)
 	return -EBUSY;
 }
 
-int tgt_device_update(int tid, uint64_t dev_id, char *params)
+tgtadm_err tgt_device_update(int tid, uint64_t dev_id, char *params)
 {
-	int err = TGTADM_INVALID_REQUEST;
+	tgtadm_err adm_err = TGTADM_INVALID_REQUEST;
 	struct target *target;
 	struct scsi_lu *lu;
 
@@ -893,9 +896,9 @@ int tgt_device_update(int tid, uint64_t dev_id, char *params)
 	}
 
 	if (lu->dev_type_template.lu_config)
-		err = lu->dev_type_template.lu_config(lu, params);
+		adm_err = lu->dev_type_template.lu_config(lu, params);
 
-	return err;
+	return adm_err;
 }
 
 static int cmd_enabled(struct tgt_cmd_queue *q, struct scsi_cmd *cmd)
@@ -1349,7 +1352,7 @@ found:
 	return 0;
 }
 
-int account_add(char *user, char *password)
+tgtadm_err account_add(char *user, char *password)
 {
 	int aid;
 	struct account_entry *ac;
@@ -1385,7 +1388,7 @@ free_account:
 	return TGTADM_NOMEM;
 }
 
-static int __inaccount_bind(struct target *target, int aid)
+static tgtadm_err __inaccount_bind(struct target *target, int aid)
 {
 	int i;
 
@@ -1421,14 +1424,15 @@ static int __inaccount_bind(struct target *target, int aid)
 		target->account.max_inaccount = new_max;
 	}
 
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
-int account_ctl(int tid, int type, char *user, int bind)
+tgtadm_err account_ctl(int tid, int type, char *user, int bind)
 {
+	tgtadm_err adm_err = 0;
 	struct target *target;
 	struct account_entry *ac;
-	int i, err = 0;
+	int i;
 
 	if (tid == GLOBAL_TID)
 		target = &global_target;
@@ -1443,10 +1447,10 @@ int account_ctl(int tid, int type, char *user, int bind)
 
 	if (bind) {
 		if (type == ACCOUNT_TYPE_INCOMING)
-			err = __inaccount_bind(target, ac->aid);
+			adm_err = __inaccount_bind(target, ac->aid);
 		else {
 			if (target->account.out_aid)
-				err = TGTADM_OUTACCOUNT_EXIST;
+				adm_err = TGTADM_OUTACCOUNT_EXIST;
 			else
 				target->account.out_aid = ac->aid;
 		}
@@ -1460,17 +1464,17 @@ int account_ctl(int tid, int type, char *user, int bind)
 				}
 
 			if (i == target->account.max_inaccount)
-				err = TGTADM_NO_USER;
+				adm_err = TGTADM_NO_USER;
 		} else
 			if (target->account.out_aid == ac->aid)
 				target->account.out_aid = 0;
 			else
-				err = TGTADM_NO_USER;
+				adm_err = TGTADM_NO_USER;
 
-	return err;
+	return adm_err;
 }
 
-int account_del(char *user)
+tgtadm_err account_del(char *user)
 {
 	struct account_entry *ac;
 	struct target *target;
@@ -1491,7 +1495,7 @@ int account_del(char *user)
 	free(ac->user);
 	free(ac->password);
 	free(ac);
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
 int account_available(int tid, int dir)
@@ -1511,7 +1515,7 @@ int account_available(int tid, int dir)
 		return target->account.out_aid;
 }
 
-int acl_add(int tid, char *address)
+tgtadm_err acl_add(int tid, char *address)
 {
 	char *str;
 	struct target *target;
@@ -1538,14 +1542,14 @@ int acl_add(int tid, char *address)
 	acl->address = str;
 	list_add_tail(&acl->aclent_list, &target->acl_list);
 
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
-int acl_del(int tid, char *address)
+tgtadm_err acl_del(int tid, char *address)
 {
 	struct target *target;
 	struct acl_entry *acl, *tmp;
-	int err = TGTADM_ACL_NOEXIST;
+	tgtadm_err adm_err = TGTADM_ACL_NOEXIST;
 
 	target = target_lookup(tid);
 	if (!target)
@@ -1556,11 +1560,11 @@ int acl_del(int tid, char *address)
 			list_del(&acl->aclent_list);
 			free(acl->address);
 			free(acl);
-			err = 0;
+			adm_err = TGTADM_SUCCESS;
 			break;
 		}
 	}
-	return err;
+	return adm_err;
 }
 
 char *acl_get(int tid, int idx)
@@ -1663,7 +1667,7 @@ struct bound_host {
 	struct list_head bhost_siblings;
 };
 
-int tgt_bind_host_to_target(int tid, int host_no)
+tgtadm_err tgt_bind_host_to_target(int tid, int host_no)
 {
 	struct target *target;
 	struct bound_host *bhost;
@@ -1671,19 +1675,19 @@ int tgt_bind_host_to_target(int tid, int host_no)
 	target = target_lookup(tid);
 	if (!target) {
 		eprintf("can't find a target %d\n", tid);
-		return -ENOENT;
+		return TGTADM_NO_TARGET;
 	}
 
 	list_for_each_entry(bhost, &bound_host_list, bhost_siblings) {
 		if (bhost->host_no == host_no) {
 			eprintf("already bound %d\n", host_no);
-			return -EINVAL;
+			return TGTADM_BINDING_EXIST;
 		}
 	}
 
 	bhost = malloc(sizeof(*bhost));
 	if (!bhost)
-		return -ENOMEM;
+		return TGTADM_NOMEM;
 
 	bhost->host_no = host_no;
 	bhost->target = target;
@@ -1692,10 +1696,10 @@ int tgt_bind_host_to_target(int tid, int host_no)
 
 	dprintf("bound the scsi host %d to the target %d\n", host_no, tid);
 
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
-int tgt_unbind_host_to_target(int tid, int host_no)
+tgtadm_err tgt_unbind_host_to_target(int tid, int host_no)
 {
 	struct bound_host *bhost;
 
@@ -1703,14 +1707,14 @@ int tgt_unbind_host_to_target(int tid, int host_no)
 		if (bhost->host_no == host_no) {
 			if (!list_empty(&bhost->target->it_nexus_list)) {
 				eprintf("the target has IT_nexus\n");
-				return -EBUSY;
+				return TGTADM_TARGET_ACTIVE;
 			}
 			list_del(&bhost->bhost_siblings);
 			free(bhost);
-			return 0;
+			return TGTADM_SUCCESS;
 		}
 	}
-	return -ENOENT;
+	return TGTADM_NO_BINDING;
 }
 
 enum scsi_target_state tgt_get_target_state(int tid)
@@ -1745,10 +1749,11 @@ static char *target_state_name(enum scsi_target_state state)
 	return name;
 }
 
-int tgt_set_target_state(int tid, char *str)
+tgtadm_err tgt_set_target_state(int tid, char *str)
 {
-	int i, err = TGTADM_INVALID_REQUEST;
+	tgtadm_err adm_err = TGTADM_INVALID_REQUEST;
 	struct target *target;
+	int i;
 
 	target = target_lookup(tid);
 	if (!target)
@@ -1757,12 +1762,12 @@ int tgt_set_target_state(int tid, char *str)
 	for (i = 0; i < ARRAY_SIZE(target_state); i++) {
 		if (!strcmp(target_state[i].name, str)) {
 			target->target_state = target_state[i].value;
-			err = 0;
+			adm_err = TGTADM_SUCCESS;
 			break;
 		}
 	}
 
-	return err;
+	return adm_err;
 }
 
 static char *print_disksize(uint64_t size)
@@ -1812,9 +1817,8 @@ static char *print_type(int type)
 	return name;
 }
 
-int tgt_target_show_all(char *buf, int rest)
+tgtadm_err tgt_target_show_all(struct concat_buf *b)
 {
-	int total = 0, max = rest;
 	char strflags[128];
 	struct target *target;
 	struct scsi_lu *lu;
@@ -1823,8 +1827,7 @@ int tgt_target_show_all(char *buf, int rest)
 	struct it_nexus *nexus;
 
 	list_for_each_entry(target, &target_list, target_siblings) {
-		shprintf(total, buf, rest,
-			 "Target %d: %s\n"
+		concat_printf(b, "Target %d: %s\n"
 			 _TAB1 "System information:\n"
 			 _TAB2 "Driver: %s\n"
 			 _TAB2 "State: %s\n",
@@ -1833,18 +1836,18 @@ int tgt_target_show_all(char *buf, int rest)
 			 tgt_drivers[target->lid]->name,
 			 target_state_name(target->target_state));
 
-		shprintf(total, buf, rest, _TAB1 "I_T nexus information:\n");
+		concat_printf(b, _TAB1 "I_T nexus information:\n");
 
 		list_for_each_entry(nexus, &target->it_nexus_list, nexus_siblings) {
-			shprintf(total, buf, rest, _TAB2 "I_T nexus: %" PRIu64 "\n",
-				 nexus->itn_id);
+			concat_printf(b, _TAB2 "I_T nexus: %" PRIu64 "\n",
+				      nexus->itn_id);
 			if (nexus->info)
-				shprintf(total, buf, rest, "%s", nexus->info);
+				concat_printf(b, "%s", nexus->info);
 		}
 
-		shprintf(total, buf, rest, _TAB1 "LUN information:\n");
+		concat_printf(b, _TAB1 "LUN information:\n");
 		list_for_each_entry(lu, &target->device_list, device_siblings)
-			shprintf(total, buf, rest,
+			concat_printf(b,
 				 _TAB2 "LUN: %" PRIu64 "\n"
   				 _TAB3 "Type: %s\n"
 				 _TAB3 "SCSI ID: %s\n"
@@ -1878,32 +1881,28 @@ int tgt_target_show_all(char *buf, int rest)
 		    !strcmp(tgt_drivers[target->lid]->name, "iser")) {
 			int i, aid;
 
-			shprintf(total, buf, rest, _TAB1
-				 "Account information:\n");
+			concat_printf(b, _TAB1 "Account information:\n");
 			for (i = 0; i < target->account.nr_inaccount; i++) {
 				aid = target->account.in_aids[i];
-				shprintf(total, buf, rest, _TAB2 "%s\n",
-					 __account_lookup_id(aid)->user);
+				concat_printf(b, _TAB2 "%s\n",
+					      __account_lookup_id(aid)->user);
 			}
 			if (target->account.out_aid) {
 				aid = target->account.out_aid;
-				shprintf(total, buf, rest,
-					 _TAB2 "%s (outgoing)\n",
+				concat_printf(b, _TAB2 "%s (outgoing)\n",
 					 __account_lookup_id(aid)->user);
 			}
 		}
 
-		shprintf(total, buf, rest, _TAB1 "ACL information:\n");
+		concat_printf(b, _TAB1 "ACL information:\n");
 		list_for_each_entry(acl, &target->acl_list, aclent_list)
-			shprintf(total, buf, rest, _TAB2 "%s\n", acl->address);
+			concat_printf(b, _TAB2 "%s\n", acl->address);
 
 		list_for_each_entry(iqn_acl, &target->iqn_acl_list, iqn_aclent_list)
-			shprintf(total, buf, rest, _TAB2 "%s\n", iqn_acl->name);
+			concat_printf(b, _TAB2 "%s\n", iqn_acl->name);
 
 	}
-	return total;
-overflow:
-	return max;
+	return TGTADM_SUCCESS;
 }
 
 char *tgt_targetname(int tid)
@@ -1919,7 +1918,7 @@ char *tgt_targetname(int tid)
 
 #define DEFAULT_NR_ACCOUNT 16
 
-int tgt_target_create(int lld, int tid, char *args)
+tgtadm_err tgt_target_create(int lld, int tid, char *args)
 {
 	struct target *target, *pos;
 	char *p, *q, *targetname = NULL;
@@ -2002,16 +2001,16 @@ int tgt_target_create(int lld, int tid, char *args)
 
 	dprintf("Succeed to create a new target %d\n", tid);
 
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
-int tgt_target_destroy(int lld_no, int tid, int force)
+tgtadm_err tgt_target_destroy(int lld_no, int tid, int force)
 {
-	int ret;
 	struct target *target;
 	struct acl_entry *acl, *tmp;
 	struct iqn_acl_entry *iqn_acl, *tmp1;
 	struct scsi_lu *lu;
+	tgtadm_err adm_err;
 
 	target = target_lookup(tid);
 	if (!target)
@@ -2026,9 +2025,9 @@ int tgt_target_destroy(int lld_no, int tid, int force)
 		/* we remove lun0 last */
 		lu = list_entry(target->device_list.prev, struct scsi_lu,
 				device_siblings);
-		ret = tgt_device_destroy(tid, lu->lun, 1);
-		if (ret)
-			return ret;
+		adm_err = tgt_device_destroy(tid, lu->lun, 1);
+		if (adm_err != TGTADM_SUCCESS)
+			return adm_err;
 	}
 
 	if (tgt_drivers[lld_no]->target_destroy)
@@ -2052,10 +2051,10 @@ int tgt_target_destroy(int lld_no, int tid, int force)
 	free(target->name);
 	free(target);
 
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
-int tgt_portal_create(int lld, char *args)
+tgtadm_err tgt_portal_create(int lld, char *args)
 {
 	char *portals = NULL;
 
@@ -2077,10 +2076,10 @@ int tgt_portal_create(int lld, char *args)
 
 	dprintf("succeed to create new portals %s\n", portals);
 
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
-int tgt_portal_destroy(int lld, char *args)
+tgtadm_err tgt_portal_destroy(int lld, char *args)
 {
 	char *portals = NULL;
 
@@ -2102,23 +2101,20 @@ int tgt_portal_destroy(int lld, char *args)
 
 	dprintf("succeed to destroy portals %s\n", portals);
 
-	return 0;
+	return TGTADM_SUCCESS;
 }
 
-int account_show(char *buf, int rest)
+tgtadm_err account_show(struct concat_buf *b)
 {
-	int total = 0, max = rest;
 	struct account_entry *ac;
 
 	if (!list_empty(&account_list))
-		shprintf(total, buf, rest, "Account list:\n");
+		concat_printf(b, "Account list:\n");
 
 	list_for_each_entry(ac, &account_list, account_siblings)
-		shprintf(total, buf, rest, _TAB1 "%s\n", ac->user);
+		concat_printf(b, _TAB1 "%s\n", ac->user);
 
-	return total;
-overflow:
-	return max;
+	return TGTADM_SUCCESS;
 }
 
 static struct {
@@ -2159,9 +2155,8 @@ int system_set_state(char *str)
 	return err;
 }
 
-int system_show(int mode, char *buf, int rest)
+tgtadm_err system_show(int mode, struct concat_buf *b)
 {
-	int total = 0, max = rest;
 	struct backingstore_template *bst;
 	struct device_type_template *devt;
 	int i;
@@ -2169,53 +2164,47 @@ int system_show(int mode, char *buf, int rest)
 
 	/* FIXME: too hacky */
 	if (mode != MODE_SYSTEM)
-		return 0;
+		return TGTADM_SUCCESS;
 
-	shprintf(total, buf, rest, "System:\n");
-	shprintf(total, buf, rest, _TAB1 "State: %s\n",
-		 system_state_name(sys_state));
+	concat_printf(b, "System:\n");
+	concat_printf(b, _TAB1 "State: %s\n", system_state_name(sys_state));
+	concat_printf(b, _TAB1 "debug: %s\n", is_debug ? "on" : "off");
 
-	shprintf(total, buf, rest, "LLDs:\n");
+	concat_printf(b, "LLDs:\n");
 	for (i = 0; tgt_drivers[i]; i++) {
-		shprintf(total, buf, rest, _TAB1 "%s: %s\n",
-			 tgt_drivers[i]->name,
-			 driver_state_name(tgt_drivers[i]));
+		concat_printf(b, _TAB1 "%s: %s\n", tgt_drivers[i]->name,
+			      driver_state_name(tgt_drivers[i]));
 	}
 
-	shprintf(total, buf, rest, "Backing stores:\n");
+	concat_printf(b, "Backing stores:\n");
 	list_for_each_entry(bst, &bst_list, backingstore_siblings) {
 		if (!bst->bs_oflags_supported)
-			shprintf(total, buf, rest, _TAB1 "%s\n", bst->bs_name);
+			concat_printf(b, _TAB1 "%s\n", bst->bs_name);
 		else
-			shprintf(total, buf, rest, _TAB1 "%s (bsoflags %s)\n",
-				 bst->bs_name,
-				 open_flags_to_str(strflags, bst->bs_oflags_supported));
+			concat_printf(b, _TAB1 "%s (bsoflags %s)\n", bst->bs_name,
+				      open_flags_to_str(strflags, bst->bs_oflags_supported));
 	}
 
-	shprintf(total, buf, rest, "Device types:\n");
+	concat_printf(b, "Device types:\n");
 	list_for_each_entry(devt, &device_type_list, device_type_siblings)
-		shprintf(total, buf, rest, _TAB1 "%s\n", print_type(devt->type));
+		concat_printf(b, _TAB1 "%s\n", print_type(devt->type));
 
 	if (global_target.account.nr_inaccount) {
 		int i, aid;
-		shprintf(total, buf, rest,
-			 "Account information:\n");
+		concat_printf(b, _TAB1 "%s\n", "Account information:\n");
 		for (i = 0; i < global_target.account.nr_inaccount; i++) {
 			aid = global_target.account.in_aids[i];
-			shprintf(total, buf, rest, _TAB1 "%s\n",
-				 __account_lookup_id(aid)->user);
+			concat_printf(b, _TAB1 "%s\n",
+				      __account_lookup_id(aid)->user);
 		}
 		if (global_target.account.out_aid) {
 			aid = global_target.account.out_aid;
-			shprintf(total, buf, rest,
-				 _TAB1 "%s (outgoing)\n",
-				 __account_lookup_id(aid)->user);
+			concat_printf(b, _TAB1 "%s (outgoing)\n",
+				      __account_lookup_id(aid)->user);
 		}
 	}
 
-	return total;
-overflow:
-	return max;
+	return TGTADM_SUCCESS;
 }
 
 int is_system_available(void)
diff --git a/usr/tgtadm.c b/usr/tgtadm.c
index ba23c3a..0d8f5c0 100644
--- a/usr/tgtadm.c
+++ b/usr/tgtadm.c
@@ -63,7 +63,9 @@ static const char * tgtadm_strerror(int err)
 		{ TGTADM_NO_LUN, "can't find the logical unit" },
 		{ TGTADM_NO_SESSION, "can't find the session" },
 		{ TGTADM_NO_CONNECTION, "can't find the connection" },
+		{ TGTADM_NO_BINDING, "can't find the binding" },
 		{ TGTADM_TARGET_EXIST, "this target already exists" },
+		{ TGTADM_BINDING_EXIST, "this binding already exists" },
 		{ TGTADM_LUN_EXIST, "this logical unit number already exists" },
 		{ TGTADM_ACL_EXIST, "this access control rule already exists" },
 		{ TGTADM_ACL_NOEXIST, "this access control rule does not exist" },
@@ -285,9 +287,11 @@ retry:
 	return 0;
 }
 
-static int ipc_mgmt_req(struct tgtadm_req *req)
+static int ipc_mgmt_req(struct tgtadm_req *req, struct concat_buf *b)
 {
-	int err, fd = 0;
+	int err, fd = 0, done = 0;
+
+	req->len = sizeof(*req) + b->size;
 
 	err = ipc_mgmt_connect(&fd);
 	if (err < 0) {
@@ -295,19 +299,31 @@ static int ipc_mgmt_req(struct tgtadm_req *req)
 		goto out;
 	}
 
-	err = write(fd, (char *) req, req->len);
-	if (err < 0) {
-		eprintf("can't send the request to the tgt daemon, %m\n");
+	err = write(fd, req, sizeof(*req));
+	if (err < 0 || err != sizeof(*req)) {
+		eprintf("failed to send request hdr to the tgt daemon, %m\n");
 		err = errno;
 		goto out;
 	}
 
-	dprintf("sent to tgtd %d\n", err);
+	while (done < b->size) {
+		err = concat_write(b, fd, done);
+		if (err > 0)
+			done += err;
+		else if (errno != EAGAIN) {
+			eprintf("failed to send request buf to the tgt daemon, %m\n");
+			err = errno;
+			goto out;
+		}
+	}
+
+	dprintf("sent to tgtd %d\n", req->len);
 
 	err = ipc_mgmt_rsp(fd, req);
 out:
 	if (fd > 0)
 		close(fd);
+	concat_buf_release(b);
 	return err;
 }
 
@@ -447,40 +463,29 @@ static int verify_mode_params(int argc, char **argv, char *allowed)
 int main(int argc, char **argv)
 {
 	int ch, longindex, rc;
-	int op, total, tid, rest, mode, dev_type, ac_dir;
+	int op, tid, mode, dev_type, ac_dir;
 	uint32_t cid, hostno;
 	uint64_t sid, lun, force;
-	char *name, *value, *path, *targetname, *params, *address, *iqnname, *targetOps;
+	char *name, *value, *path, *targetname, *address, *iqnname, *targetOps;
 	char *portalOps, *bstype;
 	char *bsoflags;
 	char *blocksize;
 	char *user, *password;
-	char *buf;
-	size_t bufsz = BUFSIZE + sizeof(struct tgtadm_req);
-	struct tgtadm_req *req;
+	struct tgtadm_req adm_req = {0}, *req = &adm_req;
+	struct concat_buf b;
 
 	op = tid = mode = -1;
-	total = cid = hostno = sid = 0;
+	cid = hostno = sid = 0;
 	lun = UINT64_MAX;
 
 	rc = 0;
 	dev_type = TYPE_DISK;
 	ac_dir = ACCOUNT_TYPE_INCOMING;
-	rest = BUFSIZE;
 	name = value = path = targetname = address = iqnname = NULL;
 	targetOps = portalOps = bstype = NULL;
 	bsoflags = blocksize = user = password = NULL;
 	force = 0;
 
-	buf = valloc(bufsz);
-	if (!buf) {
-		eprintf("%s\n",	tgtadm_strerror(TGTADM_NOMEM));
-		return ENOMEM;
-	}
-
-	memset(buf, 0, bufsz);
-	req = (struct tgtadm_req *) buf;
-
 	optind = 1;
 	while ((ch = getopt_long(argc, argv, short_options,
 				 long_options, &longindex)) >= 0) {
@@ -867,53 +872,47 @@ int main(int argc, char **argv)
 	req->ac_dir = ac_dir;
 	req->force = force;
 
-	params = buf + sizeof(*req);
+	concat_buf_init(&b);
 
 	if (name)
-		shprintf(total, params, rest, "%s=%s", name, value);
+		concat_printf(&b, "%s=%s", name, value);
 	if (path)
-		shprintf(total, params, rest, "%spath=%s",
-			 rest == BUFSIZE ? "" : ",", path);
+		concat_printf(&b, "%spath=%s", concat_delim(&b,","), path);
 
 	if (req->device_type == TYPE_TAPE)
-		shprintf(total, params, rest, "%sbstype=%s",
-			 rest == BUFSIZE ? "" : ",", "ssc");
+		concat_printf(&b, "%sbstype=%s", concat_delim(&b,","), "ssc");
 	else if (bstype)
-		shprintf(total, params, rest, "%sbstype=%s",
-			 rest == BUFSIZE ? "" : ",", bstype);
+		concat_printf(&b, "%sbstype=%s", concat_delim(&b,","), bstype);
 	if (bsoflags)
-		shprintf(total, params, rest, "%sbsoflags=%s",
-			 rest == BUFSIZE ? "" : ",", bsoflags);
+		concat_printf(&b, "%sbsoflags=%s", concat_delim(&b,","), bsoflags);
 	if (blocksize)
-		shprintf(total, params, rest, "%sblocksize=%s",
-			 rest == BUFSIZE ? "" : ",", blocksize);
+		concat_printf(&b, "%sblocksize=%s", concat_delim(&b,","), blocksize);
 	if (targetname)
-		shprintf(total, params, rest, "%stargetname=%s",
-			 rest == BUFSIZE ? "" : ",", targetname);
+		concat_printf(&b, "%stargetname=%s", concat_delim(&b,","), targetname);
 	if (address)
-		shprintf(total, params, rest, "%sinitiator-address=%s",
-			 rest == BUFSIZE ? "" : ",", address);
+		concat_printf(&b, "%sinitiator-address=%s", concat_delim(&b,","), address);
 	if (iqnname)
-		shprintf(total, params, rest, "%sinitiator-name=%s",
-			 rest == BUFSIZE ? "" : ",", iqnname);
+		concat_printf(&b, "%sinitiator-name=%s", concat_delim(&b,","), iqnname);
 	if (user)
-		shprintf(total, params, rest, "%suser=%s",
-			 rest == BUFSIZE ? "" : ",", user);
+		concat_printf(&b, "%suser=%s", concat_delim(&b,","), user);
 	if (password)
-		shprintf(total, params, rest, "%spassword=%s",
-			 rest == BUFSIZE ? "" : ",", password);
+		concat_printf(&b, "%spassword=%s", concat_delim(&b,","), password);
 	/* Trailing ',' makes parsing params in modules easier.. */
 	if (targetOps)
-		shprintf(total, params, rest, "%stargetOps %s,",
-			 rest == BUFSIZE ? "" : ",", targetOps);
+		concat_printf(&b, "%stargetOps %s,", concat_delim(&b,","), targetOps);
 	if (portalOps)
-		shprintf(total, params, rest, "%sportalOps %s,",
-			 rest == BUFSIZE ? "" : ",", portalOps);
+		concat_printf(&b, "%sportalOps %s,", concat_delim(&b,","), portalOps);
+
+	if (b.err) {
+		eprintf("BUFSIZE (%d bytes) isn't long enough\n", BUFSIZE);
+		return EINVAL;
+	}
 
-	req->len = sizeof(*req) + total;
+	rc = concat_buf_finish(&b);
+	if (rc) {
+		eprintf("failed to create request, errno:%d\n", rc);
+		exit(rc);
+	}
 
-	return ipc_mgmt_req(req);
-overflow:
-	eprintf("BUFSIZE (%d bytes) isn't long enough\n", BUFSIZE);
-	return EINVAL;
+	return ipc_mgmt_req(req, &b);
 }
diff --git a/usr/tgtadm_error.h b/usr/tgtadm_error.h
index 3a37a69..1d71cce 100644
--- a/usr/tgtadm_error.h
+++ b/usr/tgtadm_error.h
@@ -11,8 +11,10 @@ enum tgtadm_errno {
 	TGTADM_NO_LUN,
 	TGTADM_NO_SESSION,
 	TGTADM_NO_CONNECTION,
+	TGTADM_NO_BINDING,
 	TGTADM_TARGET_EXIST,
 	TGTADM_LUN_EXIST,
+	TGTADM_BINDING_EXIST,
 
 	TGTADM_ACL_EXIST,
 	TGTADM_ACL_NOEXIST,
@@ -30,4 +32,6 @@ enum tgtadm_errno {
 	TGTADM_PREVENT_REMOVAL,
 };
 
+typedef enum tgtadm_errno tgtadm_err;
+
 #endif
diff --git a/usr/tgtd.h b/usr/tgtd.h
index 49d0699..726a3f5 100644
--- a/usr/tgtd.h
+++ b/usr/tgtd.h
@@ -3,6 +3,9 @@
 
 #include "log.h"
 #include "scsi_cmnd.h"
+#include "tgtadm_error.h"
+
+struct concat_buf;
 
 #define SCSI_ID_LEN		36
 #define SCSI_SN_LEN		36
@@ -113,11 +116,11 @@ struct device_type_operations {
 struct device_type_template {
 	unsigned char type;
 
-	int (*lu_init)(struct scsi_lu *lu);
+	tgtadm_err (*lu_init)(struct scsi_lu *lu);
 	void (*lu_exit)(struct scsi_lu *lu);
-	int (*lu_config)(struct scsi_lu *lu, char *args);
-	int (*lu_online)(struct scsi_lu *lu);
-	int (*lu_offline)(struct scsi_lu *lu);
+	tgtadm_err (*lu_config)(struct scsi_lu *lu, char *args);
+	tgtadm_err (*lu_online)(struct scsi_lu *lu);
+	tgtadm_err (*lu_offline)(struct scsi_lu *lu);
 	int (*cmd_passthrough)(int, struct scsi_cmd *);
 
 	struct device_type_operations ops[256];
@@ -130,7 +133,7 @@ struct backingstore_template {
 	int bs_datasize;
 	int (*bs_open)(struct scsi_lu *dev, char *path, int *fd, uint64_t *size);
 	void (*bs_close)(struct scsi_lu *dev);
-	int (*bs_init)(struct scsi_lu *dev);
+	tgtadm_err (*bs_init)(struct scsi_lu *dev);
 	void (*bs_exit)(struct scsi_lu *dev);
 	int (*bs_cmd_submit)(struct scsi_cmd *cmd);
 	int bs_oflags_supported;
@@ -224,28 +227,28 @@ extern struct list_head bst_list;
 
 extern int ipc_init(void);
 extern void ipc_exit(void);
-extern int tgt_device_create(int tid, int dev_type, uint64_t lun, char *args, int backing);
-extern int tgt_device_destroy(int tid, uint64_t lun, int force);
-extern int tgt_device_update(int tid, uint64_t dev_id, char *name);
+extern tgtadm_err tgt_device_create(int tid, int dev_type, uint64_t lun, char *args, int backing);
+extern tgtadm_err tgt_device_destroy(int tid, uint64_t lun, int force);
+extern tgtadm_err tgt_device_update(int tid, uint64_t dev_id, char *name);
 extern int device_reserve(struct scsi_cmd *cmd);
 extern int device_release(int tid, uint64_t itn_id, uint64_t lun, int force);
 extern int device_reserved(struct scsi_cmd *cmd);
-extern int tgt_device_path_update(struct target *target, struct scsi_lu *lu, char *path);
+extern tgtadm_err tgt_device_path_update(struct target *target, struct scsi_lu *lu, char *path);
 
-extern int tgt_target_create(int lld, int tid, char *args);
-extern int tgt_target_destroy(int lld, int tid, int force);
+extern tgtadm_err tgt_target_create(int lld, int tid, char *args);
+extern tgtadm_err tgt_target_destroy(int lld, int tid, int force);
 extern char *tgt_targetname(int tid);
-extern int tgt_target_show_all(char *buf, int rest);
+extern tgtadm_err tgt_target_show_all(struct concat_buf *b);
 int system_set_state(char *str);
-int system_show(int mode, char *buf, int rest);
+tgtadm_err system_show(int mode, struct concat_buf *b);
 int is_system_available(void);
 int is_system_inactive(void);
 
-extern int tgt_portal_create(int lld, char *args);
-extern int tgt_portal_destroy(int lld, char *args);
+extern tgtadm_err tgt_portal_create(int lld, char *args);
+extern tgtadm_err tgt_portal_destroy(int lld, char *args);
 
-extern int tgt_bind_host_to_target(int tid, int host_no);
-extern int tgt_unbind_host_to_target(int tid, int host_no);
+extern tgtadm_err tgt_bind_host_to_target(int tid, int host_no);
+extern tgtadm_err tgt_unbind_host_to_target(int tid, int host_no);
 
 struct event_data;
 typedef void (*sched_event_handler_t)(struct event_data *tev);
@@ -294,10 +297,10 @@ extern int get_scsi_cdb_size(struct scsi_cmd *cmd);
 extern int get_scsi_command_size(unsigned char op);
 
 extern enum scsi_target_state tgt_get_target_state(int tid);
-extern int tgt_set_target_state(int tid, char *str);
+extern tgtadm_err tgt_set_target_state(int tid, char *str);
 
-extern int acl_add(int tid, char *address);
-extern int acl_del(int tid, char *address);
+extern tgtadm_err acl_add(int tid, char *address);
+extern tgtadm_err acl_del(int tid, char *address);
 extern char *acl_get(int tid, int idx);
 
 extern int iqn_acl_add(int tid, char *name);
@@ -305,10 +308,10 @@ extern int iqn_acl_del(int tid, char *name);
 extern char *iqn_acl_get(int tid, int idx);
 
 extern int account_lookup(int tid, int type, char *user, int ulen, char *password, int plen);
-extern int account_add(char *user, char *password);
-extern int account_del(char *user);
-extern int account_ctl(int tid, int type, char *user, int bind);
-extern int account_show(char *buf, int rest);
+extern tgtadm_err account_add(char *user, char *password);
+extern tgtadm_err account_del(char *user);
+extern tgtadm_err account_ctl(int tid, int type, char *user, int bind);
+extern tgtadm_err account_show(struct concat_buf *b);
 extern int account_available(int tid, int dir);
 
 extern int it_nexus_create(int tid, uint64_t itn_id, int host_no, char *info);
@@ -317,7 +320,7 @@ extern int it_nexus_destroy(int tid, uint64_t itn_id);
 extern int device_type_register(struct device_type_template *);
 
 extern struct lu_phy_attr *lu_attr_lookup(int tid, uint64_t lun);
-extern int dtd_load_unload(int tid, uint64_t lun, int load, char *file);
+extern tgtadm_err dtd_load_unload(int tid, uint64_t lun, int load, char *file);
 extern int dtd_check_removable(int tid, uint64_t lun);
 
 extern int register_backingstore_template(struct backingstore_template *bst);
diff --git a/usr/util.h b/usr/util.h
index 8abdb94..45a96ec 100644
--- a/usr/util.h
+++ b/usr/util.h
@@ -8,6 +8,10 @@
 #include <signal.h>
 #include <syscall.h>
 #include <unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <limits.h>
 #include <linux/types.h>
 
@@ -88,17 +92,6 @@ static inline int between(uint32_t seq1, uint32_t seq2, uint32_t seq3)
 	return seq3 - seq2 >= seq1 - seq2;
 }
 
-#define shprintf(total, buf, rest, fmt, args...)			\
-do {									\
-	int len;							\
-	len = snprintf(buf, rest, fmt, ##args);				\
-	if (len > rest)							\
-		goto overflow;						\
-	buf += len;							\
-	total += len;							\
-	rest -= len;							\
-} while (0)
-
 extern unsigned long pagesize, pageshift;
 
 #if defined(__NR_signalfd) && defined(USE_SIGNALFD)
@@ -151,4 +144,77 @@ struct signalfd_siginfo {
 	ret;						\
 })
 
+struct concat_buf {
+	FILE *streamf;
+	int err;
+	int used;
+	char *buf;
+	int size;
+};
+
+static inline void concat_buf_init(struct concat_buf *b)
+{
+	b->streamf = open_memstream(&b->buf, (size_t *)&b->size);
+	b->err = b->streamf ? 0 : errno;
+	b->used = 0;
+}
+
+static inline int concat_printf(struct concat_buf *b, const char *format, ...)
+{
+	va_list args;
+	int nprinted;
+
+	if (!b->err) {
+		va_start(args, format);
+		nprinted = vfprintf(b->streamf, format, args);
+		if (nprinted >= 0)
+			b->used += nprinted;
+		else {
+			b->err = nprinted;
+			fclose(b->streamf);
+			b->streamf = NULL;
+		}
+		va_end(args);
+	}
+	return b->err;
+}
+
+static inline const char *concat_delim(struct concat_buf *b, const char *delim)
+{
+	return !b->used ? "" : delim;
+}
+
+static inline int concat_buf_finish(struct concat_buf *b)
+{
+	if (b->streamf) {
+		fclose(b->streamf);
+		b->streamf = NULL;
+		if (b->size)
+			b->size ++; /* account for trailing NULL char */
+	}
+	return b->err;
+}
+
+static inline int concat_write(struct concat_buf *b, int fd, int offset)
+{
+	concat_buf_finish(b);
+
+	if (b->err)
+		return b->err;
+
+	if (b->size - offset > 0)
+		return write(fd, b->buf + offset, b->size - offset);
+	else
+		return 0;
+}
+
+static inline void concat_buf_release(struct concat_buf *b)
+{
+	concat_buf_finish(b);
+	if (b->buf) {
+		free(b->buf);
+		memset(b, 0, sizeof(*b));
+	}
+}
+
 #endif
--
1.7.3.2
--
To unsubscribe from this list: send the line "unsubscribe stgt" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html



More information about the stgt mailing list