1 /*
2 * SysDB - src/frontend/query.c
3 * Copyright (C) 2013-2014 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(uint32_t)) {
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(uint32_t),
146 conn->cmd_len - sizeof(uint32_t));
147 name[sizeof(name) - 1] = '\0';
148 /* TODO: support other types besides hosts */
149 return sdb_fe_exec_fetch(conn, type, name, NULL, /* filter = */ NULL);
150 } /* sdb_fe_fetch */
152 int
153 sdb_fe_list(sdb_conn_t *conn)
154 {
155 int type = SDB_HOST;
157 if ((! conn) || (conn->cmd != CONNECTION_LIST))
158 return -1;
160 if (conn->cmd_len == sizeof(uint32_t))
161 type = sdb_proto_get_int(conn->buf, 0);
162 else if (conn->cmd_len) {
163 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
164 "LIST command", conn->cmd_len);
165 sdb_strbuf_sprintf(conn->errbuf, "LIST: Invalid command length %d",
166 conn->cmd_len);
167 return -1;
168 }
169 return sdb_fe_exec_list(conn, type, /* filter = */ NULL);
170 } /* sdb_fe_list */
172 int
173 sdb_fe_lookup(sdb_conn_t *conn)
174 {
175 sdb_store_matcher_t *m;
176 const char *matcher;
177 size_t matcher_len;
179 int type;
180 int status;
182 conn_matcher_t m_node = {
183 { SDB_OBJECT_INIT, CONNECTION_MATCHER }, NULL
184 };
185 conn_lookup_t node = {
186 { SDB_OBJECT_INIT, CONNECTION_LOOKUP },
187 -1, &m_node, NULL
188 };
190 if ((! conn) || (conn->cmd != CONNECTION_LOOKUP))
191 return -1;
193 if (conn->cmd_len < sizeof(uint32_t)) {
194 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
195 "LOOKUP command", conn->cmd_len);
196 sdb_strbuf_sprintf(conn->errbuf, "LOOKUP: Invalid command length %d",
197 conn->cmd_len);
198 return -1;
199 }
200 type = sdb_proto_get_int(conn->buf, 0);
202 matcher = sdb_strbuf_string(conn->buf) + sizeof(uint32_t);
203 matcher_len = conn->cmd_len - sizeof(uint32_t);
204 m = sdb_fe_parse_matcher(matcher, (int)matcher_len);
205 if (! m) {
206 char expr[matcher_len + 1];
207 strncpy(expr, matcher, sizeof(expr));
208 expr[sizeof(expr) - 1] = '\0';
209 sdb_log(SDB_LOG_ERR, "frontend: Failed to parse "
210 "lookup condition '%s'", expr);
211 return -1;
212 }
214 node.type = type;
215 m_node.matcher = m;
217 if (sdb_fe_analyze(SDB_CONN_NODE(&node))) {
218 char expr[matcher_len + 1];
219 strncpy(expr, matcher, sizeof(expr));
220 expr[sizeof(expr) - 1] = '\0';
221 sdb_log(SDB_LOG_ERR, "frontend: Failed to verify "
222 "lookup condition '%s'", expr);
223 status = -1;
224 }
225 else
226 status = sdb_fe_exec_lookup(conn, type, m, /* filter = */ NULL);
227 sdb_object_deref(SDB_OBJ(m));
228 return status;
229 } /* sdb_fe_lookup */
231 int
232 sdb_fe_exec(sdb_conn_t *conn, sdb_conn_node_t *node)
233 {
234 sdb_store_matcher_t *m = NULL, *filter = NULL;
236 if (! node)
237 return -1;
239 switch (node->cmd) {
240 case CONNECTION_FETCH:
241 if (CONN_FETCH(node)->filter)
242 filter = CONN_FETCH(node)->filter->matcher;
243 return sdb_fe_exec_fetch(conn, CONN_FETCH(node)->type,
244 CONN_FETCH(node)->host, CONN_FETCH(node)->name, filter);
245 case CONNECTION_LIST:
246 if (CONN_LIST(node)->filter)
247 filter = CONN_LIST(node)->filter->matcher;
248 return sdb_fe_exec_list(conn, CONN_LIST(node)->type, filter);
249 case CONNECTION_LOOKUP:
250 if (CONN_LOOKUP(node)->matcher)
251 m = CONN_LOOKUP(node)->matcher->matcher;
252 if (CONN_LOOKUP(node)->filter)
253 filter = CONN_LOOKUP(node)->filter->matcher;
254 return sdb_fe_exec_lookup(conn,
255 CONN_LOOKUP(node)->type, m, filter);
256 case CONNECTION_TIMESERIES:
257 return sdb_fe_exec_timeseries(conn,
258 CONN_TS(node)->hostname, CONN_TS(node)->metric,
259 &CONN_TS(node)->opts);
261 default:
262 sdb_log(SDB_LOG_ERR, "frontend: Unknown command %i", node->cmd);
263 return -1;
264 }
265 return -1;
266 } /* sdb_fe_exec */
268 int
269 sdb_fe_exec_fetch(sdb_conn_t *conn, int type,
270 const char *hostname, const char *name, sdb_store_matcher_t *filter)
271 {
272 uint32_t res_type = htonl(CONNECTION_FETCH);
274 sdb_store_obj_t *host;
275 sdb_store_obj_t *obj;
277 sdb_store_json_formatter_t *f;
278 sdb_strbuf_t *buf;
280 if ((! hostname) || ((type == SDB_HOST) && name)
281 || ((type != SDB_HOST) && (! name))) {
282 /* This is a programming error, not something the client did wrong */
283 sdb_strbuf_sprintf(conn->errbuf, "INTERNAL ERROR: invalid "
284 "arguments to sdb_fe_exec_fetch(%s, %s, %s)",
285 SDB_STORE_TYPE_TO_NAME(type), hostname, name);
286 return -1;
287 }
289 host = sdb_store_get_host(hostname);
290 if ((! host) || (filter
291 && (! sdb_store_matcher_matches(filter, host, NULL)))) {
292 sdb_strbuf_sprintf(conn->errbuf, "Failed to fetch %s %s: "
293 "host %s not found", SDB_STORE_TYPE_TO_NAME(type),
294 name, hostname);
295 return -1;
296 }
297 if (type == SDB_HOST) {
298 obj = host;
299 }
300 else {
301 obj = sdb_store_get_child(host, type, name);
302 if ((! obj) || (filter
303 && (! sdb_store_matcher_matches(filter, obj, NULL)))) {
304 sdb_strbuf_sprintf(conn->errbuf, "Failed to fetch %s %s.%s: "
305 "%s not found", SDB_STORE_TYPE_TO_NAME(type),
306 hostname, name, name);
307 return -1;
308 }
309 sdb_object_deref(SDB_OBJ(host));
310 }
312 buf = sdb_strbuf_create(1024);
313 if (! buf) {
314 char errbuf[1024];
315 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
316 "buffer to handle FETCH command: %s",
317 sdb_strerror(errno, errbuf, sizeof(errbuf)));
319 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
320 sdb_strbuf_destroy(buf);
321 sdb_object_deref(SDB_OBJ(obj));
322 return -1;
323 }
324 f = sdb_store_json_formatter(buf, type, /* flags = */ 0);
325 if (! f) {
326 char errbuf[1024];
327 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
328 "JSON formatter to handle FETCH command: %s",
329 sdb_strerror(errno, errbuf, sizeof(errbuf)));
331 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
332 sdb_strbuf_destroy(buf);
333 sdb_object_deref(SDB_OBJ(obj));
334 return -1;
335 }
337 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
338 if (sdb_store_json_emit_full(f, obj, filter)) {
339 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
340 "%s %s.%s to JSON", SDB_STORE_TYPE_TO_NAME(type),
341 hostname, name);
342 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
343 sdb_strbuf_destroy(buf);
344 free(f);
345 sdb_object_deref(SDB_OBJ(obj));
346 return -1;
347 }
348 sdb_store_json_finish(f);
350 sdb_connection_send(conn, CONNECTION_DATA,
351 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
352 sdb_strbuf_destroy(buf);
353 free(f);
354 sdb_object_deref(SDB_OBJ(obj));
355 return 0;
356 } /* sdb_fe_exec_fetch */
358 int
359 sdb_fe_exec_list(sdb_conn_t *conn, int type, sdb_store_matcher_t *filter)
360 {
361 uint32_t res_type = htonl(CONNECTION_LIST);
363 sdb_store_json_formatter_t *f;
364 sdb_strbuf_t *buf;
366 buf = sdb_strbuf_create(1024);
367 if (! buf) {
368 char errbuf[1024];
369 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
370 "buffer to handle LIST command: %s",
371 sdb_strerror(errno, errbuf, sizeof(errbuf)));
373 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
374 sdb_strbuf_destroy(buf);
375 return -1;
376 }
377 f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
378 if (! f) {
379 char errbuf[1024];
380 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
381 "JSON formatter to handle LIST command: %s",
382 sdb_strerror(errno, errbuf, sizeof(errbuf)));
384 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
385 sdb_strbuf_destroy(buf);
386 return -1;
387 }
389 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
390 if (sdb_store_scan(type, /* m = */ NULL, filter, list_tojson, f)) {
391 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
392 "store to JSON");
393 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
394 sdb_strbuf_destroy(buf);
395 free(f);
396 return -1;
397 }
398 sdb_store_json_finish(f);
400 sdb_connection_send(conn, CONNECTION_DATA,
401 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
402 sdb_strbuf_destroy(buf);
403 free(f);
404 return 0;
405 } /* sdb_fe_exec_list */
407 int
408 sdb_fe_exec_lookup(sdb_conn_t *conn, int type,
409 sdb_store_matcher_t *m, sdb_store_matcher_t *filter)
410 {
411 uint32_t res_type = htonl(CONNECTION_LOOKUP);
413 sdb_store_json_formatter_t *f;
414 sdb_strbuf_t *buf;
416 /* XXX: support other types */
417 if (type != SDB_HOST) {
418 sdb_log(SDB_LOG_ERR, "frontend: Invalid object type %d "
419 "in LOOKUP command", type);
420 sdb_strbuf_sprintf(conn->errbuf,
421 "LOOKUP: Invalid object type %d", type);
422 return -1;
423 }
425 buf = sdb_strbuf_create(1024);
426 if (! buf) {
427 char errbuf[1024];
428 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
429 "buffer to handle LOOKUP command: %s",
430 sdb_strerror(errno, errbuf, sizeof(errbuf)));
432 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
433 return -1;
434 }
435 f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
436 if (! f) {
437 char errbuf[1024];
438 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
439 "JSON formatter to handle LOOKUP command: %s",
440 sdb_strerror(errno, errbuf, sizeof(errbuf)));
442 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
443 sdb_strbuf_destroy(buf);
444 return -1;
445 }
447 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
449 if (sdb_store_scan(SDB_HOST, m, filter, lookup_tojson, f)) {
450 sdb_log(SDB_LOG_ERR, "frontend: Failed to lookup hosts");
451 sdb_strbuf_sprintf(conn->errbuf, "Failed to lookup hosts");
452 sdb_strbuf_destroy(buf);
453 free(f);
454 return -1;
455 }
456 sdb_store_json_finish(f);
458 sdb_connection_send(conn, CONNECTION_DATA,
459 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
460 sdb_strbuf_destroy(buf);
461 free(f);
462 return 0;
463 } /* sdb_fe_exec_lookup */
465 int
466 sdb_fe_exec_timeseries(sdb_conn_t *conn,
467 const char *hostname, const char *metric,
468 sdb_timeseries_opts_t *opts)
469 {
470 sdb_strbuf_t *buf;
471 uint32_t res_type = htonl(CONNECTION_TIMESERIES);
473 buf = sdb_strbuf_create(1024);
474 if (! buf) {
475 char errbuf[1024];
476 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
477 "buffer to handle TIMESERIES command: %s",
478 sdb_strerror(errno, errbuf, sizeof(errbuf)));
480 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
481 return -1;
482 }
484 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
485 if (sdb_store_fetch_timeseries(hostname, metric, opts, buf)) {
486 sdb_log(SDB_LOG_ERR, "frontend: Failed to fetch time-series");
487 sdb_strbuf_sprintf(conn->errbuf, "Failed to fetch time-series");
488 sdb_strbuf_destroy(buf);
489 return -1;
490 }
492 sdb_connection_send(conn, CONNECTION_DATA,
493 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
494 sdb_strbuf_destroy(buf);
495 return 0;
496 } /* sdb_fe_exec_timeseries */
498 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */