Created
June 4, 2009 00:40
-
-
Save dougm/123360 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* tinkering w/ the idea of a couchdb C api, shelved in favor of http://bit.ly/ccprC | |
* but the jist is to use map keys to lookup structure offsets. | |
* did not give any thought to POST, PUT, etc. | |
* gcc -Wall -g -o ycouchdb ycouchdb.c -I/usr/local/include/yajl -L/usr/local/lib -lyajl | |
* curl -s http://localhost:5984/_stats/httpd/requests | ./ycouchdb /_stats/httpd/requests | |
*/ | |
#include <yajl/yajl_parse.h> | |
#include <yajl/yajl_gen.h> | |
#define _GNU_SOURCE /* strndup */ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#define couch_offsetof(type, field) ((size_t)(&((type *)0)->field)) | |
#define couch_map_ent(s, n) { #n, couch_offsetof(s, n) } | |
typedef struct { | |
char *ptr; | |
size_t offset; | |
} couch_map_ent_t; | |
typedef struct { | |
long doc_count; | |
long doc_del_count; | |
long update_seq; | |
long disk_size; | |
} database_t; | |
static couch_map_ent_t database_stats[] = { | |
couch_map_ent(database_t, doc_count), | |
couch_map_ent(database_t, doc_del_count), | |
couch_map_ent(database_t, update_seq), | |
couch_map_ent(database_t, disk_size), | |
{ NULL } | |
}; | |
typedef struct { | |
long current; | |
long max; | |
long min; | |
long count; | |
double mean; | |
double stddev; | |
} httpd_requests_t; | |
static couch_map_ent_t httpd_requests[] = { | |
couch_map_ent(httpd_requests_t, current), | |
couch_map_ent(httpd_requests_t, max), | |
couch_map_ent(httpd_requests_t, min), | |
couch_map_ent(httpd_requests_t, count), | |
couch_map_ent(httpd_requests_t, mean), | |
couch_map_ent(httpd_requests_t, stddev), | |
{ NULL } | |
}; | |
typedef struct { | |
int num; | |
int size; | |
char **values; | |
} couch_array_t; | |
typedef struct { | |
enum { | |
LIST, | |
HTTPD_REQUESTS, | |
DATABASE | |
} type; | |
union { | |
couch_array_t dbs; | |
database_t db; | |
httpd_requests_t hr; | |
} data; | |
couch_map_ent_t *map; | |
couch_map_ent_t cur; | |
} couchdb_t; | |
static int couchcb_null(void *ctx) | |
{ | |
return 1; | |
} | |
static int couchcb_boolean(void *ctx, int boolean) | |
{ | |
return 1; | |
} | |
static int couchcb_integer(void *ctx, long val) | |
{ | |
couchdb_t *couch = (couchdb_t *)ctx; | |
if (couch->cur.offset >= 0) { | |
*(long *)(couch->cur.ptr + couch->cur.offset) = val; | |
} | |
return 1; | |
} | |
static int couchcb_double(void *ctx, double val) | |
{ | |
couchdb_t *couch = (couchdb_t *)ctx; | |
if (couch->cur.offset >= 0) { | |
*(double *)(couch->cur.ptr + couch->cur.offset) = val; | |
} | |
return 1; | |
} | |
static int couchcb_string(void *ctx, const unsigned char *val, | |
unsigned int len) | |
{ | |
couchdb_t *couch = (couchdb_t *)ctx; | |
if (couch->cur.offset == LIST) { | |
couch_array_t *list = (couch_array_t *)couch->cur.ptr; | |
char *copy = strndup((const char *)val, len); | |
if (list->num >= list->size) { | |
list->size += 2; | |
list->values = | |
realloc(list->values, list->size * sizeof(list->values[0])); | |
} | |
list->values[list->num++] = copy; | |
} | |
return 1; | |
} | |
static int couchcb_map_key(void *ctx, const unsigned char *key, | |
unsigned int len) | |
{ | |
couchdb_t *couch = (couchdb_t *)ctx; | |
int i; | |
couch->cur.offset = -1; | |
if (couch->map == NULL) { | |
return 1; | |
} | |
for (i=0; couch->map[i].ptr != NULL; i++) { | |
if (strncmp((char *)key, couch->map[i].ptr, len) == 0) { | |
couch->cur.offset = couch->map[i].offset; | |
return 1; | |
} | |
} | |
return 1; | |
} | |
static int couchcb_start_map(void *ctx) | |
{ | |
return 1; | |
} | |
static int couchcb_end_map(void *ctx) | |
{ | |
return 1; | |
} | |
static int couchcb_start_array(void *ctx) | |
{ | |
return 1; | |
} | |
static int couchcb_end_array(void *ctx) | |
{ | |
return 1; | |
} | |
static yajl_callbacks callbacks = { | |
couchcb_null, | |
couchcb_boolean, | |
couchcb_integer, | |
couchcb_double, | |
NULL, | |
couchcb_string, | |
couchcb_start_map, | |
couchcb_map_key, | |
couchcb_end_map, | |
couchcb_start_array, | |
couchcb_end_array | |
}; | |
int main(int argc, char ** argv) | |
{ | |
yajl_handle hand; | |
unsigned char buffer[65536]; | |
yajl_status stat; | |
size_t rd; | |
int done = 0; | |
couchdb_t couch; | |
char *path = argv[1]; | |
if (strcmp(path, "/chef") == 0) { | |
couch.type = DATABASE; | |
couch.map = &database_stats[0]; | |
memset(&couch.data.db, '\0', sizeof(couch.data.db)); | |
couch.cur.ptr = (char *)&couch.data.db; | |
} | |
else if (strcmp(path, "/_stats/httpd/requests") == 0) { | |
couch.type = HTTPD_REQUESTS; | |
couch.map = &httpd_requests[0]; | |
memset(&couch.data.hr, '\0', sizeof(couch.data.hr)); | |
couch.cur.ptr = (char *)&couch.data.hr; | |
} | |
else if (strcmp(path, "/_all_dbs") == 0) { | |
couch.type = LIST; | |
couch.data.dbs.num = 0; | |
couch.data.dbs.size = 2; | |
couch.data.dbs.values = | |
malloc(couch.data.dbs.size * sizeof(couch.data.dbs.values[0])); | |
couch.map = NULL; | |
couch.cur.ptr = (char *)&couch.data.dbs; | |
couch.cur.offset = couch.type; | |
} | |
else { | |
printf("fail\n"); | |
exit(0); | |
} | |
hand = yajl_alloc(&callbacks, NULL, NULL, (void *)&couch); | |
while (!done) { | |
rd = fread((void *)buffer, 1, sizeof(buffer)-1, stdin); | |
if (rd == 0) { | |
done = 1; | |
} | |
buffer[rd] = 0; | |
if (done) | |
stat = yajl_parse_complete(hand); /* flush */ | |
else | |
stat = yajl_parse(hand, buffer, rd); | |
if (stat != yajl_status_ok && | |
stat != yajl_status_insufficient_data) | |
{ | |
unsigned char *str = yajl_get_error(hand, 1, buffer, rd); | |
fprintf(stderr, (const char *) str); | |
yajl_free_error(hand, str); | |
} | |
} | |
if (couch.type == DATABASE) { | |
printf("doc_count:%ld,doc_del_count:%ld,update_seq:%ld,disk_size:%ld\n", | |
couch.data.db.doc_count, | |
couch.data.db.doc_del_count, | |
couch.data.db.update_seq, | |
couch.data.db.disk_size); | |
} | |
else if (couch.type == HTTPD_REQUESTS) { | |
printf("current:%ld,max:%ld,min:%ld,count:%ld\n", | |
couch.data.hr.current, | |
couch.data.hr.max, | |
couch.data.hr.min, | |
couch.data.hr.count); | |
} | |
else if (couch.type == LIST) { | |
int i; | |
for (i=0; i<couch.data.dbs.num; i++) { | |
printf("%s\n", couch.data.dbs.values[i]); | |
} | |
} | |
yajl_free(hand); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment