From ff10e1b652c1cf1eed68dc40b5ca33dcfe16e762 Mon Sep 17 00:00:00 2001
From: Přemysl Janouch
Date: Sat, 13 Jun 2015 21:22:57 +0200
Subject: kike: implement STATS
---
kike-replies | 4 ++
kike.c | 195 +++++++++++++++++++++++++++++++++++++++++++++--------------
2 files changed, 152 insertions(+), 47 deletions(-)
diff --git a/kike-replies b/kike-replies
index ed26232..0011120 100644
--- a/kike-replies
+++ b/kike-replies
@@ -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)"
diff --git a/kike.c b/kike.c
index 1e3fb9b..08b015b 100644
--- a/kike.c
+++ b/kike.c
@@ -25,6 +25,8 @@
#include "kike-replies.c"
#include
+// 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[] =
- {
- { "CAP", false, irc_handle_cap },
- { "PASS", false, irc_handle_pass },
- { "NICK", false, irc_handle_nick },
- { "USER", false, irc_handle_user },
-
- { "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 },
-
- { "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 },
-
- { "KILL", true, irc_handle_kill },
- { "DIE", true, irc_handle_die },
+ static struct irc_command message_handlers[] =
+ {
+ { "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, 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, 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, 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);
--
cgit v1.2.3-70-g09d2