Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 145 additions & 9 deletions etcd-api.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ int g_inited = 0;
const char *value_path[] = { "node", "value", NULL };
const char *nodes_path[] = { "node", "nodes", NULL };
const char *entry_path[] = { "key", NULL };
const char *node_path[] = { "node", NULL };

/*
* We only call this in case where it should be safe, but gcc doesn't know
Expand Down Expand Up @@ -232,8 +233,8 @@ parse_get_response (void *ptr, size_t size, size_t nmemb, void *stream)


etcd_result
etcd_get_one (_etcd_session *session, char *key, etcd_server *srv, char *prefix,
char *post, curl_callback_t cb, char **stream)
etcd_get_one (_etcd_session *session, const char *key, etcd_server *srv, const char *prefix,
const char *post, curl_callback_t cb, char **stream)
{
char *url;
CURL *curl;
Expand Down Expand Up @@ -284,7 +285,7 @@ etcd_get_one (_etcd_session *session, char *key, etcd_server *srv, char *prefix,


char *
etcd_get (etcd_session session_as_void, char *key)
etcd_get (etcd_session session_as_void, const char *key)
{
_etcd_session *session = session_as_void;
etcd_server *srv;
Expand Down Expand Up @@ -335,7 +336,7 @@ parse_watch_response (void *ptr, size_t size, size_t nmemb, void *stream)


etcd_result
etcd_watch (etcd_session session_as_void, char *pfx,
etcd_watch (etcd_session session_as_void, const char *pfx,
char **keyp, char **valuep, int *index_in, int *index_out)
{
_etcd_session *session = session_as_void;
Expand Down Expand Up @@ -430,8 +431,8 @@ parse_lock_response (void *ptr, size_t size, size_t nmemb, void *stream)
* an HTTP PUT instead.
*/
etcd_result
etcd_set_one (_etcd_session *session, char *key, char *value,
char *precond, unsigned int ttl, etcd_server *srv,
etcd_set_one (_etcd_session *session, const char *key, const char *value,
const char *precond, unsigned int ttl, etcd_server *srv,
char **is_lock)
{
char *url;
Expand Down Expand Up @@ -606,8 +607,8 @@ etcd_set_one (_etcd_session *session, char *key, char *value,


etcd_result
etcd_set (etcd_session session_as_void, char *key, char *value,
char *precond, unsigned int ttl)
etcd_set (etcd_session session_as_void, const char *key, const char *value,
const char *precond, unsigned int ttl)
{
_etcd_session *session = session_as_void;
etcd_server *srv;
Expand Down Expand Up @@ -637,7 +638,7 @@ etcd_set (etcd_session session_as_void, char *key, char *value,
* value with a TTL, but I haven't actually tried it.
*/
etcd_result
etcd_delete (etcd_session session_as_void, char *key)
etcd_delete (etcd_session session_as_void, const char *key)
{
_etcd_session *session = session_as_void;
etcd_server *srv;
Expand Down Expand Up @@ -828,3 +829,138 @@ etcd_close_str (etcd_session session)
free_sl(((_etcd_session *)session)->servers);
etcd_close(session);
}

void etcd_list_free(etcd_tree **tree) {
if (tree == NULL || *tree == NULL) {
return;
}

free((*tree)->key);
if ((*tree)->value != NULL) free((*tree)->value);

if ((*tree)->isDir) {
etcd_tree **iter = (*tree)->nodes;
while (iter != NULL && *iter != NULL) {
etcd_list_free(iter++);
}
free((*tree)->nodes);
}

free(*tree);
}

void
parse_list_response_inner(etcd_tree *tree, yajl_val node) {
int i;
tree->value = NULL;
tree->nodes = NULL;
tree->isDir = yajl_t_false;
for (i = 0; i < node->u.object.len; ++i) {
const char *key = node->u.object.keys[i];
yajl_val value = node->u.object.values[i];
if (strcmp(key, "key") == 0 && YAJL_IS_STRING(value)) {
tree->key = strdup(MY_YAJL_GET_STRING(value));
} else if (strcmp(key, "dir") == 0) {
tree->isDir = YAJL_IS_TRUE(value);
} else if (strcmp(key, "modifiedIndex") == 0 && YAJL_IS_INTEGER(value)) {
tree->modifiedIndex = YAJL_GET_INTEGER(value);
} else if (strcmp(key, "createdIndex") == 0 && YAJL_IS_INTEGER(value)) {
tree->createdIndex = YAJL_GET_INTEGER(value);
} else if (strcmp(key, "value") == 0 && YAJL_IS_STRING(value)) {
tree->value = strdup(MY_YAJL_GET_STRING(value));
} else if (strcmp(key, "nodes") == 0 && YAJL_IS_ARRAY(value)) {
tree->nodes = malloc((value->u.array.len+1)*sizeof(struct etcd_tree*));
tree->nodes[value->u.array.len] = NULL;
int j;
for (j = 0; j < value->u.array.len; ++j) {
tree->nodes[j] = malloc(sizeof(struct etcd_tree));
parse_list_response_inner(tree->nodes[j], value->u.array.values[j]);
}
}
}
}

size_t
parse_list_response (void *ptr, size_t size, size_t nmemb, void *void_tree) {
etcd_tree **tree = (etcd_tree**) void_tree;
yajl_val node;
yajl_val value;

// TODO is this ptr null terminated?
node = yajl_tree_parse(ptr, NULL, 0);

if (node) {
// We have a result to send back
value = my_yajl_tree_get(node, node_path, yajl_t_object);
if (value && YAJL_IS_OBJECT(value)) {
*tree = malloc(sizeof(etcd_tree));
parse_list_response_inner(*tree, value);
}
}

return size*nmemb;
}

etcd_result
etcd_get_many (_etcd_session *session, const char *key, etcd_server *srv, const char *prefix,
curl_callback_t cb, etcd_tree **tree)
{
char *url;
CURL *curl;
CURLcode curl_res;
etcd_result res = ETCD_WTF;
void *err_label = &&done;

if (asprintf(&url,"http://%s:%u/v2/%s%s?recursive=true",
srv->host,srv->port,prefix,key) < 0) {
goto *err_label;
}
err_label = &&free_url;

curl = curl_easy_init();
if (!curl) {
goto *err_label;
}
err_label = &&cleanup_curl;

/* TBD: add error checking for these */
curl_easy_setopt(curl,CURLOPT_URL,url);
curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1L);
curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,cb);
curl_easy_setopt(curl,CURLOPT_WRITEDATA,tree);
#if defined(DEBUG)
curl_easy_setopt(curl,CURLOPT_VERBOSE,1L);
#endif

curl_res = curl_easy_perform(curl);
if (curl_res != CURLE_OK) {
print_curl_error("perform",curl_res);
goto *err_label;
}

res = ETCD_OK;

cleanup_curl:
curl_easy_cleanup(curl);
free_url:
free(url);
done:
return res;
}

etcd_result
etcd_list(etcd_session session_as_void, const char *key,
etcd_tree **tree) {
_etcd_session *session = session_as_void;
etcd_server *srv;
etcd_result res = ETCD_WTF;

for (srv = session->servers; srv->host; ++srv) {
res = etcd_get_many(session, key, srv, "keys/", parse_list_response, tree);
if (res == ETCD_OK) {
return res;
}
}

return res;
}
43 changes: 38 additions & 5 deletions etcd-api.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#pragma once
/*
* Copyright (c) 2013, Red Hat
* All rights reserved.
Expand Down Expand Up @@ -42,6 +43,18 @@ typedef struct {
unsigned short port;
} etcd_server;


struct etcd_tree;

typedef struct etcd_tree {
char *key;
char *value;
int isDir;
int createdIndex;
int modifiedIndex;
struct etcd_tree **nodes;
} etcd_tree;

typedef void *etcd_session;

/*
Expand Down Expand Up @@ -94,7 +107,7 @@ void etcd_close_str (etcd_session session);
* key
* The etcd key (path) to fetch.
*/
char * etcd_get (etcd_session session, char *key);
char * etcd_get (etcd_session session, const char *key);


/*
Expand Down Expand Up @@ -125,7 +138,7 @@ char * etcd_get (etcd_session session, char *key);
* the next. It's entirely legitimate to point both at the same variable.
*/

etcd_result etcd_watch (etcd_session session, char *pfx,
etcd_result etcd_watch (etcd_session session, const char *pfx,
char **keyp, char **valuep,
int *index_in, int *index_out);

Expand All @@ -151,8 +164,8 @@ etcd_result etcd_watch (etcd_session session, char *pfx,
* deleted, or zero to mean no auto-expiration.
*/

etcd_result etcd_set (etcd_session session, char *key, char *value,
char *precond, unsigned int ttl);
etcd_result etcd_set (etcd_session session, const char *key, const char *value,
const char *precond, unsigned int ttl);


/*
Expand All @@ -164,7 +177,7 @@ etcd_result etcd_set (etcd_session session, char *key, char *value,
* The etcd key (path) to delete.
*/

etcd_result etcd_delete (etcd_session session, char *key);
etcd_result etcd_delete (etcd_session session, const char *key);


/*
Expand Down Expand Up @@ -212,3 +225,23 @@ etcd_result etcd_lock (etcd_session session_as_void, char *key,
etcd_result etcd_unlock (etcd_session session_as_void, char *key,
char *index);

/*
* etcd_list_free
*
* Frees a tree retuend by etcd_list.
*/
void etcd_list_free(etcd_tree **tree);

/*
* etcd_tree
*
* Return a tree listing of the children of the specified key.
*
* key
* The path to return a tree for.
*
* tree
* The returned tree, free with etcd_list_free.
*/
etcd_result etcd_list(etcd_session session_as_void, const char *key,
etcd_tree **tree);