*/
#include "config.h"
-#include "s_stats.h"
#include "class.h"
#include "client.h"
+#include "gline.h"
#include "ircd.h"
#include "ircd_chattr.h"
+#include "ircd_events.h"
+#include "ircd_features.h"
#include "ircd_log.h"
#include "ircd_reply.h"
#include "ircd_string.h"
#include "listener.h"
+#include "list.h"
#include "match.h"
+#include "motd.h"
#include "msg.h"
+#include "msgq.h"
#include "numeric.h"
#include "numnicks.h"
+#include "s_bsd.h"
#include "s_conf.h"
+#include "s_debug.h"
+#include "s_misc.h"
+#include "s_serv.h"
+#include "s_stats.h"
#include "s_user.h"
#include "send.h"
#include "struct.h"
+#include "userload.h"
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/time.h>
* it--not reversed as in ircd.conf!
*/
-/*
- * Help info displayed when user provides no stats parameter. --Gte
- */
-const char *statsinfo[] = {
- "The following statistics are available:",
- "U - Service server & nick jupes information.",
- "u - Current uptime & highest connection count.",
- "p - Listening ports.",
- "i - Connection authorisation lines.",
- "y - Connection classes.",
- "c - Remote server connection lines.",
- "h - Hubs information.",
- "d - Dynamic routing configuration.",
- "l - Current connections information.",
- "g - Global bans (G-lines).",
- "k - Local bans (K-Lines).",
- "o - Operator information.",
- "e - Report server event loop engine.",
- "f - Feature settings.",
- "m - Message usage information.",
- "t - Local connection statistics (Total SND/RCV, etc).",
- "v - Server list with verbose information.",
- "w - Userload statistics.",
- "M - Memory allocation & leak monitoring.",
- "z - Memory/Structure allocation information.",
- "r - System resource usage (Debug only).",
- "x - List usage information (Debug only).",
- 0,
-};
-
static unsigned int report_array[17][3] = {
{CONF_SERVER, RPL_STATSCLINE, 'C'},
{CONF_CLIENT, RPL_STATSILINE, 'I'},
{0, 0}
};
-void report_configured_links(struct Client *sptr, int mask)
+static void
+stats_configured_links(struct Client *sptr, struct StatDesc* sd, int stat,
+ char* param)
{
static char null[] = "<NULL>";
struct ConfItem *tmp;
+ int mask;
unsigned int *p;
unsigned short int port;
char c, *host, *pass, *name;
+ mask = sd->sd_funcdata;
for (tmp = GlobalConfList; tmp; tmp = tmp->next)
{
}
}
-/*
- * {CONF_TLINES, RPL_STATSTLINE, 'T'},
- *
- * see now motd_report() in motd.c
- */
-/* void report_motd_list(struct Client* to) */
-/* { */
-/* const struct MotdConf* conf = conf_get_motd_list(); */
-/* for ( ; conf; conf = conf->next) */
-/* send_reply(to, RPL_STATSTLINE, 'T', conf->hostmask, conf->path); */
-/* } */
-
/*
* {CONF_CRULEALL, RPL_STATSDLINE, 'D'},
* {CONF_CRULEAUTO, RPL_STATSDLINE, 'd'},
*/
-void report_crule_list(struct Client* to, int mask)
+static void
+stats_crule_list(struct Client* to, struct StatDesc *sd, int stat,
+ char *param)
{
+ int mask;
const struct CRuleConf* p = conf_get_crule_list();
- for ( ; p; p = p->next) {
- if (0 != (p->type & mask))
- send_reply(to, RPL_STATSDLINE, (CRULE_ALL == p->type) ? 'D' : 'd', p->hostmask, p->rule);
+
+ mask = (stat == 'D') ? CRULE_ALL : CRULE_MASK;
+
+ for ( ; p; p = p->next)
+ {
+ if (p->type & mask)
+ send_reply(to, RPL_STATSDLINE, stat, p->hostmask, p->rule);
+ }
+}
+
+static void
+stats_engine(struct Client *to, struct StatDesc *sd, int stat, char *param)
+{
+ send_reply(to, RPL_STATSENGINE, engine_name());
+}
+
+static void
+stats_access(struct Client *to, struct StatDesc *sd, int stat, char *param)
+{
+ struct ConfItem *aconf;
+ int wilds = 0;
+ int count = 1000;
+
+ if (!param)
+ {
+ stats_configured_links(to, sd, stat, param);
+ return;
+ }
+
+ wilds = string_has_wildcards(param);
+
+ for (aconf = GlobalConfList; aconf; aconf = aconf->next)
+ {
+ if (aconf->status != CONF_CLIENT)
+ continue;
+ if ((!wilds && (!match(aconf->host, param) ||
+ !match(aconf->name, param))) ||
+ (wilds && (!mmatch(param, aconf->host) ||
+ !mmatch(param, aconf->name))))
+ {
+ send_reply(to, RPL_STATSILINE, 'I', aconf->host, aconf->name,
+ aconf->port, get_conf_class(aconf));
+ if (--count == 0)
+ break;
+ }
}
}
+
+
/*
* {CONF_KILL, RPL_STATSKLINE, 'K'},
* {CONF_IPKILL, RPL_STATSKLINE, 'k'},
*/
-void report_deny_list(struct Client* to)
+static void
+report_deny_list(struct Client* to)
{
const struct DenyConf* p = conf_get_deny_list();
for ( ; p; p = p->next)
p->hostmask, p->message, p->usermask);
}
-/* m_stats is so obnoxiously full of special cases that the different
- * hunt_server() possiblites were becoming very messy. It now uses a
- * switch() so as to be easier to read and update as params change.
- * -Ghostwolf
- *
- * 2.10.11: Don't check for the oper limitation if it's not our local server.
- * thusly once all the hubs have upgraded local opers will be able
- * to remote stats anywhere on the network.
- */
-int hunt_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[], char stat, int MustBeOper)
+static void
+stats_klines(struct Client *sptr, struct StatDesc *sd, int stat, char *mask)
{
- switch (stat)
+ int wilds = 0;
+ int count = 3;
+ int limit_query = 0;
+ char *user = 0;
+ char *host;
+ const struct DenyConf* conf;
+
+ if (!IsAnOper(sptr))
+ limit_query = 1;
+
+ if (!mask)
+ {
+ if (limit_query)
+ need_more_params(sptr, "STATS K");
+ else
+ report_deny_list(sptr);
+ return;
+ }
+
+ if (!limit_query)
+ {
+ wilds = string_has_wildcards(mask);
+ count = 1000;
+ }
+ if ((host = strchr(mask, '@')))
{
- /* open to all, standard # of params */
- case 'U':
- case 'u':
- case 'F':
- case 'f':
- return hunt_server_cmd(sptr, CMD_STATS, cptr, MustBeOper, "%s :%C", 2,
- parc, parv);
-
- /* open to all, varying # of params */
- case 'k':
- case 'K':
- case 'i':
- case 'I':
- case 'p':
- case 'P':
+ user = mask;
+ *host++ = '\0';
+ }
+ else
+ host = mask;
+
+ for (conf = conf_get_deny_list(); conf; conf = conf->next)
+ {
+ if ((!wilds && ((user || conf->hostmask) &&
+ !match(conf->hostmask, host) &&
+ (!user || !match(conf->usermask, user)))) ||
+ (wilds && !mmatch(host, conf->hostmask) &&
+ (!user || !mmatch(user, conf->usermask))))
{
- if (parc > 3)
- return hunt_server_cmd(sptr, CMD_STATS, cptr, MustBeOper, "%s %C :%s",
- 2, parc, parv);
- else
- return hunt_server_cmd(sptr, CMD_STATS, cptr, MustBeOper, "%s :%C", 2,
- parc, parv);
+ send_reply(sptr, RPL_STATSKLINE,
+ (conf->flags & DENY_FLAGS_IP) ? 'k' : 'K',
+ conf->hostmask, conf->message, conf->usermask);
+ if (--count == 0)
+ return;
}
+ }
+}
+
+static void
+stats_links(struct Client* sptr, struct StatDesc* sd, int stat, char* name)
+{
+ struct Client *acptr;
+ int i;
+ int wilds = 0;
- /* oper only, varying # of params */
- case 'l':
- case 'L':
- case 'M':
+ if (name)
+ wilds = string_has_wildcards(name);
+
+ /*
+ * Send info about connections which match, or all if the
+ * mask matches me.name. Only restrictions are on those who
+ * are invisible not being visible to 'foreigners' who use
+ * a wild card based search to list it.
+ */
+ send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO, "Connection SendQ "
+ "SendM SendKBytes RcveM RcveKBytes :Open since");
+ for (i = 0; i <= HighestFd; i++)
{
- if (parc == 4)
- return hunt_server_cmd(sptr, CMD_STATS, cptr,
- MyUser(sptr) ? 1 : MustBeOper, "%s %C :%s", 2,
- parc, parv);
- else if (parc > 4)
- return hunt_server_cmd(sptr, CMD_STATS, cptr,
- MyUser(sptr) ? 1 : MustBeOper, "%s %C %s :%s",
- 2, parc, parv);
- else
- return hunt_server_cmd(sptr, CMD_STATS, cptr,
- MyUser(sptr) ? 1 : MustBeOper, "%s :%C", 2,
- parc, parv);
+ if (!(acptr = LocalClientArray[i]))
+ continue;
+ /* Don't return clients when this is a request for `all' */
+ if (!name && IsUser(acptr))
+ continue;
+ /* Don't show invisible people to non opers unless they know the nick */
+ if (IsInvisible(acptr) && (!name || wilds) && !IsAnOper(acptr) &&
+ (acptr != sptr))
+ continue;
+ /* Only show the ones that match the given mask - if any */
+ if (name && wilds && match(name, cli_name(acptr)))
+ continue;
+ /* Skip all that do not match the specific query */
+ if (!(!name || wilds) && 0 != ircd_strcmp(name, cli_name(acptr)))
+ continue;
+ send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO,
+ "%s %u %u %u %u %u :%Tu",
+ (*(cli_name(acptr))) ? cli_name(acptr) : "<unregistered>",
+ (int)MsgQLength(&(cli_sendQ(acptr))), (int)cli_sendM(acptr),
+ (int)cli_sendK(acptr), (int)cli_receiveM(acptr),
+ (int)cli_receiveK(acptr), CurrentTime - cli_firsttime(acptr));
}
+}
+
+static void
+stats_commands(struct Client* to, struct StatDesc* sd, int stat, char* param)
+{
+ struct Message *mptr;
+
+ for (mptr = msgtab; mptr->cmd; mptr++)
+ if (mptr->count)
+ send_reply(to, RPL_STATSCOMMANDS, mptr->cmd, mptr->count, mptr->bytes);
+}
+
+static void
+stats_quarantine(struct Client* to, struct StatDesc* sd, int stat, char* param)
+{
+ struct qline *qline;
+
+ for (qline = GlobalQuarantineList; qline; qline = qline->next)
+ {
+ if (param && match(param, qline->chname)) /* narrow search */
+ continue;
+ send_reply(to, RPL_STATSQLINE, qline->chname, qline->reason);
+ }
+ }
+
+static void
+stats_uptime(struct Client* to, struct StatDesc* sd, int stat, char* param)
+{
+ time_t nowr;
+
+ nowr = CurrentTime - cli_since(&me);
+ send_reply(to, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
+ (nowr / 60) % 60, nowr % 60);
+ send_reply(to, RPL_STATSCONN, max_connection_count, max_client_count);
+}
+
+static void
+stats_servers_verbose(struct Client* sptr, struct StatDesc* sd, int stat,
+ char* param)
+{
+ struct Client *acptr;
+
+ /*
+ * lowercase 'v' is for human-readable,
+ * uppercase 'V' is for machine-readable
+ */
+ if (stat == 'v')
+ send_reply(sptr, SND_EXPLICIT | RPL_STATSVERBOSE,
+ "%-20s %-20s Flags Hops Numeric Lag RTT Up Down "
+ "Clients/Max Proto %-10s :Info", "Servername", "Uplink",
+ "LinkTS");
- /* oper only, standard # of params */
- default:
- return hunt_server_cmd(sptr, CMD_STATS, cptr,
- MyUser(sptr) ? 1 : MustBeOper, "%s :%C", 2, parc,
- parv);
+ for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr))
+ {
+ if (!IsServer(acptr) && !IsMe(acptr))
+ continue;
+ /* narrow search */
+ if (param && match(param, cli_name(acptr)))
+ continue;
+ send_reply(sptr, SND_EXPLICIT | RPL_STATSVERBOSE, stat == 'v' ?
+ "%-20s %-20s %c%c%c%c %4i %s %-4i %5i %4i %4i %4i %5i %5i "
+ "P%-2i %Tu :%s" :
+ "%s %s %c%c%c%c %i %s %i %i %i %i %i %i %i P%i %Tu :%s",
+ cli_name(acptr),
+ cli_name(cli_serv(acptr)->up),
+ IsBurst(acptr) ? 'B' : '-',
+ IsBurstAck(acptr) ? 'A' : '-',
+ IsHub(acptr) ? 'H' : '-',
+ IsService(acptr) ? 'S' : '-',
+ cli_hopcount(acptr),
+ NumServ(acptr),
+ base64toint(cli_yxx(acptr)),
+ cli_serv(acptr)->lag,
+ cli_serv(acptr)->asll_rtt,
+ cli_serv(acptr)->asll_to,
+ cli_serv(acptr)->asll_from,
+ cli_serv(acptr)->clients,
+ cli_serv(acptr)->nn_mask,
+ cli_serv(acptr)->prot,
+ cli_serv(acptr)->timestamp,
+ cli_info(acptr));
}
}
+#ifdef DEBUGMODE
+static void
+stats_meminfo(struct Client* to, struct StatDesc* sd, int stat, char* param)
+{
+ class_send_meminfo(to);
+ send_listinfo(to, 0);
+}
+#endif
+
+static void
+stats_help(struct Client* to, struct StatDesc* sd, int stat, char* param)
+{
+ struct StatDesc *asd;
+
+ /* only if it's my user */
+ if (MyUser(to))
+ for (asd = statsinfo; asd->sd_c; asd++)
+ if (asd->sd_c != sd->sd_c) /* don't send the help for us */
+ sendcmdto_one(&me, CMD_NOTICE, to, "%C :%c - %s", to, asd->sd_c,
+ asd->sd_desc);
+}
+
+/* This array of structures contains information about all single-character
+ * stats. Struct StatDesc is defined in s_stats.h.
+ */
+struct StatDesc statsinfo[] = {
+ { 'c', STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_c,
+ stats_configured_links, CONF_SERVER,
+ "Remote server connection lines." },
+ { 'd', STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_d,
+ stats_crule_list, 0,
+ "Dynamic routing configuration." },
+ { 'e', STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_e,
+ stats_engine, 0,
+ "Report server event loop engine." },
+ { 'f', STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_f,
+ feature_report, 0,
+ "Feature settings." },
+ { 'g', STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_g,
+ gline_stats, 0,
+ "Global bans (G-lines)." },
+ { 'h', STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_h,
+ stats_configured_links, (CONF_HUB | CONF_LEAF),
+ "Hubs information." },
+ { 'i', (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM), FEAT_HIS_STATS_i,
+ stats_access, CONF_CLIENT,
+ "Connection authorization lines." },
+ { 'j', STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_j,
+ msgq_histogram, 0,
+ "Message length histogram." },
+ { 'k', (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM), FEAT_HIS_STATS_k,
+ stats_klines, 0,
+ "Local bans (K-Lines)." },
+ { 'l', (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM), FEAT_HIS_STATS_l,
+ stats_links, 0,
+ "Current connections information." },
+#if 0
+ { 'M', (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_M,
+ stats_memtotal, 0,
+ "Memory allocation & leak monitoring." },
+#endif
+ { 'm', (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_m,
+ stats_commands, 0,
+ "Message usage information." },
+ { 'o', STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_o,
+ stats_configured_links, CONF_OPS,
+ "Operator information." },
+ { 'p', (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM), FEAT_HIS_STATS_p,
+ show_ports, 0,
+ "Listening ports." },
+ { 'q', (STAT_FLAG_OPERONLY | STAT_FLAG_VARPARAM), FEAT_HIS_STATS_q,
+ stats_quarantine, 0,
+ "Quarantined channels list." },
+#ifdef DEBUGMODE
+ { 'r', STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_r,
+ send_usage, 0,
+ "System resource usage (Debug only)." },
+#endif
+ { 'T', (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_T,
+ motd_report, 0,
+ "Configured Message Of The Day files." },
+ { 't', (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_t,
+ tstats, 0,
+ "Local connection statistics (Total SND/RCV, etc)." },
+ { 'U', (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_U,
+ stats_configured_links, CONF_UWORLD,
+ "Service server & nick jupes information." },
+ { 'u', (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_u,
+ stats_uptime, 0,
+ "Current uptime & highest connection count." },
+ { 'v', (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM), FEAT_HIS_STATS_v,
+ stats_servers_verbose, 0,
+ "Verbose server information." },
+ { 'w', STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_w,
+ calc_load, 0,
+ "Userload statistics." },
+#ifdef DEBUGMODE
+ { 'x', STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_x,
+ stats_meminfo, 0,
+ "List usage information (Debug only)." },
+#endif
+ { 'y', STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_y,
+ report_classes, 0,
+ "Connection classes." },
+ { 'z', STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_z,
+ count_memory, 0,
+ "Memory/Structure allocation information." },
+ { '*', (STAT_FLAG_CASESENS | STAT_FLAG_VARPARAM), FEAT_LAST_F,
+ stats_help, 0,
+ "Send help for stats." },
+ { '\0', 0, FEAT_LAST_F, 0, 0, 0 }
+};
+
+/* This array is for mapping from characters to statistics descriptors */
+struct StatDesc *statsmap[256];
+
+/* Function to build the statsmap from the statsinfo array */
+void
+stats_init(void)
+{
+ struct StatDesc *sd;
+ int i;
+
+ /* Make darn sure the statsmap array is initialized to all zeros */
+ for (i = 1; i < 256; i++)
+ statsmap[i] = 0;
+
+ /* Build the mapping */
+ for (sd = statsinfo; sd->sd_c; sd++)
+ {
+ if (sd->sd_flags & STAT_FLAG_CASESENS)
+ /* case sensitive character... */
+ statsmap[(int)sd->sd_c] = sd;
+ else
+ {
+ /* case insensitive--make sure to put in two entries */
+ statsmap[(int)ToLower((int)sd->sd_c)] = sd;
+ statsmap[(int)ToUpper((int)sd->sd_c)] = sd;
+ }
+ }
+}