[PATCH] chunk: add CP operation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch

* adds local, intra-table copy operation to chunkd/libhail
* illustrates what files need updating, when adding a new op to chunk
* adds some 'worker' infrastructure which should help with future ops,
  notably remote copy (RCP)
* should assist tabled's implementation of S3 copy (x-amz-copy-source)

 chunkd/chunkd.h         |   19 +++++++
 chunkd/object.c         |  117 ++++++++++++++++++++++++++++++++++++++++++++++
 chunkd/server.c         |  122 ++++++++++++++++++++++++++++++++++++++++++++----
 doc/chcli.8             |   13 ++++-
 include/chunk_msg.h     |    2 
 include/chunkc.h        |   10 +++
 lib/chunkdc.c           |   56 ++++++++++++++++++++++
 test/chunkd/Makefile.am |    5 +
 tools/chcli.c           |   77 ++++++++++++++++++++++++++++++
 9 files changed, 409 insertions(+), 12 deletions(-)

diff --git a/chunkd/chunkd.h b/chunkd/chunkd.h
index e019f0d..5d39353 100644
--- a/chunkd/chunkd.h
+++ b/chunkd/chunkd.h
@@ -104,6 +104,8 @@ struct client {
 	unsigned int		req_used;	/* amount of req_buf in use */
 	void			*req_ptr;	/* start of unexamined data */
 	uint16_t		key_len;
+	unsigned int		var_len;	/* len of vari len record */
+	bool			second_var;	/* inside 2nd vari len rec? */
 
 	char			*hdr_start;	/* current hdr start */
 	char			*hdr_end;	/* current hdr end (so far) */
@@ -124,6 +126,7 @@ struct client {
 	char			netbuf_out[CLI_DATA_BUF_SZ];
 	char			key[CHD_KEY_SZ];
 	char			table[CHD_KEY_SZ];
+	char			key2[CHD_KEY_SZ];
 };
 
 struct backend_obj {
@@ -162,6 +165,14 @@ struct volume_entry {
 	char			*owner;		/* obj owner username */
 };
 
+struct worker_info {
+	enum chunk_errcode	err;		/* error returned to pipe */
+	struct client		*cli;		/* associated client conn */
+
+	void			(*thr_ev)(struct worker_info *);
+	void			(*pipe_ev)(struct worker_info *);
+};
+
 struct server_stats {
 	unsigned long		poll;		/* number polls */
 	unsigned long		event;		/* events dispatched */
@@ -209,6 +220,10 @@ struct server {
 
 	GHashTable		*fd_info;
 
+	GThreadPool		*workers;	/* global thread worker pool */
+	int			max_workers;
+	int			worker_pipe[2];
+
 	struct list_head	wr_trash;
 	unsigned int		trash_sz;
 
@@ -278,6 +293,7 @@ extern int fs_obj_do_sum(const char *fn, unsigned int klen, char **csump);
 extern bool object_del(struct client *cli);
 extern bool object_put(struct client *cli);
 extern bool object_get(struct client *cli, bool want_body);
+extern bool object_cp(struct client *cli);
 extern bool cli_evt_data_in(struct client *cli, unsigned int events);
 extern void cli_out_end(struct client *cli);
 extern void cli_in_end(struct client *cli);
@@ -314,12 +330,15 @@ extern bool cli_err(struct client *cli, enum chunk_errcode code, bool recycle_ok
 extern int cli_writeq(struct client *cli, const void *buf, unsigned int buflen,
 		     cli_write_func cb, void *cb_data);
 extern bool cli_wr_sendfile(struct client *, cli_write_func);
+extern bool cli_rd_set_poll(struct client *cli, bool readable);
 extern void cli_wr_set_poll(struct client *cli, bool writable);
 extern bool cli_cb_free(struct client *cli, struct client_write *wr,
 			bool done);
 extern bool cli_write_start(struct client *cli);
 extern int cli_req_avail(struct client *cli);
 extern int cli_poll_mod(struct client *cli);
+extern bool worker_pipe_signal(struct worker_info *wi);
+extern bool tcp_cli_event(int fd, short events, void *userdata);
 extern void resp_init_req(struct chunksrv_resp *resp,
 		   const struct chunksrv_req *req);
 
diff --git a/chunkd/object.c b/chunkd/object.c
index 116792f..af187b6 100644
--- a/chunkd/object.c
+++ b/chunkd/object.c
@@ -25,6 +25,7 @@
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
+#include <poll.h>
 #include <stdio.h>
 #include <syslog.h>
 #include <glib.h>
@@ -356,3 +357,119 @@ start_write:
 	return cli_write_start(cli);
 }
 
+static void worker_cp_thr(struct worker_info *wi)
+{
+	static const unsigned bufsz = (1 * 1024 * 1024);
+	void *buf = NULL;
+	struct client *cli = wi->cli;
+	struct backend_obj *obj = NULL, *out_obj = NULL;
+	enum chunk_errcode err = che_InternalError;
+	unsigned char md[SHA_DIGEST_LENGTH];
+	char hashstr[50];
+
+	buf = malloc(bufsz);
+	if (!buf)
+		goto out;
+
+	cli->in_obj = obj = fs_obj_open(cli->table_id, cli->user, cli->key2,
+					cli->var_len, &err);
+	if (!obj)
+		goto out;
+
+	cli->in_len = obj->size;
+
+	cli->out_ce = objcache_get_dirty(&chunkd_srv.actives,
+					 cli->key, cli->key_len);
+	if (!cli->out_ce)
+		goto out;
+
+	cli->out_bo = out_obj = fs_obj_new(cli->table_id,
+					   cli->key, cli->key_len, &err);
+	if (!cli->out_bo)
+		goto out;
+
+	SHA1_Init(&cli->out_hash);
+
+	while (cli->in_len > 0) {
+		ssize_t rrc, wrc;
+
+		rrc = fs_obj_read(obj, buf, MIN(cli->in_len, bufsz));
+		if (rrc < 0)
+			goto err_out;
+		if (rrc == 0)
+			break;
+
+		SHA1_Update(&cli->out_hash, buf, rrc);
+		cli->in_len -= rrc;
+
+		while (rrc > 0) {
+			wrc = fs_obj_write(out_obj, buf, rrc);
+			if (wrc < 0)
+				goto err_out;
+
+			rrc -= wrc;
+		}
+	}
+
+	SHA1_Final(md, &cli->out_hash);
+	hexstr(md, SHA_DIGEST_LENGTH, hashstr);
+
+	if (!fs_obj_write_commit(out_obj, cli->user, hashstr, false))
+		goto err_out;
+
+	err = che_Success;
+
+out:
+	free(buf);
+	cli_in_end(cli);
+	cli_out_end(cli);
+	wi->err = err;
+	worker_pipe_signal(wi);
+	return;
+
+err_out:
+	/* FIXME: remove half-written destination object */
+	goto out;
+}
+
+static void worker_cp_pipe(struct worker_info *wi)
+{
+	struct client *cli = wi->cli;
+	bool rcb;
+
+	cli_rd_set_poll(cli, true);
+
+	rcb = cli_err(cli, wi->err, (wi->err == che_Success) ? true : false);
+	if (rcb) {
+		short events = POLLIN;
+		if (cli->writing)
+			events |= POLLOUT;
+		tcp_cli_event(cli->fd, events, cli);
+	}
+
+	memset(wi, 0xffffffff, sizeof(*wi));	/* poison */
+	free(wi);
+}
+
+bool object_cp(struct client *cli)
+{
+	enum chunk_errcode err = che_InternalError;
+	struct worker_info *wi;
+
+	cli_rd_set_poll(cli, false);
+
+	wi = calloc(1, sizeof(*wi));
+	if (!wi) {
+		cli_rd_set_poll(cli, true);
+		return cli_err(cli, err, false);
+	}
+
+	wi->thr_ev = worker_cp_thr;
+	wi->pipe_ev = worker_cp_pipe;
+	wi->cli = cli;
+
+	g_thread_pool_push(chunkd_srv.workers, wi, NULL);
+
+	return false;
+}
+
diff --git a/chunkd/server.c b/chunkd/server.c
index 7dd2227..c3984e9 100644
--- a/chunkd/server.c
+++ b/chunkd/server.c
@@ -418,6 +418,16 @@ static bool cli_evt_recycle(struct client *cli, unsigned int events)
 	return true;
 }
 
+bool cli_rd_set_poll(struct client *cli, bool readable)
+{
+	if (readable)
+		srv_poll_mask(cli->fd, POLLIN, 0);
+	else
+		srv_poll_mask(cli->fd, 0, POLLIN);
+	
+	return true;
+}
+
 void cli_wr_set_poll(struct client *cli, bool writable)
 {
 	if (writable)
@@ -1052,6 +1062,7 @@ static const char *op2str(enum chunksrv_ops op)
 	case CHO_CHECK_START:	return "CHO_CHECK_START";
 	case CHO_CHECK_STATUS:	return "CHO_CHECK_STATUS";
 	case CHO_START_TLS:	return "CHO_START_TLS";
+	case CHO_CP:		return "CHO_CP";
 
 	default:
 		return "BUG/UNKNOWN!";
@@ -1146,6 +1157,9 @@ static bool cli_evt_exec_req(struct client *cli, unsigned int events)
 	case CHO_DEL:
 		rcb = object_del(cli);
 		break;
+	case CHO_CP:
+		rcb = object_cp(cli);
+		break;
 	case CHO_LIST:
 		rcb = volume_list(cli);
 		break;
@@ -1229,8 +1243,10 @@ static bool cli_evt_read_fixed(struct client *cli, unsigned int events)
 
 	/* otherwise, go to read-variable-len-record state */
 	cli->req_ptr = &cli->key;
+	cli->var_len = cli->key_len;
 	cli->req_used = 0;
 	cli->state = evt_read_var;
+	cli->second_var = false;
 
 	return true;
 }
@@ -1238,7 +1254,7 @@ static bool cli_evt_read_fixed(struct client *cli, unsigned int events)
 static bool cli_evt_read_var(struct client *cli, unsigned int events)
 {
 	int rc = cli_read_data(cli, cli->req_ptr,
-			       cli->key_len - cli->req_used);
+			       cli->var_len - cli->req_used);
 	if (rc < 0) {
 		cli->state = evt_dispose;
 		return true;
@@ -1248,10 +1264,17 @@ static bool cli_evt_read_var(struct client *cli, unsigned int events)
 	cli->req_used += rc;
 
 	/* poll for more, if variable-length record not yet received */
-	if (cli->req_used < cli->key_len)
+	if (cli->req_used < cli->var_len)
 		return false;
 
-	cli->state = evt_exec_req;
+	if (cli->creq.op == CHO_CP && !cli->second_var) {
+		cli->req_ptr = &cli->key2;
+		cli->var_len = le64_to_cpu(cli->creq.data_len);
+		cli->req_used = 0;
+		cli->state = evt_read_var;
+		cli->second_var = true;
+	} else
+		cli->state = evt_exec_req;
 
 	return true;
 }
@@ -1304,7 +1327,7 @@ static void tcp_cli_wr_event(int fd, short events, void *userdata)
 		cli_writable(cli);
 }
 
-static bool tcp_cli_event(int fd, short events, void *userdata)
+bool tcp_cli_event(int fd, short events, void *userdata)
 {
 	struct client *cli = userdata;
 	bool loop = false, disposing = false;
@@ -1422,6 +1445,25 @@ static int net_write_port(const char *port_file, const char *port_str)
 	return 0;
 }
 
+static bool pipe_watch(int pipe_fd_0, 
+		       bool (*cb)(int fd, short events, void *userdata),
+		       void *userdata)
+{
+	struct server_poll *sp;
+
+	sp = calloc(1, sizeof(*sp));
+	if (!sp)
+		return false;
+
+	sp->events = POLLIN;
+	sp->cb = cb;
+	sp->userdata = userdata;
+
+	g_hash_table_insert(chunkd_srv.fd_info, GINT_TO_POINTER(pipe_fd_0), sp);
+
+	return true;
+}
+
 static int net_open_socket(const struct listen_cfg *cfg,
 			   int addr_fam, int sock_type, int sock_prot,
 			   int addr_len, void *addr_ptr)
@@ -1637,6 +1679,45 @@ static int net_open(struct listen_cfg *cfg)
 		return net_open_known(cfg);
 }
 
+static void worker_thread(gpointer data, gpointer userdata)
+{
+	struct worker_info *wi = data;
+
+	wi->thr_ev(wi);
+}
+
+bool worker_pipe_signal(struct worker_info *wi)
+{
+	ssize_t wrc;
+
+	wrc = write(chunkd_srv.worker_pipe[1], &wi, sizeof(wi));
+	if (wrc != sizeof(wi)) {
+		applog(LOG_ERR, "worker pipe output failed: %s",
+		       strerror(errno));
+		return false;
+	}
+	
+	return true;
+}
+
+static bool worker_pipe_evt(int fd, short events, void *userdata)
+{
+	struct worker_info *wi = NULL;
+
+	if (read(fd, &wi, sizeof(wi)) != sizeof(wi)) {
+		applog(LOG_ERR, "worker pipe input failed: %s",
+		       strerror(errno));
+		return false;
+	}
+
+	wi->pipe_ev(wi);
+
+	if (!srv_poll_ready(fd))
+		applog(LOG_ERR, "unable to ready worker fd for polling");
+
+	return true;
+}
+
 static void fill_poll_arr(gpointer key, gpointer val, gpointer userdata)
 {
 	int fd = GPOINTER_TO_INT(key);
@@ -1833,21 +1914,38 @@ int main (int argc, char *argv[])
 		goto err_out_session;
 	}
 
+	chunkd_srv.max_workers = 10;
+	chunkd_srv.workers = g_thread_pool_new(worker_thread, NULL,
+					       chunkd_srv.max_workers,
+					       FALSE, NULL);
+	if (!chunkd_srv.workers) {
+		rc = 1;
+		goto err_out_fd_info;
+	}
+
 	if (objcache_init(&chunkd_srv.actives) != 0) {
 		rc = 1;
-		goto err_out_objcache;
+		goto err_out_workers;
 	}
 
 	chunkd_srv.trash_sz = 0;
 
 	if (pipe(chunkd_srv.chk_pipe) < 0) {
 		rc = 1;
-		goto err_out_pipe;
+		goto err_out_objcache;
+	}
+	if (pipe(chunkd_srv.worker_pipe) < 0) {
+		rc = 1;
+		goto err_out_chk_pipe;
+	}
+	if (!pipe_watch(chunkd_srv.worker_pipe[0], worker_pipe_evt, NULL)) {
+		rc = 1;
+		goto err_out_chk_pipe;
 	}
 
 	if (fs_open()) {
 		rc = 1;
-		goto err_out_fs;
+		goto err_out_worker_pipe;
 	}
 
 	/* set up server networking */
@@ -1877,13 +1975,17 @@ err_out_cld:
 	/* net_close(); */
 err_out_listen:
 	fs_close();
-err_out_fs:
+err_out_worker_pipe:
+err_out_chk_pipe:
 	cmd = CHK_CMD_EXIT;
 	write(chunkd_srv.chk_pipe[1], &cmd, 1);
 	close(chunkd_srv.chk_pipe[1]);
-err_out_pipe:
-	objcache_fini(&chunkd_srv.actives);
 err_out_objcache:
+	objcache_fini(&chunkd_srv.actives);
+err_out_workers:
+	if (strict_free)
+		g_thread_pool_free(chunkd_srv.workers, TRUE, FALSE);
+err_out_fd_info:
 	if (strict_free)
 		g_hash_table_destroy(chunkd_srv.fd_info);
 err_out_session:
diff --git a/doc/chcli.8 b/doc/chcli.8
index 477d8d4..0de059b 100644
--- a/doc/chcli.8
+++ b/doc/chcli.8
@@ -86,7 +86,14 @@ than the command line.
 Obtain key portion of a key/value pair from the specified file,
 rather than the command line.  Keys provided on the command line
 (as opposed to via -k) are stored with a C-style nul terminating
-character appended, adding 1 byte to each key.
+character appended, adding 1 byte to each key.  If the command is copy (CP),
+this represents the destination key.
+.TP
+.B \-s \-\-src
+Obtain source-key portion of a source-key/source-value pair from the
+specified file, rather than the command line.  Keys provided on the
+command line (as opposed to via -s) are stored with a C-style nul
+terminating character appended, adding 1 byte to each key.
 .TP
 .B \-\-list-cmds
 List all supported commands, and a short command usage synopsis.
@@ -131,6 +138,10 @@ Fetch status of server self-check
 .B CHECKSTART
 Begin server self-check
 .TP
+.B CP dest-key src-key
+Copy object represented by 'src-key' into new object referenced
+by 'dest-key'
+.TP
 Keys provided on the command line (as opposed to via -k) are stored
 with a C-style nul terminating character appended, adding 1 byte to
 each key.
diff --git a/include/chunk_msg.h b/include/chunk_msg.h
index 4a3c15d..3ee1c6a 100644
--- a/include/chunk_msg.h
+++ b/include/chunk_msg.h
@@ -48,6 +48,8 @@ enum chunksrv_ops {
 	 * functions' success/failure is sufficient indication.
 	 */
 	CHO_START_TLS		= 10,	/* Encrypt all subsequent msgs */
+
+	CHO_CP			= 11,	/* local object copy (intra-table) */
 };
 
 enum chunk_errcode {
diff --git a/include/chunkc.h b/include/chunkc.h
index 683992e..1fd7066 100644
--- a/include/chunkc.h
+++ b/include/chunkc.h
@@ -85,6 +85,9 @@ extern bool stc_put_sync(struct st_client *stc);
 extern bool stc_put_inline(struct st_client *stc, const void *key,
 			   size_t key_len, void *data, uint64_t len,
 			   uint32_t flags);
+extern bool stc_cp(struct st_client *stc,
+		   const void *dest_key, size_t dest_key_len,
+		   const void *src_key, size_t src_key_len);
 
 extern bool stc_del(struct st_client *stc, const void *key, size_t key_len);
 extern bool stc_ping(struct st_client *stc);
@@ -133,4 +136,11 @@ static inline bool stc_table_openz(struct st_client *stc, const char *key,
 	return stc_table_open(stc, key, strlen(key) + 1, flags);
 }
 
+static inline bool stc_cpz(struct st_client *stc,
+			   const char *dest_key, const char *src_key)
+{
+	return stc_cp(stc, dest_key, strlen(dest_key) + 1,
+		      src_key, strlen(src_key) + 1);
+}
+
 #endif /* __STC_H__ */
diff --git a/lib/chunkdc.c b/lib/chunkdc.c
index 7441662..260f4d9 100644
--- a/lib/chunkdc.c
+++ b/lib/chunkdc.c
@@ -1091,6 +1091,62 @@ bool stc_check_status(struct st_client *stc, struct chunk_check_status *out)
 	return true;
 }
 
+bool stc_cp(struct st_client *stc,
+	    const void *dest_key, size_t dest_key_len,
+	    const void *src_key, size_t src_key_len)
+{
+	struct chunksrv_resp resp;
+	struct chunksrv_req *req;
+	void *p;
+	bool rcb = false;
+	size_t alloc_len;
+
+	if (stc->verbose)
+		fprintf(stderr, "libstc: CP\n");
+
+	alloc_len = sizeof(*req) + src_key_len + dest_key_len;
+	req = malloc(alloc_len);
+	if (!req)
+		return false;
+
+	/* initialize request */
+	req_init(stc, req);
+	req->op = CHO_CP;
+	req->data_len = cpu_to_le64(src_key_len);
+
+	/* store destination (new) key in key (1st) buffer area */
+	req_set_key(req, dest_key, dest_key_len);
+
+	/* store source (old) key in data (2nd) buffer area */
+	p = (req + 1);
+	p += dest_key_len;
+	memcpy(p, src_key, src_key_len);
+
+	/* sign request */
+	chreq_sign(req, stc->key, req->sig);
+
+	/* write request */
+	if (!net_write(stc, req, alloc_len))
+		goto out;
+
+	/* read response header */
+	if (!net_read(stc, &resp, sizeof(resp)))
+		goto out;
+
+	/* check response code */
+	if (resp.resp_code != che_Success) {
+		if (stc->verbose)
+			fprintf(stderr, "CP resp code: %d\n", resp.resp_code);
+		goto out;
+	}
+
+	rcb = true;
+
+out:
+	free(req);
+	return rcb;
+}
+
 /*
  * For extra safety, call stc_init after g_thread_init, if present.
  * Currently we just call srand(), but since we use GLib, we may need
diff --git a/test/chunkd/Makefile.am b/test/chunkd/Makefile.am
index 1cb9875..def9b36 100644
--- a/test/chunkd/Makefile.am
+++ b/test/chunkd/Makefile.am
@@ -1,5 +1,6 @@
 
 INCLUDES	= -I$(top_srcdir)/include	\
+		  -I$(top_srcdir)/lib		\
 		  @GLIB_CFLAGS@ @XML_CPPFLAGS@
 
 EXTRA_DIST =			\
@@ -24,13 +25,14 @@ TESTS =				\
 	nop			\
 	basic-object		\
 	auth			\
+	cp			\
 	large-object		\
 	lotsa-objects		\
 	selfcheck-unit		\
 	stop-daemon		\
 	clean-db
 
-check_PROGRAMS		= auth basic-object it-works large-object \
+check_PROGRAMS		= auth basic-object cp it-works large-object \
 			  lotsa-objects nop objcache-unit selfcheck-unit
 
 TESTLDADD		= ../../lib/libhail.la	\
@@ -38,6 +40,7 @@ TESTLDADD		= ../../lib/libhail.la	\
 			  @GLIB_LIBS@ @CRYPTO_LIBS@ \
 			  @XML_LIBS@ @SSL_LIBS@
 basic_object_LDADD	= $(TESTLDADD)
+cp_LDADD		= $(TESTLDADD)
 auth_LDADD		= $(TESTLDADD)
 it_works_LDADD		= $(TESTLDADD)
 large_object_LDADD	= $(TESTLDADD)
diff --git a/tools/chcli.c b/tools/chcli.c
index 55e7a7d..160af18 100644
--- a/tools/chcli.c
+++ b/tools/chcli.c
@@ -56,6 +56,8 @@ static struct argp_option options[] = {
 	  "Read value from FILE, rather than command line" },
 	{ "output", 'o', "FILE", 0,
 	  "Send GET output to FILE, rather than stdout" },
+	{ "src", 's', "FILE", 0,
+	  "Read source key from FILE, rather than command line" },
 	{ "ssl", 'S', NULL, 0,
 	  "Enable SSL channel security" },
 	{ "table", 't', "TABLE", 0,
@@ -91,6 +93,7 @@ enum chcli_cmd {
 	CHC_PING,
 	CHC_CHECKSTATUS,
 	CHC_CHECKSTART,
+	CHC_CP,
 };
 
 struct chcli_host {
@@ -107,6 +110,9 @@ static char *output_fn;
 static char *key_data;
 static gsize key_data_len;
 static bool key_in_file;
+static char *key2_data;
+static gsize key2_data_len;
+static bool key2_in_file;
 static char *input_fn;
 static bool value_in_file;
 static char *table_name;
@@ -186,6 +192,7 @@ static void show_cmds(void)
 "PING          Ping server\n"
 "CHECKSTATUS   Fetch status of server self-check\n"
 "CHECKSTART    Begin server self-check\n"
+"CP dst src    Copy object 'src' into new object 'dst'\n"
 "\n"
 "Keys provided on the command line (as opposed to via -k) are stored\n"
 "with a C-style nul terminating character appended, adding 1 byte to\n"
@@ -283,6 +290,14 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state)
 		}
 		key_in_file = true;
 		break;
+	case 's':
+		if (!g_file_get_contents(arg, &key2_data, &key2_data_len,
+					 NULL)) {
+			fprintf(stderr, "failed to read src file %s\n", arg);
+			argp_usage(state);
+		}
+		key2_in_file = true;
+		break;
 	case 'i':
 		input_fn = arg;
 		value_in_file = true;
@@ -324,6 +339,8 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state)
 			cmd_mode = CHC_CHECKSTATUS;
 		else if (!strcasecmp(arg, "checkstart"))
 			cmd_mode = CHC_CHECKSTART;
+		else if (!strcasecmp(arg, "cp"))
+			cmd_mode = CHC_CP;
 		else
 			argp_usage(state);	/* invalid cmd */
 		break;
@@ -386,6 +403,64 @@ static int cmd_ping(void)
 	return 0;
 }
 
+static int cmd_cp(void)
+{
+	struct st_client *stc;
+
+	/* if dest key data not supplied via file, absorb first cmd arg */
+	if (!key_data) {
+		if (!n_cmd_args) {
+			fprintf(stderr, "CP requires dst,src args\n");
+			return 1;
+		}
+
+		key_data = cmd_args[0];
+		key_data_len = strlen(cmd_args[0]) + 1;
+
+		cmd_args++;
+		n_cmd_args--;
+	}
+
+	/* if src key data not supplied via file, absorb second cmd arg */
+	if (!key2_data) {
+		if (!n_cmd_args) {
+			fprintf(stderr, "CP requires dst,src args\n");
+			return 1;
+		}
+
+		key2_data = cmd_args[0];
+		key2_data_len = strlen(cmd_args[0]) + 1;
+
+		cmd_args++;
+		n_cmd_args--;
+	}
+
+	if (key_data_len < 1 || key_data_len > CHD_KEY_SZ) {
+		fprintf(stderr, "CP: invalid key size %u\n",
+			(unsigned int) key_data_len);
+		return 1;
+	}
+	if (key2_data_len < 1 || key2_data_len > CHD_KEY_SZ) {
+		fprintf(stderr, "CP: invalid key size %u\n",
+			(unsigned int) key2_data_len);
+		return 1;
+	}
+
+	stc = chcli_stc_new();
+	if (!stc)
+		return 1;
+
+	if (!stc_cp(stc, key_data, key_data_len, key2_data, key2_data_len)) {
+		fprintf(stderr, "CP failed\n");
+		stc_free(stc);
+		return 1;
+	}
+
+	stc_free(stc);
+
+	return 0;
+}
+
 static int cmd_del(void)
 {
 	struct st_client *stc;
@@ -765,6 +840,8 @@ int main (int argc, char *argv[])
 		return cmd_check_status();
 	case CHC_CHECKSTART:
 		return cmd_check_start();
+	case CHC_CP:
+		return cmd_cp();
 	}
 
 	return 0;
--
To unsubscribe from this list: send the line "unsubscribe hail-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Fedora Clound]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux