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) || (filter
277 && (! sdb_store_matcher_matches(filter, host, NULL)))) {
278 sdb_log(SDB_LOG_DEBUG, "frontend: Failed to fetch host '%s': "
279 "not found", name);
281 sdb_strbuf_sprintf(conn->errbuf, "Host %s not found", name);
282 return -1;
283 }
285 buf = sdb_strbuf_create(1024);
286 if (! buf) {
287 char errbuf[1024];
288 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
289 "buffer to handle FETCH command: %s",
290 sdb_strerror(errno, errbuf, sizeof(errbuf)));
292 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
293 sdb_strbuf_destroy(buf);
294 sdb_object_deref(SDB_OBJ(host));
295 return -1;
296 }
298 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
299 if (sdb_store_host_tojson(host, buf, filter, /* flags = */ 0)) {
300 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
301 "host '%s' to JSON", name);
302 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
303 sdb_strbuf_destroy(buf);
304 sdb_object_deref(SDB_OBJ(host));
305 return -1;
306 }
308 sdb_connection_send(conn, CONNECTION_DATA,
309 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
310 sdb_strbuf_destroy(buf);
311 sdb_object_deref(SDB_OBJ(host));
312 return 0;
313 } /* sdb_fe_exec_fetch */
315 int
316 sdb_fe_exec_list(sdb_conn_t *conn, int type, sdb_store_matcher_t *filter)
317 {
318 sdb_strbuf_t *buf;
319 uint32_t res_type = htonl(CONNECTION_LIST);
321 int flags;
323 if (type == SDB_HOST)
324 flags = SDB_SKIP_ALL;
325 else if (type == SDB_SERVICE)
326 flags = (SDB_SKIP_ALL & (~SDB_SKIP_SERVICES))
327 | SDB_SKIP_EMPTY_SERVICES;
328 else if (type == SDB_METRIC)
329 flags = (SDB_SKIP_ALL & (~SDB_SKIP_METRICS))
330 | SDB_SKIP_EMPTY_METRICS;
331 else {
332 sdb_log(SDB_LOG_ERR, "frontend: Invalid object type %d "
333 "for LIST command", type);
334 sdb_strbuf_sprintf(conn->errbuf,
335 "LIST: Invalid object type %d", type);
336 return -1;
337 }
339 buf = sdb_strbuf_create(1024);
340 if (! buf) {
341 char errbuf[1024];
342 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
343 "buffer to handle LIST command: %s",
344 sdb_strerror(errno, errbuf, sizeof(errbuf)));
346 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
347 sdb_strbuf_destroy(buf);
348 return -1;
349 }
351 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
352 if (sdb_store_tojson(buf, filter, flags)) {
353 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
354 "store to JSON");
355 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
356 sdb_strbuf_destroy(buf);
357 return -1;
358 }
360 sdb_connection_send(conn, CONNECTION_DATA,
361 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
362 sdb_strbuf_destroy(buf);
363 return 0;
364 } /* sdb_fe_exec_list */
366 int
367 sdb_fe_exec_lookup(sdb_conn_t *conn, int type,
368 sdb_store_matcher_t *m, sdb_store_matcher_t *filter)
369 {
370 uint32_t res_type = htonl(CONNECTION_LOOKUP);
372 sdb_store_json_formatter_t *f;
373 sdb_strbuf_t *buf;
375 /* XXX: support other types */
376 if (type != SDB_HOST) {
377 sdb_log(SDB_LOG_ERR, "frontend: Invalid object type %d "
378 "in LOOKUP command", type);
379 sdb_strbuf_sprintf(conn->errbuf,
380 "LOOKUP: Invalid object type %d", type);
381 return -1;
382 }
384 buf = sdb_strbuf_create(1024);
385 if (! buf) {
386 char errbuf[1024];
387 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
388 "buffer to handle LOOKUP command: %s",
389 sdb_strerror(errno, errbuf, sizeof(errbuf)));
391 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
392 return -1;
393 }
394 f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
395 if (! f) {
396 char errbuf[1024];
397 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
398 "JSON formatter to handle LOOKUP command: %s",
399 sdb_strerror(errno, errbuf, sizeof(errbuf)));
401 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
402 sdb_strbuf_destroy(buf);
403 return -1;
404 }
406 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
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_connection_send(conn, CONNECTION_DATA,
418 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
419 sdb_strbuf_destroy(buf);
420 free(f);
421 return 0;
422 } /* sdb_fe_exec_lookup */
424 int
425 sdb_fe_exec_timeseries(sdb_conn_t *conn,
426 const char *hostname, const char *metric,
427 sdb_timeseries_opts_t *opts)
428 {
429 sdb_strbuf_t *buf;
430 uint32_t res_type = htonl(CONNECTION_TIMESERIES);
432 buf = sdb_strbuf_create(1024);
433 if (! buf) {
434 char errbuf[1024];
435 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
436 "buffer to handle TIMESERIES command: %s",
437 sdb_strerror(errno, errbuf, sizeof(errbuf)));
439 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
440 return -1;
441 }
443 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
444 if (sdb_store_fetch_timeseries(hostname, metric, opts, buf)) {
445 sdb_log(SDB_LOG_ERR, "frontend: Failed to fetch time-series");
446 sdb_strbuf_sprintf(conn->errbuf, "Failed to fetch time-series");
447 sdb_strbuf_destroy(buf);
448 return -1;
449 }
451 sdb_connection_send(conn, CONNECTION_DATA,
452 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
453 sdb_strbuf_destroy(buf);
454 return 0;
455 } /* sdb_fe_exec_timeseries */
457 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */