statschannel.c revision 8ac908b38a2fd9b780ae3a27ff26932a17823ae0
/*
* Copyright (C) 2008, 2009 Internet Systems Consortium, Inc. ("ISC")
*
* 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.
*/
/* $Id: statschannel.c,v 1.25 2010/02/04 00:57:25 marka Exp $ */
/*! \file */
#include <config.h>
#include <dns/resolver.h>
#include <dns/rdataclass.h>
#include <dns/rdatatype.h>
#include <named/statschannel.h>
#include "bind9.xsl.h"
struct ns_statschannel {
/* Unlocked */
/*
* Locked by channel lock: can be referenced and modified by both
* the server task and the channel task.
*/
/* Locked by server task */
};
typedef struct
void *arg; /* type dependent argument */
int ncounters; /* used for general statistics */
int *counterindices; /* used for general statistics */
/*%
* Statistics descriptions. These could be statistically initialized at
* compile time, but we configure them run time in the init_desc() function
* below so that they'll be less susceptible to counter name changes.
*/
static const char *nsstats_desc[dns_nsstatscounter_max];
static const char *resstats_desc[dns_resstatscounter_max];
static const char *zonestats_desc[dns_zonestatscounter_max];
static const char *sockstats_desc[isc_sockstatscounter_max];
#ifdef HAVE_LIBXML2
static const char *nsstats_xmldesc[dns_nsstatscounter_max];
static const char *resstats_xmldesc[dns_resstatscounter_max];
static const char *zonestats_xmldesc[dns_zonestatscounter_max];
static const char *sockstats_xmldesc[isc_sockstatscounter_max];
#else
#define nsstats_xmldesc NULL
#define resstats_xmldesc NULL
#define zonestats_xmldesc NULL
#define sockstats_xmldesc NULL
#endif /* HAVE_LIBXML2 */
/*%
* Mapping arrays to represent statistics counters in the order of our
* preference, regardless of the order of counter indices. For example,
* nsstats_desc[nsstats_index[0]] will be the description that is shown first.
*/
static int nsstats_index[dns_nsstatscounter_max];
static int resstats_index[dns_resstatscounter_max];
static int zonestats_index[dns_zonestatscounter_max];
static int sockstats_index[isc_sockstatscounter_max];
static inline void
{
#ifdef HAVE_LIBXML2
#endif
#ifdef HAVE_LIBXML2
#else
#endif
}
static void
init_desc(void) {
int i;
/* Initialize name server statistics */
for (i = 0; i < dns_nsstatscounter_max; i++)
nsstats_desc[i] = NULL;
#ifdef HAVE_LIBXML2
for (i = 0; i < dns_nsstatscounter_max; i++)
nsstats_xmldesc[i] = NULL;
#endif
do { \
} while (0)
i = 0;
"requests with unsupported EDNS version received",
"ReqBadEDNSVer");
"ReqBadSIG");
"TruncatedResp");
"QrySuccess");
"QryAuthAns");
"queries resulted in non authoritative answer",
"QryNoauthAns");
"QryReferral");
"UpdateReqFwd");
"UpdateRespFwd");
"updates rejected due to prerequisite failure",
"UpdateBadPrereq");
INSIST(i == dns_nsstatscounter_max);
/* Initialize resolver statistics */
for (i = 0; i < dns_resstatscounter_max; i++)
resstats_desc[i] = NULL;
#ifdef HAVE_LIBXML2
for (i = 0; i < dns_resstatscounter_max; i++)
resstats_xmldesc[i] = NULL;
#endif
do { \
} while (0)
i = 0;
"QueryAbort");
"QuerySockFail");
"GlueFetchv4Fail");
"GlueFetchv6Fail");
"ValNegOk");
"QryRTT" DNS_RESOLVER_QRYRTTCLASS0STR);
"QryRTT" DNS_RESOLVER_QRYRTTCLASS1STR);
"QryRTT" DNS_RESOLVER_QRYRTTCLASS2STR);
"QryRTT" DNS_RESOLVER_QRYRTTCLASS3STR);
"QryRTT" DNS_RESOLVER_QRYRTTCLASS4STR);
INSIST(i == dns_resstatscounter_max);
/* Initialize zone statistics */
for (i = 0; i < dns_zonestatscounter_max; i++)
zonestats_desc[i] = NULL;
#ifdef HAVE_LIBXML2
for (i = 0; i < dns_zonestatscounter_max; i++)
zonestats_xmldesc[i] = NULL;
#endif
do { \
} while (0)
i = 0;
INSIST(i == dns_zonestatscounter_max);
/* Initialize socket statistics */
for (i = 0; i < isc_sockstatscounter_max; i++)
sockstats_desc[i] = NULL;
#ifdef HAVE_LIBXML2
for (i = 0; i < isc_sockstatscounter_max; i++)
sockstats_xmldesc[i] = NULL;
#endif
do { \
} while (0)
i = 0;
"UDP4OpenFail");
"UDP6OpenFail");
"TCP4OpenFail");
"TCP6OpenFail");
"UnixOpenFail");
"FDWatchClose");
"UDP4BindFail");
"UDP6BindFail");
"TCP4BindFail");
"TCP6BindFail");
"UnixBindFail");
"FdwatchBindFail");
"UDP4ConnFail");
"UDP6ConnFail");
"TCP4ConnFail");
"TCP6ConnFail");
"UnixConnFail");
"FDwatchConnFail");
"UDP4Conn");
"UDP6Conn");
"TCP4Conn");
"TCP6Conn");
"UnixConn");
"FDwatch domain connections established",
"FDwatchConn");
"TCP4AcceptFail");
"TCP6AcceptFail");
"Unix domain connection accept failures",
"UnixAcceptFail");
"TCP4Accept");
"TCP6Accept");
"UnixAccept");
"UnixSendErr");
"FDwatchSendErr");
"UnixRecvErr");
"FDwatchRecvErr");
INSIST(i == isc_sockstatscounter_max);
/* Sanity check */
for (i = 0; i < dns_nsstatscounter_max; i++)
for (i = 0; i < dns_resstatscounter_max; i++)
for (i = 0; i < dns_zonestatscounter_max; i++)
for (i = 0; i < isc_sockstatscounter_max; i++)
#ifdef HAVE_LIBXML2
for (i = 0; i < dns_nsstatscounter_max; i++)
for (i = 0; i < dns_resstatscounter_max; i++)
for (i = 0; i < dns_zonestatscounter_max; i++)
for (i = 0; i < isc_sockstatscounter_max; i++)
#endif
}
/*%
* Dump callback functions.
*/
static void
}
static isc_result_t
{
int i, index;
#ifdef HAVE_LIBXML2
int xmlrc;
#endif
#ifndef HAVE_LIBXML2
#endif
for (i = 0; i < ncounters; i++) {
continue;
case statsformat_file:
break;
case statsformat_xml:
#ifdef HAVE_LIBXML2
category));
"name"));
"counter"));
} else {
}
"%"
"u", value));
#endif
break;
}
}
return (ISC_R_SUCCESS);
#ifdef HAVE_LIBXML2
return (ISC_R_FAILURE);
#endif
}
static void
char typebuf[64];
const char *typestr;
#ifdef HAVE_LIBXML2
int xmlrc;
#endif
== 0) {
sizeof(typebuf));
} else
typestr = "Others";
case statsformat_file:
break;
case statsformat_xml:
#ifdef HAVE_LIBXML2
val));
#endif
break;
}
return;
#ifdef HAVE_LIBXML2
return;
#endif
}
static void
char typebuf[64];
const char *typestr;
#ifdef HAVE_LIBXML2
int xmlrc;
#endif
!= 0) {
typestr = "NXDOMAIN";
} else if ((DNS_RDATASTATSTYPE_ATTR(type) &
DNS_RDATASTATSTYPE_ATTR_OTHERTYPE) != 0) {
typestr = "Others";
} else {
sizeof(typebuf));
}
!= 0)
case statsformat_file:
break;
case statsformat_xml:
#ifdef HAVE_LIBXML2
val));
#endif
break;
}
return;
#ifdef HAVE_LIBXML2
#endif
}
static void
isc_buffer_t b;
char codebuf[64];
#ifdef HAVE_LIBXML2
int xmlrc;
#endif
dns_opcode_totext(code, &b);
case statsformat_file:
break;
case statsformat_xml:
#ifdef HAVE_LIBXML2
val));
#endif
break;
}
return;
#ifdef HAVE_LIBXML2
return;
#endif
}
#ifdef HAVE_LIBXML2
/* XXXMLG below here sucks. */
static isc_result_t
int xmlrc;
else
if (result != ISC_R_SUCCESS)
goto error;
}
return (ISC_R_SUCCESS);
return (ISC_R_FAILURE);
}
static isc_result_t
char boottime[sizeof "yyyy-mm-ddThh:mm:ssZ"];
char nowstr[sizeof "yyyy-mm-ddThh:mm:ssZ"];
int xmlrc;
isc_time_now(&now);
goto error;
ISC_XMLCHAR "type=\"text/xsl\" href=\"/bind9.xsl\""));
ISC_XMLCHAR "1.0"));
ISC_XMLCHAR "2.2"));
/* Set common fields for statistics dump */
/*
* Start by rendering the views we know of here. For each view we
* know of, call its rendering function.
*/
writer);
if (result != ISC_R_SUCCESS)
goto error;
rdtypestat_dump, &dumparg, 0);
goto error;
}
writer, "resstat",
if (result != ISC_R_SUCCESS)
goto error;
}
if (cachestats != NULL) {
ISC_XMLCHAR "cache"));
ISC_XMLCHAR "name",
&dumparg, 0);
goto error;
}
}
0);
goto error;
&dumparg, 0);
goto error;
"nsstat", nsstats_xmldesc,
if (result != ISC_R_SUCCESS)
goto error;
"zonestat", zonestats_xmldesc,
if (result != ISC_R_SUCCESS)
goto error;
/*
* Most of the common resolver statistics entries are 0, so we don't
* use the verbose dump here.
*/
"resstat", resstats_xmldesc,
resstat_values, 0);
if (result != ISC_R_SUCCESS)
goto error;
"sockstat", sockstats_xmldesc,
if (result != ISC_R_SUCCESS)
goto error;
return (ISC_R_SUCCESS);
return (ISC_R_FAILURE);
}
static void
}
static isc_result_t
void **freecb_args)
{
unsigned char *msg;
int msglen;
if (result == ISC_R_SUCCESS) {
*retcode = 200;
*retmsg = "OK";
isc_buffer_add(b, msglen);
*freecb = wrap_xmlfree;
*freecb_args = NULL;
}
return (result);
}
#endif /* HAVE_LIBXML2 */
static isc_result_t
void **freecb_args)
{
*retcode = 200;
*retmsg = "OK";
*freecb_args = NULL;
return (ISC_R_SUCCESS);
}
static void
char socktext[ISC_SOCKADDR_FORMATSIZE];
ISC_LOG_NOTICE, "stopping statistics channel on %s",
socktext);
}
static isc_boolean_t
char socktext[ISC_SOCKADDR_FORMATSIZE];
int match;
return (ISC_TRUE);
}
"rejected statistics connection from %s", socktext);
return (ISC_FALSE);
}
static void
destroy_listener(void *arg) {
/* We don't have to acquire the lock here since it's already unlinked */
}
static isc_result_t
const char *socktext)
{
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS) {
return (ISC_R_FAILURE);
}
&new_acl);
} else
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
#ifndef ISC_ALLOW_MAPPED
#endif
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
#ifdef HAVE_LIBXML2
#endif
server);
"statistics channel listening on %s", socktext);
if (result != ISC_R_SUCCESS) {
sizeof(*listener));
}
return (result);
}
static void
const char *socktext)
{
break;
return;
}
/*
* Now, keep the old access list unless a new one can be made.
*/
&new_acl);
} else
if (result == ISC_R_SUCCESS) {
} else {
"couldn't install new acl for "
"statistics channel %s: %s",
}
}
{
char socktext[ISC_SOCKADDR_FORMATSIZE];
/*
* Get the list of named.conf 'statistics-channels' statements.
*/
/*
* already being listened on and moving them to the new list.
*
* the underlying config code, or to the bind attempt getting an
* address-in-use error.
*/
if (statschannellist != NULL) {
#ifndef HAVE_LIBXML2
"statistics-channels specified but not effective "
"due to missing XML library");
#endif
const cfg_obj_t *statschannel;
&listenercfg);
if (listenercfg == NULL)
continue;
const cfg_obj_t *listen_params;
if (isc_sockaddr_getport(&addr) == 0)
sizeof(socktext));
ISC_LOG_DEBUG(9),
"processing statistics "
"channel %s",
socktext);
/*
* Remove the listener from the old
* list, so it won't be shut down.
*/
} else {
/*
* This is a new listener.
*/
isc_result_t r;
&addr, aclconfctx,
socktext);
if (r != ISC_R_SUCCESS) {
"couldn't allocate "
"statistics channel"
" %s: %s",
isc_result_totext(r));
}
}
link);
}
}
}
listener = listener_next) {
}
return (ISC_R_SUCCESS);
}
void
}
}
/* Set common fields */
&dumparg, 0);
continue;
else
&dumparg, 0);
}
nsstats_index, nsstat_values, 0);
resstats_index, resstat_values, 0);
continue;
else
resstats_index, resstat_values, 0);
}
if (cachestats == NULL)
continue;
else
if (dns_view_iscacheshared(view)) {
/*
* Avoid dumping redundant statistics when the cache is
* shared.
*/
continue;
}
0);
}
result == ISC_R_SUCCESS;
{
char zonename[DNS_NAME_FORMATSIZE];
nsstats_index, nsstat_values, 0);
}
}
return (ISC_R_SUCCESS); /* this function currently always succeeds */
}