Merge branch 'collectd-5.8'
[collectd.git] / src / utils_format_json.c
1 /**
2  * collectd - src/utils_format_json.c
3  * Copyright (C) 2009-2015  Florian octo Forster
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Florian octo Forster <octo at collectd.org>
25  **/
26
27 #include "collectd.h"
28
29 #include "utils_format_json.h"
30
31 #include "common.h"
32 #include "plugin.h"
33 #include "utils_cache.h"
34
35 #if HAVE_LIBYAJL
36 #include <yajl/yajl_common.h>
37 #include <yajl/yajl_gen.h>
38 #if HAVE_YAJL_YAJL_VERSION_H
39 #include <yajl/yajl_version.h>
40 #endif
41 #if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1)
42 #define HAVE_YAJL_V2 1
43 #endif
44 #endif
45
46 static int json_escape_string(char *buffer, size_t buffer_size, /* {{{ */
47                               const char *string) {
48   size_t dst_pos;
49
50   if ((buffer == NULL) || (string == NULL))
51     return -EINVAL;
52
53   if (buffer_size < 3)
54     return -ENOMEM;
55
56   dst_pos = 0;
57
58 #define BUFFER_ADD(c)                                                          \
59   do {                                                                         \
60     if (dst_pos >= (buffer_size - 1)) {                                        \
61       buffer[buffer_size - 1] = 0;                                             \
62       return -ENOMEM;                                                          \
63     }                                                                          \
64     buffer[dst_pos] = (c);                                                     \
65     dst_pos++;                                                                 \
66   } while (0)
67
68   /* Escape special characters */
69   BUFFER_ADD('"');
70   for (size_t src_pos = 0; string[src_pos] != 0; src_pos++) {
71     if ((string[src_pos] == '"') || (string[src_pos] == '\\')) {
72       BUFFER_ADD('\\');
73       BUFFER_ADD(string[src_pos]);
74     } else if (string[src_pos] <= 0x001F)
75       BUFFER_ADD('?');
76     else
77       BUFFER_ADD(string[src_pos]);
78   } /* for */
79   BUFFER_ADD('"');
80   buffer[dst_pos] = 0;
81
82 #undef BUFFER_ADD
83
84   return 0;
85 } /* }}} int json_escape_string */
86
87 static int values_to_json(char *buffer, size_t buffer_size, /* {{{ */
88                           const data_set_t *ds, const value_list_t *vl,
89                           int store_rates) {
90   size_t offset = 0;
91   gauge_t *rates = NULL;
92
93   memset(buffer, 0, buffer_size);
94
95 #define BUFFER_ADD(...)                                                        \
96   do {                                                                         \
97     int status;                                                                \
98     status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__);     \
99     if (status < 1) {                                                          \
100       sfree(rates);                                                            \
101       return -1;                                                               \
102     } else if (((size_t)status) >= (buffer_size - offset)) {                   \
103       sfree(rates);                                                            \
104       return -ENOMEM;                                                          \
105     } else                                                                     \
106       offset += ((size_t)status);                                              \
107   } while (0)
108
109   BUFFER_ADD("[");
110   for (size_t i = 0; i < ds->ds_num; i++) {
111     if (i > 0)
112       BUFFER_ADD(",");
113
114     if (ds->ds[i].type == DS_TYPE_GAUGE) {
115       if (isfinite(vl->values[i].gauge))
116         BUFFER_ADD(JSON_GAUGE_FORMAT, vl->values[i].gauge);
117       else
118         BUFFER_ADD("null");
119     } else if (store_rates) {
120       if (rates == NULL)
121         rates = uc_get_rate(ds, vl);
122       if (rates == NULL) {
123         WARNING("utils_format_json: uc_get_rate failed.");
124         sfree(rates);
125         return -1;
126       }
127
128       if (isfinite(rates[i]))
129         BUFFER_ADD(JSON_GAUGE_FORMAT, rates[i]);
130       else
131         BUFFER_ADD("null");
132     } else if (ds->ds[i].type == DS_TYPE_COUNTER)
133       BUFFER_ADD("%" PRIu64, (uint64_t)vl->values[i].counter);
134     else if (ds->ds[i].type == DS_TYPE_DERIVE)
135       BUFFER_ADD("%" PRIi64, vl->values[i].derive);
136     else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
137       BUFFER_ADD("%" PRIu64, vl->values[i].absolute);
138     else {
139       ERROR("format_json: Unknown data source type: %i", ds->ds[i].type);
140       sfree(rates);
141       return -1;
142     }
143   } /* for ds->ds_num */
144   BUFFER_ADD("]");
145
146 #undef BUFFER_ADD
147
148   sfree(rates);
149   return 0;
150 } /* }}} int values_to_json */
151
152 static int dstypes_to_json(char *buffer, size_t buffer_size, /* {{{ */
153                            const data_set_t *ds) {
154   size_t offset = 0;
155
156   memset(buffer, 0, buffer_size);
157
158 #define BUFFER_ADD(...)                                                        \
159   do {                                                                         \
160     int status;                                                                \
161     status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__);     \
162     if (status < 1)                                                            \
163       return -1;                                                               \
164     else if (((size_t)status) >= (buffer_size - offset))                       \
165       return -ENOMEM;                                                          \
166     else                                                                       \
167       offset += ((size_t)status);                                              \
168   } while (0)
169
170   BUFFER_ADD("[");
171   for (size_t i = 0; i < ds->ds_num; i++) {
172     if (i > 0)
173       BUFFER_ADD(",");
174
175     BUFFER_ADD("\"%s\"", DS_TYPE_TO_STRING(ds->ds[i].type));
176   } /* for ds->ds_num */
177   BUFFER_ADD("]");
178
179 #undef BUFFER_ADD
180
181   return 0;
182 } /* }}} int dstypes_to_json */
183
184 static int dsnames_to_json(char *buffer, size_t buffer_size, /* {{{ */
185                            const data_set_t *ds) {
186   size_t offset = 0;
187
188   memset(buffer, 0, buffer_size);
189
190 #define BUFFER_ADD(...)                                                        \
191   do {                                                                         \
192     int status;                                                                \
193     status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__);     \
194     if (status < 1)                                                            \
195       return -1;                                                               \
196     else if (((size_t)status) >= (buffer_size - offset))                       \
197       return -ENOMEM;                                                          \
198     else                                                                       \
199       offset += ((size_t)status);                                              \
200   } while (0)
201
202   BUFFER_ADD("[");
203   for (size_t i = 0; i < ds->ds_num; i++) {
204     if (i > 0)
205       BUFFER_ADD(",");
206
207     BUFFER_ADD("\"%s\"", ds->ds[i].name);
208   } /* for ds->ds_num */
209   BUFFER_ADD("]");
210
211 #undef BUFFER_ADD
212
213   return 0;
214 } /* }}} int dsnames_to_json */
215
216 static int meta_data_keys_to_json(char *buffer, size_t buffer_size, /* {{{ */
217                                   meta_data_t *meta, char **keys,
218                                   size_t keys_num) {
219   size_t offset = 0;
220   int status;
221
222   buffer[0] = 0;
223
224 #define BUFFER_ADD(...)                                                        \
225   do {                                                                         \
226     status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__);     \
227     if (status < 1)                                                            \
228       return -1;                                                               \
229     else if (((size_t)status) >= (buffer_size - offset))                       \
230       return -ENOMEM;                                                          \
231     else                                                                       \
232       offset += ((size_t)status);                                              \
233   } while (0)
234
235   for (size_t i = 0; i < keys_num; ++i) {
236     int type;
237     char *key = keys[i];
238
239     type = meta_data_type(meta, key);
240     if (type == MD_TYPE_STRING) {
241       char *value = NULL;
242       if (meta_data_get_string(meta, key, &value) == 0) {
243         char temp[512] = "";
244
245         status = json_escape_string(temp, sizeof(temp), value);
246         sfree(value);
247         if (status != 0)
248           return status;
249
250         BUFFER_ADD(",\"%s\":%s", key, temp);
251       }
252     } else if (type == MD_TYPE_SIGNED_INT) {
253       int64_t value = 0;
254       if (meta_data_get_signed_int(meta, key, &value) == 0)
255         BUFFER_ADD(",\"%s\":%" PRIi64, key, value);
256     } else if (type == MD_TYPE_UNSIGNED_INT) {
257       uint64_t value = 0;
258       if (meta_data_get_unsigned_int(meta, key, &value) == 0)
259         BUFFER_ADD(",\"%s\":%" PRIu64, key, value);
260     } else if (type == MD_TYPE_DOUBLE) {
261       double value = 0.0;
262       if (meta_data_get_double(meta, key, &value) == 0)
263         BUFFER_ADD(",\"%s\":%f", key, value);
264     } else if (type == MD_TYPE_BOOLEAN) {
265       bool value = false;
266       if (meta_data_get_boolean(meta, key, &value) == 0)
267         BUFFER_ADD(",\"%s\":%s", key, value ? "true" : "false");
268     }
269   } /* for (keys) */
270
271   if (offset == 0)
272     return ENOENT;
273
274   buffer[0] = '{'; /* replace leading ',' */
275   BUFFER_ADD("}");
276
277 #undef BUFFER_ADD
278
279   return 0;
280 } /* }}} int meta_data_keys_to_json */
281
282 static int meta_data_to_json(char *buffer, size_t buffer_size, /* {{{ */
283                              meta_data_t *meta) {
284   char **keys = NULL;
285   size_t keys_num;
286   int status;
287
288   if ((buffer == NULL) || (buffer_size == 0) || (meta == NULL))
289     return EINVAL;
290
291   status = meta_data_toc(meta, &keys);
292   if (status <= 0)
293     return status;
294   keys_num = (size_t)status;
295
296   status = meta_data_keys_to_json(buffer, buffer_size, meta, keys, keys_num);
297
298   for (size_t i = 0; i < keys_num; ++i)
299     sfree(keys[i]);
300   sfree(keys);
301
302   return status;
303 } /* }}} int meta_data_to_json */
304
305 static int value_list_to_json(char *buffer, size_t buffer_size, /* {{{ */
306                               const data_set_t *ds, const value_list_t *vl,
307                               int store_rates) {
308   char temp[512];
309   size_t offset = 0;
310   int status;
311
312   memset(buffer, 0, buffer_size);
313
314 #define BUFFER_ADD(...)                                                        \
315   do {                                                                         \
316     status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__);     \
317     if (status < 1)                                                            \
318       return -1;                                                               \
319     else if (((size_t)status) >= (buffer_size - offset))                       \
320       return -ENOMEM;                                                          \
321     else                                                                       \
322       offset += ((size_t)status);                                              \
323   } while (0)
324
325   /* All value lists have a leading comma. The first one will be replaced with
326    * a square bracket in `format_json_finalize'. */
327   BUFFER_ADD(",{");
328
329   status = values_to_json(temp, sizeof(temp), ds, vl, store_rates);
330   if (status != 0)
331     return status;
332   BUFFER_ADD("\"values\":%s", temp);
333
334   status = dstypes_to_json(temp, sizeof(temp), ds);
335   if (status != 0)
336     return status;
337   BUFFER_ADD(",\"dstypes\":%s", temp);
338
339   status = dsnames_to_json(temp, sizeof(temp), ds);
340   if (status != 0)
341     return status;
342   BUFFER_ADD(",\"dsnames\":%s", temp);
343
344   BUFFER_ADD(",\"time\":%.3f", CDTIME_T_TO_DOUBLE(vl->time));
345   BUFFER_ADD(",\"interval\":%.3f", CDTIME_T_TO_DOUBLE(vl->interval));
346
347 #define BUFFER_ADD_KEYVAL(key, value)                                          \
348   do {                                                                         \
349     status = json_escape_string(temp, sizeof(temp), (value));                  \
350     if (status != 0)                                                           \
351       return status;                                                           \
352     BUFFER_ADD(",\"%s\":%s", (key), temp);                                     \
353   } while (0)
354
355   BUFFER_ADD_KEYVAL("host", vl->host);
356   BUFFER_ADD_KEYVAL("plugin", vl->plugin);
357   BUFFER_ADD_KEYVAL("plugin_instance", vl->plugin_instance);
358   BUFFER_ADD_KEYVAL("type", vl->type);
359   BUFFER_ADD_KEYVAL("type_instance", vl->type_instance);
360
361   if (vl->meta != NULL) {
362     char meta_buffer[buffer_size];
363     memset(meta_buffer, 0, sizeof(meta_buffer));
364     status = meta_data_to_json(meta_buffer, sizeof(meta_buffer), vl->meta);
365     if (status != 0)
366       return status;
367
368     BUFFER_ADD(",\"meta\":%s", meta_buffer);
369   } /* if (vl->meta != NULL) */
370
371   BUFFER_ADD("}");
372
373 #undef BUFFER_ADD_KEYVAL
374 #undef BUFFER_ADD
375
376   return 0;
377 } /* }}} int value_list_to_json */
378
379 static int format_json_value_list_nocheck(char *buffer, /* {{{ */
380                                           size_t *ret_buffer_fill,
381                                           size_t *ret_buffer_free,
382                                           const data_set_t *ds,
383                                           const value_list_t *vl,
384                                           int store_rates, size_t temp_size) {
385   char temp[temp_size];
386   int status;
387
388   status = value_list_to_json(temp, sizeof(temp), ds, vl, store_rates);
389   if (status != 0)
390     return status;
391   temp_size = strlen(temp);
392
393   memcpy(buffer + (*ret_buffer_fill), temp, temp_size + 1);
394   (*ret_buffer_fill) += temp_size;
395   (*ret_buffer_free) -= temp_size;
396
397   return 0;
398 } /* }}} int format_json_value_list_nocheck */
399
400 int format_json_initialize(char *buffer, /* {{{ */
401                            size_t *ret_buffer_fill, size_t *ret_buffer_free) {
402   size_t buffer_fill;
403   size_t buffer_free;
404
405   if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
406       (ret_buffer_free == NULL))
407     return -EINVAL;
408
409   buffer_fill = *ret_buffer_fill;
410   buffer_free = *ret_buffer_free;
411
412   buffer_free = buffer_fill + buffer_free;
413   buffer_fill = 0;
414
415   if (buffer_free < 3)
416     return -ENOMEM;
417
418   memset(buffer, 0, buffer_free);
419   *ret_buffer_fill = buffer_fill;
420   *ret_buffer_free = buffer_free;
421
422   return 0;
423 } /* }}} int format_json_initialize */
424
425 int format_json_finalize(char *buffer, /* {{{ */
426                          size_t *ret_buffer_fill, size_t *ret_buffer_free) {
427   size_t pos;
428
429   if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
430       (ret_buffer_free == NULL))
431     return -EINVAL;
432
433   if (*ret_buffer_free < 2)
434     return -ENOMEM;
435
436   /* Replace the leading comma added in `value_list_to_json' with a square
437    * bracket. */
438   if (buffer[0] != ',')
439     return -EINVAL;
440   buffer[0] = '[';
441
442   pos = *ret_buffer_fill;
443   buffer[pos] = ']';
444   buffer[pos + 1] = 0;
445
446   (*ret_buffer_fill)++;
447   (*ret_buffer_free)--;
448
449   return 0;
450 } /* }}} int format_json_finalize */
451
452 int format_json_value_list(char *buffer, /* {{{ */
453                            size_t *ret_buffer_fill, size_t *ret_buffer_free,
454                            const data_set_t *ds, const value_list_t *vl,
455                            int store_rates) {
456   if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
457       (ret_buffer_free == NULL) || (ds == NULL) || (vl == NULL))
458     return -EINVAL;
459
460   if (*ret_buffer_free < 3)
461     return -ENOMEM;
462
463   return format_json_value_list_nocheck(buffer, ret_buffer_fill,
464                                         ret_buffer_free, ds, vl, store_rates,
465                                         (*ret_buffer_free) - 2);
466 } /* }}} int format_json_value_list */
467
468 #if HAVE_LIBYAJL
469 static int json_add_string(yajl_gen g, char const *str) /* {{{ */
470 {
471   if (str == NULL)
472     return (int)yajl_gen_null(g);
473
474   return (int)yajl_gen_string(g, (const unsigned char *)str,
475                               (unsigned int)strlen(str));
476 } /* }}} int json_add_string */
477
478 #define JSON_ADD(g, str)                                                       \
479   do {                                                                         \
480     yajl_gen_status status = json_add_string(g, str);                          \
481     if (status != yajl_gen_status_ok) {                                        \
482       return -1;                                                               \
483     }                                                                          \
484   } while (0)
485
486 #define JSON_ADDF(g, format, ...)                                              \
487   do {                                                                         \
488     char *str = ssnprintf_alloc(format, __VA_ARGS__);                          \
489     yajl_gen_status status = json_add_string(g, str);                          \
490     free(str);                                                                 \
491     if (status != yajl_gen_status_ok) {                                        \
492       return -1;                                                               \
493     }                                                                          \
494   } while (0)
495
496 #define CHECK_SUCCESS(cmd)                                                     \
497   do {                                                                         \
498     yajl_gen_status s = (cmd);                                                 \
499     if (s != yajl_gen_status_ok) {                                             \
500       return (int)s;                                                           \
501     }                                                                          \
502   } while (0)
503
504 static int format_json_meta(yajl_gen g, notification_meta_t *meta) /* {{{ */
505 {
506   if (meta == NULL)
507     return 0;
508
509   JSON_ADD(g, meta->name);
510   switch (meta->type) {
511   case NM_TYPE_STRING:
512     JSON_ADD(g, meta->nm_value.nm_string);
513     break;
514   case NM_TYPE_SIGNED_INT:
515     JSON_ADDF(g, "%" PRIi64, meta->nm_value.nm_signed_int);
516     break;
517   case NM_TYPE_UNSIGNED_INT:
518     JSON_ADDF(g, "%" PRIu64, meta->nm_value.nm_unsigned_int);
519     break;
520   case NM_TYPE_DOUBLE:
521     JSON_ADDF(g, JSON_GAUGE_FORMAT, meta->nm_value.nm_double);
522     break;
523   case NM_TYPE_BOOLEAN:
524     JSON_ADD(g, meta->nm_value.nm_boolean ? "true" : "false");
525     break;
526   default:
527     ERROR("format_json_meta: unknown meta data type %d (name \"%s\")",
528           meta->type, meta->name);
529     CHECK_SUCCESS(yajl_gen_null(g));
530   }
531
532   return format_json_meta(g, meta->next);
533 } /* }}} int format_json_meta */
534
535 static int format_time(yajl_gen g, cdtime_t t) /* {{{ */
536 {
537   char buffer[RFC3339NANO_SIZE] = "";
538
539   if (rfc3339nano(buffer, sizeof(buffer), t) != 0)
540     return -1;
541
542   JSON_ADD(g, buffer);
543   return 0;
544 } /* }}} int format_time */
545
546 static int format_alert(yajl_gen g, notification_t const *n) /* {{{ */
547 {
548   CHECK_SUCCESS(yajl_gen_array_open(g)); /* BEGIN array */
549   CHECK_SUCCESS(yajl_gen_map_open(g));   /* BEGIN alert */
550
551   /*
552    * labels
553    */
554   JSON_ADD(g, "labels");
555   CHECK_SUCCESS(yajl_gen_map_open(g)); /* BEGIN labels */
556
557   JSON_ADD(g, "alertname");
558   if (strncmp(n->plugin, n->type, strlen(n->plugin)) == 0)
559     JSON_ADDF(g, "collectd_%s", n->type);
560   else
561     JSON_ADDF(g, "collectd_%s_%s", n->plugin, n->type);
562
563   JSON_ADD(g, "instance");
564   JSON_ADD(g, n->host);
565
566   /* mangling of plugin instance and type instance into labels is copied from
567    * the Prometheus collectd exporter. */
568   if (strlen(n->plugin_instance) > 0) {
569     JSON_ADD(g, n->plugin);
570     JSON_ADD(g, n->plugin_instance);
571   }
572   if (strlen(n->type_instance) > 0) {
573     if (strlen(n->plugin_instance) > 0)
574       JSON_ADD(g, "type");
575     else
576       JSON_ADD(g, n->plugin);
577     JSON_ADD(g, n->type_instance);
578   }
579
580   JSON_ADD(g, "severity");
581   JSON_ADD(g,
582            (n->severity == NOTIF_FAILURE)
583                ? "FAILURE"
584                : (n->severity == NOTIF_WARNING)
585                      ? "WARNING"
586                      : (n->severity == NOTIF_OKAY) ? "OKAY" : "UNKNOWN");
587
588   JSON_ADD(g, "service");
589   JSON_ADD(g, "collectd");
590
591   CHECK_SUCCESS(yajl_gen_map_close(g)); /* END labels */
592
593   /*
594    * annotations
595    */
596   JSON_ADD(g, "annotations");
597   CHECK_SUCCESS(yajl_gen_map_open(g)); /* BEGIN annotations */
598
599   JSON_ADD(g, "summary");
600   JSON_ADD(g, n->message);
601
602   if (format_json_meta(g, n->meta) != 0) {
603     return -1;
604   }
605
606   CHECK_SUCCESS(yajl_gen_map_close(g)); /* END annotations */
607
608   JSON_ADD(g, "startsAt");
609   if (format_time(g, n->time) != 0) {
610     return -1;
611   }
612
613   CHECK_SUCCESS(yajl_gen_map_close(g));   /* END alert */
614   CHECK_SUCCESS(yajl_gen_array_close(g)); /* END array */
615
616   return 0;
617 } /* }}} format_alert */
618
619 /*
620  * Format (prometheus/alertmanager v1):
621  *
622  * [{
623  *   "labels": {
624  *     "alertname": "collectd_cpu",
625  *     "instance":  "host.example.com",
626  *     "severity":  "FAILURE",
627  *     "service":   "collectd",
628  *     "cpu":       "0",
629  *     "type":      "wait"
630  *   },
631  *   "annotations": {
632  *     "summary": "...",
633  *     // meta
634  *   },
635  *   "startsAt": <rfc3339 time>,
636  *   "endsAt": <rfc3339 time>, // not used
637  * }]
638  */
639 int format_json_notification(char *buffer, size_t buffer_size, /* {{{ */
640                              notification_t const *n) {
641   yajl_gen g;
642   unsigned char const *out;
643 #if HAVE_YAJL_V2
644   size_t unused_out_len;
645 #else
646   unsigned int unused_out_len;
647 #endif
648
649   if ((buffer == NULL) || (n == NULL))
650     return EINVAL;
651
652 #if HAVE_YAJL_V2
653   g = yajl_gen_alloc(NULL);
654   if (g == NULL)
655     return -1;
656 #if COLLECT_DEBUG
657   yajl_gen_config(g, yajl_gen_beautify, 1);
658   yajl_gen_config(g, yajl_gen_validate_utf8, 1);
659 #endif
660
661 #else /* !HAVE_YAJL_V2 */
662   yajl_gen_config conf = {0};
663 #if COLLECT_DEBUG
664   conf.beautify = 1;
665   conf.indentString = "  ";
666 #endif
667   g = yajl_gen_alloc(&conf, NULL);
668   if (g == NULL)
669     return -1;
670 #endif
671
672   if (format_alert(g, n) != 0) {
673     yajl_gen_clear(g);
674     yajl_gen_free(g);
675     return -1;
676   }
677
678   /* copy to output buffer */
679   if (yajl_gen_get_buf(g, &out, &unused_out_len) != yajl_gen_status_ok) {
680     yajl_gen_clear(g);
681     yajl_gen_free(g);
682     return -1;
683   }
684   sstrncpy(buffer, (void *)out, buffer_size);
685
686   yajl_gen_clear(g);
687   yajl_gen_free(g);
688   return 0;
689 } /* }}} format_json_notification */
690 #else
691 int format_json_notification(char *buffer, size_t buffer_size, /* {{{ */
692                              notification_t const *n) {
693   ERROR("format_json_notification: Not available (requires libyajl).");
694   return ENOTSUP;
695 } /* }}} int format_json_notification */
696 #endif