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"
|
||||
4 IRC_RPL_MYINFO "%s %s %s %s"
|
||||
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"
|
||||
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"
|
||||
252 IRC_RPL_LUSEROP "%d :operator(s) online"
|
||||
253 IRC_RPL_LUSERUNKNOWN "%d :unknown connection(s)"
|
||||
|
|
187
kike.c
187
kike.c
|
@ -25,6 +25,8 @@
|
|||
#include "kike-replies.c"
|
||||
#include <nl_types.h>
|
||||
|
||||
// FIXME: don't use time_t to compute time deltas
|
||||
|
||||
// --- Configuration (application-specific) ------------------------------------
|
||||
|
||||
static struct config_item g_config_table[] =
|
||||
|
@ -306,6 +308,12 @@ struct client
|
|||
LIST_HEADER (struct client)
|
||||
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
|
||||
struct str read_buffer; ///< Unprocessed input
|
||||
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
|
||||
{
|
||||
int *listen_fds; ///< Listening socket FD's
|
||||
struct poller_fd *listen_events; ///< New connections available
|
||||
size_t n_listen_fds; ///< Number of listening sockets
|
||||
|
||||
time_t started; ///< When has the server been started
|
||||
|
||||
SSL_CTX *ssl_ctx; ///< SSL context
|
||||
struct client *clients; ///< Clients
|
||||
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);
|
||||
|
||||
size_t old_sendq = c->write_buffer.len;
|
||||
// TODO: kill the connection above some "SendQ" threshold (careful!)
|
||||
str_append_data (&c->write_buffer, s->str,
|
||||
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
|
||||
// as often; it's going to cause a lot of syscalls with epoll.
|
||||
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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
irc_register_handlers (struct server_context *ctx)
|
||||
{
|
||||
// TODO: add an index for IRC_ERR_NOSUCHSERVER validation?
|
||||
// TODO: add a minimal parameter count?
|
||||
// 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 },
|
||||
{ "PASS", false, irc_handle_pass },
|
||||
{ "NICK", false, irc_handle_nick },
|
||||
{ "USER", false, irc_handle_user },
|
||||
{ "CAP", false, irc_handle_cap, 0, 0 },
|
||||
{ "PASS", false, irc_handle_pass, 0, 0 },
|
||||
{ "NICK", false, irc_handle_nick, 0, 0 },
|
||||
{ "USER", false, irc_handle_user, 0, 0 },
|
||||
|
||||
{ "USERHOST", true, irc_handle_userhost },
|
||||
{ "LUSERS", true, irc_handle_lusers },
|
||||
{ "MOTD", true, irc_handle_motd },
|
||||
{ "PING", true, irc_handle_ping },
|
||||
{ "PONG", false, irc_handle_pong },
|
||||
{ "QUIT", false, irc_handle_quit },
|
||||
{ "TIME", true, irc_handle_time },
|
||||
{ "VERSION", true, irc_handle_version },
|
||||
{ "USERS", true, irc_handle_users },
|
||||
{ "SUMMON", true, irc_handle_summon },
|
||||
{ "AWAY", true, irc_handle_away },
|
||||
{ "ADMIN", true, irc_handle_admin },
|
||||
{ "USERHOST", true, irc_handle_userhost, 0, 0 },
|
||||
{ "LUSERS", true, irc_handle_lusers, 0, 0 },
|
||||
{ "MOTD", true, irc_handle_motd, 0, 0 },
|
||||
{ "PING", true, irc_handle_ping, 0, 0 },
|
||||
{ "PONG", false, irc_handle_pong, 0, 0 },
|
||||
{ "QUIT", false, irc_handle_quit, 0, 0 },
|
||||
{ "TIME", true, irc_handle_time, 0, 0 },
|
||||
{ "VERSION", true, irc_handle_version, 0, 0 },
|
||||
{ "USERS", true, irc_handle_users, 0, 0 },
|
||||
{ "SUMMON", true, irc_handle_summon, 0, 0 },
|
||||
{ "AWAY", true, irc_handle_away, 0, 0 },
|
||||
{ "ADMIN", true, irc_handle_admin, 0, 0 },
|
||||
{ "STATS", true, irc_handle_stats, 0, 0 },
|
||||
|
||||
{ "MODE", true, irc_handle_mode },
|
||||
{ "PRIVMSG", true, irc_handle_privmsg },
|
||||
{ "NOTICE", true, irc_handle_notice },
|
||||
{ "JOIN", true, irc_handle_join },
|
||||
{ "PART", true, irc_handle_part },
|
||||
{ "KICK", true, irc_handle_kick },
|
||||
{ "INVITE", true, irc_handle_invite },
|
||||
{ "TOPIC", true, irc_handle_topic },
|
||||
{ "LIST", true, irc_handle_list },
|
||||
{ "NAMES", true, irc_handle_names },
|
||||
{ "WHO", true, irc_handle_who },
|
||||
{ "WHOIS", true, irc_handle_whois },
|
||||
{ "WHOWAS", true, irc_handle_whowas },
|
||||
{ "ISON", true, irc_handle_ison },
|
||||
{ "MODE", true, irc_handle_mode, 0, 0 },
|
||||
{ "PRIVMSG", true, irc_handle_privmsg, 0, 0 },
|
||||
{ "NOTICE", true, irc_handle_notice, 0, 0 },
|
||||
{ "JOIN", true, irc_handle_join, 0, 0 },
|
||||
{ "PART", true, irc_handle_part, 0, 0 },
|
||||
{ "KICK", true, irc_handle_kick, 0, 0 },
|
||||
{ "INVITE", true, irc_handle_invite, 0, 0 },
|
||||
{ "TOPIC", true, irc_handle_topic, 0, 0 },
|
||||
{ "LIST", true, irc_handle_list, 0, 0 },
|
||||
{ "NAMES", true, irc_handle_names, 0, 0 },
|
||||
{ "WHO", true, irc_handle_who, 0, 0 },
|
||||
{ "WHOIS", true, irc_handle_whois, 0, 0 },
|
||||
{ "WHOWAS", true, irc_handle_whowas, 0, 0 },
|
||||
{ "ISON", true, irc_handle_ison, 0, 0 },
|
||||
|
||||
{ "KILL", true, irc_handle_kill },
|
||||
{ "DIE", true, irc_handle_die },
|
||||
{ "KILL", true, irc_handle_kill, 0, 0 },
|
||||
{ "DIE", true, irc_handle_die, 0, 0 },
|
||||
};
|
||||
|
||||
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)
|
||||
return;
|
||||
|
||||
c->n_received_messages++;
|
||||
c->received_bytes += strlen (raw) + 2;
|
||||
|
||||
if (!flood_detector_check (&c->antiflood))
|
||||
{
|
||||
client_close_link (c, "Excess flood");
|
||||
|
@ -2929,10 +3022,16 @@ irc_process_message (const struct irc_message *msg,
|
|||
struct irc_command *cmd = str_map_find (&c->ctx->handlers, msg->command);
|
||||
if (!cmd)
|
||||
irc_send_reply (c, IRC_ERR_UNKNOWNCOMMAND, msg->command);
|
||||
else if (cmd->requires_registration && !c->registered)
|
||||
irc_send_reply (c, IRC_ERR_NOTREGISTERED);
|
||||
else
|
||||
cmd->handler (msg, c);
|
||||
{
|
||||
cmd->n_received++;
|
||||
cmd->bytes_received += strlen (raw) + 2;
|
||||
|
||||
if (cmd->requires_registration && !c->registered)
|
||||
irc_send_reply (c, IRC_ERR_NOTREGISTERED);
|
||||
else
|
||||
cmd->handler (msg, c);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Network I/O -------------------------------------------------------------
|
||||
|
@ -3263,6 +3362,7 @@ irc_try_fetch_client (struct server_context *ctx, int listen_fd)
|
|||
struct client *c = xmalloc (sizeof *c);
|
||||
client_init (c);
|
||||
c->ctx = ctx;
|
||||
c->opened = time (NULL);
|
||||
c->socket_fd = fd;
|
||||
c->hostname = xstrdup (host);
|
||||
c->address = address;
|
||||
|
@ -3741,6 +3841,7 @@ main (int argc, char *argv[])
|
|||
|
||||
struct server_context ctx;
|
||||
server_context_init (&ctx);
|
||||
ctx.started = time (NULL);
|
||||
irc_register_handlers (&ctx);
|
||||
irc_register_cap_handlers (&ctx);
|
||||
|
||||
|
|
Loading…
Reference in New Issue