1 /*
2 * SysDB - src/frontend/query.c
3 * Copyright (C) 2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
28 #include "sysdb.h"
30 #include "core/store.h"
31 #include "frontend/connection-private.h"
32 #include "frontend/parser.h"
33 #include "utils/error.h"
34 #include "utils/proto.h"
35 #include "utils/strbuf.h"
37 #include <errno.h>
38 #include <string.h>
40 /*
41 * private helper functions
42 */
44 static int
45 list_tojson(sdb_store_obj_t *obj,
46 sdb_store_matcher_t __attribute__((unused)) *filter,
47 void *user_data)
48 {
49 sdb_store_json_formatter_t *f = user_data;
50 return sdb_store_json_emit(f, obj);
51 } /* list_tojson */
53 static int
54 lookup_tojson(sdb_store_obj_t *obj, sdb_store_matcher_t *filter,
55 void *user_data)
56 {
57 sdb_store_json_formatter_t *f = user_data;
58 return sdb_store_json_emit_full(f, obj, filter);
59 } /* lookup_tojson */
61 /*
62 * public API
63 */
65 int
66 sdb_fe_query(sdb_conn_t *conn)
67 {
68 sdb_llist_t *parsetree;
69 sdb_conn_node_t *node = NULL;
70 int status = 0;
72 if ((! conn) || (conn->cmd != CONNECTION_QUERY))
73 return -1;
75 parsetree = sdb_fe_parse(sdb_strbuf_string(conn->buf),
76 (int)conn->cmd_len);
77 if (! parsetree) {
78 char query[conn->cmd_len + 1];
79 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
80 query[sizeof(query) - 1] = '\0';
81 sdb_log(SDB_LOG_ERR, "frontend: Failed to parse query '%s'",
82 query);
83 return -1;
84 }
86 switch (sdb_llist_len(parsetree)) {
87 case 0:
88 /* skipping empty command; send back an empty reply */
89 sdb_connection_send(conn, CONNECTION_DATA, 0, NULL);
90 break;
91 case 1:
92 node = SDB_CONN_NODE(sdb_llist_get(parsetree, 0));
93 break;
95 default:
96 {
97 char query[conn->cmd_len + 1];
98 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
99 query[sizeof(query) - 1] = '\0';
100 sdb_log(SDB_LOG_WARNING, "frontend: Ignoring %zu command%s "
101 "in multi-statement query '%s'",
102 sdb_llist_len(parsetree) - 1,
103 sdb_llist_len(parsetree) == 2 ? "" : "s",
104 query);
105 node = SDB_CONN_NODE(sdb_llist_get(parsetree, 0));
106 }
107 }
109 if (node) {
110 if (sdb_fe_analyze(node)) {
111 char query[conn->cmd_len + 1];
112 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
113 query[sizeof(query) - 1] = '\0';
114 sdb_log(SDB_LOG_ERR, "frontend: Failed to verify query '%s'",
115 query);
116 status = -1;
117 }
118 else
119 status = sdb_fe_exec(conn, node);
120 sdb_object_deref(SDB_OBJ(node));
121 }
123 sdb_llist_destroy(parsetree);
124 return status;
125 } /* sdb_fe_query */
127 int
128 sdb_fe_fetch(sdb_conn_t *conn)
129 {
130 char name[conn->cmd_len + 1];
131 int type;
133 if ((! conn) || (conn->cmd != CONNECTION_FETCH))
134 return -1;
136 if (conn->cmd_len < sizeof(type)) {
137 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
138 "FETCH command", conn->cmd_len);
139 sdb_strbuf_sprintf(conn->errbuf, "FETCH: Invalid command length %d",
140 conn->cmd_len);
141 return -1;
142 }
144 type = sdb_proto_get_int(conn->buf, 0);
145 strncpy(name, sdb_strbuf_string(conn->buf) + sizeof(type),
146 conn->cmd_len - sizeof(type));
147 name[sizeof(name) - 1] = '\0';
148 return sdb_fe_exec_fetch(conn, type, name, /* filter = */ NULL);
149 } /* sdb_fe_fetch */
151 int
152 sdb_fe_list(sdb_conn_t *conn)
153 {
154 int type = SDB_HOST;
156 if ((! conn) || (conn->cmd != CONNECTION_LIST))
157 return -1;
159 if (conn->cmd_len == sizeof(uint32_t))
160 type = sdb_proto_get_int(conn->buf, 0);
161 else if (conn->cmd_len) {
162 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
163 "LIST command", conn->cmd_len);
164 sdb_strbuf_sprintf(conn->errbuf, "LIST: Invalid command length %d",
165 conn->cmd_len);
166 return -1;
167 }
168 return sdb_fe_exec_list(conn, type, /* filter = */ NULL);
169 } /* sdb_fe_list */
171 int
172 sdb_fe_lookup(sdb_conn_t *conn)
173 {
174 sdb_store_matcher_t *m;
175 const char *matcher;
176 size_t matcher_len;
178 uint32_t type;
179 int status;
181 conn_matcher_t m_node = {
182 { SDB_OBJECT_INIT, CONNECTION_MATCHER }, NULL
183 };
184 conn_lookup_t node = {
185 { SDB_OBJECT_INIT, CONNECTION_LOOKUP },
186 -1, &m_node, NULL
187 };
189 if ((! conn) || (conn->cmd != CONNECTION_LOOKUP))
190 return -1;
192 if (conn->cmd_len < sizeof(type)) {
193 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
194 "LOOKUP command", conn->cmd_len);
195 sdb_strbuf_sprintf(conn->errbuf, "LOOKUP: Invalid command length %d",
196 conn->cmd_len);
197 return -1;
198 }
199 type = sdb_proto_get_int(conn->buf, 0);
201 matcher = sdb_strbuf_string(conn->buf) + sizeof(type);
202 matcher_len = conn->cmd_len - sizeof(type);
203 m = sdb_fe_parse_matcher(matcher, (int)matcher_len);
204 if (! m) {
205 char expr[matcher_len + 1];
206 strncpy(expr, matcher, sizeof(expr));
207 expr[sizeof(expr) - 1] = '\0';
208 sdb_log(SDB_LOG_ERR, "frontend: Failed to parse "
209 "lookup condition '%s'", expr);
210 return -1;
211 }
213 node.type = type;
214 m_node.matcher = m;
216 if (sdb_fe_analyze(SDB_CONN_NODE(&node))) {
217 char expr[matcher_len + 1];
218 strncpy(expr, matcher, sizeof(expr));
219 expr[sizeof(expr) - 1] = '\0';
220 sdb_log(SDB_LOG_ERR, "frontend: Failed to verify "
221 "lookup condition '%s'", expr);
222 status = -1;
223 }
224 else
225 status = sdb_fe_exec_lookup(conn, type, m, /* filter = */ NULL);
226 sdb_object_deref(SDB_OBJ(m));
227 return status;
228 } /* sdb_fe_lookup */
230 int
231 sdb_fe_exec(sdb_conn_t *conn, sdb_conn_node_t *node)
232 {
233 sdb_store_matcher_t *m = NULL, *filter = NULL;
235 if (! node)
236 return -1;
238 switch (node->cmd) {
239 case CONNECTION_FETCH:
240 if (CONN_FETCH(node)->filter)
241 filter = CONN_FETCH(node)->filter->matcher;
242 return sdb_fe_exec_fetch(conn, CONN_FETCH(node)->type,
243 CONN_FETCH(node)->name, filter);
244 case CONNECTION_LIST:
245 if (CONN_LIST(node)->filter)
246 filter = CONN_LIST(node)->filter->matcher;
247 return sdb_fe_exec_list(conn, CONN_LIST(node)->type, filter);
248 case CONNECTION_LOOKUP:
249 if (CONN_LOOKUP(node)->matcher)
250 m = CONN_LOOKUP(node)->matcher->matcher;
251 if (CONN_LOOKUP(node)->filter)
252 filter = CONN_LOOKUP(node)->filter->matcher;
253 return sdb_fe_exec_lookup(conn,
254 CONN_LOOKUP(node)->type, m, filter);
255 case CONNECTION_TIMESERIES:
256 return sdb_fe_exec_timeseries(conn,
257 CONN_TS(node)->hostname, CONN_TS(node)->metric,
258 &CONN_TS(node)->opts);
260 default:
261 sdb_log(SDB_LOG_ERR, "frontend: Unknown command %i", node->cmd);
262 return -1;
263 }
264 return -1;
265 } /* sdb_fe_exec */
267 int
268 sdb_fe_exec_fetch(sdb_conn_t *conn, int type, const char *name,
269 sdb_store_matcher_t *filter)
270 {
271 sdb_store_obj_t *host;
272 uint32_t res_type = htonl(CONNECTION_FETCH);
274 sdb_store_json_formatter_t *f;
275 sdb_strbuf_t *buf;
277 /* XXX: support other types */
278 if (type != SDB_HOST) {
279 sdb_log(SDB_LOG_ERR, "frontend: Invalid object type %d "
280 "in FETCH command", type);
281 sdb_strbuf_sprintf(conn->errbuf,
282 "FETCH: Invalid object type %d", type);
283 return -1;
284 }
286 host = sdb_store_get_host(name);
287 if ((! host) || (filter
288 && (! sdb_store_matcher_matches(filter, host, NULL)))) {
289 sdb_log(SDB_LOG_DEBUG, "frontend: Failed to fetch host '%s': "
290 "not found", name);
292 sdb_strbuf_sprintf(conn->errbuf, "Host %s not found", name);
293 return -1;
294 }
296 buf = sdb_strbuf_create(1024);
297 if (! buf) {
298 char errbuf[1024];
299 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
300 "buffer to handle FETCH command: %s",
301 sdb_strerror(errno, errbuf, sizeof(errbuf)));
303 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
304 sdb_strbuf_destroy(buf);
305 sdb_object_deref(SDB_OBJ(host));
306 return -1;
307 }
308 f = sdb_store_json_formatter(buf, type, /* flags = */ 0);
309 if (! f) {
310 char errbuf[1024];
311 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
312 "JSON formatter to handle FETCH command: %s",
313 sdb_strerror(errno, errbuf, sizeof(errbuf)));
315 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
316 sdb_strbuf_destroy(buf);
317 sdb_object_deref(SDB_OBJ(host));
318 return -1;
319 }
321 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
322 if (sdb_store_json_emit_full(f, host, filter)) {
323 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
324 "host '%s' to JSON", name);
325 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
326 sdb_strbuf_destroy(buf);
327 free(f);
328 sdb_object_deref(SDB_OBJ(host));
329 return -1;
330 }
331 sdb_store_json_finish(f);
333 sdb_connection_send(conn, CONNECTION_DATA,
334 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
335 sdb_strbuf_destroy(buf);
336 free(f);
337 sdb_object_deref(SDB_OBJ(host));
338 return 0;
339 } /* sdb_fe_exec_fetch */
341 int
342 sdb_fe_exec_list(sdb_conn_t *conn, int type, sdb_store_matcher_t *filter)
343 {
344 uint32_t res_type = htonl(CONNECTION_LIST);
346 sdb_store_json_formatter_t *f;
347 sdb_strbuf_t *buf;
349 buf = sdb_strbuf_create(1024);
350 if (! buf) {
351 char errbuf[1024];
352 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
353 "buffer to handle LIST command: %s",
354 sdb_strerror(errno, errbuf, sizeof(errbuf)));
356 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
357 sdb_strbuf_destroy(buf);
358 return -1;
359 }
360 f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
361 if (! f) {
362 char errbuf[1024];
363 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
364 "JSON formatter to handle LIST command: %s",
365 sdb_strerror(errno, errbuf, sizeof(errbuf)));
367 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
368 sdb_strbuf_destroy(buf);
369 return -1;
370 }
372 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
373 if (sdb_store_scan(type, /* m = */ NULL, filter, list_tojson, f)) {
374 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
375 "store to JSON");
376 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
377 sdb_strbuf_destroy(buf);
378 free(f);
379 return -1;
380 }
381 sdb_store_json_finish(f);
383 sdb_connection_send(conn, CONNECTION_DATA,
384 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
385 sdb_strbuf_destroy(buf);
386 free(f);
387 return 0;
388 } /* sdb_fe_exec_list */
390 int
391 sdb_fe_exec_lookup(sdb_conn_t *conn, int type,
392 sdb_store_matcher_t *m, sdb_store_matcher_t *filter)
393 {
394 uint32_t res_type = htonl(CONNECTION_LOOKUP);
396 sdb_store_json_formatter_t *f;
397 sdb_strbuf_t *buf;
399 /* XXX: support other types */
400 if (type != SDB_HOST) {
401 sdb_log(SDB_LOG_ERR, "frontend: Invalid object type %d "
402 "in LOOKUP command", type);
403 sdb_strbuf_sprintf(conn->errbuf,
404 "LOOKUP: Invalid object type %d", type);
405 return -1;
406 }
408 buf = sdb_strbuf_create(1024);
409 if (! buf) {
410 char errbuf[1024];
411 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
412 "buffer to handle LOOKUP command: %s",
413 sdb_strerror(errno, errbuf, sizeof(errbuf)));
415 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
416 return -1;
417 }
418 f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
419 if (! f) {
420 char errbuf[1024];
421 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
422 "JSON formatter to handle LOOKUP command: %s",
423 sdb_strerror(errno, errbuf, sizeof(errbuf)));
425 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
426 sdb_strbuf_destroy(buf);
427 return -1;
428 }
430 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
432 if (sdb_store_scan(SDB_HOST, m, filter, lookup_tojson, f)) {
433 sdb_log(SDB_LOG_ERR, "frontend: Failed to lookup hosts");
434 sdb_strbuf_sprintf(conn->errbuf, "Failed to lookup hosts");
435 sdb_strbuf_destroy(buf);
436 free(f);
437 return -1;
438 }
439 sdb_store_json_finish(f);
441 sdb_connection_send(conn, CONNECTION_DATA,
442 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
443 sdb_strbuf_destroy(buf);
444 free(f);
445 return 0;
446 } /* sdb_fe_exec_lookup */
448 int
449 sdb_fe_exec_timeseries(sdb_conn_t *conn,
450 const char *hostname, const char *metric,
451 sdb_timeseries_opts_t *opts)
452 {
453 sdb_strbuf_t *buf;
454 uint32_t res_type = htonl(CONNECTION_TIMESERIES);
456 buf = sdb_strbuf_create(1024);
457 if (! buf) {
458 char errbuf[1024];
459 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
460 "buffer to handle TIMESERIES command: %s",
461 sdb_strerror(errno, errbuf, sizeof(errbuf)));
463 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
464 return -1;
465 }
467 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
468 if (sdb_store_fetch_timeseries(hostname, metric, opts, buf)) {
469 sdb_log(SDB_LOG_ERR, "frontend: Failed to fetch time-series");
470 sdb_strbuf_sprintf(conn->errbuf, "Failed to fetch time-series");
471 sdb_strbuf_destroy(buf);
472 return -1;
473 }
475 sdb_connection_send(conn, CONNECTION_DATA,
476 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
477 sdb_strbuf_destroy(buf);
478 return 0;
479 } /* sdb_fe_exec_timeseries */
481 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */