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

Reply via email to