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)
107 {
108 size_t i;
109 for (i = 0; i < f->current - 1; i++)
110 print(f, "\t", -1);
111 }
113 static void
114 format(formatter_t *f, const char *s, ssize_t l)
115 {
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)
128 {
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;
157 }
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;
167 }
169 static int
170 fmt_number(void *ctx, const char *v, size_t l)
171 {
172 formatter_t *f = F(ctx);
173 format(f, v, l);
174 return true;
175 }
177 static int
178 fmt_string(void *ctx, const unsigned char *v, size_t l)
179 {
180 formatter_t *f = F(ctx);
181 format(f, (const char *)v, l);
182 return true;
183 }
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;
204 }
206 static int
207 fmt_map_key(void *ctx, const unsigned char *v, size_t l)
208 {
209 formatter_t *f = F(ctx);
210 return format_key(f, (const char *)v, l);
211 }
213 static int
214 fmt_end_map(void *ctx) {
215 formatter_t *f = F(ctx);
216 pop(f);
217 return true;
218 }
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;
225 }
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;
232 }
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)
256 {
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 : */