This is fully fleshed out and working, but I have not committed it yet,
in case there is feedback or major objections.
The following patch presents the last major chunkd conceptual change
I felt was needed in the chunkd API: multiple key/value tables.
Applications and users will often want to be partitioned away from
each other, or simply have a need for something other than simply a
flat namespace. With this change, chunkd's API is now much like Amazon
S3, which has a shared namespace for buckets, and then each bucket has
its own namespace.
This is made possible with a single API addition, TABLE OPEN.
Here is what a chunkd session now looks like, with this patch:
LOGIN(user="jgarzik")
TABLE-OPEN(name="tabled")
GET...
GET...
GET...
PUT...
PUT...
PUT...
The corresponding C API call is stc_table_open().
With this change, some of my upper layer projects (NFS, SQL) become
more manageable. This change enables me to dedicate several tables
to each application and/or user.
configure.ac | 1
doc/chcli.cfg | 3 +
include/chunk_msg.h | 4 +
include/chunkc.h | 8 ++
lib/chunkdc.c | 42 ++++++++++++++
server/Makefile.am | 6 +-
server/be-fs.c | 152 ++++++++++++++++++++++++++++++++++++++++++++-------
server/chunkd.h | 15 +++--
server/object.c | 7 +-
server/server.c | 64 +++++++++++++++++++--
test/auth.c | 6 ++
test/basic-object.c | 3 +
test/it-works.c | 9 +++
test/large-object.c | 3 +
test/lotsa-objects.c | 3 +
test/nop.c | 3 +
test/test.h | 2
tools/chcli.c | 38 +++++++++++-
18 files changed, 332 insertions(+), 37 deletions(-)
diff --git a/configure.ac b/configure.ac
index 8c32383..f54cf27 100644
--- a/configure.ac
+++ b/configure.ac
@@ -79,6 +79,7 @@ AC_CHECK_LIB(event, event_base_new, EVENT_LIBS=-levent,
AC_CHECK_LIB(argp, argp_parse, ARGP_LIBS=-largp)
AC_CHECK_LIB(socket, bind, SOCKET_LIBS=-lsocket)
PKG_CHECK_MODULES(CLDC, libcldc)
+PKG_CHECK_MODULES(TOKYOCABINET, tokyocabinet)
dnl -----------------------------
dnl Check for cld program, used
diff --git a/doc/chcli.cfg b/doc/chcli.cfg
index c27b956..63ef148 100644
--- a/doc/chcli.cfg
+++ b/doc/chcli.cfg
@@ -9,6 +9,9 @@
## provide the host:port pair of the chunkd service
# host=127.0.0.1:9191
+## provide the initial table to open and communicate with
+# table=my_table_name
+
## provide the username and secret key password for authentication.
## password is ready from CHCLI_PASSWORD env var, if not supplied here.
# username=guest
diff --git a/include/chunk_msg.h b/include/chunk_msg.h
index 90272ca..4d3d208 100644
--- a/include/chunk_msg.h
+++ b/include/chunk_msg.h
@@ -21,6 +21,8 @@ enum chunksrv_ops {
CHO_DEL = 4,
CHO_LIST = 5,
CHO_LOGIN = 6,
+ CHO_TABLE_OPEN = 7,
+ CHO_TABLE_DEL = 8,
};
enum chunk_errcode {
@@ -32,10 +34,12 @@ enum chunk_errcode {
che_NoSuchKey = 5,
che_SignatureDoesNotMatch = 6,
che_InvalidKey = 7,
+ che_InvalidTable = 8,
};
enum chunk_flags {
CHF_SYNC = (1 << 0), /* force write to media */
+ CHF_TABLE_NEW = (1 << 1), /* create table */
};
struct chunksrv_req {
diff --git a/include/chunkc.h b/include/chunkc.h
index 768eecd..04cbd87 100644
--- a/include/chunkc.h
+++ b/include/chunkc.h
@@ -44,6 +44,8 @@ extern void stc_init(void);
extern struct st_client *stc_new(const char *service_host, int port,
const char *user, const char *secret_key,
bool encrypt);
+extern bool stc_table_open(struct st_client *stc, const void *key, size_t
key_len,
+ uint32_t flags);
extern bool stc_get(struct st_client *stc, const void *key, size_t key_len,
size_t (*write_cb)(void *, size_t, size_t, void *),
@@ -103,4 +105,10 @@ static inline bool stc_delz(struct st_client *stc, const
char *key)
return stc_del(stc, key, strlen(key) + 1);
}
+static inline bool stc_table_openz(struct st_client *stc, const char *key,
+ uint32_t flags)
+{
+ return stc_table_open(stc, key, strlen(key) + 1, flags);
+}
+
#endif /* __STC_H__ */
diff --git a/lib/chunkdc.c b/lib/chunkdc.c
index 1597e91..c9606a8 100644
--- a/lib/chunkdc.c
+++ b/lib/chunkdc.c
@@ -453,6 +453,48 @@ size_t stc_get_recv(struct st_client *stc, void *data,
size_t data_len)
return done_cnt;
}
+bool stc_table_open(struct st_client *stc, const void *key, size_t key_len,
+ uint32_t flags)
+{
+ struct chunksrv_resp resp;
+ struct chunksrv_req *req = (struct chunksrv_req *) stc->req_buf;
+
+ if (stc->verbose)
+ fprintf(stderr, "libstc: TABLE OPEN(%u, %u)\n",
+ (unsigned int) key_len,
+ flags);
+
+ if (!key_valid(key, key_len))
+ return false;
+
+ /* initialize request */
+ req_init(stc, req);
+ req->op = CHO_TABLE_OPEN;
+ req->flags = (flags & CHF_TABLE_NEW);
+ req_set_key(req, key, key_len);
+
+ /* sign request */
+ chreq_sign(req, stc->key, req->sig);
+
+ /* write request */
+ if (!net_write(stc, req, req_len(req)))
+ return false;
+
+ /* read response header */
+ if (!net_read(stc, &resp, sizeof(resp)))
+ return false;
+
+ /* check response code */
+ if (resp.resp_code != che_Success) {
+ if (stc->verbose)
+ fprintf(stderr, "TABLE OPEN resp code: %d\n",
+ resp.resp_code);
+ return false;
+ }
+
+ return true;
+}
+
bool stc_put(struct st_client *stc, const void *key, size_t key_len,
size_t (*read_cb)(void *, size_t, size_t, void *),
uint64_t len, void *user_data, uint32_t flags)
diff --git a/server/Makefile.am b/server/Makefile.am
index 70fd066..7589a38 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -1,5 +1,6 @@
-INCLUDES = -I$(top_srcdir)/include @GLIB_CFLAGS@ @CLDC_CFLAGS@
+INCLUDES = -I$(top_srcdir)/include @GLIB_CFLAGS@ @CLDC_CFLAGS@ \
+ @TOKYOCABINET_CFLAGS@
sbin_PROGRAMS = chunkd
@@ -8,4 +9,5 @@ chunkd_SOURCES = chunkd.h \
be-fs.c object.c server.c config.c cldu.c util.c
chunkd_LDADD = \
@CLDC_LIBS@ @GLIB_LIBS@ @CRYPTO_LIBS@ \
- @SSL_LIBS@ @EVENT_LIBS@ @ARGP_LIBS@ @SOCKET_LIBS@
+ @SSL_LIBS@ @EVENT_LIBS@ @ARGP_LIBS@ @SOCKET_LIBS@ \
+ @TOKYOCABINET_LIBS@
diff --git a/server/be-fs.c b/server/be-fs.c
index fb301b8..48ce698 100644
--- a/server/be-fs.c
+++ b/server/be-fs.c
@@ -16,10 +16,14 @@
#include <string.h>
#include <errno.h>
#include <syslog.h>
+#include <tcutil.h>
+#include <tchdb.h>
#include "chunkd.h"
#define BE_NAME "fs"
+#define MDB_TABLE_ID "__chunkd_table_id"
+
struct fs_obj {
struct backend_obj bo;
@@ -37,6 +41,103 @@ struct be_fs_obj_hdr {
uint32_t key_len;
};
+bool fs_table_open(const char *user, const void *kbuf, size_t klen,
+ bool create_tbl, uint32_t *table_id,
+ enum chunk_errcode *err_code)
+{
+ TCHDB *hdb;
+ char *db_fn = NULL, *table_path = NULL;
+ int omode, osize = 0, next_num;
+ bool rc = false;
+ uint32_t *val_p, table_id_le;
+
+ *err_code = che_InternalError;
+
+ /* validate table name */
+ if (klen < 1 || klen > CHD_KEY_SZ ||
+ (klen >= strlen(MDB_TABLE_ID) &&
+ !memcmp(kbuf, MDB_TABLE_ID, strlen(MDB_TABLE_ID)))) {
+ *err_code = che_InvalidArgument;
+ return false;
+ }
+
+ /*
+ * open master database
+ */
+ if (asprintf(&db_fn, "%s/master.tch", chunkd_srv.vol_path) < 0)
+ return false;
+
+ hdb = tchdbnew();
+ if (!hdb)
+ goto out;
+
+ omode = HDBOREADER | HDBONOLCK;
+ if (create_tbl)
+ omode |= HDBOWRITER | HDBOCREAT | HDBOTSYNC;
+ if (!tchdbopen(hdb, db_fn, omode)) {
+ applog(LOG_ERR, "failed to open master table %s", db_fn);
+ goto out_hdb;
+ }
+
+ /*
+ * lookup table name. if found, return immediately
+ */
+ val_p = tchdbget(hdb, kbuf, klen, &osize);
+ if (val_p) {
+ if (create_tbl) {
+ *err_code = che_InvalidArgument;
+ goto out_close;
+ }
+
+ *table_id = GUINT32_FROM_LE(*val_p);
+ goto out_ok;
+ }
+
+ /*
+ * otherwise, we now begin the process of table creation
+ */
+
+ if (!create_tbl) {
+ *err_code = che_InvalidArgument;
+ goto out_close;
+ }
+
+ /* allocate unique integer id for table */
+ next_num = tchdbaddint(hdb, MDB_TABLE_ID, strlen(MDB_TABLE_ID)+1, 1);
+ if (next_num == INT_MIN)
+ goto out_close;
+
+ *table_id = next_num;
+ table_id_le = GUINT32_TO_LE(next_num);
+
+ /*
+ * create table directory, $BASE_PATH/table-id
+ */
+ if (asprintf(&table_path, "%s/%d", chunkd_srv.vol_path, next_num) < 0)
+ goto out_close;
+
+ if ((mkdir(table_path, 0777) < 0) && (errno != EEXIST)) {
+ applog(LOG_ERR, "mkdir(%s): %s", table_path, strerror(errno));
+ goto out_close;
+ }
+
+ /* finally, store in table_name->table_id map */
+ if (!tchdbput(hdb, kbuf, klen, &table_id_le, sizeof(table_id_le)))
+ goto out_close;
+
+out_ok:
+ *err_code = che_Success;
+ rc = true;
+out_close:
+ tchdbclose(hdb);
+out_hdb:
+ tchdbdel(hdb);
+out:
+ free(db_fn);
+ free(table_path);
+ return rc;
+}
+
static struct fs_obj *fs_obj_alloc(void)
{
struct fs_obj *obj;
@@ -53,7 +154,7 @@ static struct fs_obj *fs_obj_alloc(void)
return obj;
}
-static char *fs_obj_pathname(const void *key, size_t key_len)
+static char *fs_obj_pathname(uint32_t table_id,const void *key, size_t key_len)
{
char *s = NULL;
char prefix[5] = "";
@@ -62,19 +163,23 @@ static char *fs_obj_pathname(const void *key, size_t
key_len)
unsigned char md[SHA256_DIGEST_LENGTH];
char mdstr[(SHA256_DIGEST_LENGTH * 2) + 1];
+ if (!table_id || !key || !key_len)
+ return NULL;
+
SHA256(key, key_len, md);
hexstr(md, SHA256_DIGEST_LENGTH, mdstr);
memcpy(prefix, mdstr, 4);
- slen = strlen(chunkd_srv.vol_path) + 1 +
- strlen(prefix) + 1 +
- strlen(mdstr) + 1;
+ slen = strlen(chunkd_srv.vol_path) + 1 + /* volume */
+ 16 + /* table id */
+ strlen(prefix) + 1 + /* prefix */
+ strlen(mdstr) + 1; /* filename */
s = malloc(slen);
if (!s)
return NULL;
- sprintf(s, "%s/%s", chunkd_srv.vol_path, prefix);
+ sprintf(s, "%s/%u/%s", chunkd_srv.vol_path, table_id, prefix);
/* create subdir on the fly, if not already exists */
if (stat(s, &st) < 0) {
@@ -97,7 +202,8 @@ static char *fs_obj_pathname(const void *key, size_t key_len)
goto err_out;
}
- sprintf(s, "%s/%s/%s", chunkd_srv.vol_path, prefix, mdstr + 4);
+ sprintf(s, "%s/%u/%s/%s", chunkd_srv.vol_path, table_id,
+ prefix, mdstr + 4);
return s;
@@ -114,7 +220,8 @@ static bool key_valid(const void *key, size_t key_len)
return true;
}
-struct backend_obj *fs_obj_new(const void *key, size_t key_len,
+struct backend_obj *fs_obj_new(uint32_t table_id,
+ const void *key, size_t key_len,
enum chunk_errcode *err_code)
{
struct fs_obj *obj;
@@ -136,7 +243,7 @@ struct backend_obj *fs_obj_new(const void *key, size_t
key_len,
}
/* build local fs pathname */
- fn = fs_obj_pathname(key, key_len);
+ fn = fs_obj_pathname(table_id, key, key_len);
if (!fn) {
applog(LOG_ERR, "OOM in object_put");
*err_code = che_InternalError;
@@ -194,8 +301,9 @@ err_out:
return NULL;
}
-struct backend_obj *fs_obj_open(const char *user, const void *key,
- size_t key_len, enum chunk_errcode *err_code)
+struct backend_obj *fs_obj_open(uint32_t table_id, const char *user,
+ const void *key, size_t key_len,
+ enum chunk_errcode *err_code)
{
struct fs_obj *obj;
struct stat st;
@@ -214,7 +322,7 @@ struct backend_obj *fs_obj_open(const char *user, const
void *key,
}
/* build local fs pathname */
- obj->in_fn = fs_obj_pathname(key, key_len);
+ obj->in_fn = fs_obj_pathname(table_id, key, key_len);
if (!obj->in_fn) {
*err_code = che_InternalError;
goto err_out;
@@ -457,7 +565,8 @@ bool fs_obj_write_commit(struct backend_obj *bo, const char
*user,
return true;
}
-bool fs_obj_delete(const char *user, const void *key, size_t key_len,
+bool fs_obj_delete(uint32_t table_id, const char *user,
+ const void *key, size_t key_len,
enum chunk_errcode *err_code)
{
char *fn = NULL;
@@ -473,7 +582,7 @@ bool fs_obj_delete(const char *user, const void *key,
size_t key_len,
}
/* build local fs pathname */
- fn = fs_obj_pathname(key, key_len);
+ fn = fs_obj_pathname(table_id, key, key_len);
if (!fn)
goto err_out;
@@ -532,18 +641,22 @@ err_out:
return false;
}
-GList *fs_list_objs(const char *user)
+GList *fs_list_objs(uint32_t table_id, const char *user)
{
GList *res = NULL;
struct dirent *de, *root_de;
DIR *d, *root;
- char *sub;
+ char *sub, *table_path = NULL;
+
+ sub = alloca(strlen(chunkd_srv.vol_path) + 1 + 16 + 4 + 1);
- sub = alloca(strlen(chunkd_srv.vol_path) + 1 + 4 + 1);
+ if (asprintf(&table_path, "%s/%u", chunkd_srv.vol_path, table_id) < 0)
+ return NULL;
- root = opendir(chunkd_srv.vol_path);
+ root = opendir(table_path);
if (!root) {
- syslogerr(chunkd_srv.vol_path);
+ syslogerr(table_path);
+ free(table_path);
return NULL;
}
@@ -555,7 +668,7 @@ GList *fs_list_objs(const char *user)
if (strlen(root_de->d_name) != 4)
continue;
- sprintf(sub, "%s/%s", chunkd_srv.vol_path, root_de->d_name);
+ sprintf(sub, "%s/%s", table_path, root_de->d_name);
d = opendir(sub);
if (!d) {
syslogerr(sub);
@@ -688,6 +801,7 @@ GList *fs_list_objs(const char *user)
closedir(root);
+ free(table_path);
return res;
}
diff --git a/server/chunkd.h b/server/chunkd.h
index d6b37c6..2058144 100644
--- a/server/chunkd.h
+++ b/server/chunkd.h
@@ -78,6 +78,9 @@ struct client {
char user[CHD_USER_SZ + 1];
+ size_t table_len;
+ uint32_t table_id;
+
SSL *ssl;
bool read_want_write;
bool write_want_read;
@@ -107,6 +110,7 @@ struct client {
char netbuf[CLI_DATA_BUF_SZ];
char netbuf_out[CLI_DATA_BUF_SZ];
char key[CHD_KEY_SZ];
+ char table[CHD_KEY_SZ];
};
struct backend_obj {
@@ -192,9 +196,9 @@ struct server {
};
/* be-fs.c */
-extern struct backend_obj *fs_obj_new(const void *kbuf, size_t klen,
+extern struct backend_obj *fs_obj_new(uint32_t table_id, const void *kbuf,
size_t klen,
enum chunk_errcode *err_code);
-extern struct backend_obj *fs_obj_open(const char *user,
+extern struct backend_obj *fs_obj_open(uint32_t table_id, const char *user,
const void *kbuf, size_t klen,
enum chunk_errcode *err_code);
extern ssize_t fs_obj_write(struct backend_obj *bo, const void *ptr, size_t
len);
@@ -202,11 +206,14 @@ extern ssize_t fs_obj_read(struct backend_obj *bo, void
*ptr, size_t len);
extern void fs_obj_free(struct backend_obj *bo);
extern bool fs_obj_write_commit(struct backend_obj *bo, const char *user,
const char *hashstr, bool sync_data);
-extern bool fs_obj_delete(const char *user,
+extern bool fs_obj_delete(uint32_t table_id, const char *user,
const void *kbuf, size_t klen,
enum chunk_errcode *err_code);
-extern GList *fs_list_objs(const char *user);
extern ssize_t fs_obj_sendfile(struct backend_obj *bo, int out_fd, size_t len);
+extern GList *fs_list_objs(uint32_t table_id, const char *user);
+extern bool fs_table_open(const char *user, const void *kbuf, size_t klen,
+ bool create_tbl, uint32_t *table_id,
+ enum chunk_errcode *err_code);
/* object.c */
extern bool object_del(struct client *cli);
diff --git a/server/object.c b/server/object.c
index 23b0aa9..027ce2b 100644
--- a/server/object.c
+++ b/server/object.c
@@ -30,7 +30,8 @@ bool object_del(struct client *cli)
resp_init_req(resp, &cli->creq);
- rcb = fs_obj_delete(cli->user, cli->key, cli->key_len, &err);
+ rcb = fs_obj_delete(cli->table_id, cli->user,
+ cli->key, cli->key_len, &err);
if (!rcb)
return cli_err(cli, err, true);
@@ -196,7 +197,7 @@ bool object_put(struct client *cli)
if (!user)
return cli_err(cli, che_AccessDenied, true);
- cli->out_bo = fs_obj_new(cli->key, cli->key_len, &err);
+ cli->out_bo = fs_obj_new(cli->table_id, cli->key, cli->key_len, &err);
if (!cli->out_bo)
return cli_err(cli, err, true);
@@ -286,7 +287,7 @@ bool object_get(struct client *cli, bool want_body)
resp_init_req(&get_resp->resp, &cli->creq);
- cli->in_obj = obj = fs_obj_open(cli->user, cli->key,
+ cli->in_obj = obj = fs_obj_open(cli->table_id, cli->user, cli->key,
cli->key_len, &err);
if (!obj) {
free(get_resp);
diff --git a/server/server.c b/server/server.c
index 2d9095f..a145706 100644
--- a/server/server.c
+++ b/server/server.c
@@ -117,6 +117,10 @@ static struct {
[che_InvalidKey] =
{ "che_InvalidKey", 400,
"Invalid key presented" },
+
+ [che_InvalidTable] =
+ { "che_InvalidTable", 400,
+ "Invalid table requested, or table not open" },
};
void applog(int prio, const char *fmt, ...)
@@ -728,7 +732,7 @@ static bool volume_list(struct client *cli)
bool rcb;
GList *res = NULL;
- res = fs_list_objs(cli->user);
+ res = fs_list_objs(cli->table_id, cli->user);
s = g_markup_printf_escaped(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
@@ -784,6 +788,23 @@ static bool volume_list(struct client *cli)
return rcb;
}
+static bool volume_open(struct client *cli)
+{
+ enum chunk_errcode err = che_Success;
+
+ if (!fs_table_open(cli->user, cli->key, cli->key_len,
+ (cli->creq.flags & CHF_TABLE_NEW),
+ &cli->table_id, &err))
+ goto out;
+
+ memset(cli->table, 0, sizeof(cli->table));
+ memcpy(cli->table, cli->key, cli->key_len);
+ cli->table_len = cli->key_len;
+
+out:
+ return cli_err(cli, err, true);
+}
+
static bool authcheck(const struct chunksrv_req *req, const void *key,
size_t key_len, const char *secret_key)
{
@@ -854,6 +875,8 @@ static const char *op2str(enum chunksrv_ops op)
case CHO_DEL: return "CHO_DEL";
case CHO_LIST: return "CHO_LIST";
case CHO_LOGIN: return "CHO_LOGIN";
+ case CHO_TABLE_OPEN: return "CHO_TABLE_OPEN";
+ case CHO_TABLE_DEL: return "CHO_TABLE_DEL";
default:
return "BUG/UNKNOWN!";
@@ -867,14 +890,13 @@ static bool cli_evt_exec_req(struct client *cli, unsigned
int events)
{
struct chunksrv_req *req = &cli->creq;
bool rcb;
- enum chunk_errcode err;
+ enum chunk_errcode err = che_InvalidArgument;
bool logged_in = (cli->user[0] != 0);
+ bool have_table = (cli->table_len > 0);
/* validate request header */
- if (!valid_req_hdr(req)) {
- err = che_InvalidArgument;
+ if (!valid_req_hdr(req))
goto err_out;
- }
if (debugging)
applog(LOG_DEBUG, "REQ(op %s, key %s (%u), user %s) "
@@ -905,10 +927,31 @@ static bool cli_evt_exec_req(struct client *cli, unsigned
int events)
}
/*
+ * verify open-table requirement, for the operations that need it
+ */
+ switch (req->op) {
+ case CHO_GET:
+ case CHO_GET_META:
+ case CHO_PUT:
+ case CHO_DEL:
+ case CHO_LIST:
+ if (!have_table) {
+ err = che_InvalidTable;
+ goto err_out;
+ }
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+
+ /*
* operations on objects
*/
switch (req->op) {
case CHO_LOGIN:
+ if (logged_in)
+ goto err_out;
rcb = login_user(cli);
break;
case CHO_NOP:
@@ -929,6 +972,17 @@ static bool cli_evt_exec_req(struct client *cli, unsigned
int events)
case CHO_LIST:
rcb = volume_list(cli);
break;
+ case CHO_TABLE_OPEN:
+ rcb = volume_open(cli);
+ break;
+ case CHO_TABLE_DEL:
+#if 0
+ /* not implemented yet */
+ rcv = volume_del(cli);
+#else
+ rcb = cli_err(cli, che_InternalError, true);
+#endif
+ break;
default:
rcb = cli_err(cli, che_InvalidURI, true);
break;
diff --git a/test/auth.c b/test/auth.c
index 232efa7..ae28620 100644
--- a/test/auth.c
+++ b/test/auth.c
@@ -29,9 +29,15 @@ static void test(bool encrypt)
stc1 = stc_new(TEST_HOST, port, TEST_USER, TEST_USER_KEY, encrypt);
OK(stc1);
+ rcb = stc_table_openz(stc1, TEST_TABLE, 0);
+ OK(rcb);
+
stc2 = stc_new(TEST_HOST, port, TEST_USER2, TEST_USER2_KEY, encrypt);
OK(stc2);
+ rcb = stc_table_openz(stc2, TEST_TABLE, 0);
+ OK(rcb);
+
/* store object 1 */
rcb = stc_put_inlinez(stc1, key1, val1, strlen(val1), 0);
OK(rcb);
diff --git a/test/basic-object.c b/test/basic-object.c
index 8f4c040..05b8630 100644
--- a/test/basic-object.c
+++ b/test/basic-object.c
@@ -27,6 +27,9 @@ static void test(bool encrypt)
stc = stc_new(TEST_HOST, port, TEST_USER, TEST_USER_KEY, encrypt);
OK(stc);
+ rcb = stc_table_openz(stc, TEST_TABLE, 0);
+ OK(rcb);
+
/* store object */
rcb = stc_put_inlinez(stc, key, val, strlen(val), 0);
OK(rcb);
diff --git a/test/it-works.c b/test/it-works.c
index ef923d8..87233bc 100644
--- a/test/it-works.c
+++ b/test/it-works.c
@@ -21,6 +21,15 @@ static void test(bool ssl)
stc = stc_new(TEST_HOST, port, TEST_USER, TEST_USER_KEY, ssl);
OK(stc);
+ /*
+ * we must supply CHF_TABLE_NEW on the first iteration of
+ * this test, because we are the first test in the testsuite,
+ * and must create the database to be used by all other tests.
+ */
+ rcb = stc_table_openz(stc, TEST_TABLE,
+ ssl ? 0 : CHF_TABLE_NEW);
+ OK(rcb);
+
rcb = stc_ping(stc);
OK(rcb);
diff --git a/test/large-object.c b/test/large-object.c
index f49ca64..f0884b8 100644
--- a/test/large-object.c
+++ b/test/large-object.c
@@ -93,6 +93,9 @@ static void test(bool encrypt)
stc = stc_new(TEST_HOST, port, TEST_USER, TEST_USER_KEY, encrypt);
OK(stc);
+ rcb = stc_table_openz(stc, TEST_TABLE, 0);
+ OK(rcb);
+
sync();
gettimeofday(&ta, NULL);
diff --git a/test/lotsa-objects.c b/test/lotsa-objects.c
index bf6b96d..fbf5f81 100644
--- a/test/lotsa-objects.c
+++ b/test/lotsa-objects.c
@@ -33,6 +33,9 @@ static void test(int n_objects, bool encrypt)
stc = stc_new(TEST_HOST, port, TEST_USER, TEST_USER_KEY, encrypt);
OK(stc);
+ rcb = stc_table_openz(stc, TEST_TABLE, 0);
+ OK(rcb);
+
fprintf(stderr, " lotsa-objects syncing...\n");
sync();
diff --git a/test/nop.c b/test/nop.c
index c683dc4..5771a2c 100644
--- a/test/nop.c
+++ b/test/nop.c
@@ -28,6 +28,9 @@ static void test(int n_nops, bool encrypt)
stc = stc_new(TEST_HOST, port, TEST_USER, TEST_USER_KEY, encrypt);
OK(stc);
+ rcb = stc_table_openz(stc, TEST_TABLE, 0);
+ OK(rcb);
+
gettimeofday(&ta, NULL);
/* send NOP messages */
diff --git a/test/test.h b/test/test.h
index dd99843..3321587 100644
--- a/test/test.h
+++ b/test/test.h
@@ -8,6 +8,8 @@
#define TEST_HOST "localhost"
+#define TEST_TABLE "test"
+
#define TEST_USER "testuser"
#define TEST_USER_KEY "testuser"
diff --git a/tools/chcli.c b/tools/chcli.c
index bc8fad5..0d995a9 100644
--- a/tools/chcli.c
+++ b/tools/chcli.c
@@ -36,6 +36,8 @@ static struct argp_option options[] = {
"Send GET output to FILE, rather than stdout" },
{ "ssl", 'S', NULL, 0,
"Enable SSL channel security" },
+ { "table", 't', "TABLE", 0,
+ "Set table for storage and retrieval" },
{ "user", 'u', "USER", 0,
"Set username to USER" },
{ "verbose", 'v', NULL, 0,
@@ -43,6 +45,8 @@ static struct argp_option options[] = {
{ "list-cmds", 1001, NULL, 0,
"List supported commands" },
+ { "create", 1002, NULL, 0,
+ "Create new table (required, if table does not exist)" },
{ }
};
@@ -78,6 +82,9 @@ static char *password;
static char *output_fn;
static char *key_data;
static gsize key_data_len;
+static char *table_name;
+static size_t table_name_len;
+static bool table_create;
static char *password_env = "CHCLI_PASSWORD";
static bool chcli_verbose;
static bool use_ssl;
@@ -197,6 +204,10 @@ static error_t parse_opt (int key, char *arg, struct
argp_state *state)
free(s);
}
+ table_name = g_key_file_get_string(config, "global", "table",
+ NULL);
+ if (table_name)
+ table_name_len = strlen(table_name) + 1;
password = g_key_file_get_string(config, "global", "password",
NULL);
@@ -245,16 +256,23 @@ static error_t parse_opt (int key, char *arg, struct
argp_state *state)
case 'o':
output_fn = arg;
break;
- case 'v':
- chcli_verbose = true;
- break;
case 'S':
use_ssl = true;
break;
+ case 't':
+ table_name = arg;
+ table_name_len = strlen(arg) + 1;
+ break;
+ case 'v':
+ chcli_verbose = true;
+ break;
case 1001: /* --list-cmds */
show_cmds();
break;
+ case 1002: /* --create */
+ table_create = true;
+ break;
case ARGP_KEY_ARG:
if (cmd_mode != CHC_NONE)
@@ -298,6 +316,15 @@ static struct st_client *chcli_stc_new(void)
stc->verbose = chcli_verbose;
+ if (!stc_table_open(stc, table_name, table_name_len,
+ table_create ? CHF_TABLE_NEW : 0)) {
+ fprintf(stderr, "%s:%u: failed to open table\n",
+ host->name,
+ host->port);
+ stc_free(stc);
+ return NULL;
+ }
+
return stc;
}
@@ -527,7 +554,10 @@ int main (int argc, char *argv[])
fprintf(stderr, "no host specified\n");
return 1;
}
-
+ if (!table_name || !table_name_len) {
+ fprintf(stderr, "no table name specified\n");
+ return 1;
+ }
if (strlen(username) == 0) {
fprintf(stderr, "no username specified\n");
return 1;
--
To unsubscribe from this list: send the line "unsubscribe hail-devel" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html