53a38b89118a04ceea0f4d9d2a430d9feb87e6af
[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("%llu", 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   DEBUG("format_json: values_to_json: buffer = %s;", buffer);
149   sfree(rates);
150   return 0;
151 } /* }}} int values_to_json */
152
153 static int dstypes_to_json(char *buffer, size_t buffer_size, /* {{{ */
154                            const data_set_t *ds) {
155   size_t offset = 0;
156
157   memset(buffer, 0, buffer_size);
158
159 #define BUFFER_ADD(...)                                                        \
160   do {                                                                         \
161     int status;                                                                \
162     status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__);     \
163     if (status < 1)                                                            \
164       return -1;                                                               \
165     else if (((size_t)status) >= (buffer_size - offset))                       \
166       return -ENOMEM;                                                          \
167     else                                                                       \
168       offset += ((size_t)status);                                              \
169   } while (0)
170
171   BUFFER_ADD("[");
172   for (size_t i = 0; i < ds->ds_num; i++) {
173     if (i > 0)
174       BUFFER_ADD(",");
175
176     BUFFER_ADD("\"%s\"", DS_TYPE_TO_STRING(ds->ds[i].type));
177   } /* for ds->ds_num */
178   BUFFER_ADD("]");
179
180 #undef BUFFER_ADD
181
182   DEBUG("format_json: dstypes_to_json: buffer = %s;", buffer);
183
184   return 0;
185 } /* }}} int dstypes_to_json */
186
187 static int dsnames_to_json(char *buffer, size_t buffer_size, /* {{{ */
188                            const data_set_t *ds) {
189   size_t offset = 0;
190
191   memset(buffer, 0, buffer_size);
192
193 #define BUFFER_ADD(...)                                                        \
194   do {                                                                         \
195     int status;                                                                \
196     status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__);     \
197     if (status < 1)                                                            \
198       return -1;                                                               \
199     else if (((size_t)status) >= (buffer_size - offset))                       \
200       return -ENOMEM;                                                          \
201     else                                                                       \
202       offset += ((size_t)status);                                              \
203   } while (0)
204
205   BUFFER_ADD("[");
206   for (size_t i = 0; i < ds->ds_num; i++) {
207     if (i > 0)
208       BUFFER_ADD(",");
209
210     BUFFER_ADD("\"%s\"", ds->ds[i].name);
211   } /* for ds->ds_num */
212   BUFFER_ADD("]");
213
214 #undef BUFFER_ADD
215
216   DEBUG("format_json: dsnames_to_json: buffer = %s;", buffer);
217
218   return 0;
219 } /* }}} int dsnames_to_json */
220
221 static int meta_data_keys_to_json(char *buffer, size_t buffer_size, /* {{{ */
222                                   meta_data_t *meta, char **keys,
223                                   size_t keys_num) {
224   size_t offset = 0;
225   int status;
226
227   buffer[0] = 0;
228
229 #define BUFFER_ADD(...)                                                        \
230   do {                                                                         \
231     status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__);     \
232     if (status < 1)                                                            \
233       return -1;                                                               \
234     else if (((size_t)status) >= (buffer_size - offset))                       \
235       return -ENOMEM;                                                          \
236     else                                                                       \
237       offset += ((size_t)status);                                              \
238   } while (0)
239
240   for (size_t i = 0; i < keys_num; ++i) {
241     int type;
242     char *key = keys[i];
243
244     type = meta_data_type(meta, key);
245     if (type == MD_TYPE_STRING) {
246       char *value = NULL;
247       if (meta_data_get_string(meta, key, &value) == 0) {
248         char temp[512] = "";
249
250         status = json_escape_string(temp, sizeof(temp), value);
251         sfree(value);
252         if (status != 0)
253           return status;
254
255         BUFFER_ADD(",\"%s\":%s", key, temp);
256       }
257     } else if (type == MD_TYPE_SIGNED_INT) {
258       int64_t value = 0;
259       if (meta_data_get_signed_int(meta, key, &value) == 0)
260         BUFFER_ADD(",\"%s\":%" PRIi64, key, value);
261     } else if (type == MD_TYPE_UNSIGNED_INT) {
262       uint64_t value = 0;
263       if (meta_data_get_unsigned_int(meta, key, &value) == 0)
264         BUFFER_ADD(",\"%s\":%" PRIu64, key, value);
265     } else if (type == MD_TYPE_DOUBLE) {
266       double value = 0.0;
267       if (meta_data_get_double(meta, key, &value) == 0)
268         BUFFER_ADD(",\"%s\":%f", key, value);
269     } else if (type == MD_TYPE_BOOLEAN) {
270       _Bool value = 0;
271       if (meta_data_get_boolean(meta, key, &value) == 0)
272         BUFFER_ADD(",\"%s\":%s", key, value ? "true" : "false");
273     }
274   } /* for (keys) */
275
276   if (offset == 0)
277     return ENOENT;
278
279   buffer[0] = '{'; /* replace leading ',' */
280   BUFFER_ADD("}");
281
282 #undef BUFFER_ADD
283
284   return 0;
285 } /* }}} int meta_data_keys_to_json */
286
287 static int meta_data_to_json(char *buffer, size_t buffer_size, /* {{{ */
288                              meta_data_t *meta) {
289   char **keys = NULL;
290   size_t keys_num;
291   int status;
292
293   if ((buffer == NULL) || (buffer_size == 0) || (meta == NULL))
294     return EINVAL;
295
296   status = meta_data_toc(meta, &keys);
297   if (status <= 0)
298     return status;
299   keys_num = (size_t)status;
300
301   status = meta_data_keys_to_json(buffer, buffer_size, meta, keys, keys_num);
302
303   for (size_t i = 0; i < keys_num; ++i)
304     sfree(keys[i]);
305   sfree(keys);
306
307   return status;
308 } /* }}} int meta_data_to_json */
309
310 static int value_list_to_json(char *buffer, size_t buffer_size, /* {{{ */
311                               const data_set_t *ds, const value_list_t *vl,
312                               int store_rates) {
313   char temp[512];
314   size_t offset = 0;
315   int status;
316
317   memset(buffer, 0, buffer_size);
318
319 #define BUFFER_ADD(...)                                                        \
320   do {                                                                         \
321     status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__);     \
322     if (status < 1)                                                            \
323       return -1;                                                               \
324     else if (((size_t)status) >= (buffer_size - offset))                       \
325       return -ENOMEM;                                                          \
326     else                                                                       \
327       offset += ((size_t)status);                                              \
328   } while (0)
329
330   /* All value lists have a leading comma. The first one will be replaced with
331    * a square bracket in `format_json_finalize'. */
332   BUFFER_ADD(",{");
333
334   status = values_to_json(temp, sizeof(temp), ds, vl, store_rates);
335   if (status != 0)
336     return status;
337   BUFFER_ADD("\"values\":%s", temp);
338
339   status = dstypes_to_json(temp, sizeof(temp), ds);
340   if (status != 0)
341     return status;
342   BUFFER_ADD(",\"dstypes\":%s", temp);
343
344   status = dsnames_to_json(temp, sizeof(temp), ds);
345   if (status != 0)
346     return status;
347   BUFFER_ADD(",\"dsnames\":%s", temp);
348
349   BUFFER_ADD(",\"time\":%.3f", CDTIME_T_TO_DOUBLE(vl->time));
350   BUFFER_ADD(",\"interval\":%.3f", CDTIME_T_TO_DOUBLE(vl->interval));
351
352 #define BUFFER_ADD_KEYVAL(key, value)                                          \
353   do {                                                                         \
354     status = json_escape_string(temp, sizeof(temp), (value));                  \
355     if (status != 0)                                                           \
356       return status;                                                           \
357     BUFFER_ADD(",\"%s\":%s", (key), temp);                                     \
358   } while (0)
359
360   BUFFER_ADD_KEYVAL("host", vl->host);
361   BUFFER_ADD_KEYVAL("plugin", vl->plugin);
362   BUFFER_ADD_KEYVAL("plugin_instance", vl->plugin_instance);
363   BUFFER_ADD_KEYVAL("type", vl->type);
364   BUFFER_ADD_KEYVAL("type_instance", vl->type_instance);
365
366   if (vl->meta != NULL) {
367     char meta_buffer[buffer_size];
368     memset(meta_buffer, 0, sizeof(meta_buffer));
369     status = meta_data_to_json(meta_buffer, sizeof(meta_buffer), vl->meta);
370     if (status != 0)
371       return status;
372
373     BUFFER_ADD(",\"meta\":%s", meta_buffer);
374   } /* if (vl->meta != NULL) */
375
376   BUFFER_ADD("}");
377
378 #undef BUFFER_ADD_KEYVAL
379 #undef BUFFER_ADD
380
381   DEBUG("format_json: value_list_to_json: buffer = %s;", buffer);
382
383   return 0;
384 } /* }}} int value_list_to_json */
385
386 static int format_json_value_list_nocheck(char *buffer, /* {{{ */
387                                           size_t *ret_buffer_fill,
388                                           size_t *ret_buffer_free,
389                                           const data_set_t *ds,
390                                           const value_list_t *vl,
391                                           int store_rates, size_t temp_size) {
392   char temp[temp_size];
393   int status;
394
395   status = value_list_to_json(temp, sizeof(temp), ds, vl, store_rates);
396   if (status != 0)
397     return status;
398   temp_size = strlen(temp);
399
400   memcpy(buffer + (*ret_buffer_fill), temp, temp_size + 1);
401   (*ret_buffer_fill) += temp_size;
402   (*ret_buffer_free) -= temp_size;
403
404   return 0;
405 } /* }}} int format_json_value_list_nocheck */
406
407 int format_json_initialize(char *buffer, /* {{{ */
408                            size_t *ret_buffer_fill, size_t *ret_buffer_free) {
409   size_t buffer_fill;
410   size_t buffer_free;
411
412   if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
413       (ret_buffer_free == NULL))
414     return -EINVAL;
415
416   buffer_fill = *ret_buffer_fill;
417   buffer_free = *ret_buffer_free;
418
419   buffer_free = buffer_fill + buffer_free;
420   buffer_fill = 0;
421
422   if (buffer_free < 3)
423     return -ENOMEM;
424
425   memset(buffer, 0, buffer_free);
426   *ret_buffer_fill = buffer_fill;
427   *ret_buffer_free = buffer_free;
428
429   return 0;
430 } /* }}} int format_json_initialize */
431
432 int format_json_finalize(char *buffer, /* {{{ */
433                          size_t *ret_buffer_fill, size_t *ret_buffer_free) {
434   size_t pos;
435
436   if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
437       (ret_buffer_free == NULL))
438     return -EINVAL;
439
440   if (*ret_buffer_free < 2)
441     return -ENOMEM;
442
443   /* Replace the leading comma added in `value_list_to_json' with a square
444    * bracket. */
445   if (buffer[0] != ',')
446     return -EINVAL;
447   buffer[0] = '[';
448
449   pos = *ret_buffer_fill;
450   buffer[pos] = ']';
451   buffer[pos + 1] = 0;
452
453   (*ret_buffer_fill)++;
454   (*ret_buffer_free)--;
455
456   return 0;
457 } /* }}} int format_json_finalize */
458
459 int format_json_value_list(char *buffer, /* {{{ */
460                            size_t *ret_buffer_fill, size_t *ret_buffer_free,
461                            const data_set_t *ds, const value_list_t *vl,
462                            int store_rates) {
463   if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
464       (ret_buffer_free == NULL) || (ds == NULL) || (vl == NULL))
465     return -EINVAL;
466
467   if (*ret_buffer_free < 3)
468     return -ENOMEM;
469
470   return format_json_value_list_nocheck(buffer, ret_buffer_fill,
471                                         ret_buffer_free, ds, vl, store_rates,
472                                         (*ret_buffer_free) - 2);
473 } /* }}} int format_json_value_list */
474
475 #if HAVE_LIBYAJL
476 static int json_add_string(yajl_gen g, char const *str) /* {{{ */
477 {
478   if (str == NULL)
479     return (int)yajl_gen_null(g);
480
481   return (int)yajl_gen_string(g, (const unsigned char *)str,
482                               (unsigned int)strlen(str));
483 } /* }}} int json_add_string */
484
485 #define JSON_ADD(g, str)                                                       \
486   do {                                                                         \
487     yajl_gen_status status = json_add_string(g, str);                          \
488     if (status != yajl_gen_status_ok) {                                        \
489       return -1;                                                               \
490     }                                                                          \
491   } while (0)
492
493 #define JSON_ADDF(g, format, ...)                                              \
494   do {                                                                         \
495     char *str = ssnprintf_alloc(format, __VA_ARGS__);                          \
496     yajl_gen_status status = json_add_string(g, str);                          \
497     free(str);                                                                 \
498     if (status != yajl_gen_status_ok) {                                        \
499       return -1;                                                               \
500     }                                                                          \
501   } while (0)
502
503 static int format_json_meta(yajl_gen g, notification_meta_t *meta) /* {{{ */
504 {
505   if (meta == NULL)
506     return 0;
507
508   JSON_ADD(g, meta->name);
509   switch (meta->type) {
510   case NM_TYPE_STRING:
511     JSON_ADD(g, meta->nm_value.nm_string);
512     break;
513   case NM_TYPE_SIGNED_INT:
514     JSON_ADDF(g, "%" PRIi64, meta->nm_value.nm_signed_int);
515     break;
516   case NM_TYPE_UNSIGNED_INT:
517     JSON_ADDF(g, "%" PRIu64, meta->nm_value.nm_unsigned_int);
518     break;
519   case NM_TYPE_DOUBLE:
520     JSON_ADDF(g, JSON_GAUGE_FORMAT, meta->nm_value.nm_double);
521     break;
522   case NM_TYPE_BOOLEAN:
523     JSON_ADD(g, meta->nm_value.nm_boolean ? "true" : "false");
524     break;
525   default:
526     ERROR("format_json_meta: unknown meta data type %d (name \"%s\")",
527           meta->type, meta->name);
528     yajl_gen_null(g);
529   }
530
531   return format_json_meta(g, meta->next);
532 } /* }}} int format_json_meta */
533
534 static int format_time(yajl_gen g, cdtime_t t) /* {{{ */
535 {
536   char buffer[RFC3339NANO_SIZE] = "";
537
538   if (rfc3339nano(buffer, sizeof(buffer), t) != 0)
539     return -1;
540
541   JSON_ADD(g, buffer);
542   return 0;
543 } /* }}} int format_time */
544
545 static int format_alert(yajl_gen g, notification_t const *n) /* {{{ */
546 {
547   yajl_gen_array_open(g);
548   yajl_gen_map_open(g); /* BEGIN alert */
549
550   /*
551    * labels
552    */
553   JSON_ADD(g, "labels");
554   yajl_gen_map_open(g); /* BEGIN labels */
555
556   JSON_ADD(g, "alertname");
557   if (strncmp(n->plugin, n->type, strlen(n->plugin)) == 0)
558     JSON_ADDF(g, "collectd_%s", n->type);
559   else
560     JSON_ADDF(g, "collectd_%s_%s", n->plugin, n->type);
561
562   JSON_ADD(g, "instance");
563   JSON_ADD(g, n->host);
564
565   /* mangling of plugin instance and type instance into labels is copied from
566    * the Prometheus collectd exporter. */
567   if (strlen(n->plugin_instance) > 0) {
568     JSON_ADD(g, n->plugin);
569     JSON_ADD(g, n->plugin_instance);
570   }
571   if (strlen(n->type_instance) > 0) {
572     if (strlen(n->plugin_instance) > 0)
573       JSON_ADD(g, "type");
574     else
575       JSON_ADD(g, n->plugin);
576     JSON_ADD(g, n->type_instance);
577   }
578
579   JSON_ADD(g, "severity");
580   JSON_ADD(g,
581            (n->severity == NOTIF_FAILURE)
582                ? "FAILURE"
583                : (n->severity == NOTIF_WARNING)
584                      ? "WARNING"
585                      : (n->severity == NOTIF_OKAY) ? "OKAY" : "UNKNOWN");
586
587   JSON_ADD(g, "service");
588   JSON_ADD(g, "collectd");
589
590   yajl_gen_map_close(g); /* END labels */
591
592   /*
593    * annotations
594    */
595   JSON_ADD(g, "annotations");
596   yajl_gen_map_open(g); /* BEGIN annotations */
597
598   JSON_ADD(g, "summary");
599   JSON_ADD(g, n->message);
600
601   if (format_json_meta(g, n->meta) != 0)
602     return -1;
603
604   yajl_gen_map_close(g); /* END annotations */
605
606   JSON_ADD(g, "startsAt");
607   format_time(g, n->time);
608
609   yajl_gen_map_close(g); /* END alert */
610   yajl_gen_array_close(g);
611
612   return 0;
613 } /* }}} format_alert */
614
615 /*
616  * Format (prometheus/alertmanager v1):
617  *
618  * [{
619  *   "labels": {
620  *     "alertname": "collectd_cpu",
621  *     "instance":  "host.example.com",
622  *     "severity":  "FAILURE",
623  *     "service":   "collectd",
624  *     "cpu":       "0",
625  *     "type":      "wait"
626  *   },
627  *   "annotations": {
628  *     "summary": "...",
629  *     // meta
630  *   },
631  *   "startsAt": <rfc3339 time>,
632  *   "endsAt": <rfc3339 time>, // not used
633  * }]
634  */
635 int format_json_notification(char *buffer, size_t buffer_size, /* {{{ */
636                              notification_t const *n) {
637   yajl_gen g;
638   unsigned char const *out;
639 #if HAVE_YAJL_V2
640   size_t unused_out_len;
641 #else
642   unsigned int unused_out_len;
643 #endif
644
645   if ((buffer == NULL) || (n == NULL))
646     return EINVAL;
647
648 #if HAVE_YAJL_V2
649   g = yajl_gen_alloc(NULL);
650   if (g == NULL)
651     return -1;
652 #if COLLECT_DEBUG
653   yajl_gen_config(g, yajl_gen_beautify, 1);
654   yajl_gen_config(g, yajl_gen_validate_utf8, 1);
655 #endif
656
657 #else /* !HAVE_YAJL_V2 */
658   yajl_gen_config conf = {0};
659 #if COLLECT_DEBUG
660   conf.beautify = 1;
661   conf.indentString = "  ";
662 #endif
663   g = yajl_gen_alloc(&conf, NULL);
664   if (g == NULL)
665     return -1;
666 #endif
667
668   if (format_alert(g, n) != 0) {
669     yajl_gen_clear(g);
670     yajl_gen_free(g);
671     return -1;
672   }
673
674   /* copy to output buffer */
675   yajl_gen_get_buf(g, &out, &unused_out_len);
676   sstrncpy(buffer, (void *)out, buffer_size);
677
678   yajl_gen_clear(g);
679   yajl_gen_free(g);
680   return 0;
681 } /* }}} format_json_notification */
682 #else
683 int format_json_notification(char *buffer, size_t buffer_size, /* {{{ */
684                              notification_t const *n) {
685   ERROR("format_json_notification: Not available (requires libyajl).");
686   return ENOTSUP;
687 } /* }}} int format_json_notification */
688 #endif