Code

parser: Let the TIMESERIES command accept optional data-source names.
[sysdb.git] / src / tools / sysdb / json.c
1 /*
2  * SysDB - src/tools/sysdb/json.c
3  * Copyright (C) 2016 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 #if HAVE_CONFIG_H
29 #       include "config.h"
30 #endif /* HAVE_CONFIG_H */
32 #include "sysdb.h"
34 #include "core/store.h"
35 #include "frontend/proto.h"
36 #include "utils/error.h"
37 #include "utils/strbuf.h"
38 #include "tools/sysdb/json.h"
40 #ifdef HAVE_LIBYAJL
41 #       include <yajl/yajl_parse.h>
42 #endif
44 #include <stdbool.h>
45 #include <stdio.h>
46 #include <string.h>
48 #ifdef HAVE_LIBYAJL
50 /*
51  * formatter
52  */
54 typedef struct {
55         FILE *out;
57         /* The context describes the state of the formatter along with the
58          * respective parent contexts. */
59         int context[8];
60         ssize_t array_indices[8];
61         size_t current;
62         int next_context;
64         bool have_output;
65 } formatter_t;
66 #define F(obj) ((formatter_t *)(obj))
67 #define F_INIT(out) { (out), { 0 }, { -1, -1, -1, -1, -1, -1, -1, -1 }, 0, 0, false }
69 static int
70 push(formatter_t *f, int type)
71 {
72         f->current++;
73         if (f->current >= SDB_STATIC_ARRAY_LEN(f->context)) {
74                 sdb_log(SDB_LOG_ERR, "Nesting level too deep");
75                 return false;
76         }
77         f->context[f->current] = type;
78         return true;
79 } /* push */
81 static void
82 pop(formatter_t *f)
83 {
84         if (f->current == 0)
85                 return;
87         f->next_context = f->context[f->current];
88         f->current--;
89 } /* pop */
91 static void
92 print(formatter_t *f, const char *s, ssize_t l)
93 {
94         if (l >= 0) {
95                 /* 's' may point into a larger buffer and not be null-terminated */
96                 char buf[l + 1];
97                 strncpy(buf, s, l);
98                 buf[l] = '\0';
99                 fprintf(f->out, "%s", buf);
100         }
101         else
102                 fprintf(f->out, "%s", s);
104         f->have_output = true;
105 } /* print */
107 static void
108 indent(formatter_t *f)
110         size_t i;
111         for (i = 0; i < f->current - 1; i++)
112                 print(f, "\t", -1);
115 static void
116 format(formatter_t *f, const char *s, ssize_t l)
118         if (f->array_indices[f->current] >= 0) {
119                 if (f->array_indices[f->current] != 0)
120                         print(f, ", ", -1);
121                 f->array_indices[f->current]++;
122         }
124         print(f, s, l);
125         return;
126 } /* format */
128 static int
129 format_key(formatter_t *f, const char *k, ssize_t l)
131         int type = 0;
133         if (! strncasecmp("services", k, l))
134                 type = SDB_SERVICE;
135         else if (! strncasecmp("metrics", k, l))
136                 type = SDB_METRIC;
137         else if (! strncasecmp("attributes", k, l))
138                 type = SDB_ATTRIBUTE;
140         if (f->have_output)
141                 print(f, "\n", -1);
142         indent(f);
143         print(f, k, l);
144         print(f, ": ", -1);
146         f->next_context = type;
147         return true;
148 } /* format_key */
150 /*
151  * YAJL callbacks
152  */
154 static int
155 fmt_null(void *ctx) {
156         formatter_t *f = F(ctx);
157         format(f, "NULL", -1);
158         return true;
161 static int
162 fmt_boolean(void *ctx, int v) {
163         formatter_t *f = F(ctx);
164         if (v)
165                 format(f, "true", -1);
166         else
167                 format(f, "false", -1);
168         return true;
171 static int
172 fmt_number(void *ctx, const char *v, size_t l)
174         formatter_t *f = F(ctx);
175         format(f, v, l);
176         return true;
179 static int
180 fmt_string(void *ctx, const unsigned char *v, size_t l)
182         formatter_t *f = F(ctx);
183         format(f, (const char *)v, l);
184         return true;
187 static int
188 fmt_start_map(void *ctx) {
189         formatter_t *f = F(ctx);
190         const char *name;
192         if (!push(f, f->next_context))
193                 return false;
195         if (f->have_output)
196                 print(f, "\n", -1);
198         name = SDB_STORE_TYPE_TO_NAME(f->context[f->current]);
199         if (strcmp(name, "unknown")) {
200                 if (f->have_output)
201                         print(f, "\n", -1);
202                 indent(f);
203                 format(f, name, -1);
204         }
205         return true;
208 static int
209 fmt_map_key(void *ctx, const unsigned char *v, size_t l)
211         formatter_t *f = F(ctx);
212         return format_key(f, (const char *)v, l);
215 static int
216 fmt_end_map(void *ctx) {
217         formatter_t *f = F(ctx);
218         pop(f);
219         return true;
222 static int
223 fmt_start_array(void *ctx) {
224         formatter_t *f = F(ctx);
225         f->array_indices[f->current] = 0;
226         return true;
229 static int
230 fmt_end_array(void *ctx) {
231         formatter_t *f = F(ctx);
232         f->array_indices[f->current] = -1;
233         return true;
236 static yajl_callbacks fmts = {
237         fmt_null,
238         fmt_boolean,
239         NULL, /* fmt_integer; */
240         NULL, /* fmt_double; both default to fmt_number */
241         fmt_number,
242         fmt_string,
243         fmt_start_map,
244         fmt_map_key,
245         fmt_end_map,
246         fmt_start_array,
247         fmt_end_array,
248 };
250 #endif /* HAVE_LIBYAJL */
252 /*
253  * public API
254  */
256 int
257 sdb_json_print(FILE *out, sdb_input_t *input, int type, sdb_strbuf_t *buf)
259 #ifdef HAVE_LIBYAJL
260         const unsigned char *json;
261         size_t json_len;
263         yajl_handle h;
264         yajl_status status;
265         formatter_t f = F_INIT(out);
267         int ret = 0;
269         if (!input->interactive) {
270                 /* no formatting */
271                 fprintf(out, "%s\n", sdb_strbuf_string(buf));
272                 return 0;
273         }
275         /* Store lookups always return hosts at the top-level. */
276         f.context[0] = SDB_HOST;
277         switch (type) {
278         case SDB_CONNECTION_LIST:
279         case SDB_CONNECTION_LOOKUP:
280                 /* Array types */
281                 f.array_indices[0] = 0;
282                 break;
283         case SDB_CONNECTION_TIMESERIES:
284                 f.context[0] = SDB_TIMESERIES;
285                 break;
286         }
287         f.next_context = f.context[0];
289         h = yajl_alloc(&fmts, /* alloc_funcs */ NULL, &f);
290         if (! h)
291                 return -1;
293         json = (const unsigned char *)sdb_strbuf_string(buf);
294         json_len = sdb_strbuf_len(buf);
295         status = yajl_parse(h, json, json_len);
296         if (status == yajl_status_ok)
297                 status = yajl_complete_parse(h);
299         if (status != yajl_status_ok) {
300                 unsigned char *err = yajl_get_error(h, 1, json, json_len);
301                 sdb_log(SDB_LOG_ERR, "%s", err);
302                 yajl_free_error(h, err);
303                 ret = -1;
304         }
306         yajl_free(h);
307         return ret;
308 #else /* HAVE_LIBYAJL */
309         (void)input;
310         (void)type;
311         fprintf(out, "%s\n", sdb_strbuf_string(buf));
312         return 0;
313 #endif /* HAVE_LIBYAJL */
314 } /* sdb_json_print */
316 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */