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