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 lookup_tojson(sdb_store_obj_t *obj, sdb_store_matcher_t *filter,
46 void *user_data)
47 {
48 sdb_store_json_formatter_t *f = user_data;
49 return sdb_store_json_emit_full(f, obj, filter);
50 } /* lookup_tojson */
52 /*
53 * public API
54 */
56 int
57 sdb_fe_query(sdb_conn_t *conn)
58 {
59 sdb_llist_t *parsetree;
60 sdb_conn_node_t *node = NULL;
61 int status = 0;
63 if ((! conn) || (conn->cmd != CONNECTION_QUERY))
64 return -1;
66 parsetree = sdb_fe_parse(sdb_strbuf_string(conn->buf),
67 (int)conn->cmd_len);
68 if (! parsetree) {
69 char query[conn->cmd_len + 1];
70 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
71 query[sizeof(query) - 1] = '\0';
72 sdb_log(SDB_LOG_ERR, "frontend: Failed to parse query '%s'",
73 query);
74 return -1;
75 }
77 switch (sdb_llist_len(parsetree)) {
78 case 0:
79 /* skipping empty command; send back an empty reply */
80 sdb_connection_send(conn, CONNECTION_DATA, 0, NULL);
81 break;
82 case 1:
83 node = SDB_CONN_NODE(sdb_llist_get(parsetree, 0));
84 break;
86 default:
87 {
88 char query[conn->cmd_len + 1];
89 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
90 query[sizeof(query) - 1] = '\0';
91 sdb_log(SDB_LOG_WARNING, "frontend: Ignoring %zu command%s "
92 "in multi-statement query '%s'",
93 sdb_llist_len(parsetree) - 1,
94 sdb_llist_len(parsetree) == 2 ? "" : "s",
95 query);
96 node = SDB_CONN_NODE(sdb_llist_get(parsetree, 0));
97 }
98 }
100 if (node) {
101 if (sdb_fe_analyze(node)) {
102 char query[conn->cmd_len + 1];
103 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
104 query[sizeof(query) - 1] = '\0';
105 sdb_log(SDB_LOG_ERR, "frontend: Failed to verify query '%s'",
106 query);
107 status = -1;
108 }
109 else
110 status = sdb_fe_exec(conn, node);
111 sdb_object_deref(SDB_OBJ(node));
112 }
114 sdb_llist_destroy(parsetree);
115 return status;
116 } /* sdb_fe_query */
118 int
119 sdb_fe_fetch(sdb_conn_t *conn)
120 {
121 char name[conn->cmd_len + 1];
122 int type;
124 if ((! conn) || (conn->cmd != CONNECTION_FETCH))
125 return -1;
127 if (conn->cmd_len < sizeof(type)) {
128 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
129 "FETCH command", conn->cmd_len);
130 sdb_strbuf_sprintf(conn->errbuf, "FETCH: Invalid command length %d",
131 conn->cmd_len);
132 return -1;
133 }
135 type = sdb_proto_get_int(conn->buf, 0);
136 strncpy(name, sdb_strbuf_string(conn->buf) + sizeof(type),
137 conn->cmd_len - sizeof(type));
138 name[sizeof(name) - 1] = '\0';
139 return sdb_fe_exec_fetch(conn, type, name, /* filter = */ NULL);
140 } /* sdb_fe_fetch */
142 int
143 sdb_fe_list(sdb_conn_t *conn)
144 {
145 int type = SDB_HOST;
147 if ((! conn) || (conn->cmd != CONNECTION_LIST))
148 return -1;
150 if (conn->cmd_len == sizeof(uint32_t))
151 type = sdb_proto_get_int(conn->buf, 0);
152 else if (conn->cmd_len) {
153 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
154 "LIST command", conn->cmd_len);
155 sdb_strbuf_sprintf(conn->errbuf, "LIST: Invalid command length %d",
156 conn->cmd_len);
157 return -1;
158 }
159 return sdb_fe_exec_list(conn, type, /* filter = */ NULL);
160 } /* sdb_fe_list */
162 int
163 sdb_fe_lookup(sdb_conn_t *conn)
164 {
165 sdb_store_matcher_t *m;
166 const char *matcher;
167 size_t matcher_len;
169 uint32_t type;
170 int status;
172 conn_matcher_t m_node = {
173 { SDB_OBJECT_INIT, CONNECTION_MATCHER }, NULL
174 };
175 conn_lookup_t node = {
176 { SDB_OBJECT_INIT, CONNECTION_LOOKUP },
177 -1, &m_node, NULL
178 };
180 if ((! conn) || (conn->cmd != CONNECTION_LOOKUP))
181 return -1;
183 if (conn->cmd_len < sizeof(type)) {
184 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
185 "LOOKUP command", conn->cmd_len);
186 sdb_strbuf_sprintf(conn->errbuf, "LOOKUP: Invalid command length %d",
187 conn->cmd_len);
188 return -1;
189 }
190 type = sdb_proto_get_int(conn->buf, 0);
192 matcher = sdb_strbuf_string(conn->buf) + sizeof(type);
193 matcher_len = conn->cmd_len - sizeof(type);
194 m = sdb_fe_parse_matcher(matcher, (int)matcher_len);
195 if (! m) {
196 char expr[matcher_len + 1];
197 strncpy(expr, matcher, sizeof(expr));
198 expr[sizeof(expr) - 1] = '\0';
199 sdb_log(SDB_LOG_ERR, "frontend: Failed to parse "
200 "lookup condition '%s'", expr);
201 return -1;
202 }
204 node.type = type;
205 m_node.matcher = m;
207 if (sdb_fe_analyze(SDB_CONN_NODE(&node))) {
208 char expr[matcher_len + 1];
209 strncpy(expr, matcher, sizeof(expr));
210 expr[sizeof(expr) - 1] = '\0';
211 sdb_log(SDB_LOG_ERR, "frontend: Failed to verify "
212 "lookup condition '%s'", expr);
213 status = -1;
214 }
215 else
216 status = sdb_fe_exec_lookup(conn, type, m, /* filter = */ NULL);
217 sdb_object_deref(SDB_OBJ(m));
218 return status;
219 } /* sdb_fe_lookup */
221 int
222 sdb_fe_exec(sdb_conn_t *conn, sdb_conn_node_t *node)
223 {
224 sdb_store_matcher_t *m = NULL, *filter = NULL;
226 if (! node)
227 return -1;
229 switch (node->cmd) {
230 case CONNECTION_FETCH:
231 if (CONN_FETCH(node)->filter)
232 filter = CONN_FETCH(node)->filter->matcher;
233 return sdb_fe_exec_fetch(conn, CONN_FETCH(node)->type,
234 CONN_FETCH(node)->name, filter);
235 case CONNECTION_LIST:
236 if (CONN_LIST(node)->filter)
237 filter = CONN_LIST(node)->filter->matcher;
238 return sdb_fe_exec_list(conn, CONN_LIST(node)->type, filter);
239 case CONNECTION_LOOKUP:
240 if (CONN_LOOKUP(node)->matcher)
241 m = CONN_LOOKUP(node)->matcher->matcher;
242 if (CONN_LOOKUP(node)->filter)
243 filter = CONN_LOOKUP(node)->filter->matcher;
244 return sdb_fe_exec_lookup(conn,
245 CONN_LOOKUP(node)->type, m, filter);
246 case CONNECTION_TIMESERIES:
247 return sdb_fe_exec_timeseries(conn,
248 CONN_TS(node)->hostname, CONN_TS(node)->metric,
249 &CONN_TS(node)->opts);
251 default:
252 sdb_log(SDB_LOG_ERR, "frontend: Unknown command %i", node->cmd);
253 return -1;
254 }
255 return -1;
256 } /* sdb_fe_exec */
258 int
259 sdb_fe_exec_fetch(sdb_conn_t *conn, int type, const char *name,
260 sdb_store_matcher_t *filter)
261 {
262 sdb_strbuf_t *buf;
263 sdb_store_obj_t *host;
264 uint32_t res_type = htonl(CONNECTION_FETCH);
266 /* XXX: support other types */
267 if (type != SDB_HOST) {
268 sdb_log(SDB_LOG_ERR, "frontend: Invalid object type %d "
269 "in FETCH command", type);
270 sdb_strbuf_sprintf(conn->errbuf,
271 "FETCH: Invalid object type %d", type);
272 return -1;
273 }
275 host = sdb_store_get_host(name);
276 if (! host) {
277 sdb_log(SDB_LOG_DEBUG, "frontend: Failed to fetch host '%s': "
278 "not found", name);
280 sdb_strbuf_sprintf(conn->errbuf, "Host %s not found", name);
281 return -1;
282 }
284 buf = sdb_strbuf_create(1024);
285 if (! buf) {
286 char errbuf[1024];
287 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
288 "buffer to handle FETCH command: %s",
289 sdb_strerror(errno, errbuf, sizeof(errbuf)));
291 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
292 sdb_strbuf_destroy(buf);
293 sdb_object_deref(SDB_OBJ(host));
294 return -1;
295 }
297 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
298 if (sdb_store_host_tojson(host, buf, filter, /* flags = */ 0)) {
299 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
300 "host '%s' to JSON", name);
301 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
302 sdb_strbuf_destroy(buf);
303 sdb_object_deref(SDB_OBJ(host));
304 return -1;
305 }
307 sdb_connection_send(conn, CONNECTION_DATA,
308 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
309 sdb_strbuf_destroy(buf);
310 sdb_object_deref(SDB_OBJ(host));
311 return 0;
312 } /* sdb_fe_exec_fetch */
314 int
315 sdb_fe_exec_list(sdb_conn_t *conn, int type, sdb_store_matcher_t *filter)
316 {
317 sdb_strbuf_t *buf;
318 uint32_t res_type = htonl(CONNECTION_LIST);
320 int flags;
322 if (type == SDB_HOST)
323 flags = SDB_SKIP_ALL;
324 else if (type == SDB_SERVICE)
325 flags = (SDB_SKIP_ALL & (~SDB_SKIP_SERVICES))
326 | SDB_SKIP_EMPTY_SERVICES;
327 else if (type == SDB_METRIC)
328 flags = (SDB_SKIP_ALL & (~SDB_SKIP_METRICS))
329 | SDB_SKIP_EMPTY_METRICS;
330 else {
331 sdb_log(SDB_LOG_ERR, "frontend: Invalid object type %d "
332 "for LIST command", type);
333 sdb_strbuf_sprintf(conn->errbuf,
334 "LIST: Invalid object type %d", type);
335 return -1;
336 }
338 buf = sdb_strbuf_create(1024);
339 if (! buf) {
340 char errbuf[1024];
341 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
342 "buffer to handle LIST command: %s",
343 sdb_strerror(errno, errbuf, sizeof(errbuf)));
345 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
346 sdb_strbuf_destroy(buf);
347 return -1;
348 }
350 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
351 if (sdb_store_tojson(buf, filter, flags)) {
352 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
353 "store to JSON");
354 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
355 sdb_strbuf_destroy(buf);
356 return -1;
357 }
359 sdb_connection_send(conn, CONNECTION_DATA,
360 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
361 sdb_strbuf_destroy(buf);
362 return 0;
363 } /* sdb_fe_exec_list */
365 int
366 sdb_fe_exec_lookup(sdb_conn_t *conn, int type,
367 sdb_store_matcher_t *m, sdb_store_matcher_t *filter)
368 {
369 uint32_t res_type = htonl(CONNECTION_LOOKUP);
371 sdb_store_json_formatter_t *f;
372 sdb_strbuf_t *buf;
374 /* XXX: support other types */
375 if (type != SDB_HOST) {
376 sdb_log(SDB_LOG_ERR, "frontend: Invalid object type %d "
377 "in LOOKUP command", type);
378 sdb_strbuf_sprintf(conn->errbuf,
379 "LOOKUP: Invalid object type %d", type);
380 return -1;
381 }
383 buf = sdb_strbuf_create(1024);
384 if (! buf) {
385 char errbuf[1024];
386 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
387 "buffer to handle LOOKUP command: %s",
388 sdb_strerror(errno, errbuf, sizeof(errbuf)));
390 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
391 return -1;
392 }
393 f = sdb_store_json_formatter(buf);
394 if (! f) {
395 char errbuf[1024];
396 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
397 "JSON formatter to handle LOOKUP command: %s",
398 sdb_strerror(errno, errbuf, sizeof(errbuf)));
400 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
401 sdb_strbuf_destroy(buf);
402 return -1;
403 }
405 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
406 sdb_strbuf_append(buf, "[");
408 if (sdb_store_scan(SDB_HOST, m, filter, lookup_tojson, f)) {
409 sdb_log(SDB_LOG_ERR, "frontend: Failed to lookup hosts");
410 sdb_strbuf_sprintf(conn->errbuf, "Failed to lookup hosts");
411 sdb_strbuf_destroy(buf);
412 free(f);
413 return -1;
414 }
415 sdb_store_json_finish(f);
417 sdb_strbuf_append(buf, "]");
419 sdb_connection_send(conn, CONNECTION_DATA,
420 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
421 sdb_strbuf_destroy(buf);
422 free(f);
423 return 0;
424 } /* sdb_fe_exec_lookup */
426 int
427 sdb_fe_exec_timeseries(sdb_conn_t *conn,
428 const char *hostname, const char *metric,
429 sdb_timeseries_opts_t *opts)
430 {
431 sdb_strbuf_t *buf;
432 uint32_t res_type = htonl(CONNECTION_TIMESERIES);
434 buf = sdb_strbuf_create(1024);
435 if (! buf) {
436 char errbuf[1024];
437 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
438 "buffer to handle TIMESERIES command: %s",
439 sdb_strerror(errno, errbuf, sizeof(errbuf)));
441 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
442 return -1;
443 }
445 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
446 if (sdb_store_fetch_timeseries(hostname, metric, opts, buf)) {
447 sdb_log(SDB_LOG_ERR, "frontend: Failed to fetch time-series");
448 sdb_strbuf_sprintf(conn->errbuf, "Failed to fetch time-series");
449 sdb_strbuf_destroy(buf);
450 return -1;
451 }
453 sdb_connection_send(conn, CONNECTION_DATA,
454 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
455 sdb_strbuf_destroy(buf);
456 return 0;
457 } /* sdb_fe_exec_timeseries */
459 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */