kike: implement STATS
This commit is contained in:
parent
ba14a30c0a
commit
ff10e1b652
|
@ -3,7 +3,11 @@
|
||||||
3 IRC_RPL_CREATED ":This server was created %s"
|
3 IRC_RPL_CREATED ":This server was created %s"
|
||||||
4 IRC_RPL_MYINFO "%s %s %s %s"
|
4 IRC_RPL_MYINFO "%s %s %s %s"
|
||||||
5 IRC_RPL_ISUPPORT "%s :are supported by this server"
|
5 IRC_RPL_ISUPPORT "%s :are supported by this server"
|
||||||
|
211 IRC_RPL_STATSLINKINFO "%s %zu %zu %zu %zu %zu %lld"
|
||||||
|
212 IRC_RPL_STATSCOMMANDS "%s %zu %zu %zu"
|
||||||
|
219 IRC_RPL_ENDOFSTATS "%c :End of STATS report"
|
||||||
221 IRC_RPL_UMODEIS "+%s"
|
221 IRC_RPL_UMODEIS "+%s"
|
||||||
|
242 IRC_RPL_STATSUPTIME ":Server Up %d days %d:%02d:%02d"
|
||||||
251 IRC_RPL_LUSERCLIENT ":There are %d users and %d services on %d servers"
|
251 IRC_RPL_LUSERCLIENT ":There are %d users and %d services on %d servers"
|
||||||
252 IRC_RPL_LUSEROP "%d :operator(s) online"
|
252 IRC_RPL_LUSEROP "%d :operator(s) online"
|
||||||
253 IRC_RPL_LUSERUNKNOWN "%d :unknown connection(s)"
|
253 IRC_RPL_LUSERUNKNOWN "%d :unknown connection(s)"
|
||||||
|
|
183
kike.c
183
kike.c
|
@ -25,6 +25,8 @@
|
||||||
#include "kike-replies.c"
|
#include "kike-replies.c"
|
||||||
#include <nl_types.h>
|
#include <nl_types.h>
|
||||||
|
|
||||||
|
// FIXME: don't use time_t to compute time deltas
|
||||||
|
|
||||||
// --- Configuration (application-specific) ------------------------------------
|
// --- Configuration (application-specific) ------------------------------------
|
||||||
|
|
||||||
static struct config_item g_config_table[] =
|
static struct config_item g_config_table[] =
|
||||||
|
@ -306,6 +308,12 @@ struct client
|
||||||
LIST_HEADER (struct client)
|
LIST_HEADER (struct client)
|
||||||
struct server_context *ctx; ///< Server context
|
struct server_context *ctx; ///< Server context
|
||||||
|
|
||||||
|
time_t opened; ///< When the connection was opened
|
||||||
|
size_t n_sent_messages; ///< Number of sent messages total
|
||||||
|
size_t sent_bytes; ///< Number of sent bytes total
|
||||||
|
size_t n_received_messages; ///< Number of received messages total
|
||||||
|
size_t received_bytes; ///< Number of received bytes total
|
||||||
|
|
||||||
int socket_fd; ///< The TCP socket
|
int socket_fd; ///< The TCP socket
|
||||||
struct str read_buffer; ///< Unprocessed input
|
struct str read_buffer; ///< Unprocessed input
|
||||||
struct str write_buffer; ///< Output yet to be sent out
|
struct str write_buffer; ///< Output yet to be sent out
|
||||||
|
@ -566,12 +574,24 @@ whowas_info_destroy (struct whowas_info *self)
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
struct irc_command
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
bool requires_registration;
|
||||||
|
void (*handler) (const struct irc_message *, struct client *);
|
||||||
|
|
||||||
|
size_t n_received; ///< Number of commands received
|
||||||
|
size_t bytes_received; ///< Number of bytes received total
|
||||||
|
};
|
||||||
|
|
||||||
struct server_context
|
struct server_context
|
||||||
{
|
{
|
||||||
int *listen_fds; ///< Listening socket FD's
|
int *listen_fds; ///< Listening socket FD's
|
||||||
struct poller_fd *listen_events; ///< New connections available
|
struct poller_fd *listen_events; ///< New connections available
|
||||||
size_t n_listen_fds; ///< Number of listening sockets
|
size_t n_listen_fds; ///< Number of listening sockets
|
||||||
|
|
||||||
|
time_t started; ///< When has the server been started
|
||||||
|
|
||||||
SSL_CTX *ssl_ctx; ///< SSL context
|
SSL_CTX *ssl_ctx; ///< SSL context
|
||||||
struct client *clients; ///< Clients
|
struct client *clients; ///< Clients
|
||||||
unsigned n_clients; ///< Current number of connections
|
unsigned n_clients; ///< Current number of connections
|
||||||
|
@ -798,6 +818,7 @@ client_send_str (struct client *c, const struct str *s)
|
||||||
{
|
{
|
||||||
hard_assert (c->initialized && !c->closing_link);
|
hard_assert (c->initialized && !c->closing_link);
|
||||||
|
|
||||||
|
size_t old_sendq = c->write_buffer.len;
|
||||||
// TODO: kill the connection above some "SendQ" threshold (careful!)
|
// TODO: kill the connection above some "SendQ" threshold (careful!)
|
||||||
str_append_data (&c->write_buffer, s->str,
|
str_append_data (&c->write_buffer, s->str,
|
||||||
s->len > IRC_MAX_MESSAGE_LENGTH ? IRC_MAX_MESSAGE_LENGTH : s->len);
|
s->len > IRC_MAX_MESSAGE_LENGTH ? IRC_MAX_MESSAGE_LENGTH : s->len);
|
||||||
|
@ -805,6 +826,10 @@ client_send_str (struct client *c, const struct str *s)
|
||||||
// XXX: we might want to move this elsewhere, so that it doesn't get called
|
// XXX: we might want to move this elsewhere, so that it doesn't get called
|
||||||
// as often; it's going to cause a lot of syscalls with epoll.
|
// as often; it's going to cause a lot of syscalls with epoll.
|
||||||
client_update_poller (c, NULL);
|
client_update_poller (c, NULL);
|
||||||
|
|
||||||
|
// Technically we haven't sent it yet but that's a minor detail
|
||||||
|
c->n_sent_messages++;
|
||||||
|
c->sent_bytes += c->write_buffer.len - old_sendq;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -2821,6 +2846,77 @@ irc_handle_admin (const struct irc_message *msg, struct client *c)
|
||||||
irc_send_reply (c, IRC_ERR_NOADMININFO, c->ctx->server_name);
|
irc_send_reply (c, IRC_ERR_NOADMININFO, c->ctx->server_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
irc_handle_stats_links (struct client *c, const struct irc_message *msg)
|
||||||
|
{
|
||||||
|
// There is only an "l" query in RFC 2812 but we cannot link,
|
||||||
|
// so instead we provide the "L" query giving information for all users
|
||||||
|
const char *filter = NULL;
|
||||||
|
if (msg->params.len > 1)
|
||||||
|
filter = msg->params.vector[1];
|
||||||
|
|
||||||
|
for (struct client *iter = c->ctx->clients; iter; iter = iter->next)
|
||||||
|
{
|
||||||
|
if (filter && irc_strcmp (iter->nickname, filter))
|
||||||
|
continue;
|
||||||
|
irc_send_reply (c, IRC_RPL_STATSLINKINFO,
|
||||||
|
iter->address, // linkname
|
||||||
|
iter->write_buffer.len, // sendq
|
||||||
|
iter->n_sent_messages, iter->sent_bytes / 1024,
|
||||||
|
iter->n_received_messages, iter->received_bytes / 1024,
|
||||||
|
(long long) (time (NULL) - iter->opened));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
irc_handle_stats_commands (struct client *c)
|
||||||
|
{
|
||||||
|
struct str_map_iter iter;
|
||||||
|
str_map_iter_init (&iter, &c->ctx->handlers);
|
||||||
|
struct irc_command *handler;
|
||||||
|
while ((handler = str_map_iter_next (&iter)))
|
||||||
|
{
|
||||||
|
if (!handler->n_received)
|
||||||
|
continue;
|
||||||
|
irc_send_reply (c, IRC_RPL_STATSCOMMANDS, handler->name,
|
||||||
|
handler->n_received, handler->bytes_received, (size_t) 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
irc_handle_stats_uptime (struct client *c)
|
||||||
|
{
|
||||||
|
time_t uptime = time (NULL) - c->ctx->started;
|
||||||
|
|
||||||
|
int days = uptime / 60 / 60 / 24;
|
||||||
|
int hours = (uptime % (60 * 60 * 24)) / 60 / 60;
|
||||||
|
int mins = (uptime % (60 * 60)) / 60;
|
||||||
|
int secs = uptime % 60;
|
||||||
|
|
||||||
|
irc_send_reply (c, IRC_RPL_STATSUPTIME, days, hours, mins, secs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
irc_handle_stats (const struct irc_message *msg, struct client *c)
|
||||||
|
{
|
||||||
|
char query;
|
||||||
|
if (msg->params.len < 1 || !(query = *msg->params.vector[0]))
|
||||||
|
RETURN_WITH_REPLY (c, IRC_ERR_NEEDMOREPARAMS, msg->command);
|
||||||
|
if (msg->params.len > 1 && !irc_is_this_me (c->ctx, msg->params.vector[1]))
|
||||||
|
RETURN_WITH_REPLY (c, IRC_ERR_NOSUCHSERVER, msg->params.vector[1]);
|
||||||
|
if (!(c->mode & IRC_USER_MODE_OPERATOR))
|
||||||
|
RETURN_WITH_REPLY (c, IRC_ERR_NOPRIVILEGES);
|
||||||
|
|
||||||
|
switch (query)
|
||||||
|
{
|
||||||
|
case 'L': irc_handle_stats_links (c, msg); break;
|
||||||
|
case 'm': irc_handle_stats_commands (c); break;
|
||||||
|
case 'u': irc_handle_stats_uptime (c); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
irc_send_reply (c, IRC_RPL_ENDOFSTATS, query);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
irc_handle_kill (const struct irc_message *msg, struct client *c)
|
irc_handle_kill (const struct irc_message *msg, struct client *c)
|
||||||
{
|
{
|
||||||
|
@ -2851,56 +2947,50 @@ irc_handle_die (const struct irc_message *msg, struct client *c)
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
struct irc_command
|
|
||||||
{
|
|
||||||
const char *name;
|
|
||||||
bool requires_registration;
|
|
||||||
void (*handler) (const struct irc_message *, struct client *);
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
irc_register_handlers (struct server_context *ctx)
|
irc_register_handlers (struct server_context *ctx)
|
||||||
{
|
{
|
||||||
// TODO: add an index for IRC_ERR_NOSUCHSERVER validation?
|
// TODO: add an index for IRC_ERR_NOSUCHSERVER validation?
|
||||||
// TODO: add a minimal parameter count?
|
// TODO: add a minimal parameter count?
|
||||||
// TODO: add a field for oper-only commands?
|
// TODO: add a field for oper-only commands?
|
||||||
static const struct irc_command message_handlers[] =
|
static struct irc_command message_handlers[] =
|
||||||
{
|
{
|
||||||
{ "CAP", false, irc_handle_cap },
|
{ "CAP", false, irc_handle_cap, 0, 0 },
|
||||||
{ "PASS", false, irc_handle_pass },
|
{ "PASS", false, irc_handle_pass, 0, 0 },
|
||||||
{ "NICK", false, irc_handle_nick },
|
{ "NICK", false, irc_handle_nick, 0, 0 },
|
||||||
{ "USER", false, irc_handle_user },
|
{ "USER", false, irc_handle_user, 0, 0 },
|
||||||
|
|
||||||
{ "USERHOST", true, irc_handle_userhost },
|
{ "USERHOST", true, irc_handle_userhost, 0, 0 },
|
||||||
{ "LUSERS", true, irc_handle_lusers },
|
{ "LUSERS", true, irc_handle_lusers, 0, 0 },
|
||||||
{ "MOTD", true, irc_handle_motd },
|
{ "MOTD", true, irc_handle_motd, 0, 0 },
|
||||||
{ "PING", true, irc_handle_ping },
|
{ "PING", true, irc_handle_ping, 0, 0 },
|
||||||
{ "PONG", false, irc_handle_pong },
|
{ "PONG", false, irc_handle_pong, 0, 0 },
|
||||||
{ "QUIT", false, irc_handle_quit },
|
{ "QUIT", false, irc_handle_quit, 0, 0 },
|
||||||
{ "TIME", true, irc_handle_time },
|
{ "TIME", true, irc_handle_time, 0, 0 },
|
||||||
{ "VERSION", true, irc_handle_version },
|
{ "VERSION", true, irc_handle_version, 0, 0 },
|
||||||
{ "USERS", true, irc_handle_users },
|
{ "USERS", true, irc_handle_users, 0, 0 },
|
||||||
{ "SUMMON", true, irc_handle_summon },
|
{ "SUMMON", true, irc_handle_summon, 0, 0 },
|
||||||
{ "AWAY", true, irc_handle_away },
|
{ "AWAY", true, irc_handle_away, 0, 0 },
|
||||||
{ "ADMIN", true, irc_handle_admin },
|
{ "ADMIN", true, irc_handle_admin, 0, 0 },
|
||||||
|
{ "STATS", true, irc_handle_stats, 0, 0 },
|
||||||
|
|
||||||
{ "MODE", true, irc_handle_mode },
|
{ "MODE", true, irc_handle_mode, 0, 0 },
|
||||||
{ "PRIVMSG", true, irc_handle_privmsg },
|
{ "PRIVMSG", true, irc_handle_privmsg, 0, 0 },
|
||||||
{ "NOTICE", true, irc_handle_notice },
|
{ "NOTICE", true, irc_handle_notice, 0, 0 },
|
||||||
{ "JOIN", true, irc_handle_join },
|
{ "JOIN", true, irc_handle_join, 0, 0 },
|
||||||
{ "PART", true, irc_handle_part },
|
{ "PART", true, irc_handle_part, 0, 0 },
|
||||||
{ "KICK", true, irc_handle_kick },
|
{ "KICK", true, irc_handle_kick, 0, 0 },
|
||||||
{ "INVITE", true, irc_handle_invite },
|
{ "INVITE", true, irc_handle_invite, 0, 0 },
|
||||||
{ "TOPIC", true, irc_handle_topic },
|
{ "TOPIC", true, irc_handle_topic, 0, 0 },
|
||||||
{ "LIST", true, irc_handle_list },
|
{ "LIST", true, irc_handle_list, 0, 0 },
|
||||||
{ "NAMES", true, irc_handle_names },
|
{ "NAMES", true, irc_handle_names, 0, 0 },
|
||||||
{ "WHO", true, irc_handle_who },
|
{ "WHO", true, irc_handle_who, 0, 0 },
|
||||||
{ "WHOIS", true, irc_handle_whois },
|
{ "WHOIS", true, irc_handle_whois, 0, 0 },
|
||||||
{ "WHOWAS", true, irc_handle_whowas },
|
{ "WHOWAS", true, irc_handle_whowas, 0, 0 },
|
||||||
{ "ISON", true, irc_handle_ison },
|
{ "ISON", true, irc_handle_ison, 0, 0 },
|
||||||
|
|
||||||
{ "KILL", true, irc_handle_kill },
|
{ "KILL", true, irc_handle_kill, 0, 0 },
|
||||||
{ "DIE", true, irc_handle_die },
|
{ "DIE", true, irc_handle_die, 0, 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
for (size_t i = 0; i < N_ELEMENTS (message_handlers); i++)
|
for (size_t i = 0; i < N_ELEMENTS (message_handlers); i++)
|
||||||
|
@ -2920,6 +3010,9 @@ irc_process_message (const struct irc_message *msg,
|
||||||
if (c->closing_link)
|
if (c->closing_link)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
c->n_received_messages++;
|
||||||
|
c->received_bytes += strlen (raw) + 2;
|
||||||
|
|
||||||
if (!flood_detector_check (&c->antiflood))
|
if (!flood_detector_check (&c->antiflood))
|
||||||
{
|
{
|
||||||
client_close_link (c, "Excess flood");
|
client_close_link (c, "Excess flood");
|
||||||
|
@ -2929,11 +3022,17 @@ irc_process_message (const struct irc_message *msg,
|
||||||
struct irc_command *cmd = str_map_find (&c->ctx->handlers, msg->command);
|
struct irc_command *cmd = str_map_find (&c->ctx->handlers, msg->command);
|
||||||
if (!cmd)
|
if (!cmd)
|
||||||
irc_send_reply (c, IRC_ERR_UNKNOWNCOMMAND, msg->command);
|
irc_send_reply (c, IRC_ERR_UNKNOWNCOMMAND, msg->command);
|
||||||
else if (cmd->requires_registration && !c->registered)
|
else
|
||||||
|
{
|
||||||
|
cmd->n_received++;
|
||||||
|
cmd->bytes_received += strlen (raw) + 2;
|
||||||
|
|
||||||
|
if (cmd->requires_registration && !c->registered)
|
||||||
irc_send_reply (c, IRC_ERR_NOTREGISTERED);
|
irc_send_reply (c, IRC_ERR_NOTREGISTERED);
|
||||||
else
|
else
|
||||||
cmd->handler (msg, c);
|
cmd->handler (msg, c);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- Network I/O -------------------------------------------------------------
|
// --- Network I/O -------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -3263,6 +3362,7 @@ irc_try_fetch_client (struct server_context *ctx, int listen_fd)
|
||||||
struct client *c = xmalloc (sizeof *c);
|
struct client *c = xmalloc (sizeof *c);
|
||||||
client_init (c);
|
client_init (c);
|
||||||
c->ctx = ctx;
|
c->ctx = ctx;
|
||||||
|
c->opened = time (NULL);
|
||||||
c->socket_fd = fd;
|
c->socket_fd = fd;
|
||||||
c->hostname = xstrdup (host);
|
c->hostname = xstrdup (host);
|
||||||
c->address = address;
|
c->address = address;
|
||||||
|
@ -3741,6 +3841,7 @@ main (int argc, char *argv[])
|
||||||
|
|
||||||
struct server_context ctx;
|
struct server_context ctx;
|
||||||
server_context_init (&ctx);
|
server_context_init (&ctx);
|
||||||
|
ctx.started = time (NULL);
|
||||||
irc_register_handlers (&ctx);
|
irc_register_handlers (&ctx);
|
||||||
irc_register_cap_handlers (&ctx);
|
irc_register_cap_handlers (&ctx);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue