1 /*
2 * SysDB - src/core/store_exec.c
3 * Copyright (C) 2014-2015 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 "core/object.h"
29 #include "core/plugin.h"
30 #include "core/store-private.h"
31 #include "frontend/connection.h"
32 #include "parser/ast.h"
33 #include "utils/error.h"
35 #include <errno.h>
37 #include <arpa/inet.h>
38 #include <ctype.h>
39 #include <stdlib.h>
40 #include <string.h>
42 /*
43 * private helper functions
44 */
46 static int
47 list_tojson(sdb_store_obj_t *obj,
48 sdb_store_matcher_t __attribute__((unused)) *filter,
49 void *user_data)
50 {
51 sdb_store_json_formatter_t *f = user_data;
52 return sdb_store_json_emit(f, obj);
53 } /* list_tojson */
55 static int
56 lookup_tojson(sdb_store_obj_t *obj, sdb_store_matcher_t *filter,
57 void *user_data)
58 {
59 sdb_store_json_formatter_t *f = user_data;
60 return sdb_store_json_emit_full(f, obj, filter);
61 } /* lookup_tojson */
63 static size_t
64 sstrlen(const char *s)
65 {
66 return s ? strlen(s) : 0;
67 } /* sstrlen */
69 /*
70 * query implementations
71 */
73 static int
74 exec_fetch(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
75 int type, const char *hostname, const char *name,
76 sdb_store_matcher_t *filter)
77 {
78 uint32_t res_type = htonl(SDB_CONNECTION_FETCH);
80 sdb_store_obj_t *host;
81 sdb_store_obj_t *obj;
83 sdb_store_json_formatter_t *f;
85 if ((! name) || ((type == SDB_HOST) && hostname)
86 || ((type != SDB_HOST) && (! hostname))) {
87 /* This is a programming error, not something the client did wrong */
88 sdb_strbuf_sprintf(errbuf, "INTERNAL ERROR: invalid "
89 "arguments to FETCH(%s, %s, %s)",
90 SDB_STORE_TYPE_TO_NAME(type), hostname, name);
91 return -1;
92 }
93 if (type == SDB_HOST)
94 hostname = name;
96 host = sdb_store_get_host(store, hostname);
97 if ((! host)
98 || (filter && (! sdb_store_matcher_matches(filter, host, NULL)))) {
99 sdb_strbuf_sprintf(errbuf, "Failed to fetch %s %s: "
100 "host %s not found", SDB_STORE_TYPE_TO_NAME(type),
101 name, hostname);
102 sdb_object_deref(SDB_OBJ(host));
103 return -1;
104 }
105 if (type == SDB_HOST) {
106 obj = host;
107 }
108 else {
109 obj = sdb_store_get_child(host, type, name);
110 if ((! obj)
111 || (filter && (! sdb_store_matcher_matches(filter, obj, NULL)))) {
112 sdb_strbuf_sprintf(errbuf, "Failed to fetch %s %s.%s: "
113 "%s not found", SDB_STORE_TYPE_TO_NAME(type),
114 hostname, name, name);
115 if (obj)
116 sdb_object_deref(SDB_OBJ(obj));
117 sdb_object_deref(SDB_OBJ(host));
118 return -1;
119 }
120 sdb_object_deref(SDB_OBJ(host));
121 }
122 host = NULL;
124 f = sdb_store_json_formatter(buf, type, /* flags = */ 0);
125 if (! f) {
126 char err[1024];
127 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
128 "JSON formatter to handle FETCH command: %s",
129 sdb_strerror(errno, err, sizeof(err)));
131 sdb_strbuf_sprintf(errbuf, "Out of memory");
132 sdb_object_deref(SDB_OBJ(obj));
133 return -1;
134 }
136 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
137 if (sdb_store_json_emit_full(f, obj, filter)) {
138 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
139 "%s %s.%s to JSON", SDB_STORE_TYPE_TO_NAME(type),
140 hostname, name);
141 sdb_strbuf_sprintf(errbuf, "Out of memory");
142 sdb_object_deref(SDB_OBJ(f));
143 sdb_object_deref(SDB_OBJ(obj));
144 return -1;
145 }
147 sdb_object_deref(SDB_OBJ(obj));
148 sdb_store_json_finish(f);
149 sdb_object_deref(SDB_OBJ(f));
151 return SDB_CONNECTION_DATA;
152 } /* exec_fetch */
154 static int
155 exec_list(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
156 int type, sdb_store_matcher_t *filter)
157 {
158 uint32_t res_type = htonl(SDB_CONNECTION_LIST);
159 sdb_store_json_formatter_t *f;
161 f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
162 if (! f) {
163 char err[1024];
164 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
165 "JSON formatter to handle LIST command: %s",
166 sdb_strerror(errno, err, sizeof(err)));
168 sdb_strbuf_sprintf(errbuf, "Out of memory");
169 return -1;
170 }
172 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
173 if (sdb_store_scan(store, type, /* m = */ NULL, filter, list_tojson, f)) {
174 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
175 "store to JSON");
176 sdb_strbuf_sprintf(errbuf, "Out of memory");
177 sdb_object_deref(SDB_OBJ(f));
178 return -1;
179 }
181 sdb_store_json_finish(f);
182 sdb_object_deref(SDB_OBJ(f));
184 return SDB_CONNECTION_DATA;
185 } /* exec_list */
187 static int
188 exec_lookup(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
189 int type, sdb_store_matcher_t *m, sdb_store_matcher_t *filter)
190 {
191 uint32_t res_type = htonl(SDB_CONNECTION_LOOKUP);
192 sdb_store_json_formatter_t *f;
194 f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY);
195 if (! f) {
196 char err[1024];
197 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
198 "JSON formatter to handle LOOKUP command: %s",
199 sdb_strerror(errno, err, sizeof(err)));
201 sdb_strbuf_sprintf(errbuf, "Out of memory");
202 return -1;
203 }
205 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
207 if (sdb_store_scan(store, type, m, filter, lookup_tojson, f)) {
208 sdb_log(SDB_LOG_ERR, "frontend: Failed to lookup %ss",
209 SDB_STORE_TYPE_TO_NAME(type));
210 sdb_strbuf_sprintf(errbuf, "Failed to lookup %ss",
211 SDB_STORE_TYPE_TO_NAME(type));
212 sdb_object_deref(SDB_OBJ(f));
213 return -1;
214 }
216 sdb_store_json_finish(f);
217 sdb_object_deref(SDB_OBJ(f));
219 return SDB_CONNECTION_DATA;
220 } /* exec_lookup */
222 static int
223 exec_store(sdb_strbuf_t *buf, sdb_strbuf_t *errbuf, sdb_ast_store_t *st)
224 {
225 char name[sstrlen(st->hostname) + sstrlen(st->parent) + sstrlen(st->name) + 3];
226 sdb_metric_store_t metric_store;
227 int type = st->obj_type, status = -1;
229 switch (st->obj_type) {
230 case SDB_HOST:
231 strncpy(name, st->name, sizeof(name));
232 status = sdb_plugin_store_host(st->name, st->last_update);
233 break;
235 case SDB_SERVICE:
236 snprintf(name, sizeof(name), "%s.%s", st->hostname, st->name);
237 status = sdb_plugin_store_service(st->hostname, st->name, st->last_update);
238 break;
240 case SDB_METRIC:
241 snprintf(name, sizeof(name), "%s.%s", st->hostname, st->name);
242 metric_store.type = st->store_type;
243 metric_store.id = st->store_id;
244 status = sdb_plugin_store_metric(st->hostname, st->name,
245 &metric_store, st->last_update);
246 break;
248 case SDB_ATTRIBUTE:
249 type |= st->parent_type;
251 if (st->parent)
252 snprintf(name, sizeof(name), "%s.%s.%s",
253 st->hostname, st->parent, st->name);
254 else
255 snprintf(name, sizeof(name), "%s.%s", st->hostname, st->name);
257 switch (st->parent_type) {
258 case 0:
259 type |= SDB_HOST;
260 status = sdb_plugin_store_attribute(st->hostname,
261 st->name, &st->value, st->last_update);
262 break;
264 case SDB_SERVICE:
265 status = sdb_plugin_store_service_attribute(st->hostname, st->parent,
266 st->name, &st->value, st->last_update);
267 break;
269 case SDB_METRIC:
270 status = sdb_plugin_store_metric_attribute(st->hostname, st->parent,
271 st->name, &st->value, st->last_update);
272 break;
274 default:
275 sdb_log(SDB_LOG_ERR, "store: Invalid parent type in STORE: %s",
276 SDB_STORE_TYPE_TO_NAME(st->parent_type));
277 return -1;
278 }
279 break;
281 default:
282 sdb_log(SDB_LOG_ERR, "store: Invalid object type in STORE: %s",
283 SDB_STORE_TYPE_TO_NAME(st->obj_type));
284 return -1;
285 }
287 if (status < 0) {
288 sdb_strbuf_sprintf(errbuf, "STORE: Failed to store %s object",
289 SDB_STORE_TYPE_TO_NAME(type));
290 return -1;
291 }
293 if (! status) {
294 sdb_strbuf_sprintf(buf, "Successfully stored %s %s",
295 SDB_STORE_TYPE_TO_NAME(type), name);
296 }
297 else {
298 char type_str[32];
299 strncpy(type_str, SDB_STORE_TYPE_TO_NAME(type), sizeof(type_str));
300 type_str[0] = (char)toupper((int)type_str[0]);
301 sdb_strbuf_sprintf(buf, "%s %s already up to date", type_str, name);
302 }
304 return SDB_CONNECTION_OK;
305 } /* exec_store */
307 static int
308 exec_timeseries(sdb_store_t *store, sdb_strbuf_t *buf, sdb_strbuf_t *errbuf,
309 const char *hostname, const char *metric,
310 sdb_timeseries_opts_t *opts)
311 {
312 uint32_t res_type = htonl(SDB_CONNECTION_TIMESERIES);
314 sdb_strbuf_memcpy(buf, &res_type, sizeof(uint32_t));
315 if (sdb_store_fetch_timeseries(store, hostname, metric, opts, buf)) {
316 sdb_log(SDB_LOG_ERR, "frontend: Failed to fetch time-series");
317 sdb_strbuf_sprintf(errbuf, "Failed to fetch time-series");
318 return -1;
319 }
321 return SDB_CONNECTION_DATA;
322 } /* exec_timeseries */
324 /*
325 * public API
326 */
328 int
329 sdb_store_query_execute(sdb_store_t *store, sdb_store_query_t *q,
330 sdb_strbuf_t *buf, sdb_strbuf_t *errbuf)
331 {
332 sdb_timeseries_opts_t ts_opts;
333 sdb_ast_node_t *ast;
335 if (! q)
336 return -1;
337 if (! q->ast) {
338 sdb_log(SDB_LOG_ERR, "store: Invalid empty query");
339 return -1;
340 }
342 ast = q->ast;
343 switch (ast->type) {
344 case SDB_AST_TYPE_FETCH:
345 return exec_fetch(store, buf, errbuf, SDB_AST_FETCH(ast)->obj_type,
346 SDB_AST_FETCH(ast)->hostname, SDB_AST_FETCH(ast)->name,
347 q->filter);
349 case SDB_AST_TYPE_LIST:
350 return exec_list(store, buf, errbuf, SDB_AST_LIST(ast)->obj_type,
351 q->filter);
353 case SDB_AST_TYPE_LOOKUP:
354 return exec_lookup(store, buf, errbuf, SDB_AST_LOOKUP(ast)->obj_type,
355 q->matcher, q->filter);
357 case SDB_AST_TYPE_STORE:
358 if (ast->type != SDB_AST_TYPE_STORE) {
359 sdb_log(SDB_LOG_ERR, "store: Invalid AST node for STORE command: %s",
360 SDB_AST_TYPE_TO_STRING(ast));
361 return -1;
362 }
363 return exec_store(buf, errbuf, SDB_AST_STORE(ast));
365 case SDB_AST_TYPE_TIMESERIES:
366 ts_opts.start = SDB_AST_TIMESERIES(ast)->start;
367 ts_opts.end = SDB_AST_TIMESERIES(ast)->end;
368 return exec_timeseries(store, buf, errbuf,
369 SDB_AST_TIMESERIES(ast)->hostname,
370 SDB_AST_TIMESERIES(ast)->metric, &ts_opts);
372 default:
373 sdb_log(SDB_LOG_ERR, "store: Invalid query of type %s",
374 SDB_AST_TYPE_TO_STRING(ast));
375 return -1;
376 }
378 return 0;
379 } /* sdb_store_query_execute */
381 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */