cmdmon: update CLIENT_ACCESSES_BY_INDEX command

Add new fields from clientlog to the report and print them in chronyc.
Rework the code to skip empty records in the hash table. The reply no
longer has variable length, all client fields are filled even if some
are empty. Reply with RPY_NULL when the facility is disabled.
This commit is contained in:
Miroslav Lichvar 2015-11-27 15:25:24 +01:00
parent b506594c2d
commit 657929f8ec
7 changed files with 128 additions and 151 deletions

17
candm.h
View file

@ -284,7 +284,7 @@ typedef struct {
typedef struct {
uint32_t first_index;
uint32_t n_indices;
uint32_t n_clients;
int32_t EOR;
} REQ_ClientAccessesByIndex;
@ -351,7 +351,7 @@ typedef struct {
#define PROTO_VERSION_PADDING 6
/* The maximum length of padding in request packet, currently
defined by CLIENT_ACCESSES_BY_INDEX and MANUAL_LIST */
defined by MANUAL_LIST */
#define MAX_PADDING_LENGTH 396
/* ================================================== */
@ -545,11 +545,14 @@ typedef struct {
typedef struct {
IPAddr ip;
uint32_t client_hits;
uint32_t peer_hits;
uint32_t cmd_hits_auth;
uint32_t cmd_hits_normal;
uint32_t cmd_hits_bad;
uint32_t ntp_hits;
uint32_t cmd_hits;
uint16_t ntp_drops;
uint16_t cmd_drops;
int8_t ntp_interval;
int8_t cmd_interval;
int8_t ntp_timeout_interval;
int8_t pad;
uint32_t last_ntp_hit_ago;
uint32_t last_cmd_hit_ago;
} RPY_ClientAccesses_Client;

104
client.c
View file

@ -2047,87 +2047,61 @@ process_cmd_clients(char *line)
{
CMD_Request request;
CMD_Reply reply;
unsigned long next_index;
int j;
IPAddr ip;
unsigned long client_hits;
unsigned long peer_hits;
unsigned long cmd_hits_auth;
unsigned long cmd_hits_normal;
unsigned long cmd_hits_bad;
unsigned long last_ntp_hit_ago;
unsigned long last_cmd_hit_ago;
char hostname_buf[50];
int n_replies;
int n_indices_in_table;
uint32_t i, n_clients, next_index, n_indices;
RPY_ClientAccesses_Client *client;
char hostname[26];
next_index = 0;
printf("Hostname Client Peer CmdAuth CmdNorm CmdBad LstN LstC\n"
"========================= ====== ====== ====== ====== ====== ==== ====\n");
do {
printf("Hostname NTP Drop Int IntL Last Cmd Drop Int Last\n"
"===============================================================================\n");
while (1) {
request.command = htons(REQ_CLIENT_ACCESSES_BY_INDEX);
request.data.client_accesses_by_index.first_index = htonl(next_index);
request.data.client_accesses_by_index.n_indices = htonl(MAX_CLIENT_ACCESSES);
request.data.client_accesses_by_index.n_clients = htonl(MAX_CLIENT_ACCESSES);
if (request_reply(&request, &reply, RPY_CLIENT_ACCESSES_BY_INDEX, 0)) {
n_replies = ntohl(reply.data.client_accesses_by_index.n_clients);
n_indices_in_table = ntohl(reply.data.client_accesses_by_index.n_indices);
if (n_replies == 0) {
goto finished;
}
for (j=0; j<n_replies; j++) {
UTI_IPNetworkToHost(&reply.data.client_accesses_by_index.clients[j].ip, &ip);
if (ip.family != IPADDR_UNSPEC) {
/* UNSPEC implies that the node could not be found in
the daemon's tables; we shouldn't ever generate this
case, but ignore it if we do. (In future there might
be a protocol to reset the client logging; if another
administrator runs that while we're doing the clients
command, there will be a race condition that could
cause this). */
if (!request_reply(&request, &reply, RPY_CLIENT_ACCESSES_BY_INDEX, 0))
return 0;
client_hits = ntohl(reply.data.client_accesses_by_index.clients[j].client_hits);
peer_hits = ntohl(reply.data.client_accesses_by_index.clients[j].peer_hits);
cmd_hits_auth = ntohl(reply.data.client_accesses_by_index.clients[j].cmd_hits_auth);
cmd_hits_normal = ntohl(reply.data.client_accesses_by_index.clients[j].cmd_hits_normal);
cmd_hits_bad = ntohl(reply.data.client_accesses_by_index.clients[j].cmd_hits_bad);
last_ntp_hit_ago = ntohl(reply.data.client_accesses_by_index.clients[j].last_ntp_hit_ago);
last_cmd_hit_ago = ntohl(reply.data.client_accesses_by_index.clients[j].last_cmd_hit_ago);
n_clients = ntohl(reply.data.client_accesses_by_index.n_clients);
n_indices = ntohl(reply.data.client_accesses_by_index.n_indices);
if (no_dns) {
snprintf(hostname_buf, sizeof(hostname_buf),
"%s", UTI_IPToString(&ip));
} else {
DNS_IPAddress2Name(&ip, hostname_buf, sizeof(hostname_buf));
hostname_buf[25] = 0;
}
printf("%-25s %6ld %6ld %6ld %6ld %6ld ",
hostname_buf,
client_hits, peer_hits,
cmd_hits_auth, cmd_hits_normal, cmd_hits_bad);
print_seconds(last_ntp_hit_ago);
printf(" ");
print_seconds(last_cmd_hit_ago);
for (i = 0; i < n_clients && i < MAX_CLIENT_ACCESSES; i++) {
client = &reply.data.client_accesses_by_index.clients[i];
UTI_IPNetworkToHost(&client->ip, &ip);
/* UNSPEC means the record could not be found in the daemon's tables.
We shouldn't ever generate this case, but ignore it if we do. */
if (ip.family == IPADDR_UNSPEC)
continue;
if (no_dns)
snprintf(hostname, sizeof (hostname), "%s", UTI_IPToString(&ip));
else
DNS_IPAddress2Name(&ip, hostname, sizeof (hostname));
printf("%-25s", hostname);
printf(" %6"PRIu32" %5"PRIu16" %2d %2d ",
ntohl(client->ntp_hits), ntohs(client->ntp_drops),
client->ntp_interval, client->ntp_timeout_interval);
print_seconds(ntohl(client->last_ntp_hit_ago));
printf(" %6"PRIu32" %5"PRIu16" %2d ",
ntohl(client->cmd_hits), ntohs(client->cmd_drops),
client->cmd_interval);
print_seconds(ntohl(client->last_cmd_hit_ago));
printf("\n");
}
}
/* Set the next index to probe based on what the server tells us */
next_index = ntohl(reply.data.client_accesses_by_index.next_index);
if (next_index >= n_indices_in_table) {
goto finished;
}
} else {
return 0;
}
} while (1); /* keep going until all subnets have been expanded,
down to single nodes */
finished:
if (next_index >= n_indices || n_clients < MAX_CLIENT_ACCESSES)
break;
}
return 1;
}

View file

@ -481,26 +481,41 @@ CLG_LimitCommandResponseRate(int index)
/* ================================================== */
CLG_Status
CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report,
time_t now, unsigned long *n_indices)
extern int
CLG_GetNumberOfIndices(void)
{
if (!active)
return -1;
return ARR_GetSize(records);
}
/* ================================================== */
int
CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, time_t now)
{
Record *record;
if (!active)
return CLG_INACTIVE;
*n_indices = ARR_GetSize(records);
if (index < 0 || index >= *n_indices)
return CLG_INDEXTOOLARGE;
if (!active || index < 0 || index >= ARR_GetSize(records))
return 0;
record = ARR_GetElement(records, index);
if (record->ip_addr.family == IPADDR_UNSPEC)
return 0;
report->ip_addr = record->ip_addr;
report->ntp_hits = record->ntp_hits;
report->cmd_hits = record->cmd_hits;
report->ntp_drops = record->ntp_drops;
report->cmd_drops = record->cmd_drops;
report->ntp_interval = (record->ntp_rate - RATE_SCALE / 2) / -RATE_SCALE;
report->cmd_interval = (record->cmd_rate - RATE_SCALE / 2) / -RATE_SCALE;
report->ntp_timeout_interval =
(record->ntp_timeout_rate - RATE_SCALE / 2) / -RATE_SCALE;
report->last_ntp_hit_ago = now - record->last_ntp_hit;
report->last_cmd_hit_ago = now - record->last_cmd_hit;
return CLG_SUCCESS;
return 1;
}

View file

@ -39,18 +39,8 @@ extern int CLG_LimitNTPResponseRate(int index);
extern int CLG_LimitCommandResponseRate(int index);
/* And some reporting functions, for use by chronyc. */
/* TBD */
typedef enum {
CLG_SUCCESS, /* All is well */
CLG_EMPTYSUBNET, /* No hosts logged in requested subnet */
CLG_BADSUBNET, /* Subnet requested is not 0, 8, 16 or 24 bits */
CLG_INACTIVE, /* Facility not active */
CLG_INDEXTOOLARGE /* Node index is higher than number of nodes present */
} CLG_Status;
CLG_Status
CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report,
time_t now, unsigned long *n_indices);
extern int CLG_GetNumberOfIndices(void);
extern int CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, time_t now);
#endif /* GOT_CLIENTLOG_H */

View file

@ -1017,50 +1017,50 @@ handle_cyclelogs(CMD_Request *rx_message, CMD_Reply *tx_message)
static void
handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
{
CLG_Status result;
RPT_ClientAccessByIndex_Report report;
unsigned long first_index, n_indices, n_indices_in_table;
int i, j;
RPY_ClientAccesses_Client *client;
int n_indices;
uint32_t i, j, req_first_index, req_n_clients;
struct timeval now;
SCH_GetLastEventTime(&now, NULL, NULL);
first_index = ntohl(rx_message->data.client_accesses_by_index.first_index);
n_indices = ntohl(rx_message->data.client_accesses_by_index.n_indices);
if (n_indices > MAX_CLIENT_ACCESSES)
n_indices = MAX_CLIENT_ACCESSES;
req_first_index = ntohl(rx_message->data.client_accesses_by_index.first_index);
req_n_clients = ntohl(rx_message->data.client_accesses_by_index.n_clients);
if (req_n_clients > MAX_CLIENT_ACCESSES)
req_n_clients = MAX_CLIENT_ACCESSES;
tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX);
for (i = 0, j = 0; i < n_indices; i++) {
result = CLG_GetClientAccessReportByIndex(first_index + i, &report,
now.tv_sec, &n_indices_in_table);
tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices_in_table);
switch (result) {
case CLG_SUCCESS:
UTI_IPHostToNetwork(&report.ip_addr, &tx_message->data.client_accesses_by_index.clients[j].ip);
tx_message->data.client_accesses_by_index.clients[j].client_hits = htonl(report.ntp_hits);
tx_message->data.client_accesses_by_index.clients[j].peer_hits = htonl(0);
tx_message->data.client_accesses_by_index.clients[j].cmd_hits_auth = htonl(0);
tx_message->data.client_accesses_by_index.clients[j].cmd_hits_normal = htonl(report.cmd_hits);
tx_message->data.client_accesses_by_index.clients[j].cmd_hits_bad = htonl(0);
tx_message->data.client_accesses_by_index.clients[j].last_ntp_hit_ago = htonl(report.last_ntp_hit_ago);
tx_message->data.client_accesses_by_index.clients[j].last_cmd_hit_ago = htonl(report.last_cmd_hit_ago);
j++;
break;
case CLG_INDEXTOOLARGE:
break; /* ignore this index */
case CLG_INACTIVE:
n_indices = CLG_GetNumberOfIndices();
if (n_indices < 0) {
tx_message->status = htons(STT_INACTIVE);
return;
default:
assert(0);
break;
}
}
tx_message->data.client_accesses_by_index.next_index = htonl(first_index + i);
tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX);
tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices);
memset(tx_message->data.client_accesses_by_index.clients, 0,
sizeof (tx_message->data.client_accesses_by_index.clients));
for (i = req_first_index, j = 0; i < (uint32_t)n_indices && j < req_n_clients; i++) {
if (!CLG_GetClientAccessReportByIndex(i, &report, now.tv_sec))
continue;
client = &tx_message->data.client_accesses_by_index.clients[j++];
UTI_IPHostToNetwork(&report.ip_addr, &client->ip);
client->ntp_hits = htonl(report.ntp_hits);
client->cmd_hits = htonl(report.cmd_hits);
client->ntp_drops = htons(report.ntp_drops);
client->cmd_drops = htons(report.cmd_drops);
client->ntp_interval = report.ntp_interval;
client->cmd_interval = report.cmd_interval;
client->ntp_timeout_interval = report.ntp_timeout_interval;
client->last_ntp_hit_ago = htonl(report.last_ntp_hit_ago);
client->last_cmd_hit_ago = htonl(report.last_cmd_hit_ago);
}
tx_message->data.client_accesses_by_index.next_index = htonl(i);
tx_message->data.client_accesses_by_index.n_clients = htonl(j);
}

View file

@ -346,17 +346,7 @@ PKL_ReplyLength(CMD_Reply *r)
/* No longer supported */
return 0;
case RPY_CLIENT_ACCESSES_BY_INDEX:
{
unsigned long nc = ntohl(r->data.client_accesses_by_index.n_clients);
if (r->status == htons(STT_SUCCESS)) {
if (nc > MAX_CLIENT_ACCESSES)
return 0;
return (offsetof(CMD_Reply, data.client_accesses_by_index.clients) +
nc * sizeof(RPY_ClientAccesses_Client));
} else {
return offsetof(CMD_Reply, data);
}
}
return offsetof(CMD_Reply, data.client_accesses_by_index.EOR);
case RPY_MANUAL_LIST:
{
unsigned long ns = ntohl(r->data.manual_list.n_samples);

View file

@ -88,10 +88,15 @@ typedef struct {
typedef struct {
IPAddr ip_addr;
unsigned long ntp_hits;
unsigned long cmd_hits;
unsigned long last_ntp_hit_ago;
unsigned long last_cmd_hit_ago;
uint32_t ntp_hits;
uint32_t cmd_hits;
uint16_t ntp_drops;
uint16_t cmd_drops;
int8_t ntp_interval;
int8_t cmd_interval;
int8_t ntp_timeout_interval;
uint32_t last_ntp_hit_ago;
uint32_t last_cmd_hit_ago;
} RPT_ClientAccessByIndex_Report;
typedef struct {