All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
couchbase.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or
5  * (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: dc9e3b4cb17048dd0f6316fe90111e94433853d9 $
19  *
20  * @brief Wrapper functions around the libcouchbase Couchbase client driver.
21  * @file couchbase.c
22  *
23  * @author Aaron Hurt <ahurt@anbcs.com>
24  * @copyright 2013-2014 The FreeRADIUS Server Project.
25  */
26 
27 RCSID("$Id: dc9e3b4cb17048dd0f6316fe90111e94433853d9 $")
28 
29 #include <freeradius-devel/radiusd.h>
30 
31 #include <libcouchbase/couchbase.h>
32 #include "../rlm_json/json.h"
33 
34 #include "couchbase.h"
35 
36 /** Couchbase callback for cluster statistics requests
37  *
38  * @param instance Couchbase connection instance.
39  * @param cookie Couchbase cookie for returning information from callbacks.
40  * @param error Couchbase error object.
41  * @param resp Couchbase statistics response object.
42  */
43 void couchbase_stat_callback(lcb_t instance, const void *cookie, lcb_error_t error, const lcb_server_stat_resp_t *resp)
44 {
45  if (error != LCB_SUCCESS) {
46  /* log error */
47  ERROR("rlm_couchbase: (stats_callback) %s (0x%x)", lcb_strerror(instance, error), error);
48  }
49  /* silent compiler */
50  (void)cookie;
51  (void)resp;
52 }
53 
54 /** Couchbase callback for store (write) operations
55  *
56  * @param instance Couchbase connection instance.
57  * @param cookie Couchbase cookie for returning information from callbacks.
58  * @param operation Couchbase storage operation object.
59  * @param error Couchbase error object.
60  * @param resp Couchbase store operation response object.
61  */
62 void couchbase_store_callback(lcb_t instance, const void *cookie, lcb_storage_t operation,
63  lcb_error_t error, const lcb_store_resp_t *resp)
64 {
65  if (error != LCB_SUCCESS) {
66  /* log error */
67  ERROR("rlm_couchbase: (store_callback) %s (0x%x)", lcb_strerror(instance, error), error);
68  }
69  /* silent compiler */
70  (void)cookie;
71  (void)operation;
72  (void)resp;
73 }
74 
75 /** Couchbase callback for get (read) operations
76  *
77  * @param instance Couchbase connection instance.
78  * @param cookie Couchbase cookie for returning information from callbacks.
79  * @param error Couchbase error object.
80  * @param resp Couchbase get operation response object.
81  */
82 void couchbase_get_callback(lcb_t instance, const void *cookie, lcb_error_t error, const lcb_get_resp_t *resp)
83 {
84  cookie_u cu; /* union of const and non const pointers */
85  cu.cdata = cookie; /* set const union member to cookie passed from couchbase */
86  cookie_t *c = (cookie_t *) cu.data; /* set our cookie struct using non-const member */
87  const char *bytes = resp->v.v0.bytes; /* the payload of this chunk */
88  lcb_size_t nbytes = resp->v.v0.nbytes; /* length of this data chunk */
89 
90  /* check error */
91  switch (error) {
92  case LCB_SUCCESS:
93  /* check for valid bytes */
94  if (bytes && nbytes > 1) {
95  /* debug */
96  DEBUG("rlm_couchbase: (get_callback) got %zu bytes", nbytes);
97  /* parse string to json object */
98  c->jobj = json_tokener_parse_ex(c->jtok, bytes, nbytes);
99  /* switch on tokener error */
100  switch ((c->jerr = json_tokener_get_error(c->jtok))) {
101  case json_tokener_continue:
102  /* check object - should be null */
103  if (c->jobj != NULL) {
104  ERROR("rlm_couchbase: (get_callback) object not null on continue!");
105  }
106  break;
107  case json_tokener_success:
108  /* do nothing */
109  break;
110  default:
111  /* log error */
112  ERROR("rlm_couchbase: (get_callback) json parsing error: %s",
114  break;
115  }
116  }
117  break;
118 
119  case LCB_KEY_ENOENT:
120  /* ignored */
121  DEBUG("rlm_couchbase: (get_callback) key does not exist");
122  break;
123 
124  default:
125  /* log error */
126  ERROR("rlm_couchbase: (get_callback) %s (0x%x)", lcb_strerror(instance, error), error);
127  break;
128  }
129 }
130 
131 /** Couchbase callback for http (view) operations
132  *
133  * @param request Couchbase http request object.
134  * @param instance Couchbase connection instance.
135  * @param cookie Couchbase cookie for returning information from callbacks.
136  * @param error Couchbase error object.
137  * @param resp Couchbase http response object.
138  */
139 void couchbase_http_data_callback(lcb_http_request_t request, lcb_t instance, const void *cookie,
140  lcb_error_t error, const lcb_http_resp_t *resp)
141 {
142  cookie_u cu; /* union of const and non const pointers */
143  cu.cdata = cookie; /* set const union member to cookie passed from couchbase */
144  cookie_t *c = (cookie_t *) cu.data; /* set our cookie struct using non-const member */
145  const char *bytes = resp->v.v0.bytes; /* the payload of this chunk */
146  lcb_size_t nbytes = resp->v.v0.nbytes; /* length of this data chunk */
147 
148  /* check error */
149  switch (error) {
150  case LCB_SUCCESS:
151  /* check for valid bytes */
152  if (bytes && nbytes > 1) {
153  /* debug */
154  DEBUG("rlm_couchbase: (http_data_callback) got %zu bytes", nbytes);
155  /* parse string to json object */
156  c->jobj = json_tokener_parse_ex(c->jtok, bytes, nbytes);
157  /* switch on tokener error */
158  switch ((c->jerr = json_tokener_get_error(c->jtok))) {
159  case json_tokener_continue:
160  /* check object - should be null */
161  if (c->jobj != NULL) {
162  ERROR("rlm_couchbase: (http_data_callback) object not null on continue!");
163  }
164  break;
165  case json_tokener_success:
166  /* do nothing */
167  break;
168  default:
169  /* log error */
170  ERROR("rlm_couchbase: (http_data_callback) json parsing error: %s",
172  break;
173  }
174  }
175  break;
176 
177  default:
178  /* log error */
179  ERROR("rlm_couchbase: (http_data_callback) %s (0x%x)", lcb_strerror(instance, error), error);
180  break;
181  }
182  /* silent compiler */
183  (void)request;
184 }
185 
186 /** Initialize a Couchbase connection instance
187  *
188  * Initialize all information relating to a Couchbase instance and configure available method callbacks.
189  * This function forces synchronous operation and will wait for a connection or timeout.
190  *
191  * @param instance Empty (un-allocated) Couchbase instance object.
192  * @param host The Couchbase server or list of servers.
193  * @param bucket The Couchbase bucket to associate with the instance.
194  * @param pass The Couchbase bucket password (NULL if none).
195  * @param timeout Maximum time to wait for obtaining the initial configuration.
196  * @return Couchbase error object.
197  */
198 lcb_error_t couchbase_init_connection(lcb_t *instance, const char *host, const char *bucket, const char *pass,
199  lcb_uint32_t timeout)
200 {
201  lcb_error_t error; /* couchbase command return */
202  struct lcb_create_st options; /* init create struct */
203 
204  /* init options */
205  memset(&options, 0, sizeof(options));
206 
207  /* assign couchbase create options */
208  options.v.v0.host = host;
209  options.v.v0.bucket = bucket;
210 
211  /* assign user and password if they were both passed */
212  if (bucket != NULL && pass != NULL) {
213  options.v.v0.user = bucket;
214  options.v.v0.passwd = pass;
215  }
216 
217  /* create couchbase connection instance */
218  error = lcb_create(instance, &options);
219  if (error != LCB_SUCCESS) return error;
220 
221  error = lcb_cntl(*instance, LCB_CNTL_SET, LCB_CNTL_CONFIGURATION_TIMEOUT, &timeout);
222  if (error != LCB_SUCCESS) return error;
223 
224  /* initiate connection */
225  error = lcb_connect(*instance);
226  if (error != LCB_SUCCESS) return error;
227 
228  /* set general method callbacks */
229  lcb_set_stat_callback(*instance, couchbase_stat_callback);
230  lcb_set_store_callback(*instance, couchbase_store_callback);
231  lcb_set_get_callback(*instance, couchbase_get_callback);
232  lcb_set_http_data_callback(*instance, couchbase_http_data_callback);
233  /* wait on connection */
234  lcb_wait(*instance);
235 
236  return LCB_SUCCESS;
237 }
238 
239 /** Request Couchbase server statistics
240  *
241  * Setup and execute a request for cluster statistics and wait for the result.
242  *
243  * @param instance Couchbase connection instance.
244  * @param cookie Couchbase cookie for returning information from callbacks.
245  * @return Couchbase error object.
246  */
247 lcb_error_t couchbase_server_stats(lcb_t instance, const void *cookie)
248 {
249  lcb_error_t error; /* couchbase command return */
250  lcb_server_stats_cmd_t cmd; /* server stats command stuct */
251  const lcb_server_stats_cmd_t *commands[1]; /* server stats commands array */
252 
253  /* init commands */
254  commands[0] = &cmd;
255  memset(&cmd, 0, sizeof(cmd));
256 
257  /* populate command struct */
258  cmd.v.v0.name = "tap";
259  cmd.v.v0.nname = strlen(cmd.v.v0.name);
260 
261  /* get statistics */
262  if ((error = lcb_server_stats(instance, cookie, 1, commands)) == LCB_SUCCESS) {
263  /* enter event look on success */
264  lcb_wait(instance);
265  }
266 
267  /* return error */
268  return error;
269 }
270 
271 /** Store a document by key in Couchbase
272  *
273  * Setup and execute a Couchbase set operation and wait for the result.
274  *
275  * @param instance Couchbase connection instance.
276  * @param key Document key to store in the database.
277  * @param document Document body to store in the database.
278  * @param expire Expiration time for the document (0 = never)
279  * @return Couchbase error object.
280  */
281 lcb_error_t couchbase_set_key(lcb_t instance, const char *key, const char *document, int expire)
282 {
283  lcb_error_t error; /* couchbase command return */
284  lcb_store_cmd_t cmd; /* store command stuct */
285  const lcb_store_cmd_t *commands[1]; /* store commands array */
286 
287  /* init commands */
288  commands[0] = &cmd;
289  memset(&cmd, 0, sizeof(cmd));
290 
291  /* populate command struct */
292  cmd.v.v0.key = key;
293  cmd.v.v0.nkey = strlen(cmd.v.v0.key);
294  cmd.v.v0.bytes = document;
295  cmd.v.v0.nbytes = strlen(cmd.v.v0.bytes);
296  cmd.v.v0.exptime = expire;
297  cmd.v.v0.operation = LCB_SET;
298 
299  /* store key/document in couchbase */
300  if ((error = lcb_store(instance, NULL, 1, commands)) == LCB_SUCCESS) {
301  /* enter event loop on success */
302  lcb_wait(instance);
303  }
304 
305  /* return error */
306  return error;
307 }
308 
309 /** Retrieve a document by key from Couchbase
310  *
311  * Setup and execute a Couchbase get request and wait for the result.
312  *
313  * @param instance Couchbase connection instance.
314  * @param cookie Couchbase cookie for returning information from callbacks.
315  * @param key Document key to fetch.
316  * @return Couchbase error object.
317  */
318 lcb_error_t couchbase_get_key(lcb_t instance, const void *cookie, const char *key)
319 {
320  cookie_u cu; /* union of const and non const pointers */
321  cu.cdata = cookie; /* set const union member to cookie passed from couchbase */
322  cookie_t *c = (cookie_t *) cu.data; /* set our cookie struct using non-const member */
323  lcb_error_t error; /* couchbase command return */
324  lcb_get_cmd_t cmd; /* get command struct */
325  const lcb_get_cmd_t *commands[1]; /* get commands array */
326 
327  /* init commands */
328  commands[0] = &cmd;
329  memset(&cmd, 0, sizeof(cmd));
330 
331  /* populate command struct */
332  cmd.v.v0.key = key;
333  cmd.v.v0.nkey = strlen(cmd.v.v0.key);
334 
335  /* clear cookie */
336  memset(c, 0, sizeof(cookie_t));
337 
338  /* init tokener error */
339  c->jerr = json_tokener_success;
340 
341  /* create token */
342  c->jtok = json_tokener_new();
343 
344  /* debugging */
345  DEBUG3("rlm_couchbase: fetching document %s", key);
346 
347  /* get document */
348  if ((error = lcb_get(instance, c, 1, commands)) == LCB_SUCCESS) {
349  /* enter event loop on success */
350  lcb_wait(instance);
351  }
352 
353  /* free token */
354  json_tokener_free(c->jtok);
355 
356  /* return error */
357  return error;
358 }
359 
360 /** Query a Couchbase design document view
361  *
362  * Setup and execute a Couchbase view request and wait for the result.
363  *
364  * @param instance Couchbase connection instance.
365  * @param cookie Couchbase cookie for returning information from callbacks.
366  * @param path The fully qualified view path including the design document and view name.
367  * @param post The post payload (NULL for none).
368  * @return Couchbase error object.
369  */
370 lcb_error_t couchbase_query_view(lcb_t instance, const void *cookie, const char *path, const char *post)
371 {
372  cookie_u cu; /* union of const and non const pointers */
373  cu.cdata = cookie; /* set const union member to cookie passed from couchbase */
374  cookie_t *c = (cookie_t *) cu.data; /* set our cookie struct using non-const member */
375  lcb_error_t error; /* couchbase command return */
376  lcb_http_cmd_t cmd; /* http command struct */
377  const lcb_http_cmd_t *commands; /* http commands array */
378 
379  commands = &cmd;
380  memset(&cmd, 0, sizeof(cmd));
381 
382  /* populate command struct */
383  cmd.v.v0.path = path;
384  cmd.v.v0.npath = strlen(cmd.v.v0.path);
385  cmd.v.v0.body = post;
386  cmd.v.v0.nbody = post ? strlen(post) : 0;
387  cmd.v.v0.method = post ? LCB_HTTP_METHOD_POST : LCB_HTTP_METHOD_GET;
388  cmd.v.v0.chunked = 1;
389  cmd.v.v0.content_type = "application/json";
390 
391  /* clear cookie */
392  memset(c, 0, sizeof(cookie_t));
393 
394  /* init tokener error */
395  c->jerr = json_tokener_success;
396 
397  /* create token */
398  c->jtok = json_tokener_new();
399 
400  /* debugging */
401  DEBUG3("rlm_couchbase: fetching view %s", path);
402 
403  /* query the view */
404  if ((error = lcb_make_http_request(instance, c, LCB_HTTP_TYPE_VIEW, commands, NULL)) == LCB_SUCCESS) {
405  /* enter event loop on success */
406  lcb_wait(instance);
407  }
408 
409  /* free token */
410  json_tokener_free(c->jtok);
411 
412  /* return error */
413  return error;
414 }
const void * cdata
Constant pointer to cookie payload (cookie_t).
Definition: couchbase.h:52
#define DEBUG3(fmt,...)
Definition: log.h:177
void couchbase_store_callback(lcb_t instance, const void *cookie, lcb_storage_t operation, lcb_error_t error, const lcb_store_resp_t *resp)
Couchbase callback for store (write) operations.
Definition: couchbase.c:62
void * data
Non-constant pointer to data payload (cookie_t).
Definition: couchbase.h:53
lcb_error_t couchbase_set_key(lcb_t instance, const char *key, const char *document, int expire)
Store a document by key in Couchbase.
Definition: couchbase.c:281
void couchbase_stat_callback(lcb_t instance, const void *cookie, lcb_error_t error, const lcb_server_stat_resp_t *resp)
Couchbase callback for cluster statistics requests.
Definition: couchbase.c:43
lcb_error_t couchbase_server_stats(lcb_t instance, const void *cookie)
Request Couchbase server statistics.
Definition: couchbase.c:247
lcb_error_t couchbase_get_key(lcb_t instance, const void *cookie, const char *key)
Retrieve a document by key from Couchbase.
Definition: couchbase.c:318
Couchbase wrapper function prototypes and datatypes.
static float timeout
Definition: radclient.c:43
void couchbase_http_data_callback(lcb_http_request_t request, lcb_t instance, const void *cookie, lcb_error_t error, const lcb_http_resp_t *resp)
Couchbase callback for http (view) operations.
Definition: couchbase.c:139
void couchbase_get_callback(lcb_t instance, const void *cookie, lcb_error_t error, const lcb_get_resp_t *resp)
Couchbase callback for get (read) operations.
Definition: couchbase.c:82
#define DEBUG(fmt,...)
Definition: log.h:175
json_tokener * jtok
JSON tokener objects handled by the json-c library.
Definition: couchbase.h:42
Union of constant and non-constant pointers.
Definition: couchbase.h:51
json_object * jobj
JSON objects handled by the json-c library.
Definition: couchbase.h:41
const char * json_tokener_error_desc(enum json_tokener_error jerr)
Definition: json_missing.c:68
lcb_error_t couchbase_init_connection(lcb_t *instance, const char *host, const char *bucket, const char *pass, lcb_uint32_t timeout)
Initialize a Couchbase connection instance.
Definition: couchbase.c:198
enum json_tokener_error jerr
Error values produced by the json-c library.
Definition: couchbase.h:43
lcb_error_t couchbase_query_view(lcb_t instance, const void *cookie, const char *path, const char *post)
Query a Couchbase design document view.
Definition: couchbase.c:370
Information relating to the parsing of Couchbase document payloads.
Definition: couchbase.h:40
enum json_tokener_error json_tokener_get_error(json_tokener *tok)
Definition: json_missing.c:62
#define RCSID(id)
Definition: build.h:135
#define ERROR(fmt,...)
Definition: log.h:145