X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=ircd%2Fs_stats.c;h=c3f01cd7c43abc0e64b5c2c5fe01dc748d253a47;hb=31c730086f07bd21737fe3808853d22d30661e53;hp=87591d9e1e4af738b3058c1a47d80343b91a7aee;hpb=8927a39c968bc739ebb61ff972a7fe0c375930a1;p=ircu2.10.12-pk.git diff --git a/ircd/s_stats.c b/ircd/s_stats.c index 87591d9..c3f01cd 100644 --- a/ircd/s_stats.c +++ b/ircd/s_stats.c @@ -18,199 +18,696 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ */ +#include "config.h" -#include "s_stats.h" #include "class.h" #include "client.h" +#include "gline.h" +#include "hash.h" #include "ircd.h" #include "ircd_chattr.h" +#include "ircd_events.h" +#include "ircd_features.h" +#include "ircd_crypt.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 "querycmds.h" +#include "res.h" +#include "s_auth.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 #include +#include #include - -/* - * m_stats/s_stats - * - * Report configuration lines and other statistics from this - * server. +/** @file + * @brief Report configuration lines and other statistics from this + * server. + * @version $Id$ * * Note: The info is reported in the order the server uses * 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.", - "m - Message usage information.", - "t - Local connection statistics (Total SND/RCV, etc).", - "w - Userload statistics.", - "v - Connection class information.", - "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'}, - {CONF_LEAF, RPL_STATSLLINE, 'L'}, - {CONF_OPERATOR, RPL_STATSOLINE, 'O'}, - {CONF_HUB, RPL_STATSHLINE, 'H'}, - {CONF_LOCOP, RPL_STATSOLINE, 'o'}, - {CONF_UWORLD, RPL_STATSULINE, 'U'}, - {0, 0} -}; +/* The statsinfo array should only be used in this file, but just TRY + * telling the compiler that you want to forward declare a static + * array without specifying a length, and see how it responds. So we + * forward declare it "extern". + */ +extern struct StatDesc statsinfo[]; -void report_configured_links(struct Client *sptr, int mask) +/** Report items from #GlobalConfList. + * Uses sd->sd_funcdata as a filter for ConfItem::status. + * @param[in] sptr Client requesting statistics. + * @param[in] sd Stats descriptor for request. + * @param[in] param Extra parameter from user (ignored). + */ +static void +stats_configured_links(struct Client *sptr, const struct StatDesc* sd, + char* param) { static char null[] = ""; struct ConfItem *tmp; - unsigned int *p; unsigned short int port; - char c, *host, *pass, *name; - + int maximum; + char *host, *pass, *name, *username, *hub_limit; - for (tmp = GlobalConfList; tmp; tmp = tmp->next) + for (tmp = GlobalConfList; tmp; tmp = tmp->next) { - if ((tmp->status & mask)) + if ((tmp->status & sd->sd_funcdata)) { - for (p = &report_array[0][0]; *p; p += 3) - if (*p == tmp->status) - break; - if (!*p) - continue; - c = (char)*(p + 2); host = BadPtr(tmp->host) ? null : tmp->host; pass = BadPtr(tmp->passwd) ? null : tmp->passwd; name = BadPtr(tmp->name) ? null : tmp->name; - port = tmp->port; - /* - * On K line the passwd contents can be - * displayed on STATS reply. -Vesa - */ - /* Special-case 'k' or 'K' lines as appropriate... -Kev */ - if ((tmp->status & CONF_UWORLD)) - send_reply(sptr, p[1], c, host, pass, name, port, get_conf_class(tmp)); - else if ((tmp->status & (CONF_SERVER | CONF_HUB))) - send_reply(sptr, p[1], c, "*", name, port, get_conf_class(tmp)); - else - send_reply(sptr, p[1], c, host, name, port, get_conf_class(tmp)); + username = BadPtr(tmp->username) ? null : tmp->username; + hub_limit = BadPtr(tmp->hub_limit) ? null : tmp->hub_limit; + maximum = tmp->maximum; + port = tmp->address.port; + + if (tmp->status & CONF_UWORLD) + send_reply(sptr, RPL_STATSULINE, host); + else if (tmp->status & CONF_SERVER) + send_reply(sptr, RPL_STATSCLINE, name, port, maximum, hub_limit, get_conf_class(tmp)); + else if (tmp->status & CONF_CLIENT) + send_reply(sptr, RPL_STATSILINE, + (tmp->username ? tmp->username : ""), (tmp->username ? "@" : ""), + (tmp->host ? tmp->host : "*"), maximum, + (name[0] == ':' ? "0" : ""), (tmp->name ? tmp->name : "*"), + port, get_conf_class(tmp)); + else if (tmp->status & CONF_OPERATOR) + send_reply(sptr, RPL_STATSOLINE, + ((FlagHas(&tmp->privs_dirty, PRIV_PROPAGATE) + && FlagHas(&tmp->privs, PRIV_PROPAGATE)) + || (FlagHas(&tmp->conn_class->privs_dirty, PRIV_PROPAGATE) + && FlagHas(&tmp->conn_class->privs, PRIV_PROPAGATE))) + ? 'O' : 'o', username, host, name, get_conf_class(tmp)); } } } -/* - * {CONF_TLINES, RPL_STATSTLINE, 'T'}, +/** Report connection rules from conf_get_crule_list(). + * Uses sd->sd_funcdata as a filter for CRuleConf::type. + * @param[in] to Client requesting statistics. + * @param[in] sd Stats descriptor for request. + * @param[in] param Extra parameter from user (ignored). + */ +static void +stats_crule_list(struct Client* to, const struct StatDesc *sd, + char *param) +{ + const struct CRuleConf* p = conf_get_crule_list(); + + for ( ; p; p = p->next) + { + if (p->type & sd->sd_funcdata) + send_reply(to, RPL_STATSDLINE, (p->type & CRULE_ALL ? 'D' : 'd'), p->hostmask, p->rule); + } +} + +/** Report active event engine name. + * @param[in] to Client requesting statistics. + * @param[in] sd Stats descriptor for request (ignored). + * @param[in] param Extra parameter from user (ignored). */ -void report_motd_list(struct Client* to) +static void +stats_engine(struct Client *to, const struct StatDesc *sd, char *param) { - const struct MotdConf* conf = conf_get_motd_list(); - for ( ; conf; conf = conf->next) - send_reply(to, RPL_STATSTLINE, 'T', conf->hostmask, conf->path); + send_reply(to, RPL_STATSENGINE, engine_name()); } -/* - * {CONF_CRULEALL, RPL_STATSDLINE, 'D'}, - * {CONF_CRULEAUTO, RPL_STATSDLINE, 'd'}, +/** Report client access lists. + * @param[in] to Client requesting statistics. + * @param[in] sd Stats descriptor for request. + * @param[in] param Filter for hostname or IP (NULL to show all). */ -void report_crule_list(struct Client* to, int mask) +static void +stats_access(struct Client *to, const struct StatDesc *sd, char *param) { - 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); + struct ConfItem *aconf; + int wilds = 0; + int count = 1000; + + if (!param) + { + stats_configured_links(to, sd, param); + return; + } + + wilds = string_has_wildcards(param); + + for (aconf = GlobalConfList; aconf; aconf = aconf->next) + { + if (aconf->status != CONF_CLIENT) + continue; + if (wilds ? ((aconf->host && !mmatch(aconf->host, param)) + || (aconf->name && !mmatch(aconf->name, param))) + : ((aconf->host && !match(param, aconf->host)) + || (aconf->name && !match(param, aconf->name)))) + { + send_reply(to, RPL_STATSILINE, + (aconf->username ? aconf->username : ""), (aconf->username ? "@" : ""), + (aconf->host ? aconf->host : "*"), aconf->maximum, + (aconf->name && aconf->name[0] == ':' ? "0":""), + aconf->name ? aconf->name : "*", + aconf->address.port, get_conf_class(aconf)); + if (--count == 0) + break; + } } } -/* - * {CONF_KILL, RPL_STATSKLINE, 'K'}, - * {CONF_IPKILL, RPL_STATSKLINE, 'k'}, + +/** Report DenyConf entries. + * @param[in] to Client requesting list. */ -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) - send_reply(to, RPL_STATSKLINE, (p->ip_kill) ? 'k' : 'K', - p->hostmask, p->message, p->usermask); + send_reply(to, RPL_STATSKLINE, p->bits > 0 ? 'k' : 'K', + p->usermask ? p->usermask : "*", + p->hostmask ? p->hostmask : "*", + p->message ? p->message : "(none)", + p->realmask ? p->realmask : "*"); } -/* 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 +/** Report K/k-lines to a user. + * @param[in] sptr Client requesting statistics. + * @param[in] sd Stats descriptor for request (ignored). + * @param[in] mask Filter for hostmasks to show. */ -int hunt_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[], char stat) +static void +stats_klines(struct Client *sptr, const struct StatDesc *sd, 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': - return hunt_server_cmd(sptr, CMD_STATS, cptr, 0, "%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) + { + /* Skip this block if the user is searching for a user-matching + * mask but the current Kill doesn't have a usermask, or if user + * is searching for a host-matching mask but the Kill has no + * hostmask, or if the user mask is specified and doesn't match, + * or if the host mask is specified and doesn't match. + */ + if ((user && !conf->usermask) + || (host && !conf->hostmask) + || (user && conf->usermask + && (wilds + ? mmatch(user, conf->usermask) + : match(conf->usermask, user))) + || (host && conf->hostmask + && (wilds + ? mmatch(host, conf->hostmask) + : match(conf->hostmask, host)))) + continue; + send_reply(sptr, RPL_STATSKLINE, conf->bits > 0 ? 'k' : 'K', + conf->usermask ? conf->usermask : "*", + conf->hostmask ? conf->hostmask : "*", + conf->message ? conf->message : "(none)", + conf->realmask ? conf->realmask : "*"); + if (--count == 0) + return; + } +} + +/** Report on servers and/or clients connected to the network. + * @param[in] sptr Client requesting statistics. + * @param[in] sd Stats descriptor for request (ignored). + * @param[in] name Filter for client names to show. + */ +static void +stats_links(struct Client* sptr, const struct StatDesc* sd, char* name) +{ + struct Client *acptr; + int i; + int wilds = 0; + + 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 > 3) - return hunt_server_cmd(sptr, CMD_STATS, cptr, 0, "%s %C :%s", 2, parc, parv); - else - return hunt_server_cmd(sptr, CMD_STATS, cptr, 0, "%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 %Lu %u %Lu :%Tu", + (*(cli_name(acptr))) ? cli_name(acptr) : "", + (int)MsgQLength(&(cli_sendQ(acptr))), (int)cli_sendM(acptr), + (cli_sendB(acptr) >> 10), (int)cli_receiveM(acptr), + (cli_receiveB(acptr) >> 10), CurrentTime - cli_firsttime(acptr)); } +} - /* oper only, varying # of params */ - case 'l': - case 'L': - case 'M': - { - if (parc == 4) - return hunt_server_cmd(sptr, CMD_STATS, cptr, 1, "%s %C :%s", 2, parc, parv); - else if (parc > 4) - return hunt_server_cmd(sptr, CMD_STATS, cptr, 1, "%s %C %s :%s", 2, parc, parv); - else - return hunt_server_cmd(sptr, CMD_STATS, cptr, 1, "%s :%C", 2, parc, parv); +/** Report on loaded modules. + * @param[in] to Client requesting statistics. + * @param[in] sd Stats descriptor for request (ignored). + * @param[in] param Extra parameter from user (ignored). + */ +static void +stats_modules(struct Client* to, const struct StatDesc* sd, char* param) +{ +crypt_mechs_t* mechs; + + send_reply(to, SND_EXPLICIT | RPL_STATSLLINE, + "Module Description Entry Point"); + + /* atm the only "modules" we have are the crypto mechanisms, + eventualy they'll be part of a global dl module list, for now + i'll just output data about them -- hikari */ + + if(crypt_mechs_root == NULL) + return; + + mechs = crypt_mechs_root->next; + + for(;;) + { + if(mechs == NULL) + return; + + send_reply(to, SND_EXPLICIT | RPL_STATSLLINE, + "%s %s 0x%X", + mechs->mech->shortname, mechs->mech->description, + mechs->mech->crypt_function); + + mechs = mechs->next; + } + +} + +/** Report how many times each command has been used. + * @param[in] to Client requesting statistics. + * @param[in] sd Stats descriptor for request (ignored). + * @param[in] param Extra parameter from user (ignored). + */ +static void +stats_commands(struct Client* to, const struct StatDesc* sd, 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); +} + +/** List channel quarantines. + * @param[in] to Client requesting statistics. + * @param[in] sd Stats descriptor for request (ignored). + * @param[in] param Filter for quarantined channel names. + */ +static void +stats_quarantine(struct Client* to, const struct StatDesc* sd, 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); + } +} + +/** List service pseudo-command mappings. + * @param[in] to Client requesting statistics. + * @param[in] sd Stats descriptor for request (ignored). + * @param[in] param Extra parameter from user (ignored). + */ +static void +stats_mapping(struct Client *to, const struct StatDesc* sd, char* param) +{ + struct s_map *map; + + send_reply(to, RPL_STATSRLINE, "Command", "Name", "Prepend", "Target"); + for (map = GlobalServiceMapList; map; map = map->next) { + struct nick_host *nh; + for (nh = map->services; nh; nh = nh->next) { + send_reply(to, RPL_STATSRLINE, map->command, map->name, + (map->prepend ? map->prepend : "*"), nh->nick); } + } +} - /* oper only, standard # of params */ - default: - return hunt_server_cmd(sptr, CMD_STATS, cptr, 1, "%s :%C", 2, parc, parv); +/** Report server uptime and maximum connection/client counts. + * @param[in] to Client requesting statistics. + * @param[in] sd Stats descriptor for request (ignored). + * @param[in] param Extra parameter from user (ignored). + */ +static void +stats_uptime(struct Client* to, const struct StatDesc* sd, 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); +} + +/** Verbosely report on servers connected to the network. + * If sd->sd_funcdata != 0, then display in a more human-friendly format. + * @param[in] sptr Client requesting statistics. + * @param[in] sd Stats descriptor for request. + * @param[in] param Filter for server names to display. + */ +static void +stats_servers_verbose(struct Client* sptr, const struct StatDesc* sd, + char* param) +{ + struct Client *acptr; + const char *fmt; + + /* + * lowercase 'v' is for human-readable, + * uppercase 'V' is for machine-readable + */ + if (sd->sd_funcdata) { + send_reply(sptr, SND_EXPLICIT | RPL_STATSVERBOSE, + "%-20s %-20s Flags Hops Numeric Lag RTT Up Down " + "Clients/Max Proto %-10s :Info", "Servername", "Uplink", + "LinkTS"); + fmt = "%-20s %-20s %c%c%c%c%c %4i %s %-4i %5i %4i %4i %4i %5i %5i P%-2i %Tu :%s"; + } else { + fmt = "%s %s %c%c%c%c%c %i %s %i %i %i %i %i %i %i P%i %Tu :%s"; + } + + 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, fmt, + cli_name(acptr), + cli_name(cli_serv(acptr)->up), + IsBurst(acptr) ? 'B' : '-', + IsBurstAck(acptr) ? 'A' : '-', + IsHub(acptr) ? 'H' : '-', + IsService(acptr) ? 'S' : '-', + IsIPv6(acptr) ? '6' : '-', + 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, + (acptr == &me ? UserStats.local_clients : cli_serv(acptr)->clients), + cli_serv(acptr)->nn_mask, + cli_serv(acptr)->prot, + cli_serv(acptr)->timestamp, + cli_info(acptr)); } } +/** Display objects allocated (and total memory used by them) for + * several types of structures. + * @param[in] to Client requesting statistics. + * @param[in] sd Stats descriptor for request (ignored). + * @param[in] param Extra parameter from user (ignored). + */ +static void +stats_meminfo(struct Client* to, const struct StatDesc* sd, char* param) +{ + extern void bans_send_meminfo(struct Client *cptr); + + class_send_meminfo(to); + bans_send_meminfo(to); + send_listinfo(to, 0); +} + +/** Send a list of available statistics. + * @param[in] to Client requesting statistics. + * @param[in] sd Stats descriptor for request. + * @param[in] param Extra parameter from user (ignored). + */ +static void +stats_help(struct Client* to, const struct StatDesc* sd, char* param) +{ + struct StatDesc *asd; + + /* only if it's my user */ + if (MyUser(to)) + for (asd = statsinfo; asd->sd_name; asd++) + if (asd != sd) /* don't send the help for us */ + sendcmdto_one(&me, CMD_NOTICE, to, "%C :%c (%s) - %s", to, asd->sd_c, + asd->sd_name, asd->sd_desc); +} + +/** Contains information about all statistics. */ +struct StatDesc statsinfo[] = { + { 'a', "nameservers", STAT_FLAG_OPERFEAT|STAT_FLAG_LOCONLY, FEAT_HIS_STATS_a, + report_dns_servers, 0, + "DNS servers." }, + { 'c', "connect", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_c, + stats_configured_links, CONF_SERVER, + "Remote server connection lines." }, + { 'd', "maskrules", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_d, + stats_crule_list, CRULE_MASK, + "Dynamic routing configuration." }, + { 'D', "crules", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_d, + stats_crule_list, CRULE_ALL, + "Dynamic routing configuration." }, + { 'e', "engine", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_e, + stats_engine, 0, + "Report server event loop engine." }, + { 'f', "features", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_f, + feature_report, 0, + "Feature settings." }, + { 'F', "featuresall", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_f, + feature_report, 1, + "All feature settings, including defaulted values." }, + { 'g', "glines", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_g, + gline_stats, 0, + "Global bans (G-lines)." }, + { 'i', "access", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM), FEAT_HIS_STATS_i, + stats_access, CONF_CLIENT, + "Connection authorization lines." }, + { 'j', "histogram", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_j, + msgq_histogram, 0, + "Message length histogram." }, + { 'J', "jupes", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_J, + stats_nickjupes, 0, + "Nickname jupes." }, + { 'k', "klines", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM), FEAT_HIS_STATS_k, + stats_klines, 0, + "Local bans (K-Lines)." }, + { 'l', "links", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM | STAT_FLAG_CASESENS), + FEAT_HIS_STATS_l, + stats_links, 0, + "Current connections information." }, + { 'L', "modules", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), + FEAT_HIS_STATS_L, + stats_modules, 0, + "Dynamically loaded modules." }, + { 'm', "commands", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_m, + stats_commands, 0, + "Message usage information." }, + { 'o', "operators", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_o, + stats_configured_links, CONF_OPERATOR, + "Operator information." }, + { 'p', "ports", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM), FEAT_HIS_STATS_p, + show_ports, 0, + "Listening ports." }, + { 'q', "quarantines", (STAT_FLAG_OPERONLY | STAT_FLAG_VARPARAM), FEAT_HIS_STATS_q, + stats_quarantine, 0, + "Quarantined channels list." }, + { 'R', "mappings", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_R, + stats_mapping, 0, + "Service mappings." }, +#ifdef DEBUGMODE + { 'r', "usage", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_r, + send_usage, 0, + "System resource usage (Debug only)." }, +#endif + { 'T', "motds", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_T, + motd_report, 0, + "Configured Message Of The Day files." }, + { 't', "locals", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_t, + tstats, 0, + "Local connection statistics (Total SND/RCV, etc)." }, + { 'U', "uworld", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_U, + stats_configured_links, CONF_UWORLD, + "Service server information." }, + { 'u', "uptime", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_u, + stats_uptime, 0, + "Current uptime & highest connection count." }, + { 'v', "vservers", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM | STAT_FLAG_CASESENS), FEAT_HIS_STATS_v, + stats_servers_verbose, 1, + "Verbose server information." }, + { 'V', "vserversmach", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM | STAT_FLAG_CASESENS), FEAT_HIS_STATS_v, + stats_servers_verbose, 0, + "Verbose server information." }, + { 'w', "userload", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_w, + calc_load, 0, + "Userload statistics." }, + { 'x', "memusage", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_x, + stats_meminfo, 0, + "List usage information." }, + { 'y', "classes", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_y, + report_classes, 0, + "Connection classes." }, + { 'z', "memory", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_z, + count_memory, 0, + "Memory/Structure allocation information." }, + { ' ', "iauth", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_IAUTH, + report_iauth_stats, 0, + "IAuth statistics." }, + { ' ', "iauthconf", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_IAUTH, + report_iauth_conf, 0, + "IAuth configuration." }, + { '*', "help", STAT_FLAG_CASESENS, FEAT_LAST_F, + stats_help, 0, + "Send help for stats." }, + { '\0', 0, FEAT_LAST_F, 0, 0, 0 } +}; + +/** Maps from characters to statistics descriptors. + * Statistics descriptors with no single-character alias are not included. + */ +static struct StatDesc *statsmap[256]; +/** Number of statistics descriptors. */ +static int statscount; + +/** Compare two StatDesc structures by long name (StatDesc::sd_name). + * @param[in] a_ Pointer to a StatDesc. + * @param[in] b_ Pointer to a StatDesc. + * @return Less than, equal to, or greater than zero if \a a_ is + * lexicographically less than, equal to, or greater than \a b_. + */ +static int +stats_cmp(const void *a_, const void *b_) +{ + const struct StatDesc *a = a_; + const struct StatDesc *b = b_; + return ircd_strcmp(a->sd_name, b->sd_name); +} + +/** Compare a StatDesc's name against a string. + * @param[in] key Pointer to a null-terminated string. + * @param[in] sd_ Pointer to a StatDesc. + * @return Less than, equal to, or greater than zero if \a key is + * lexicographically less than, equal to, or greater than \a + * sd_->sd_name. + */ +static int +stats_search(const void *key, const void *sd_) +{ + const struct StatDesc *sd = sd_; + return ircd_strcmp(key, sd->sd_name); +} + +/** Look up a stats handler. If name_or_char is just one character + * long, use that as a character index; otherwise, look it up by name + * in #statsinfo. + * @param[in] name_or_char Null-terminated string to look up. + * @return The statistics descriptor for \a name_or_char (NULL if none). + */ +const struct StatDesc * +stats_find(const char *name_or_char) +{ + if (!name_or_char[1]) + return statsmap[name_or_char[0] - CHAR_MIN]; + else + return bsearch(name_or_char, statsinfo, statscount, sizeof(statsinfo[0]), stats_search); +} + +/** Build statsmap from the statsinfo array. */ +void +stats_init(void) +{ + struct StatDesc *sd; + + /* Count number of stats entries and sort them. */ + for (statscount = 0, sd = statsinfo; sd->sd_name; sd++, statscount++) {} + qsort(statsinfo, statscount, sizeof(statsinfo[0]), stats_cmp); + + /* Build the mapping */ + for (sd = statsinfo; sd->sd_name; sd++) + { + if (!sd->sd_c) + continue; + else if (sd->sd_flags & STAT_FLAG_CASESENS) + /* case sensitive character... */ + statsmap[sd->sd_c - CHAR_MIN] = sd; + else + { + /* case insensitive--make sure to put in two entries */ + statsmap[ToLower(sd->sd_c) - CHAR_MIN] = sd; + statsmap[ToUpper(sd->sd_c) - CHAR_MIN] = sd; + } + } +}