*/
#include "config.h"
-#include "handlers.h"
-/*
- * XXX - ack!!!
- */
-#include "s_stats.h"
-#include "channel.h"
-#include "class.h"
#include "client.h"
-#include "gline.h"
-#include "hash.h"
#include "ircd.h"
-#include "ircd_alloc.h"
-#include "ircd_chattr.h"
-#include "ircd_events.h"
#include "ircd_features.h"
-#include "ircd_policy.h"
+#include "ircd_log.h"
#include "ircd_reply.h"
#include "ircd_string.h"
-#include "list.h"
-#include "listener.h"
-#include "match.h"
-#include "motd.h"
#include "msg.h"
#include "numeric.h"
-#include "numnicks.h"
-#include "opercmds.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 <assert.h>
+/* #include <assert.h> -- Now using assert in ircd_log.h */
#include <stdlib.h>
#include <string.h>
-
-int report_klines(struct Client* sptr, char* mask, int limit_query)
-{
- int wilds = 0;
- int count = 3;
- char* user = 0;
- char* host;
- const struct DenyConf* conf;
-
- if (EmptyString(mask)) {
- if (limit_query)
- return need_more_params(sptr, "STATS K");
- else
- report_deny_list(sptr);
- return 1;
- }
-
- if (!limit_query) {
- wilds = string_has_wildcards(mask);
- count = 1000;
- }
-
- if ((host = strchr(mask, '@'))) {
- 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))))
- {
- send_reply(sptr, RPL_STATSKLINE,
- (conf->flags & DENY_FLAGS_IP) ? 'k' : 'K',
- conf->hostmask, conf->message, conf->usermask);
- if (--count == 0)
- return 1;
- }
- }
- /* send_reply(sptr, RPL_ENDOFSTATS, stat); */
- return 1;
-}
-
-static int report_servers_verbose(struct Client *sptr, char stat)
-{
- struct Client *acptr;
- int i;
-
- /* lowercase 'v' is for human-readable,
- * uppercase 'V' is for machine-readable */
- if (stat == 'v')
- send_reply(sptr, SND_EXPLICIT | RPL_STATSVERBOSE,
- "%-20s Burst Hops Numeric Lag Clients/Max Proto "
- "%-10s :Info", "Servername", "LinkTS");
- for (i = -1; i <= HighestFd; i++) {
- if (i == -1)
- /* Hack 'me' in the list as well */
- acptr = &me;
- else if (!(acptr = LocalClientArray[i]) || !IsServer(acptr))
- continue;
- send_reply(sptr, SND_EXPLICIT | RPL_STATSVERBOSE, stat == 'v' ?
- "%-20s %c%c %4i %s %-4i %5i %5i %5i P%-2i %Tu :%s" :
- "%s %c%c %i %s %i %i %i %i P%i %Tu :%s",
- cli_name(acptr),
- IsBurst(acptr) ? 'B' : '-',
- IsBurstAck(acptr) ? 'A' : '-',
- cli_hopcount(acptr),
- NumServ(acptr),
- base64toint(cli_yxx(acptr)),
- cli_serv(acptr)->lag,
- cli_serv(acptr)->clients,
- cli_serv(acptr)->nn_mask,
- cli_serv(acptr)->prot,
- cli_serv(acptr)->timestamp,
- cli_info(acptr));
- }
- return 0;
-}
-
-
/*
* m_stats - generic message handler
*
* parv[0] = sender prefix
- * parv[1] = statistics selector (defaults to Message frequency)
- * parv[2] = target server (current server defaulted, if omitted)
- * And 'stats l' and 'stats' L:
- * parv[3] = server mask ("*" defaulted, if omitted)
- * Or for stats p,P:
- * parv[3] = port mask (returns p-lines when its port is matched by this)
- * Or for stats k,K,i and I:
- * parv[3] = [user@]host.name (returns which K/I-lines match this)
- * or [user@]host.mask (returns which K/I-lines are mmatched by this)
- * (defaults to old reply if ommitted, when local or Oper)
- * A remote mask (something containing wildcards) is only
- * allowed for IRC Operators.
- * Or for stats M:
- * parv[3] = time param
- * parv[4] = time param
- * (see report_memleak_stats() in runmalloc.c for details)
- *
- * This function is getting really ugly. -Ghostwolf
- */
-int m_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
-{
- char stat = parc > 1 ? parv[1][0] : '\0';
- const char **infotext = statsinfo;
-
- if (hunt_stats(cptr, sptr, parc, parv, stat, HEAD_IN_SAND_REMOTE)
- != HUNTED_ISME)
- return 0;
-
- switch (stat)
- {
- case 'L':
- case 'l':
-#ifdef HEAD_IN_SAND_STATS_L
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- {
- struct Client *acptr;
- int i;
- int doall = 0;
- int wilds = 0;
- char *name = "*";
-
- if (parc > 3 && !EmptyString(parv[3])) {
- name = parv[3];
- wilds = string_has_wildcards(name);
- }
- else
- doall = 1;
- /*
- * 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 (!(acptr = LocalClientArray[i]))
- continue;
- /* Don't return clients when this is a request for `all' */
- if (doall && IsUser(acptr))
- continue;
- /* Don't show invisible people to non opers unless they know the nick */
- if (IsInvisible(acptr) && (doall || wilds) && !IsAnOper(acptr) && (acptr != sptr))
- continue;
- /* Only show the ones that match the given mask - if any */
- if (!doall && wilds && match(name, cli_name(acptr)))
- continue;
- /* Skip all that do not match the specific query */
- if (!(doall || 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));
- }
- }
-#endif
- break;
-
- case 'C':
- case 'c':
-#ifdef HEAD_IN_SAND_STATS_C
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- report_configured_links(sptr, CONF_SERVER);
-#endif
- break;
-
- case 'E':
- case 'e': /* report engine name */
-#ifdef HEAD_IN_SAND_STATS_E
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- send_reply(sptr, RPL_STATSENGINE, engine_name());
-#endif
- break;
-
- case 'G':
- case 'g': /* send glines */
-#ifdef HEAD_IN_SAND_STATS_G
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- gline_stats(sptr);
-#endif
- break;
-
- case 'H':
- case 'h':
-#ifdef HEAD_IN_SAND_STATS_H
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- report_configured_links(sptr, CONF_HUB | CONF_LEAF);
-#endif
- break;
-
- case 'K':
- case 'k': /* display CONF_IPKILL as well as CONF_KILL -Kev */
-#ifdef HEAD_IN_SAND_STATS_K
- /* Simple version - if you want to fix it - send in a patch */
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- if (0 == report_klines(sptr, (parc == 4) ? parv[3] : 0, 0))
- return 0;
-#endif
- break;
- case 'F':
- case 'f':
-#ifdef HEAD_IN_SAND_STATS_F
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- feature_report(sptr);
-#endif
- break;
-
- case 'I':
- case 'i':
-#ifdef HEAD_IN_SAND_STATS_I
- /* Simple version - if you want to fix it - send in a patch */
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- {
- struct ConfItem *aconf;
- int wilds = 0;
- int count = 1000;
- char* host;
-
- if (parc < 4) {
- report_configured_links(sptr, CONF_CLIENT);
- break;
- }
- if (EmptyString(parv[3]))
- return need_more_params(sptr, "STATS I");
-
- host = parv[3];
- wilds = string_has_wildcards(host);
-
- for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
- if (CONF_CLIENT == aconf->status) {
- if ((!wilds && (!match(aconf->host, host) ||
- !match(aconf->name, host))) ||
- (wilds && (!mmatch(host, aconf->host) ||
- !mmatch(host, aconf->name))))
- {
- if (aconf->passwd && IsDigit(*aconf->passwd) &&
- (!aconf->passwd[1] ||
- (IsDigit(aconf->passwd[1]) && !aconf->passwd[2])))
- send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, aconf->passwd,
- aconf->name, aconf->port, get_conf_class(aconf));
- else
- send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, "*",
- aconf->name, aconf->port, get_conf_class(aconf));
-
- if (--count == 0)
- break;
- }
- }
- }
- }
-#endif
- break;
-
- case 'M':
-#ifdef HEAD_IN_SAND_STATS_M
- return m_not_oper(sptr,cptr,parc,parv);
-#else
-#if defined(MDEBUG)
- send_reply(sptr, RPL_STATMEMTOT, fda_get_byte_count(),
- fda_get_block_count());
-#endif
-#endif
- break;
-
- case 'm':
-#ifdef HEAD_IN_SAND_STATS_m
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- {
- struct Message *mptr;
-
- for (mptr = msgtab; mptr->cmd; mptr++)
- if (mptr->count)
- send_reply(sptr, RPL_STATSCOMMANDS, mptr->cmd, mptr->count,
- mptr->bytes);
- }
-#endif
- break;
-
- case 'o':
- case 'O':
-#ifdef HEAD_IN_SAND_STATS_O
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- report_configured_links(sptr, CONF_OPS);
-#endif
- break;
-
- case 'p':
- case 'P':
- /*
- * show listener ports
- * show hidden ports to opers, if there are more than 3 parameters,
- * interpret the fourth parameter as the port number.
- */
-#ifdef HEAD_IN_SAND_STATS_P
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- show_ports(sptr, 0, (parc > 3) ? atoi(parv[3]) : 0, 100);
-#endif
- break;
-
- case 'R':
- case 'r':
-#ifdef HEAD_IN_SAND_STATS_R
- return m_not_oper(sptr,cptr,parc,parv);
-#else
-#ifdef DEBUGMODE
- send_usage(sptr, parv[0]);
-#endif
-#endif
- break;
-
- case 'D':
-#ifdef HEAD_IN_SAND_STATS_D
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- report_crule_list(sptr, CRULE_ALL);
-#endif
- break;
-
- case 'd':
-#ifdef HEAD_IN_SAND_STATS_d
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- report_crule_list(sptr, CRULE_MASK);
-#endif
- break;
-
- case 't':
-#ifdef HEAD_IN_SAND_STATS_t
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- tstats(sptr, parv[0]);
-#endif
- break;
-
- case 'T':
-#ifdef HEAD_IN_SAND_STATS_T
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- motd_report(sptr);
-#endif
- break;
-
- case 'U':
-#ifdef HEAD_IN_SAND_STATS_U
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- report_configured_links(sptr, CONF_UWORLD);
-#endif
- break;
-
- case 'u':
-#ifdef HEAD_IN_SAND_STATS_u
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- {
- time_t nowr;
-
- nowr = CurrentTime - cli_since(&me);
- send_reply(sptr, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
- (nowr / 60) % 60, nowr % 60);
- send_reply(sptr, RPL_STATSCONN, max_connection_count, max_client_count);
- }
-#endif
- break;
-
- case 'W':
- case 'w':
-#ifdef HEAD_IN_SAND_STATS_W
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- calc_load(sptr);
-#endif
- break;
-
- case 'X':
- case 'x':
-#ifdef HEAD_IN_SAND_STATS_X
- return m_not_oper(sptr,cptr,parc,parv);
-#else
-#ifdef DEBUGMODE
- class_send_meminfo(sptr);
- send_listinfo(sptr, parv[0]);
-#endif
-#endif
- break;
-
- case 'Y':
- case 'y':
-#ifdef HEAD_IN_SAND_STATS_Y
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- report_classes(sptr);
-#endif
- break;
-
- case 'Z':
- case 'z':
-#ifdef HEAD_IN_SAND_STATS_Z
- return m_not_oper(sptr,cptr,parc,parv);
-#else
- count_memory(sptr, parv[0]);
-#endif
- break;
-
- default:
- stat = '*';
- while (*infotext)
- sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, *infotext++);
- break;
- }
- send_reply(sptr, RPL_ENDOFSTATS, stat);
- return 0;
-}
-
-/*
- * ms_stats - server message handler
- *
- * parv[0] = sender prefix
- * parv[1] = statistics selector (defaults to Message frequency)
- * parv[2] = target server (current server defaulted, if omitted)
- * And 'stats l' and 'stats' L:
- * parv[3] = server mask ("*" defaulted, if omitted)
- * Or for stats p,P:
- * parv[3] = port mask (returns p-lines when its port is matched by this)
- * Or for stats k,K,i and I:
- * parv[3] = [user@]host.name (returns which K/I-lines match this)
- * or [user@]host.mask (returns which K/I-lines are mmatched by this)
- * (defaults to old reply if ommitted, when local or Oper)
- * A remote mask (something containing wildcards) is only
- * allowed for IRC Operators.
- * Or for stats M:
- * parv[3] = time param
- * parv[4] = time param
- * (see report_memleak_stats() in runmalloc.c for details)
- *
- * This function is getting really ugly. -Ghostwolf
- */
-int ms_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
-{
- struct Message *mptr;
- struct Client *acptr;
- struct ConfItem *aconf;
- char stat = parc > 1 ? parv[1][0] : '\0';
- int i;
-
- if (hunt_stats(cptr, sptr, parc, parv, stat, 0) != HUNTED_ISME)
- return 0;
-
- switch (stat)
- {
- case 'L':
- case 'l':
- {
- int doall = 0;
- int wilds = 0;
- char *name = "*";
-
- if (parc > 3 && !EmptyString(parv[3])) {
- name = parv[3];
- wilds = string_has_wildcards(name);
- }
- else
- doall = 1;
- /*
- * 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 (!(acptr = LocalClientArray[i]))
- continue;
- /* Don't return clients when this is a request for `all' */
- if (doall && IsUser(acptr))
- continue;
- /* Don't show invisible people to unauthorized people when using
- * wildcards -- Is this still needed now /stats is oper only ?
- * Not here, because ms_stats is specifically a remote command,
- * thus the check was removed. -Ghostwolf */
- /* Only show the ones that match the given mask - if any */
- if (!doall && wilds && match(name, cli_name(acptr)))
- continue;
- /* Skip all that do not match the specific query */
- if (!(doall || 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),
- (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));
- }
- break;
- }
- case 'C':
- case 'c':
- report_configured_links(sptr, CONF_SERVER);
- break;
- case 'E':
- case 'e': /* report engine name */
- send_reply(sptr, RPL_STATSENGINE, engine_name());
- break;
- case 'G':
- case 'g': /* send glines */
- gline_stats(sptr);
- break;
- case 'H':
- case 'h':
- report_configured_links(sptr, CONF_HUB | CONF_LEAF);
- break;
- case 'K':
- case 'k': /* display CONF_IPKILL as well as CONF_KILL -Kev */
- if (0 == report_klines(sptr, (parc > 3) ? parv[3] : 0, !IsOper(sptr)))
- return 0;
- break;
- case 'F':
- case 'f':
- feature_report(sptr);
- break;
- case 'I':
- case 'i':
- {
- int wilds = 0;
- int count = 3;
- char* host;
-
- if (parc < 4 && IsOper(sptr)) {
- report_configured_links(sptr, CONF_CLIENT);
- break;
- }
- if (parc < 4 || EmptyString(parv[3]))
- return need_more_params(sptr, "STATS I");
-
- if (IsOper(sptr)) {
- wilds = string_has_wildcards(parv[3]);
- count = 1000;
- }
-
- host = parv[3];
-
- for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
- if (CONF_CLIENT == aconf->status) {
- if ((!wilds && (!match(aconf->host, host) ||
- !match(aconf->name, host))) ||
- (wilds && (!mmatch(host, aconf->host) ||
- !mmatch(host, aconf->name))))
- {
- if (aconf->passwd && IsDigit(*aconf->passwd) &&
- (!aconf->passwd[1] ||
- (IsDigit(aconf->passwd[1]) && !aconf->passwd[2])))
- send_reply(sptr, RPL_STATSILINE, 'I', aconf->host,
- aconf->passwd, aconf->name, aconf->port,
- get_conf_class(aconf));
- else
- send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, "*",
- aconf->name, aconf->port, get_conf_class(aconf));
-
- if (--count == 0)
- break;
- }
- }
- }
- break;
- }
- case 'M':
-#if defined(MDEBUG)
- send_reply(sptr, RPL_STATMEMTOT, fda_get_byte_count(),
- fda_get_block_count());
-#endif
- break;
- case 'm':
- for (mptr = msgtab; mptr->cmd; mptr++)
- if (mptr->count)
- send_reply(sptr, RPL_STATSCOMMANDS, mptr->cmd, mptr->count,
- mptr->bytes);
- break;
- case 'o':
- case 'O':
- report_configured_links(sptr, CONF_OPS);
- break;
- case 'p':
- case 'P':
- /*
- * show listener ports
- * show hidden ports to opers, if there are more than 3 parameters,
- * interpret the fourth parameter as the port number, limit non-local
- * or non-oper results to 8 ports.
- */
- show_ports(sptr, IsOper(sptr), (parc > 3) ? atoi(parv[3]) : 0, IsOper(sptr) ? 100 : 8);
- break;
- case 'R':
- case 'r':
-#ifdef DEBUGMODE
- send_usage(sptr, parv[0]);
-#endif
- break;
- case 'D':
- report_crule_list(sptr, CRULE_ALL);
- break;
- case 'd':
- report_crule_list(sptr, CRULE_MASK);
- break;
- case 't':
- tstats(sptr, parv[0]);
- break;
- case 'T':
- motd_report(sptr);
- break;
- case 'U':
- report_configured_links(sptr, CONF_UWORLD);
- break;
- case 'u':
- {
- time_t nowr;
-
- nowr = CurrentTime - cli_since(&me);
- send_reply(sptr, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
- (nowr / 60) % 60, nowr % 60);
- send_reply(sptr, RPL_STATSCONN, max_connection_count, max_client_count);
- break;
- }
- case 'V':
- case 'v':
- report_servers_verbose(sptr, stat);
- break;
- case 'W':
- case 'w':
- calc_load(sptr);
- break;
- case 'X':
- case 'x':
-#ifdef DEBUGMODE
- class_send_meminfo(sptr);
- send_listinfo(sptr, parv[0]);
-#endif
- break;
- case 'Y':
- case 'y':
- report_classes(sptr);
- break;
- case 'Z':
- case 'z':
- count_memory(sptr, parv[0]);
- break;
- default:
- stat = '*';
- break;
- }
- send_reply(sptr, RPL_ENDOFSTATS, stat);
- return 0;
-}
-
-/*
- * mo_stats - oper message handler
- *
- * parv[0] = sender prefix
- * parv[1] = statistics selector (defaults to Message frequency)
+ * parv[1] = statistics selector
* parv[2] = target server (current server defaulted, if omitted)
* And 'stats l' and 'stats' L:
- * parv[3] = server mask ("*" defaulted, if omitted)
+ * parv[3] = server mask ("*" default, if omitted)
* Or for stats p,P:
* parv[3] = port mask (returns p-lines when its port is matched by this)
* Or for stats k,K,i and I:
- * parv[3] = [user@]host.name (returns which K/I-lines match this)
- * or [user@]host.mask (returns which K/I-lines are mmatched by this)
- * (defaults to old reply if ommitted, when local or Oper)
+ * parv[3] = [user@]host.name (returns which K/I-lines match this)
+ * or [user@]host.mask (returns which K/I-lines are mmatched by this)
+ * (defaults to old reply if omitted, when local or Oper)
* A remote mask (something containing wildcards) is only
* allowed for IRC Operators.
- * Or for stats M:
- * parv[3] = time param
- * parv[4] = time param
- * (see report_memleak_stats() in runmalloc.c for details)
- *
- * This function is getting really ugly. -Ghostwolf
*/
-int mo_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
+int
+m_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
- struct Message* mptr;
- struct Client* acptr;
- struct ConfItem* aconf;
- char stat = parc > 1 ? parv[1][0] : '\0';
- const char** infotext = statsinfo;
- int i;
-
- if (hunt_stats(cptr, sptr, parc, parv, stat, HEAD_IN_SAND_REMOTE)
- != HUNTED_ISME)
- return 0;
-
- switch (stat)
- {
- case 'L':
- case 'l':
- {
- int doall = 0, wilds = 0;
- char* name = "*";
- if (parc > 3 && !EmptyString(parv[3])) {
- name = parv[3];
- wilds = string_has_wildcards(name);
- }
- else
- doall = 1;
- /*
- * 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 (!(acptr = LocalClientArray[i]))
- continue;
- /* Don't return clients when this is a request for `all' */
- if (doall && IsUser(acptr))
- continue;
- /* Only show the ones that match the given mask - if any */
- if (!doall && wilds && match(name, cli_name(acptr)))
- continue;
- /* Skip all that do not match the specific query */
- if (!(doall || 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),
- (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));
- }
- break;
- }
- case 'C':
- case 'c':
- report_configured_links(sptr, CONF_SERVER);
- break;
- case 'E':
- case 'e': /* report engine name */
- send_reply(sptr, RPL_STATSENGINE, engine_name());
- break;
- case 'G':
- case 'g': /* send glines */
- gline_stats(sptr);
- break;
- case 'H':
- case 'h':
- report_configured_links(sptr, CONF_HUB | CONF_LEAF);
- break;
- case 'K':
- case 'k': /* display CONF_IPKILL as well as CONF_KILL -Kev */
- if (0 == report_klines(sptr, (parc > 3) ? parv[3] : 0, 0))
- return 0;
- break;
- case 'F':
- case 'f':
- feature_report(sptr);
- break;
- case 'I':
- case 'i':
- {
- int wilds = 0;
- int count = 1000;
- char* host;
-
- if (parc < 4) {
- report_configured_links(sptr, CONF_CLIENT);
- break;
- }
- if (EmptyString(parv[3]))
- return need_more_params(sptr, "STATS I");
-
- host = parv[3];
- wilds = string_has_wildcards(host);
-
- for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
- if (CONF_CLIENT == aconf->status) {
- if ((!wilds && (!match(aconf->host, host) ||
- !match(aconf->name, host))) ||
- (wilds && (!mmatch(host, aconf->host) ||
- !mmatch(host, aconf->name))))
- {
- if (aconf->passwd && IsDigit(*aconf->passwd) &&
- (!aconf->passwd[1] ||
- (IsDigit(aconf->passwd[1]) && !aconf->passwd[2])))
- send_reply(sptr, RPL_STATSILINE, 'I', aconf->host,
- aconf->passwd, aconf->name, aconf->port,
- get_conf_class(aconf));
- else
- send_reply(sptr, RPL_STATSILINE, 'I', aconf->host, "*",
- aconf->name, aconf->port, get_conf_class(aconf));
-
- if (--count == 0)
- break;
- }
- }
- }
- }
- break;
- case 'M':
-#if defined(MDEBUG)
- send_reply(sptr, RPL_STATMEMTOT, fda_get_byte_count(),
- fda_get_block_count());
-#endif
- break;
- case 'm':
- for (mptr = msgtab; mptr->cmd; mptr++)
- if (mptr->count)
- send_reply(sptr, RPL_STATSCOMMANDS, mptr->cmd, mptr->count,
- mptr->bytes);
- break;
- case 'o':
- case 'O':
- report_configured_links(sptr, CONF_OPS);
- break;
- case 'p':
- case 'P':
- /*
- * show listener ports
- * show hidden ports to opers, if there are more than 3 parameters,
- * interpret the fourth parameter as the port number, limit non-local
- * or non-oper results to 8 ports.
- */
- show_ports(sptr, 1, (parc > 3) ? atoi(parv[3]) : 0, 100);
- break;
- case 'R':
- case 'r':
-#ifdef DEBUGMODE
- send_usage(sptr, parv[0]);
-#endif
- break;
- case 'D':
- report_crule_list(sptr, CRULE_ALL);
- break;
- case 'd':
- report_crule_list(sptr, CRULE_MASK);
- break;
- case 't':
- tstats(sptr, parv[0]);
- break;
- case 'T':
- motd_report(sptr);
- break;
- case 'U':
- report_configured_links(sptr, CONF_UWORLD);
- break;
- case 'u':
- {
- time_t nowr;
-
- nowr = CurrentTime - cli_since(&me);
- send_reply(sptr, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24,
- (nowr / 60) % 60, nowr % 60);
- send_reply(sptr, RPL_STATSCONN, max_connection_count, max_client_count);
- break;
- }
- case 'V':
- case 'v':
- report_servers_verbose(sptr, stat);
- break;
- case 'W':
- case 'w':
- calc_load(sptr);
- break;
- case 'X':
- case 'x':
-#ifdef DEBUGMODE
- class_send_meminfo(sptr);
- send_listinfo(sptr, parv[0]);
-#endif
- break;
- case 'Y':
- case 'y':
- report_classes(sptr);
- break;
- case 'Z':
- case 'z':
- count_memory(sptr, parv[0]);
- break;
-
- default:
- stat = '*';
- while (*infotext)
- sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, *infotext++);
- break;
- }
- send_reply(sptr, RPL_ENDOFSTATS, stat);
- return 0;
+ const struct StatDesc *sd;
+ char *param;
+
+ /* If we didn't find a descriptor, send them help */
+ if ((parc < 2) || !(sd = stats_find(parv[1])))
+ parv[1] = "*", sd = stats_find("*");
+
+ assert(sd != 0);
+
+ /* Check whether the client can issue this command. If source is
+ * not privileged (server or an operator), then the STAT_FLAG_OPERONLY
+ * flag must not be set, and if the STAT_FLAG_OPERFEAT flag is set,
+ * then the feature given by sd->sd_control must be off.
+ *
+ * This checks cptr rather than sptr so that a local oper may send
+ * /stats queries to other servers.
+ */
+ if (!IsPrivileged(cptr) &&
+ ((sd->sd_flags & STAT_FLAG_OPERONLY) ||
+ ((sd->sd_flags & STAT_FLAG_OPERFEAT) && feature_bool(sd->sd_control))))
+ return send_reply(sptr, ERR_NOPRIVILEGES);
+
+ /* Check for extra parameter */
+ if ((sd->sd_flags & STAT_FLAG_VARPARAM) && parc > 3 && !EmptyString(parv[3]))
+ param = parv[3];
+ else
+ param = NULL;
+
+ /* Ok, track down who's supposed to get this... */
+ if (hunt_server_cmd(sptr, CMD_STATS, cptr, feature_int(FEAT_HIS_REMOTE),
+ param ? "%s %C :%s" : "%s :%C", 2, parc, parv) !=
+ HUNTED_ISME)
+ return 0; /* Someone else--cool :) */
+
+ /* Check if they are a local user */
+ if ((sd->sd_flags & STAT_FLAG_LOCONLY) && !MyUser(sptr))
+ return send_reply(sptr, ERR_NOPRIVILEGES);
+
+ assert(sd->sd_func != 0);
+
+ /* Ok, dispatch the stats function */
+ (*sd->sd_func)(sptr, sd, param);
+
+ /* Done sending them the stats */
+ return send_reply(sptr, RPL_ENDOFSTATS, parv[1]);
}
-