Code

1bd8080970b831fe5a00c1dd586e887ee616e8e5
[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         /* The context describes the state of the formatter along with the
56          * respective parent contexts. */
57         int context[8];
58         ssize_t array_indices[8];
59         size_t current;
60         int next_context;
62         bool have_output;
63 } formatter_t;
64 #define F(obj) ((formatter_t *)(obj))
65 #define F_INIT { { 0 }, { -1, -1, -1, -1, -1, -1, -1, -1 }, 0, 0, false }
67 static int
68 push(formatter_t *f, int type)
69 {
70         f->current++;
71         if (f->current >= SDB_STATIC_ARRAY_LEN(f->context)) {
72                 sdb_log(SDB_LOG_ERR, "Nesting level too deep");
73                 return false;
74         }
75         f->context[f->current] = type;
76         return true;
77 } /* push */
79 static void
80 pop(formatter_t *f)
81 {
82         if (f->current == 0)
83                 return;
85         f->next_context = f->context[f->current];
86         f->current--;
87 } /* pop */
89 static void
90 print(formatter_t *f, const char *s, ssize_t l)
91 {
92         if (l >= 0) {
93                 /* 's' may point into a larger buffer and not be null-terminated */
94                 char buf[l + 1];
95                 strncpy(buf, s, l);
96                 buf[l] = '\0';
97                 printf("%s", buf);
98         }
99         else
100                 printf("%s", s);
102         f->have_output = true;
103 } /* print */
105 static void
106 indent(formatter_t *f)
108         size_t i;
109         for (i = 0; i < f->current - 1; i++)
110                 print(f, "\t", -1);
113 static void
114 format(formatter_t *f, const char *s, ssize_t l)
116         if (f->array_indices[f->current] >= 0) {
117                 if (f->array_indices[f->current] != 0)
118                         print(f, ", ", -1);
119                 f->array_indices[f->current]++;
120         }
122         print(f, s, l);
123         return;
124 } /* format */
126 static int
127 format_key(formatter_t *f, const char *k, ssize_t l)
129         int type = 0;
131         if (! strncasecmp("services", k, l))
132                 type = SDB_SERVICE;
133         else if (! strncasecmp("metrics", k, l))
134                 type = SDB_METRIC;
135         else if (! strncasecmp("attributes", k, l))
136                 type = SDB_ATTRIBUTE;
138         if (f->have_output)
139                 print(f, "\n", -1);
140         indent(f);
141         print(f, k, l);
142         print(f, ": ", -1);
144         f->next_context = type;
145         return true;
146 } /* format_key */
148 /*
149  * YAJL callbacks
150  */
152 static int
153 fmt_null(void *ctx) {
154         formatter_t *f = F(ctx);
155         format(f, "NULL", -1);
156         return true;
159 static int
160 fmt_boolean(void *ctx, int v) {
161         formatter_t *f = F(ctx);
162         if (v)
163                 format(f, "true", -1);
164         else
165                 format(f, "false", -1);
166         return true;
169 static int
170 fmt_number(void *ctx, const char *v, size_t l)
172         formatter_t *f = F(ctx);
173         format(f, v, l);
174         return true;
177 static int
178 fmt_string(void *ctx, const unsigned char *v, size_t l)
180         formatter_t *f = F(ctx);
181         format(f, (const char *)v, l);
182         return true;
185 static int
186 fmt_start_map(void *ctx) {
187         formatter_t *f = F(ctx);
188         const char *name;
190         if (!push(f, f->next_context))
191                 return false;
193         if (f->have_output)
194                 print(f, "\n", -1);
196         name = SDB_STORE_TYPE_TO_NAME(f->context[f->current]);
197         if (strcmp(name, "unknown")) {
198                 if (f->have_output)
199                         print(f, "\n", -1);
200                 indent(f);
201                 format(f, name, -1);
202         }
203         return true;
206 static int
207 fmt_map_key(void *ctx, const unsigned char *v, size_t l)
209         formatter_t *f = F(ctx);
210         return format_key(f, (const char *)v, l);
213 static int
214 fmt_end_map(void *ctx) {
215         formatter_t *f = F(ctx);
216         pop(f);
217         return true;
220 static int
221 fmt_start_array(void *ctx) {
222         formatter_t *f = F(ctx);
223         f->array_indices[f->current] = 0;
224         return true;
227 static int
228 fmt_end_array(void *ctx) {
229         formatter_t *f = F(ctx);
230         f->array_indices[f->current] = -1;
231         return true;
234 static yajl_callbacks fmts = {
235         fmt_null,
236         fmt_boolean,
237         NULL, /* fmt_integer; */
238         NULL, /* fmt_double; both default to fmt_number */
239         fmt_number,
240         fmt_string,
241         fmt_start_map,
242         fmt_map_key,
243         fmt_end_map,
244         fmt_start_array,
245         fmt_end_array,
246 };
248 #endif /* HAVE_LIBYAJL */
250 /*
251  * public API
252  */
254 int
255 sdb_json_print(sdb_input_t *input, int type, sdb_strbuf_t *buf)
257 #ifdef HAVE_LIBYAJL
258         const unsigned char *json;
259         size_t json_len;
261         yajl_handle h;
262         yajl_status status;
263         formatter_t f = F_INIT;
265         int ret = 0;
267         if (!input->interactive) {
268                 /* no formatting */
269                 printf("%s\n", sdb_strbuf_string(buf));
270                 return 0;
271         }
273         /* Store lookups always return hosts at the top-level. */
274         f.context[0] = SDB_HOST;
275         switch (type) {
276         case SDB_CONNECTION_LIST:
277         case SDB_CONNECTION_LOOKUP:
278                 /* Array types */
279                 f.array_indices[0] = 0;
280                 break;
281         case SDB_CONNECTION_TIMESERIES:
282                 f.context[0] = SDB_TIMESERIES;
283                 break;
284         }
285         f.next_context = f.context[0];
287         h = yajl_alloc(&fmts, /* alloc_funcs */ NULL, &f);
288         if (! h)
289                 return -1;
291         json = (const unsigned char *)sdb_strbuf_string(buf);
292         json_len = sdb_strbuf_len(buf);
293         status = yajl_parse(h, json, json_len);
294         if (status == yajl_status_ok)
295                 status = yajl_complete_parse(h);
297         if (status != yajl_status_ok) {
298                 unsigned char *err = yajl_get_error(h, 1, json, json_len);
299                 sdb_log(SDB_LOG_ERR, "%s", err);
300                 yajl_free_error(h, err);
301                 ret = -1;
302         }
304         yajl_free(h);
305         return ret;
306 #else /* HAVE_LIBYAJL */
307         (void)input;
308         (void)type;
309         printf("%s\n", sdb_strbuf_string(buf));
310         return 0;
311 #endif /* HAVE_LIBYAJL */
312 } /* sdb_json_print */
314 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */