Code

When querying services/metrics skip hosts without such children.
[sysdb.git] / src / frontend / query.c
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 typedef struct {
45         sdb_strbuf_t *buf;
46         sdb_store_matcher_t *filter;
48         size_t last_len;
49 } tojson_data_t;
51 static int
52 lookup_tojson(sdb_store_obj_t *obj, void *user_data)
53 {
54         tojson_data_t *data = user_data;
55         int status;
57         if (data->filter && (! sdb_store_matcher_matches(data->filter, obj, NULL)))
58                 return 0;
60         if (sdb_strbuf_len(data->buf) > data->last_len)
61                 sdb_strbuf_append(data->buf, ",");
62         data->last_len = sdb_strbuf_len(data->buf);
63         status = sdb_store_host_tojson(obj, data->buf,
64                         data->filter, /* flags = */ 0);
65         return status;
66 } /* lookup_tojson */
68 /*
69  * public API
70  */
72 int
73 sdb_fe_query(sdb_conn_t *conn)
74 {
75         sdb_llist_t *parsetree;
76         sdb_conn_node_t *node = NULL;
77         int status = 0;
79         if ((! conn) || (conn->cmd != CONNECTION_QUERY))
80                 return -1;
82         parsetree = sdb_fe_parse(sdb_strbuf_string(conn->buf),
83                         (int)conn->cmd_len);
84         if (! parsetree) {
85                 char query[conn->cmd_len + 1];
86                 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
87                 query[sizeof(query) - 1] = '\0';
88                 sdb_log(SDB_LOG_ERR, "frontend: Failed to parse query '%s'",
89                                 query);
90                 return -1;
91         }
93         switch (sdb_llist_len(parsetree)) {
94                 case 0:
95                         /* skipping empty command; send back an empty reply */
96                         sdb_connection_send(conn, CONNECTION_DATA, 0, NULL);
97                         break;
98                 case 1:
99                         node = SDB_CONN_NODE(sdb_llist_get(parsetree, 0));
100                         break;
102                 default:
103                         {
104                                 char query[conn->cmd_len + 1];
105                                 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
106                                 query[sizeof(query) - 1] = '\0';
107                                 sdb_log(SDB_LOG_WARNING, "frontend: Ignoring %zu command%s "
108                                                 "in multi-statement query '%s'",
109                                                 sdb_llist_len(parsetree) - 1,
110                                                 sdb_llist_len(parsetree) == 2 ? "" : "s",
111                                                 query);
112                                 node = SDB_CONN_NODE(sdb_llist_get(parsetree, 0));
113                         }
114         }
116         if (node) {
117                 status = sdb_fe_exec(conn, node);
118                 sdb_object_deref(SDB_OBJ(node));
119         }
121         sdb_llist_destroy(parsetree);
122         return status;
123 } /* sdb_fe_query */
125 int
126 sdb_fe_fetch(sdb_conn_t *conn)
128         char hostname[conn->cmd_len + 1];
129         if ((! conn) || (conn->cmd != CONNECTION_FETCH))
130                 return -1;
131         strncpy(hostname, sdb_strbuf_string(conn->buf), conn->cmd_len);
132         hostname[sizeof(hostname) - 1] = '\0';
133         return sdb_fe_exec_fetch(conn, hostname, /* filter = */ NULL);
134 } /* sdb_fe_fetch */
136 int
137 sdb_fe_list(sdb_conn_t *conn)
139         int type = SDB_HOST;
141         if ((! conn) || (conn->cmd != CONNECTION_LIST))
142                 return -1;
144         if (conn->cmd_len == sizeof(uint32_t))
145                 type = sdb_proto_get_int(conn->buf, 0);
146         else if (conn->cmd_len) {
147                 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
148                                 "LIST command", conn->cmd_len);
149                 sdb_strbuf_sprintf(conn->errbuf, "LIST: Invalid command length %d",
150                                 conn->cmd_len);
151                 return -1;
152         }
153         return sdb_fe_exec_list(conn, type, /* filter = */ NULL);
154 } /* sdb_fe_list */
156 int
157 sdb_fe_lookup(sdb_conn_t *conn)
159         sdb_store_matcher_t *m;
160         int status;
162         if ((! conn) || (conn->cmd != CONNECTION_LOOKUP))
163                 return -1;
165         m = sdb_fe_parse_matcher(sdb_strbuf_string(conn->buf),
166                         (int)conn->cmd_len);
167         if (! m) {
168                 char expr[conn->cmd_len + 1];
169                 strncpy(expr, sdb_strbuf_string(conn->buf), conn->cmd_len);
170                 expr[sizeof(expr) - 1] = '\0';
171                 sdb_log(SDB_LOG_ERR, "frontend: Failed to parse "
172                                 "lookup condition '%s'", expr);
173                 return -1;
174         }
176         status = sdb_fe_exec_lookup(conn, m, /* filter = */ NULL);
177         sdb_object_deref(SDB_OBJ(m));
178         return status;
179 } /* sdb_fe_lookup */
181 int
182 sdb_fe_exec(sdb_conn_t *conn, sdb_conn_node_t *node)
184         sdb_store_matcher_t *m = NULL, *filter = NULL;
186         if (! node)
187                 return -1;
189         switch (node->cmd) {
190                 case CONNECTION_FETCH:
191                         if (CONN_FETCH(node)->filter)
192                                 filter = CONN_FETCH(node)->filter->matcher;
193                         return sdb_fe_exec_fetch(conn, CONN_FETCH(node)->name, filter);
194                 case CONNECTION_LIST:
195                         if (CONN_LIST(node)->filter)
196                                 filter = CONN_LIST(node)->filter->matcher;
197                         return sdb_fe_exec_list(conn, CONN_LIST(node)->type, filter);
198                 case CONNECTION_LOOKUP:
199                         if (CONN_LOOKUP(node)->matcher)
200                                 m = CONN_LOOKUP(node)->matcher->matcher;
201                         if (CONN_LOOKUP(node)->filter)
202                                 filter = CONN_LOOKUP(node)->filter->matcher;
203                         return sdb_fe_exec_lookup(conn, m, filter);
204                 case CONNECTION_TIMESERIES:
205                         return sdb_fe_exec_timeseries(conn,
206                                         CONN_TS(node)->hostname, CONN_TS(node)->metric,
207                                         &CONN_TS(node)->opts);
209                 default:
210                         sdb_log(SDB_LOG_ERR, "frontend: Unknown command %i", node->cmd);
211                         return -1;
212         }
213         return -1;
214 } /* sdb_fe_exec */
216 int
217 sdb_fe_exec_fetch(sdb_conn_t *conn, const char *name,
218                 sdb_store_matcher_t *filter)
220         sdb_strbuf_t *buf;
221         sdb_store_obj_t *host;
222         uint32_t res_type = htonl(CONNECTION_FETCH);
224         host = sdb_store_get_host(name);
225         if (! host) {
226                 sdb_log(SDB_LOG_DEBUG, "frontend: Failed to fetch host '%s': "
227                                 "not found", name);
229                 sdb_strbuf_sprintf(conn->errbuf, "Host %s not found", name);
230                 return -1;
231         }
233         buf = sdb_strbuf_create(1024);
234         if (! buf) {
235                 char errbuf[1024];
236                 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
237                                 "buffer to handle FETCH command: %s",
238                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
240                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
241                 sdb_strbuf_destroy(buf);
242                 sdb_object_deref(SDB_OBJ(host));
243                 return -1;
244         }
246         sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
247         if (sdb_store_host_tojson(host, buf, filter, /* flags = */ 0)) {
248                 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
249                                 "host '%s' to JSON", name);
250                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
251                 sdb_strbuf_destroy(buf);
252                 sdb_object_deref(SDB_OBJ(host));
253                 return -1;
254         }
256         sdb_connection_send(conn, CONNECTION_DATA,
257                         (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
258         sdb_strbuf_destroy(buf);
259         sdb_object_deref(SDB_OBJ(host));
260         return 0;
261 } /* sdb_fe_exec_fetch */
263 int
264 sdb_fe_exec_list(sdb_conn_t *conn, int type, sdb_store_matcher_t *filter)
266         sdb_strbuf_t *buf;
267         uint32_t res_type = htonl(CONNECTION_LIST);
269         int flags;
271         if (type == SDB_HOST)
272                 flags = SDB_SKIP_ALL;
273         else if (type == SDB_SERVICE)
274                 flags = (SDB_SKIP_ALL & (~SDB_SKIP_SERVICES))
275                         | SDB_SKIP_EMPTY_SERVICES;
276         else if (type == SDB_METRIC)
277                 flags = (SDB_SKIP_ALL & (~SDB_SKIP_METRICS))
278                         | SDB_SKIP_EMPTY_METRICS;
279         else {
280                 sdb_log(SDB_LOG_ERR, "frontend: Invalid object type %d "
281                                 "for LIST command", type);
282                 sdb_strbuf_sprintf(conn->errbuf,
283                                 "LIST: Invalid object type %d", type);
284                 return -1;
285         }
287         buf = sdb_strbuf_create(1024);
288         if (! buf) {
289                 char errbuf[1024];
290                 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
291                                 "buffer to handle LIST command: %s",
292                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
294                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
295                 sdb_strbuf_destroy(buf);
296                 return -1;
297         }
299         sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
300         if (sdb_store_tojson(buf, filter, flags)) {
301                 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
302                                 "store to JSON");
303                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
304                 sdb_strbuf_destroy(buf);
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         return 0;
312 } /* sdb_fe_exec_list */
314 int
315 sdb_fe_exec_lookup(sdb_conn_t *conn, sdb_store_matcher_t *m,
316                 sdb_store_matcher_t *filter)
318         tojson_data_t data = { NULL, filter, 0 };
319         uint32_t res_type = htonl(CONNECTION_LOOKUP);
321         data.buf = sdb_strbuf_create(1024);
322         if (! data.buf) {
323                 char errbuf[1024];
324                 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
325                                 "buffer to handle LOOKUP command: %s",
326                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
328                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
329                 sdb_strbuf_destroy(data.buf);
330                 return -1;
331         }
333         sdb_strbuf_memcpy(data.buf, &res_type, sizeof(uint32_t));
334         sdb_strbuf_append(data.buf, "[");
336         /* Let the JSON serializer handle the filter instead of the scanner. Else,
337          * we'd have to filter twice -- once in the scanner and then again in the
338          * serializer. */
339         data.last_len = sdb_strbuf_len(data.buf);
340         if (sdb_store_scan(m, /* filter */ NULL, lookup_tojson, &data)) {
341                 sdb_log(SDB_LOG_ERR, "frontend: Failed to lookup hosts");
342                 sdb_strbuf_sprintf(conn->errbuf, "Failed to lookup hosts");
343                 sdb_strbuf_destroy(data.buf);
344                 return -1;
345         }
347         sdb_strbuf_append(data.buf, "]");
349         sdb_connection_send(conn, CONNECTION_DATA,
350                         (uint32_t)sdb_strbuf_len(data.buf), sdb_strbuf_string(data.buf));
351         sdb_strbuf_destroy(data.buf);
352         return 0;
353 } /* sdb_fe_exec_lookup */
355 int
356 sdb_fe_exec_timeseries(sdb_conn_t *conn,
357                 const char *hostname, const char *metric,
358                 sdb_timeseries_opts_t *opts)
360         sdb_strbuf_t *buf;
361         uint32_t res_type = htonl(CONNECTION_TIMESERIES);
363         buf = sdb_strbuf_create(1024);
364         if (! buf) {
365                 char errbuf[1024];
366                 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
367                                 "buffer to handle TIMESERIES command: %s",
368                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
370                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
371                 return -1;
372         }
374         sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
375         if (sdb_store_fetch_timeseries(hostname, metric, opts, buf)) {
376                 sdb_log(SDB_LOG_ERR, "frontend: Failed to fetch time-series");
377                 sdb_strbuf_sprintf(conn->errbuf, "Failed to fetch time-series");
378                 sdb_strbuf_destroy(buf);
379                 return -1;
380         }
382         sdb_connection_send(conn, CONNECTION_DATA,
383                         (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
384         sdb_strbuf_destroy(buf);
385         return 0;
386 } /* sdb_fe_exec_timeseries */
388 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */