Code

frontend: Use the plugin query interface instead of direct store access.
[sysdb.git] / src / frontend / query.c
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 #ifdef HAVE_CONFIG_H
29 #       include "config.h"
30 #endif
32 #include "sysdb.h"
34 #include "core/plugin.h"
35 #include "frontend/connection-private.h"
36 #include "parser/ast.h"
37 #include "parser/parser.h"
38 #include "utils/error.h"
39 #include "utils/proto.h"
40 #include "utils/strbuf.h"
42 #include <errno.h>
43 #include <string.h>
45 /*
46  * private helper functions
47  */
49 static char *
50 sstrdup(const char *s)
51 {
52         return s ? strdup(s) : NULL;
53 } /* sstrdup */
55 static int
56 query_exec(sdb_conn_t *conn, sdb_ast_node_t *ast)
57 {
58         sdb_strbuf_t *buf;
59         int status;
61         if (! ast) {
62                 sdb_strbuf_sprintf(conn->errbuf, "out of memory");
63                 return -1;
64         }
66         buf = sdb_strbuf_create(1024);
67         if (! buf) {
68                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
69                 return -1;
70         }
71         status = sdb_plugin_query(ast, buf, conn->errbuf);
72         if (status < 0) {
73                 char query[conn->cmd_len + 1];
74                 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
75                 query[sizeof(query) - 1] = '\0';
76                 sdb_log(SDB_LOG_ERR, "frontend: failed to execute query '%s'", query);
77         }
78         else
79                 sdb_connection_send(conn, status,
80                                 (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
82         sdb_strbuf_destroy(buf);
83         return status < 0 ? status : 0;
84 } /* query_exec */
86 /*
87  * public API
88  */
90 int
91 sdb_fe_query(sdb_conn_t *conn)
92 {
93         sdb_llist_t *parsetree;
94         sdb_ast_node_t *ast = NULL;
95         int status = 0;
97         if ((! conn) || (conn->cmd != SDB_CONNECTION_QUERY))
98                 return -1;
100         parsetree = sdb_parser_parse(sdb_strbuf_string(conn->buf),
101                         (int)conn->cmd_len, conn->errbuf);
102         if (! parsetree) {
103                 char query[conn->cmd_len + 1];
104                 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
105                 query[sizeof(query) - 1] = '\0';
106                 sdb_log(SDB_LOG_ERR, "frontend: Failed to parse query '%s': %s",
107                                 query, sdb_strbuf_string(conn->errbuf));
108                 return -1;
109         }
111         switch (sdb_llist_len(parsetree)) {
112                 case 0:
113                         /* skipping empty command; send back an empty reply */
114                         sdb_connection_send(conn, SDB_CONNECTION_DATA, 0, NULL);
115                         break;
116                 case 1:
117                         ast = SDB_AST_NODE(sdb_llist_get(parsetree, 0));
118                         break;
120                 default:
121                         {
122                                 char query[conn->cmd_len + 1];
123                                 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
124                                 query[sizeof(query) - 1] = '\0';
125                                 sdb_log(SDB_LOG_WARNING, "frontend: Ignoring %zu command%s "
126                                                 "in multi-statement query '%s'",
127                                                 sdb_llist_len(parsetree) - 1,
128                                                 sdb_llist_len(parsetree) == 2 ? "" : "s",
129                                                 query);
130                                 ast = SDB_AST_NODE(sdb_llist_get(parsetree, 0));
131                         }
132         }
134         if (ast) {
135                 status = query_exec(conn, ast);
136                 sdb_object_deref(SDB_OBJ(ast));
137         }
138         sdb_llist_destroy(parsetree);
139         return status;
140 } /* sdb_fe_query */
142 int
143 sdb_fe_fetch(sdb_conn_t *conn)
145         sdb_ast_node_t *ast;
146         char hostname[conn->cmd_len + 1];
147         char name[conn->cmd_len + 1];
148         uint32_t type;
149         int status;
151         if ((! conn) || (conn->cmd != SDB_CONNECTION_FETCH))
152                 return -1;
154         if (conn->cmd_len < sizeof(uint32_t)) {
155                 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
156                                 "FETCH command", conn->cmd_len);
157                 sdb_strbuf_sprintf(conn->errbuf, "FETCH: Invalid command length %d",
158                                 conn->cmd_len);
159                 return -1;
160         }
162         /* TODO: support other types besides hosts */
163         hostname[0] = '\0';
165         sdb_proto_unmarshal_int32(SDB_STRBUF_STR(conn->buf), &type);
166         strncpy(name, sdb_strbuf_string(conn->buf) + sizeof(uint32_t),
167                         conn->cmd_len - sizeof(uint32_t));
168         name[sizeof(name) - 1] = '\0';
170         ast = sdb_ast_fetch_create((int)type,
171                         hostname[0] ? strdup(hostname) : NULL,
172                         name[0] ? strdup(name) : NULL,
173                         /* filter = */ NULL);
174         status = query_exec(conn, ast);
175         sdb_object_deref(SDB_OBJ(ast));
176         return status;
177 } /* sdb_fe_fetch */
179 int
180 sdb_fe_list(sdb_conn_t *conn)
182         sdb_ast_node_t *ast;
183         uint32_t type = SDB_HOST;
184         int status;
186         if ((! conn) || (conn->cmd != SDB_CONNECTION_LIST))
187                 return -1;
189         if (conn->cmd_len == sizeof(uint32_t))
190                 sdb_proto_unmarshal_int32(SDB_STRBUF_STR(conn->buf), &type);
191         else if (conn->cmd_len) {
192                 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
193                                 "LIST command", conn->cmd_len);
194                 sdb_strbuf_sprintf(conn->errbuf, "LIST: Invalid command length %d",
195                                 conn->cmd_len);
196                 return -1;
197         }
199         ast = sdb_ast_list_create((int)type, /* filter = */ NULL);
200         status = query_exec(conn, ast);
201         sdb_object_deref(SDB_OBJ(ast));
202         return status;
203 } /* sdb_fe_list */
205 int
206 sdb_fe_lookup(sdb_conn_t *conn)
208         sdb_ast_node_t *ast, *m;
209         const char *matcher;
210         size_t matcher_len;
212         uint32_t type;
213         int status;
215         if ((! conn) || (conn->cmd != SDB_CONNECTION_LOOKUP))
216                 return -1;
218         if (conn->cmd_len < sizeof(uint32_t)) {
219                 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %d for "
220                                 "LOOKUP command", conn->cmd_len);
221                 sdb_strbuf_sprintf(conn->errbuf, "LOOKUP: Invalid command length %d",
222                                 conn->cmd_len);
223                 return -1;
224         }
225         sdb_proto_unmarshal_int32(SDB_STRBUF_STR(conn->buf), &type);
227         matcher = sdb_strbuf_string(conn->buf) + sizeof(uint32_t);
228         matcher_len = conn->cmd_len - sizeof(uint32_t);
229         m = sdb_parser_parse_conditional(matcher, (int)matcher_len, conn->errbuf);
230         if (! m) {
231                 char expr[matcher_len + 1];
232                 strncpy(expr, matcher, sizeof(expr));
233                 expr[sizeof(expr) - 1] = '\0';
234                 sdb_log(SDB_LOG_ERR, "frontend: Failed to parse lookup condition '%s': %s",
235                                 expr, sdb_strbuf_string(conn->errbuf));
236                 return -1;
237         }
239         ast = sdb_ast_lookup_create((int)type, m, /* filter = */ NULL);
240         /* run analyzer using the full context */
241         if (ast && sdb_parser_analyze(ast, conn->errbuf)) {
242                 char expr[matcher_len + 1];
243                 char err[sdb_strbuf_len(conn->errbuf) + sizeof(expr) + 64];
244                 strncpy(expr, matcher, sizeof(expr));
245                 expr[sizeof(expr) - 1] = '\0';
246                 snprintf(err, sizeof(err), "Failed to parse lookup condition '%s': %s",
247                                 expr, sdb_strbuf_string(conn->errbuf));
248                 sdb_strbuf_sprintf(conn->errbuf, "%s", err);
249                 status = -1;
250         }
251         else
252                 status = query_exec(conn, ast);
253         if (! ast)
254                 sdb_object_deref(SDB_OBJ(m));
255         sdb_object_deref(SDB_OBJ(ast));
256         return status;
257 } /* sdb_fe_lookup */
259 int
260 sdb_fe_store(sdb_conn_t *conn)
262         sdb_ast_node_t *ast;
263         const char *buf = sdb_strbuf_string(conn->buf);
264         size_t len = conn->cmd_len;
265         uint32_t type;
266         ssize_t n;
267         int status;
269         if ((! conn) || (conn->cmd != SDB_CONNECTION_STORE))
270                 return -1;
272         if ((n = sdb_proto_unmarshal_int32(buf, len, &type)) < 0) {
273                 sdb_log(SDB_LOG_ERR, "frontend: Invalid command length %zu for "
274                                 "STORE command", len);
275                 sdb_strbuf_sprintf(conn->errbuf,
276                                 "STORE: Invalid command length %zu", len);
277                 return -1;
278         }
280         switch (type) {
281                 case SDB_HOST:
282                 {
283                         sdb_proto_host_t host;
284                         if (sdb_proto_unmarshal_host(buf, len, &host) < 0) {
285                                 sdb_strbuf_sprintf(conn->errbuf,
286                                                 "STORE: Failed to unmarshal host object");
287                                 return -1;
288                         }
289                         ast = sdb_ast_store_create(SDB_HOST, /* host */ NULL,
290                                         /* parent */ 0, NULL, sstrdup(host.name), host.last_update,
291                                         /* metric store */ NULL, NULL, SDB_DATA_NULL);
292                 }
293                 break;
295                 case SDB_SERVICE:
296                 {
297                         sdb_proto_service_t svc;
298                         if (sdb_proto_unmarshal_service(buf, len, &svc) < 0) {
299                                 sdb_strbuf_sprintf(conn->errbuf,
300                                                 "STORE: Failed to unmarshal service object");
301                                 return -1;
302                         }
303                         ast = sdb_ast_store_create(SDB_SERVICE, sstrdup(svc.hostname),
304                                         /* parent */ 0, NULL, sstrdup(svc.name), svc.last_update,
305                                         /* metric store */ NULL, NULL, SDB_DATA_NULL);
306                 }
307                 break;
309                 case SDB_METRIC:
310                 {
311                         sdb_proto_metric_t metric;
312                         if (sdb_proto_unmarshal_metric(buf, len, &metric) < 0) {
313                                 sdb_strbuf_sprintf(conn->errbuf,
314                                                 "STORE: Failed to unmarshal metric object");
315                                 return -1;
316                         }
317                         ast = sdb_ast_store_create(SDB_METRIC, sstrdup(metric.hostname),
318                                         /* parent */ 0, NULL, sstrdup(metric.name), metric.last_update,
319                                         sstrdup(metric.store_type), sstrdup(metric.store_id),
320                                         SDB_DATA_NULL);
321                 }
322                 break;
323         }
325         if (type & SDB_ATTRIBUTE) {
326                 sdb_proto_attribute_t attr;
327                 const char *hostname, *parent;
328                 int parent_type;
329                 if (sdb_proto_unmarshal_attribute(buf, len, &attr) < 0) {
330                         sdb_strbuf_sprintf(conn->errbuf,
331                                         "STORE: Failed to unmarshal attribute object");
332                         return -1;
333                 }
334                 if (attr.parent_type == SDB_HOST) {
335                         hostname = attr.parent;
336                         parent_type = 0;
337                         parent = NULL;
338                 }
339                 else {
340                         hostname = attr.hostname;
341                         parent_type = attr.parent_type;
342                         parent = attr.parent;
343                 }
344                 ast = sdb_ast_store_create(SDB_ATTRIBUTE, sstrdup(hostname),
345                                 parent_type, sstrdup(parent), sstrdup(attr.key),
346                                 attr.last_update, /* metric store */ NULL, NULL,
347                                 attr.value);
348         }
350         if (! ast) {
351                 sdb_log(SDB_LOG_ERR, "frontend: Invalid object type %d for "
352                                 "STORE COMMAND", type);
353                 sdb_strbuf_sprintf(conn->errbuf, "STORE: Invalid object type %d", type);
354                 return -1;
355         }
357         status = sdb_parser_analyze(ast, conn->errbuf);
358         if (! status)
359                 status = query_exec(conn, ast);
360         sdb_object_deref(SDB_OBJ(ast));
361         return status;
362 } /* sdb_fe_store */
364 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */