Commit feb067b2 authored by Evan Hunt's avatar Evan Hunt

[master] add JSON statistics channel

3524.	[func]		Added an alternate statistics channel in JSON format,
			when the server is built with the json-c library:
			http://[address]:[port]/json.  [RT #32630]
parent 8a642530
3524. [func] Added an alternate statistics channel in JSON format,
when the server is built with the json-c library:
http://[address]:[port]/json. [RT #32630]
3523. [contrib] Ported filesystem and ldap DLZ drivers to
dynamically-loadable modules, and added the
"wildcard" module based on a contribution from
......
......@@ -230,6 +230,12 @@ Building
a nonstandard prefix, you can tell configure where to
look for it using "--with-openssl=/prefix".
To support the HTTP statistics channel, the server must
be linked with at least one of the following: libxml2
(http://xmlsoft.org) or json-c (https://github.com/json-c).
If these are installed at a nonstandard prefix, use
"--with-libxml2=/prefix" or "--with-libjson=/prefix".
On some platforms it is necessary to explictly request large
file support to handle files bigger than 2GB. This can be
done by "--enable-largefile" on the configure command line.
......
This diff is collapsed.
......@@ -239,6 +239,9 @@ int sigwait(const unsigned int *set, int *sig);
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define if libjson was found */
#undef HAVE_JSON
/* Define to 1 if you have the <kerberosv5/krb5.h> header file. */
#undef HAVE_KERBEROSV5_KRB5_H
......
......@@ -1467,6 +1467,7 @@ with_gssapi
with_randomdev
enable_threads
with_libxml2
with_libjson
enable_largefile
with_purify
with_libtool
......@@ -2177,6 +2178,7 @@ Optional Packages:
--with-gssapi=PATH Specify path for system-supplied GSSAPI [default=yes]
--with-randomdev=PATH Specify path for random device
--with-libxml2=PATH Build with libxml2 library yes|no|path
--with-libjson=PATH Build with libjson0 library yes|no|path
--with-purify=PATH use Rational purify
--with-libtool use GNU libtool
--with-export-libdir=PATH
......@@ -16011,6 +16013,59 @@ else
$as_echo "no" >&6; }
fi
#
# was --with-libjson specified?
#
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for json library" >&5
$as_echo_n "checking for json library... " >&6; }
# Check whether --with-libjson was given.
if test "${with_libjson+set}" = set; then :
withval=$with_libjson; use_libjson="$withval"
else
use_libjson="auto"
fi
case "$use_libjson" in
no)
libjson_libs=""
;;
auto|yes)
if test -f "/usr/include/json/json.h"
then
libjson_libs="-ljson"
libjson_cflags="-I /usr/include/json"
else
libjson_libs=""
fi
;;
*)
if test -f "${use_libjson}/include/json/json.h"
then
libjson_libs="-L${use_libjson}/lib -ljson"
libjson_cflags="-I${use_libjson}/include/json"
else
as_fn_error $? "$use_libjson/include/json.h not found." "$LINENO" 5
fi
;;
esac
HERE
if test "X${libjson_libs}" != "X"
then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
CFLAGS="$CFLAGS $libjson_cflags"
LIBS="$LIBS $libjson_libs"
$as_echo "#define HAVE_JSON 1" >>confdefs.h
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
#
# In solaris 10, SMF can manage named service
#
......
......@@ -1483,6 +1483,49 @@ else
AC_MSG_RESULT(no)
fi
#
# was --with-libjson specified?
#
AC_MSG_CHECKING(for json library)
AC_ARG_WITH(libjson,
[ --with-libjson[=PATH] Build with libjson0 library [yes|no|path]],
use_libjson="$withval", use_libjson="auto")
case "$use_libjson" in
no)
libjson_libs=""
;;
auto|yes)
if test -f "/usr/include/json/json.h"
then
libjson_libs="-ljson"
libjson_cflags="-I /usr/include/json"
else
libjson_libs=""
fi
;;
*)
if test -f "${use_libjson}/include/json/json.h"
then
libjson_libs="-L${use_libjson}/lib -ljson"
libjson_cflags="-I${use_libjson}/include/json"
else
AC_MSG_ERROR([$use_libjson/include/json.h not found.])
fi
;;
esac
HERE
if test "X${libjson_libs}" != "X"
then
AC_MSG_RESULT(yes)
CFLAGS="$CFLAGS $libjson_cflags"
LIBS="$LIBS $libjson_libs"
AC_DEFINE(HAVE_JSON, 1, [Define if libjson was found])
else
AC_MSG_RESULT(no)
fi
#
# In solaris 10, SMF can manage named service
#
......
......@@ -10504,8 +10504,9 @@ ns.domain.com.rpz-nsdname CNAME .
This statement intends to be flexible to support multiple
communication protocols in the future, but currently only
HTTP access is supported.
It requires that BIND 9 be compiled with libxml2;
the <command>statistics-channels</command> statement is
It requires that BIND 9 be compiled with libxml2 and/or
json-c (also known as libjson0); the
<command>statistics-channels</command> statement is
still accepted even if it is built without the library,
but any HTTP access will fail with an error.
</para>
......@@ -10514,7 +10515,8 @@ ns.domain.com.rpz-nsdname CNAME .
An <command>inet</command> control channel is a TCP socket
listening at the specified <command>ip_port</command> on the
specified <command>ip_addr</command>, which can be an IPv4 or IPv6
address. An <command>ip_addr</command> of <literal>*</literal> (asterisk) is
address. An <command>ip_addr</command> of <literal>*</literal>
(asterisk) is
interpreted as the IPv4 wildcard address; connections will be
accepted on any of the system's IPv4 addresses.
To listen on the IPv6 wildcard address,
......@@ -10545,6 +10547,41 @@ ns.domain.com.rpz-nsdname CNAME .
<command>named</command> will not open any communication channels.
</para>
<para>
The statistics are available in various formats and views
depending on the URI used to access them. For example, if
the statistics channel is configured to listen on 127.0.0.1
port 8888, then the statistics are accessible in XML format at
<ulink url="http://127.0.0.1:8888/"
>http://127.0.0.1:8888/</ulink> or
<ulink url="http://127.0.0.1:8888/xml"
>http://127.0.0.1:8888/xml</ulink>. A CSS file is
included which can format the XML statistics into tables
when viewed with a stylesheet-capable browser, and into
charts and graphs when using a javascript-capable browser.
</para>
<para>
The full set of statistics can also be read in JSON format at
<ulink url="http://127.0.0.1:8888/json"
>http://127.0.0.1:8888/json</ulink>.
Broken-out subsets of the statistics can be viewed at
<ulink url="http://127.0.0.1:8888/json/server"
>http://127.0.0.1:8888/json/server</ulink>
(server and resolver statistics),
<ulink url="http://127.0.0.1:8888/json/zones"
>http://127.0.0.1:8888/json/zones</ulink>
(zone statistics),
<ulink url="http://127.0.0.1:8888/json/net"
>http://127.0.0.1:8888/json/net</ulink>
(network status and socket statistics),
<ulink url="http://127.0.0.1:8888/json/mem"
>http://127.0.0.1:8888/json/mem</ulink>
(memory manager statistics),
<ulink url="http://127.0.0.1:8888/json/tasks"
>http://127.0.0.1:8888/json/tasks</ulink>
(task manager statistics).
</para>
</sect2>
<sect2 id="trusted-keys">
......
......@@ -21,6 +21,7 @@
#include <config.h>
#include <isc/json.h>
#include <isc/mem.h>
#include <isc/print.h>
#include <isc/string.h>
......@@ -29,6 +30,7 @@
#include <isc/time.h>
#include <isc/timer.h>
#include <isc/util.h>
#include <isc/xml.h>
#include <dns/cache.h>
#include <dns/db.h>
......@@ -1448,3 +1450,85 @@ error:
return (xmlrc);
}
#endif
#ifdef HAVE_JSON
#define CHECKMEM(m) do { \
if (m == NULL) { \
result = ISC_R_NOMEMORY;\
goto error;\
} \
} while(0)
isc_result_t
dns_cache_renderjson(dns_cache_t *cache, json_object *cstats) {
isc_result_t result = ISC_R_SUCCESS;
int indices[dns_cachestatscounter_max];
isc_uint64_t values[dns_cachestatscounter_max];
json_object *obj;
REQUIRE(VALID_CACHE(cache));
getcounters(cache->stats, isc_statsformat_file,
dns_cachestatscounter_max, indices, values);
obj = json_object_new_int64(values[dns_cachestatscounter_hits]);
CHECKMEM(obj);
json_object_object_add(cstats, "CacheHits", obj);
obj = json_object_new_int64(values[dns_cachestatscounter_misses]);
CHECKMEM(obj);
json_object_object_add(cstats, "CacheMisses", obj);
obj = json_object_new_int64(values[dns_cachestatscounter_queryhits]);
CHECKMEM(obj);
json_object_object_add(cstats, "QueryHits", obj);
obj = json_object_new_int64(values[dns_cachestatscounter_querymisses]);
CHECKMEM(obj);
json_object_object_add(cstats, "QueryMisses", obj);
obj = json_object_new_int64(values[dns_cachestatscounter_deletelru]);
CHECKMEM(obj);
json_object_object_add(cstats, "DeleteLRU", obj);
obj = json_object_new_int64(values[dns_cachestatscounter_deletettl]);
CHECKMEM(obj);
json_object_object_add(cstats, "DeleteTTL", obj);
obj = json_object_new_int64(dns_db_nodecount(cache->db));
CHECKMEM(obj);
json_object_object_add(cstats, "CacheNodes", obj);
obj = json_object_new_int64(dns_db_hashsize(cache->db));
CHECKMEM(obj);
json_object_object_add(cstats, "CacheBuckets", obj);
obj = json_object_new_int64(isc_mem_total(cache->mctx));
CHECKMEM(obj);
json_object_object_add(cstats, "TreeMemTotal", obj);
obj = json_object_new_int64(isc_mem_inuse(cache->mctx));
CHECKMEM(obj);
json_object_object_add(cstats, "TreeMemInUse", obj);
obj = json_object_new_int64(isc_mem_maxinuse(cache->mctx));
CHECKMEM(obj);
json_object_object_add(cstats, "HeapMemMax", obj);
obj = json_object_new_int64(isc_mem_total(cache->hmctx));
CHECKMEM(obj);
json_object_object_add(cstats, "HeapMemTotal", obj);
obj = json_object_new_int64(isc_mem_inuse(cache->hmctx));
CHECKMEM(obj);
json_object_object_add(cstats, "HeapMemInUse", obj);
obj = json_object_new_int64(isc_mem_maxinuse(cache->hmctx));
CHECKMEM(obj);
json_object_object_add(cstats, "HeapMemMax", obj);
result = ISC_R_SUCCESS;
error:
return (result);
}
#endif
......@@ -49,6 +49,7 @@
*** Imports
***/
#include <isc/json.h>
#include <isc/lang.h>
#include <isc/stats.h>
#include <isc/stdtime.h>
......@@ -327,6 +328,14 @@ dns_cache_renderxml(dns_cache_t *cache, xmlTextWriterPtr writer);
*/
#endif /* HAVE_LIBXML2 */
#ifdef HAVE_JSON
isc_result_t
dns_cache_renderjson(dns_cache_t *cache, json_object *cstats);
/*
* Render cache statistics and status in JSON
*/
#endif /* HAVE_JSON */
ISC_LANG_ENDDECLS
#endif /* DNS_CACHE_H */
......@@ -938,9 +938,10 @@ isc_httpd_senddone(isc_task_t *task, isc_event_t *ev)
*/
if (httpd->freecb != NULL) {
isc_buffer_t *b = NULL;
if (isc_buffer_length(&httpd->bodybuffer) > 0)
if (isc_buffer_length(&httpd->bodybuffer) > 0) {
b = &httpd->bodybuffer;
httpd->freecb(b, httpd->freecb_arg);
httpd->freecb(b, httpd->freecb_arg);
}
NOTICE("senddone free callback performed");
}
if (ISC_LINK_LINKED(&httpd->bodybuffer, link)) {
......
/*
* Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef ISC_JSON_H
#define ISC_JSON_H 1
/*
* This file is here mostly to make it easy to add additional libjson header
* files as needed across all the users of this file. Rather than place
* these libjson includes in each file, one include makes it easy to handle
* the ifdef as well as adding the ability to add additional functions
* which may be useful.
*/
#ifdef HAVE_JSON
#include <json/json.h>
#endif
#define ISC_JSON_RENDERCONFIG 0x00000001 /* render config data */
#define ISC_JSON_RENDERSTATS 0x00000002 /* render stats */
#define ISC_JSON_RENDERALL 0x000000ff /* render everything */
#endif /* ISC_JSON_H */
......@@ -24,6 +24,7 @@
#include <stdio.h>
#include <isc/json.h>
#include <isc/lang.h>
#include <isc/mutex.h>
#include <isc/platform.h>
......@@ -554,6 +555,15 @@ isc_mem_renderxml(xmlTextWriterPtr writer);
*/
#endif /* HAVE_LIBXML2 */
#ifdef HAVE_JSON
isc_result_t
isc_mem_renderjson(json_object *memobj);
/*%<
* Render all contexts' statistics and status in JSON.
*/
#endif /* HAVE_JSON */
/*
* Memory pools
*/
......
......@@ -57,13 +57,14 @@
*** Imports
***/
#include <isc/lang.h>
#include <isc/types.h>
#include <isc/event.h>
#include <isc/eventclass.h>
#include <isc/time.h>
#include <isc/lang.h>
#include <isc/json.h>
#include <isc/region.h>
#include <isc/sockaddr.h>
#include <isc/time.h>
#include <isc/types.h>
#include <isc/xml.h>
ISC_LANG_BEGINDECLS
......@@ -1143,15 +1144,21 @@ isc__socketmgr_maxudp(isc_socketmgr_t *mgr, int maxudp);
*/
#ifdef HAVE_LIBXML2
int
isc_socketmgr_renderxml(isc_socketmgr_t *mgr, xmlTextWriterPtr writer);
/*%<
* Render internal statistics and other state into the XML document.
*/
#endif /* HAVE_LIBXML2 */
#ifdef HAVE_JSON
isc_result_t
isc_socketmgr_renderjson(isc_socketmgr_t *mgr, json_object *stats);
/*%<
* Render internal statistics and other state into JSON format.
*/
#endif /* HAVE_JSON */
#ifdef USE_SOCKETIMPREGISTER
/*%<
* See isc_socketmgr_create() above.
......
......@@ -81,6 +81,7 @@
***/
#include <isc/eventclass.h>
#include <isc/json.h>
#include <isc/lang.h>
#include <isc/stdtime.h>
#include <isc/types.h>
......@@ -787,10 +788,13 @@ isc_taskmgr_excltask(isc_taskmgr_t *mgr, isc_task_t **taskp);
#ifdef HAVE_LIBXML2
int
isc_taskmgr_renderxml(isc_taskmgr_t *mgr, xmlTextWriterPtr writer);
#endif
#ifdef HAVE_JSON
isc_result_t
isc_taskmgr_renderjson(isc_taskmgr_t *mgr, json_object *tasksobj);
#endif
/*%<
......
......@@ -129,7 +129,8 @@ typedef enum {
/*% Statistics formats (text file or XML) */
typedef enum {
isc_statsformat_file,
isc_statsformat_xml
isc_statsformat_xml,
isc_statsformat_json
} isc_statsformat_t;
#endif /* ISC_TYPES_H */
......@@ -27,6 +27,7 @@
#include <limits.h>
#include <isc/json.h>
#include <isc/magic.h>
#include <isc/mem.h>
#include <isc/msgs.h>
......@@ -2379,18 +2380,21 @@ isc_mem_references(isc_mem_t *ctx0) {
return (references);
}
#ifdef HAVE_LIBXML2
#if defined(HAVE_LIBXML2) || defined(HAVE_JSON)
typedef struct summarystat {
isc_uint64_t total;
isc_uint64_t inuse;
isc_uint64_t blocksize;
isc_uint64_t contextsize;
} summarystat_t;
#endif
#ifdef HAVE_LIBXML2
#define TRY0(a) do { xmlrc = (a); if (xmlrc < 0) goto error; } while(0)
static int
renderctx(isc__mem_t *ctx, summarystat_t *summary, xmlTextWriterPtr writer) {
xml_renderctx(isc__mem_t *ctx, summarystat_t *summary,
xmlTextWriterPtr writer)
{
int xmlrc;
REQUIRE(VALID_CONTEXT(ctx));
......@@ -2501,7 +2505,7 @@ isc_mem_renderxml(xmlTextWriterPtr writer) {
for (ctx = ISC_LIST_HEAD(contexts);
ctx != NULL;
ctx = ISC_LIST_NEXT(ctx, link)) {
xmlrc = renderctx(ctx, &summary, writer);
xmlrc = xml_renderctx(ctx, &summary, writer);
if (xmlrc < 0) {
UNLOCK(&lock);
goto error;
......@@ -2549,4 +2553,160 @@ isc_mem_renderxml(xmlTextWriterPtr writer) {
}
#endif /* HAVE_LIBXML2 */
#ifdef HAVE_JSON
#define CHECKMEM(m) do { \
if (m == NULL) { \
result = ISC_R_NOMEMORY;\
goto error;\
} \
} while(0)
static isc_result_t
json_renderctx(isc__mem_t *ctx, summarystat_t *summary, json_object *array) {
isc_result_t result = ISC_R_FAILURE;
json_object *ctxobj, *obj;
char buf[1024];
REQUIRE(VALID_CONTEXT(ctx));
REQUIRE(summary != NULL);
REQUIRE(array != NULL);
MCTXLOCK(ctx, &ctx->lock);
summary->contextsize += sizeof(*ctx) +
(ctx->max_size + 1) * sizeof(struct stats) +
ctx->max_size * sizeof(element *) +
ctx->basic_table_count * sizeof(char *);
summary->total += ctx->total;
summary->inuse += ctx->inuse;
if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0)
summary->blocksize += ctx->basic_table_count *
NUM_BASIC_BLOCKS * ctx->mem_target;
#if ISC_MEM_TRACKLINES
if (ctx->debuglist != NULL) {
summary->contextsize +=
(ctx->max_size + 1) * sizeof(debuglist_t) +
ctx->debuglistcnt * sizeof(debuglink_t);
}
#endif
ctxobj = json_object_new_object();
CHECKMEM(ctxobj);
sprintf(buf, "%p", ctx);
obj = json_object_new_string(buf);
CHECKMEM(obj);
json_object_object_add(ctxobj, "id", obj);
if (ctx->name[0] != 0) {
obj = json_object_new_string(ctx->name);
CHECKMEM(obj);
json_object_object_add(ctxobj, "name", obj);
}
obj = json_object_new_int64(ctx->references);
CHECKMEM(obj);
json_object_object_add(ctxobj, "references", obj);
obj = json_object_new_int64(ctx->total);
CHECKMEM(obj);
json_object_object_add(ctxobj, "total", obj);
obj = json_object_new_int64(ctx->inuse);
CHECKMEM(obj);
json_object_object_add(ctxobj, "inuse", obj);
obj = json_object_new_int64(ctx->maxinuse);
CHECKMEM(obj);
json_object_object_add(ctxobj, "maxinuse", obj);
if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
isc_uint64_t blocksize;
blocksize = ctx->basic_table_count * NUM_BASIC_BLOCKS *
ctx->mem_target;
obj = json_object_new_int64(blocksize);
CHECKMEM(obj);
json_object_object_add(ctxobj, "blocksize", obj);
}
obj = json_object_new_int64(ctx->poolcnt);
CHECKMEM(obj);
json_object_object_add(ctxobj, "pools", obj);
obj = json_object_new_int64(ctx->hi_water);
CHECKMEM(obj);
json_object_object_add(ctxobj, "hiwater", obj);
obj = json_object_new_int64(ctx->lo_water);
CHECKMEM(obj);
json_object_object_add(ctxobj, "lowater", obj);
MCTXUNLOCK(ctx, &ctx->lock);
json_object_array_add(array, ctxobj);
return (ISC_R_SUCCESS);
error:
MCTXUNLOCK(ctx, &ctx->lock);
if (ctxobj != NULL)
json_object_put(ctxobj);
return (result);
}
isc_result_t
isc_mem_renderjson(json_object *memobj) {
isc_result_t result = ISC_R_SUCCESS;
isc__mem_t *ctx;
summarystat_t summary;
isc_uint64_t lost;
json_object *ctxarray, *obj;
memset(&summary, 0, sizeof(summary));
RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
ctxarray = json_object_new_array();
CHECKMEM(ctxarray);
LOCK(&lock);
lost = totallost;
for (ctx = ISC_LIST_HEAD(contexts);
ctx != NULL;