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