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)
109 {
110 size_t i;
111 for (i = 0; i < f->current - 1; i++)
112 print(f, "\t", -1);
113 }
115 static void
116 format(formatter_t *f, const char *s, ssize_t l)
117 {
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)
130 {
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;
159 }
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;
169 }
171 static int
172 fmt_number(void *ctx, const char *v, size_t l)
173 {
174 formatter_t *f = F(ctx);
175 format(f, v, l);
176 return true;
177 }
179 static int
180 fmt_string(void *ctx, const unsigned char *v, size_t l)
181 {
182 formatter_t *f = F(ctx);
183 format(f, (const char *)v, l);
184 return true;
185 }
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;
206 }
208 static int
209 fmt_map_key(void *ctx, const unsigned char *v, size_t l)
210 {
211 formatter_t *f = F(ctx);
212 return format_key(f, (const char *)v, l);
213 }
215 static int
216 fmt_end_map(void *ctx) {
217 formatter_t *f = F(ctx);
218 pop(f);
219 return true;
220 }
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;
227 }
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;
234 }
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)
258 {
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 : */