The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
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 */
26RCSID("$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 */
42void 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 */
61void 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 */
81void 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 */
138void 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 */
196lcb_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 */
254lcb_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 */
288lcb_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 */
325lcb_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 */
377lcb_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:483
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
#define DEBUG3(_fmt,...)
Definition log.h:266
static fr_table_ptr_sorted_t commands[]