solanum/modules/m_stats.c
Ed Kellett 1123eefcb0
Rework oper hiding
As it stands, oper hiding is rather messy and inconsistent. Add
SeesOper(target, source), which is true iff target should appear as an
oper to source. If I haven't missed something, all commands that reveal
oper status now use the same logic.

general::hide_opers_in_whois is a special case, and affects /whois only.

general::hide_opers is introduced, and has the same effect as giving
everyone oper:hidden. All commands that reveal oper status respect both.
2019-09-12 23:14:15 +01:00

1791 lines
47 KiB
C

/*
* ircd-ratbox: an advanced Internet Relay Chat Daemon(ircd).
* m_stats.c: Sends the user statistics or config information.
*
* Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
* Copyright (C) 1996-2002 Hybrid Development Team
* Copyright (C) 2002-2005 ircd-ratbox development team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#include "stdinc.h"
#include "class.h" /* report_classes */
#include "client.h" /* Client */
#include "match.h"
#include "ircd.h" /* me */
#include "listener.h" /* show_ports */
#include "msg.h" /* Message */
#include "hostmask.h" /* report_mtrie_conf_links */
#include "numeric.h" /* ERR_xxx */
#include "scache.h" /* list_scache */
#include "send.h" /* sendto_one */
#include "s_conf.h" /* ConfItem */
#include "s_serv.h" /* hunt_server */
#include "s_stats.h"
#include "s_user.h" /* show_opers */
#include "parse.h"
#include "modules.h"
#include "hook.h"
#include "s_newconf.h"
#include "hash.h"
#include "reject.h"
#include "whowas.h"
#include "rb_radixtree.h"
#include "sslproc.h"
#include "s_assert.h"
static const char stats_desc[] =
"Provides the STATS command to inspect various server/network information";
static void m_stats (struct MsgBuf *, struct Client *, struct Client *, int, const char **);
struct Message stats_msgtab = {
"STATS", false, false, false, false,
{mg_unreg, {m_stats, 2}, {m_stats, 3}, mg_ignore, mg_ignore, {m_stats, 2}}
};
int doing_stats_hook;
int doing_stats_p_hook;
mapi_clist_av1 stats_clist[] = { &stats_msgtab, NULL };
mapi_hlist_av1 stats_hlist[] = {
{ "doing_stats", &doing_stats_hook },
{ "doing_stats_p", &doing_stats_p_hook },
{ NULL, NULL }
};
DECLARE_MODULE_AV2(stats, NULL, NULL, stats_clist, stats_hlist, NULL, NULL, NULL, stats_desc);
const char *Lformat = "%s %u %u %u %u %u :%u %u %s";
static void stats_l_list(struct Client *s, const char *, bool, bool, rb_dlink_list *, char,
bool (*check_fn)(struct Client *target_p));
static void stats_l_client(struct Client *source_p, struct Client *target_p,
char statchar);
static int stats_spy(struct Client *, char, const char *);
static void stats_p_spy(struct Client *);
typedef void (*handler_t)(struct Client *source_p);
typedef void (*handler_parv_t)(struct Client *source_p, int parc, const char *parv[]);
struct stats_cmd
{
union
{
handler_t handler;
handler_parv_t handler_parv;
};
bool need_parv;
bool need_oper;
bool need_admin;
};
static void stats_dns_servers(struct Client *);
static void stats_delay(struct Client *);
static void stats_hash(struct Client *);
static void stats_connect(struct Client *);
static void stats_tdeny(struct Client *);
static void stats_deny(struct Client *);
static void stats_exempt(struct Client *);
static void stats_events(struct Client *);
static void stats_prop_klines(struct Client *);
static void stats_hubleaf(struct Client *);
static void stats_auth(struct Client *);
static void stats_tklines(struct Client *);
static void stats_klines(struct Client *);
static void stats_messages(struct Client *);
static void stats_dnsbl(struct Client *);
static void stats_oper(struct Client *);
static void stats_privset(struct Client *);
static void stats_operedup(struct Client *);
static void stats_ports(struct Client *);
static void stats_tresv(struct Client *);
static void stats_resv(struct Client *);
static void stats_ssld(struct Client *);
static void stats_usage(struct Client *);
static void stats_tstats(struct Client *);
static void stats_uptime(struct Client *);
static void stats_shared(struct Client *);
static void stats_servers(struct Client *);
static void stats_tgecos(struct Client *);
static void stats_gecos(struct Client *);
static void stats_class(struct Client *);
static void stats_memory(struct Client *);
static void stats_servlinks(struct Client *);
static void stats_ltrace(struct Client *, int, const char **);
static void stats_ziplinks(struct Client *);
static void stats_comm(struct Client *);
static void stats_capability(struct Client *);
#define HANDLER_NORM(fn, oper, admin) { { .handler = fn }, false, oper, admin }
#define HANDLER_PARV(fn, oper, admin) { { .handler_parv = fn }, true, oper, admin }
/* This table contains the possible stats items, in order:
* stats letter, function to call, operonly? adminonly? --fl_
*
* Previously in this table letters were a column. I fixed it to use modern
* C initalisers so we don't have to iterate anymore
* --Elizafox
*/
static struct stats_cmd stats_cmd_table[256] = {
/* letter handler oper admin */
['a'] = HANDLER_NORM(stats_dns_servers, true, true),
['A'] = HANDLER_NORM(stats_dns_servers, true, true),
['b'] = HANDLER_NORM(stats_delay, true, true),
['B'] = HANDLER_NORM(stats_hash, true, true),
['c'] = HANDLER_NORM(stats_connect, false, false),
['C'] = HANDLER_NORM(stats_capability, true, false),
['d'] = HANDLER_NORM(stats_tdeny, true, false),
['D'] = HANDLER_NORM(stats_deny, true, false),
['e'] = HANDLER_NORM(stats_exempt, true, false),
['E'] = HANDLER_NORM(stats_events, true, true),
['f'] = HANDLER_NORM(stats_comm, true, true),
['F'] = HANDLER_NORM(stats_comm, true, true),
['g'] = HANDLER_NORM(stats_prop_klines, true, false),
['h'] = HANDLER_NORM(stats_hubleaf, false, false),
['H'] = HANDLER_NORM(stats_hubleaf, false, false),
['i'] = HANDLER_NORM(stats_auth, false, false),
['I'] = HANDLER_NORM(stats_auth, false, false),
['k'] = HANDLER_NORM(stats_tklines, false, false),
['K'] = HANDLER_NORM(stats_klines, false, false),
['l'] = HANDLER_PARV(stats_ltrace, false, false),
['L'] = HANDLER_PARV(stats_ltrace, false, false),
['m'] = HANDLER_NORM(stats_messages, false, false),
['M'] = HANDLER_NORM(stats_messages, false, false),
['n'] = HANDLER_NORM(stats_dnsbl, false, false),
['o'] = HANDLER_NORM(stats_oper, false, false),
['O'] = HANDLER_NORM(stats_privset, true, false),
['p'] = HANDLER_NORM(stats_operedup, false, false),
['P'] = HANDLER_NORM(stats_ports, false, false),
['q'] = HANDLER_NORM(stats_tresv, true, false),
['Q'] = HANDLER_NORM(stats_resv, true, false),
['r'] = HANDLER_NORM(stats_usage, true, false),
['R'] = HANDLER_NORM(stats_usage, true, false),
['s'] = HANDLER_NORM(stats_ssld, true, true),
['S'] = HANDLER_NORM(stats_ssld, true, true),
['t'] = HANDLER_NORM(stats_tstats, true, false),
['T'] = HANDLER_NORM(stats_tstats, true, false),
['u'] = HANDLER_NORM(stats_uptime, false, false),
['U'] = HANDLER_NORM(stats_shared, true, false),
['v'] = HANDLER_NORM(stats_servers, false, false),
['V'] = HANDLER_NORM(stats_servers, false, false),
['x'] = HANDLER_NORM(stats_tgecos, true, false),
['X'] = HANDLER_NORM(stats_gecos, true, false),
['y'] = HANDLER_NORM(stats_class, false, false),
['Y'] = HANDLER_NORM(stats_class, false, false),
['z'] = HANDLER_NORM(stats_memory, true, false),
['Z'] = HANDLER_NORM(stats_ziplinks, true, false),
['?'] = HANDLER_NORM(stats_servlinks, false, false),
};
/*
* m_stats by fl_
* Modified heavily by Elizafox
* parv[1] = stat letter/command
* parv[2] = (if present) server/mask in stats L, or target
*
* This will search the tables for the appropriate stats letter,
* if found execute it.
*/
static void
m_stats(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
{
static time_t last_used = 0;
struct stats_cmd *cmd;
unsigned char statchar;
int did_stats = 0;
statchar = parv[1][0];
if(MyClient(source_p) && !IsOper(source_p))
{
/* Check the user is actually allowed to do /stats, and isnt flooding */
if((last_used + ConfigFileEntry.pace_wait) > rb_current_time())
{
/* safe enough to give this on a local connect only */
sendto_one(source_p, form_str(RPL_LOAD2HI),
me.name, source_p->name, "STATS");
sendto_one_numeric(source_p, RPL_ENDOFSTATS,
form_str(RPL_ENDOFSTATS), statchar);
return;
}
else
last_used = rb_current_time();
}
if(hunt_server (client_p, source_p, ":%s STATS %s :%s", 2, parc, parv) != HUNTED_ISME)
return;
if(tolower(statchar) != 'l')
/* FIXME */
did_stats = stats_spy(source_p, statchar, NULL);
/* if did_stats is true, a module grabbed this STATS request */
if(did_stats)
goto stats_out;
/* Look up */
cmd = &stats_cmd_table[statchar];
if(cmd->handler != NULL)
{
/* The stats table says what privs are needed, so check --fl_ */
/* Called for remote clients and for local opers, so check need_admin
* and need_oper
*/
if(cmd->need_admin && !IsOperAdmin(source_p))
{
sendto_one(source_p, form_str(ERR_NOPRIVS),
me.name, source_p->name, "admin");
goto stats_out;
}
if(cmd->need_oper && !IsOper(source_p))
{
sendto_one_numeric(source_p, ERR_NOPRIVILEGES,
form_str (ERR_NOPRIVILEGES));
goto stats_out;
}
if(cmd->need_parv)
cmd->handler_parv(source_p, parc, parv);
else
cmd->handler(source_p);
}
stats_out:
/* Send the end of stats notice, and the stats_spy */
sendto_one_numeric(source_p, RPL_ENDOFSTATS,
form_str(RPL_ENDOFSTATS), statchar);
}
static void
stats_dns_servers (struct Client *source_p)
{
rb_dlink_node *n;
RB_DLINK_FOREACH(n, nameservers.head)
{
sendto_one_numeric(source_p, RPL_STATSDEBUG, "A %s", (char *)n->data);
}
}
static void
stats_delay(struct Client *source_p)
{
struct nd_entry *nd;
rb_dictionary_iter iter;
RB_DICTIONARY_FOREACH(nd, &iter, nd_dict)
{
sendto_one_notice(source_p, ":Delaying: %s for %ld",
nd->name, (long) nd->expire);
}
}
static void
stats_hash_cb(const char *buf, void *client_p)
{
sendto_one_numeric(client_p, RPL_STATSDEBUG, "B :%s", buf);
}
static void
stats_hash(struct Client *source_p)
{
sendto_one_numeric(source_p, RPL_STATSDEBUG, "B :%-30s %-15s %-10s %-10s %-10s %-10s",
"NAME", "TYPE", "OBJECTS", "DEPTH SUM", "AVG DEPTH", "MAX DEPTH");
rb_dictionary_stats_walk(stats_hash_cb, source_p);
rb_radixtree_stats_walk(stats_hash_cb, source_p);
}
static void
stats_connect(struct Client *source_p)
{
static char buf[BUFSIZE];
struct server_conf *server_p;
char *s;
rb_dlink_node *ptr;
if((ConfigFileEntry.stats_c_oper_only ||
(ConfigServerHide.flatten_links && !IsExemptShide(source_p))) &&
!IsOper(source_p))
{
sendto_one_numeric(source_p, ERR_NOPRIVILEGES,
form_str(ERR_NOPRIVILEGES));
return;
}
RB_DLINK_FOREACH(ptr, server_conf_list.head)
{
server_p = ptr->data;
if(ServerConfIllegal(server_p))
continue;
s = buf;
if(IsOper(source_p))
{
if(ServerConfAutoconn(server_p))
*s++ = 'A';
if(ServerConfSCTP(server_p))
*s++ = 'M';
if(ServerConfSSL(server_p))
*s++ = 'S';
if(ServerConfTb(server_p))
*s++ = 'T';
if(ServerConfCompressed(server_p))
*s++ = 'Z';
}
if(s == buf)
*s++ = '*';
*s = '\0';
sendto_one_numeric(source_p, RPL_STATSCLINE,
form_str(RPL_STATSCLINE),
"*@127.0.0.1",
buf, server_p->name,
server_p->port, server_p->class_name,
server_p->certfp ? server_p->certfp : "*");
}
}
/* stats_tdeny()
*
* input - client to report to
* output - none
* side effects - client is given temp dline list.
*/
static void
stats_tdeny (struct Client *source_p)
{
char *host, *pass, *user, *oper_reason;
struct AddressRec *arec;
struct ConfItem *aconf;
int i;
for (i = 0; i < ATABLE_SIZE; i++)
{
for (arec = atable[i]; arec; arec = arec->next)
{
if(arec->type == CONF_DLINE)
{
aconf = arec->aconf;
if(!(aconf->flags & CONF_FLAGS_TEMPORARY))
continue;
get_printable_kline(source_p, aconf, &host, &pass, &user, &oper_reason);
sendto_one_numeric(source_p, RPL_STATSDLINE,
form_str (RPL_STATSDLINE),
'd', host, pass,
oper_reason ? "|" : "",
oper_reason ? oper_reason : "");
}
}
}
}
/* stats_deny()
*
* input - client to report to
* output - none
* side effects - client is given dline list.
*/
static void
stats_deny (struct Client *source_p)
{
char *host, *pass, *user, *oper_reason;
struct AddressRec *arec;
struct ConfItem *aconf;
int i;
for (i = 0; i < ATABLE_SIZE; i++)
{
for (arec = atable[i]; arec; arec = arec->next)
{
if(arec->type == CONF_DLINE)
{
aconf = arec->aconf;
if(aconf->flags & CONF_FLAGS_TEMPORARY)
continue;
get_printable_kline(source_p, aconf, &host, &pass, &user, &oper_reason);
sendto_one_numeric(source_p, RPL_STATSDLINE,
form_str (RPL_STATSDLINE),
'D', host, pass,
oper_reason ? "|" : "",
oper_reason ? oper_reason : "");
}
}
}
}
/* stats_exempt()
*
* input - client to report to
* output - none
* side effects - client is given list of exempt blocks
*/
static void
stats_exempt(struct Client *source_p)
{
char *name, *host, *user, *classname;
const char *pass;
struct AddressRec *arec;
struct ConfItem *aconf;
int i, port;
if(ConfigFileEntry.stats_e_disabled)
{
sendto_one_numeric(source_p, ERR_DISABLED,
form_str(ERR_DISABLED), "STATS e");
return;
}
for (i = 0; i < ATABLE_SIZE; i++)
{
for (arec = atable[i]; arec; arec = arec->next)
{
if(arec->type == CONF_EXEMPTDLINE)
{
aconf = arec->aconf;
get_printable_conf (aconf, &name, &host, &pass,
&user, &port, &classname);
sendto_one_numeric(source_p, RPL_STATSDLINE,
form_str(RPL_STATSDLINE),
'e', host, pass, "", "");
}
}
}}
static void
stats_events_cb(char *str, void *ptr)
{
sendto_one_numeric(ptr, RPL_STATSDEBUG, "E :%s", str);
}
static void
stats_events (struct Client *source_p)
{
rb_dump_events(stats_events_cb, source_p);
}
static void
stats_prop_klines(struct Client *source_p)
{
struct ConfItem *aconf;
rb_dlink_node *ptr;
char *user, *host, *pass, *oper_reason;
RB_DLINK_FOREACH(ptr, prop_bans.head)
{
aconf = ptr->data;
/* Skip non-klines and deactivated klines. */
if(aconf->status != CONF_KILL)
continue;
get_printable_kline(source_p, aconf, &host, &pass,
&user, &oper_reason);
sendto_one_numeric(source_p, RPL_STATSKLINE,
form_str(RPL_STATSKLINE),
'g', host, user, pass,
oper_reason ? "|" : "",
oper_reason ? oper_reason : "");
}
}
static void
stats_hubleaf(struct Client *source_p)
{
struct remote_conf *hub_p;
rb_dlink_node *ptr;
if((ConfigFileEntry.stats_h_oper_only ||
(ConfigServerHide.flatten_links && !IsExemptShide(source_p))) &&
!IsOper(source_p))
{
sendto_one_numeric(source_p, ERR_NOPRIVILEGES,
form_str (ERR_NOPRIVILEGES));
return;
}
RB_DLINK_FOREACH(ptr, hubleaf_conf_list.head)
{
hub_p = ptr->data;
if(hub_p->flags & CONF_HUB)
sendto_one_numeric(source_p, RPL_STATSHLINE,
form_str(RPL_STATSHLINE),
hub_p->host, hub_p->server);
else
sendto_one_numeric(source_p, RPL_STATSLLINE,
form_str(RPL_STATSLLINE),
hub_p->host, hub_p->server);
}
}
static void
stats_auth (struct Client *source_p)
{
/* Oper only, if unopered, return ERR_NOPRIVS */
if((ConfigFileEntry.stats_i_oper_only == 2) && !IsOper (source_p))
sendto_one_numeric(source_p, ERR_NOPRIVILEGES,
form_str (ERR_NOPRIVILEGES));
/* If unopered, Only return matching auth blocks */
else if((ConfigFileEntry.stats_i_oper_only == 1) && !IsOper (source_p))
{
struct ConfItem *aconf;
char *name, *host, *user, *classname;
const char *pass = "*";
int port;
if(MyConnect (source_p))
aconf = find_conf_by_address (source_p->host, source_p->sockhost, NULL,
(struct sockaddr *)&source_p->localClient->ip,
CONF_CLIENT,
GET_SS_FAMILY(&source_p->localClient->ip),
source_p->username, NULL);
else
aconf = find_conf_by_address (source_p->host, NULL, NULL, NULL, CONF_CLIENT,
0, source_p->username, NULL);
if(aconf == NULL)
return;
get_printable_conf (aconf, &name, &host, &pass, &user, &port, &classname);
if(!EmptyString(aconf->spasswd))
pass = aconf->spasswd;
sendto_one_numeric(source_p, RPL_STATSILINE, form_str(RPL_STATSILINE),
name, pass, show_iline_prefix(source_p, aconf, user),
host, port, classname);
}
/* Theyre opered, or allowed to see all auth blocks */
else
report_auth (source_p);
}
static void
stats_tklines(struct Client *source_p)
{
/* Oper only, if unopered, return ERR_NOPRIVS */
if((ConfigFileEntry.stats_k_oper_only == 2) && !IsOper (source_p))
sendto_one_numeric(source_p, ERR_NOPRIVILEGES,
form_str (ERR_NOPRIVILEGES));
/* If unopered, Only return matching klines */
else if((ConfigFileEntry.stats_k_oper_only == 1) && !IsOper (source_p))
{
struct ConfItem *aconf;
char *host, *pass, *user, *oper_reason;
if(MyConnect (source_p))
aconf = find_conf_by_address (source_p->host, source_p->sockhost, NULL,
(struct sockaddr *)&source_p->localClient->ip,
CONF_KILL,
GET_SS_FAMILY(&source_p->localClient->ip),
source_p->username, NULL);
else
aconf = find_conf_by_address (source_p->host, NULL, NULL, NULL, CONF_KILL,
0, source_p->username, NULL);
if(aconf == NULL)
return;
/* dont report a permanent kline as a tkline */
if((aconf->flags & CONF_FLAGS_TEMPORARY) == 0)
return;
get_printable_kline(source_p, aconf, &host, &pass, &user, &oper_reason);
sendto_one_numeric(source_p, RPL_STATSKLINE,
form_str(RPL_STATSKLINE), (aconf->flags & CONF_FLAGS_TEMPORARY) ? 'k' : 'K',
host, user, pass, oper_reason ? "|" : "",
oper_reason ? oper_reason : "");
}
/* Theyre opered, or allowed to see all klines */
else
{
struct ConfItem *aconf;
rb_dlink_node *ptr;
int i;
char *user, *host, *pass, *oper_reason;
for(i = 0; i < LAST_TEMP_TYPE; i++)
{
RB_DLINK_FOREACH(ptr, temp_klines[i].head)
{
aconf = ptr->data;
get_printable_kline(source_p, aconf, &host, &pass,
&user, &oper_reason);
sendto_one_numeric(source_p, RPL_STATSKLINE,
form_str(RPL_STATSKLINE),
'k', host, user, pass,
oper_reason ? "|" : "",
oper_reason ? oper_reason : "");
}
}
}
}
/* report_Klines()
*
* inputs - Client to report to, mask
* outputs -
* side effects - Reports configured K-lines to client_p.
*/
static void
report_Klines(struct Client *source_p)
{
char *host, *pass, *user, *oper_reason;
struct AddressRec *arec;
struct ConfItem *aconf = NULL;
int i;
for (i = 0; i < ATABLE_SIZE; i++)
{
for (arec = atable[i]; arec; arec = arec->next)
{
if(arec->type == CONF_KILL)
{
aconf = arec->aconf;
/* its a tempkline, theyre reported elsewhere */
if(aconf->flags & CONF_FLAGS_TEMPORARY)
continue;
get_printable_kline(source_p, aconf, &host, &pass, &user, &oper_reason);
sendto_one_numeric(source_p, RPL_STATSKLINE,
form_str(RPL_STATSKLINE),
'K', host, user, pass,
oper_reason ? "|" : "",
oper_reason ? oper_reason : "");
}
}
}
}
static void
stats_klines(struct Client *source_p)
{
/* Oper only, if unopered, return ERR_NOPRIVS */
if((ConfigFileEntry.stats_k_oper_only == 2) && !IsOper (source_p))
sendto_one_numeric(source_p, ERR_NOPRIVILEGES,
form_str (ERR_NOPRIVILEGES));
/* If unopered, Only return matching klines */
else if((ConfigFileEntry.stats_k_oper_only == 1) && !IsOper (source_p))
{
struct ConfItem *aconf;
char *host, *pass, *user, *oper_reason;
/* search for a kline */
if(MyConnect (source_p))
aconf = find_conf_by_address (source_p->host, source_p->sockhost, NULL,
(struct sockaddr *)&source_p->localClient->ip,
CONF_KILL,
GET_SS_FAMILY(&source_p->localClient->ip),
source_p->username, NULL);
else
aconf = find_conf_by_address (source_p->host, NULL, NULL, NULL, CONF_KILL,
0, source_p->username, NULL);
if(aconf == NULL)
return;
get_printable_kline(source_p, aconf, &host, &pass, &user, &oper_reason);
sendto_one_numeric(source_p, RPL_STATSKLINE, form_str(RPL_STATSKLINE),
(aconf->flags & CONF_FLAGS_TEMPORARY) ? 'k' : 'K',
host, user, pass, oper_reason ? "|" : "",
oper_reason ? oper_reason : "");
}
/* Theyre opered, or allowed to see all klines */
else
report_Klines (source_p);
}
static void
stats_messages(struct Client *source_p)
{
rb_dictionary_iter iter;
struct Message *msg;
RB_DICTIONARY_FOREACH(msg, &iter, cmd_dict)
{
s_assert(msg->cmd != NULL);
sendto_one_numeric(source_p, RPL_STATSCOMMANDS,
form_str(RPL_STATSCOMMANDS),
msg->cmd, msg->count,
msg->bytes, msg->rcount);
}
}
static void
stats_dnsbl(struct Client *source_p)
{
rb_dictionary_iter iter;
struct BlacklistStats *stats;
if(bl_stats == NULL)
return;
RB_DICTIONARY_FOREACH(stats, &iter, bl_stats)
{
/* use RPL_STATSDEBUG for now -- jilles */
sendto_one_numeric(source_p, RPL_STATSDEBUG, "n :%d %s",
stats->hits, (const char *)iter.cur->key);
}
}
static void
stats_oper(struct Client *source_p)
{
struct oper_conf *oper_p;
rb_dlink_node *ptr;
if(!IsOper(source_p) && ConfigFileEntry.stats_o_oper_only)
{
sendto_one_numeric(source_p, ERR_NOPRIVILEGES,
form_str (ERR_NOPRIVILEGES));
return;
}
RB_DLINK_FOREACH(ptr, oper_conf_list.head)
{
oper_p = ptr->data;
sendto_one_numeric(source_p, RPL_STATSOLINE,
form_str(RPL_STATSOLINE),
oper_p->username, oper_p->host, oper_p->name,
IsOper(source_p) ? oper_p->privset->name : "0", "-1");
}
}
static void
stats_capability_walk(const char *line, void *data)
{
struct Client *client_p = data;
sendto_one_numeric(client_p, RPL_STATSDEBUG, "C :%s", line);
}
static void
stats_capability(struct Client *client_p)
{
capability_index_stats(stats_capability_walk, client_p);
}
static void
stats_privset(struct Client *source_p)
{
privilegeset_report(source_p);
}
/* stats_operedup()
*
* input - client pointer
* output - none
* side effects - client is shown a list of active opers
*/
static void
stats_operedup (struct Client *source_p)
{
struct Client *target_p;
rb_dlink_node *oper_ptr;
unsigned int count = 0;
RB_DLINK_FOREACH (oper_ptr, oper_list.head)
{
target_p = oper_ptr->data;
if(!SeesOper(target_p, source_p))
continue;
if(target_p->user->away)
continue;
count++;
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"p :%s (%s@%s)",
target_p->name, target_p->username,
target_p->host);
}
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"p :%u staff members", count);
stats_p_spy (source_p);
}
static void
stats_ports (struct Client *source_p)
{
if(!IsOper (source_p) && ConfigFileEntry.stats_P_oper_only)
sendto_one_numeric(source_p, ERR_NOPRIVILEGES,
form_str (ERR_NOPRIVILEGES));
else
show_ports (source_p);
}
static void
stats_tresv(struct Client *source_p)
{
struct ConfItem *aconf;
rb_radixtree_iteration_state state;
rb_dlink_node *ptr;
RB_DLINK_FOREACH(ptr, resv_conf_list.head)
{
aconf = ptr->data;
if(aconf->hold)
sendto_one_numeric(source_p, RPL_STATSQLINE,
form_str(RPL_STATSQLINE),
'q', aconf->port, aconf->host, aconf->passwd);
}
RB_RADIXTREE_FOREACH(aconf, &state, resv_tree)
{
if(aconf->hold)
sendto_one_numeric(source_p, RPL_STATSQLINE,
form_str(RPL_STATSQLINE),
'q', aconf->port, aconf->host, aconf->passwd);
}
}
static void
stats_resv(struct Client *source_p)
{
struct ConfItem *aconf;
rb_radixtree_iteration_state state;
rb_dlink_node *ptr;
RB_DLINK_FOREACH(ptr, resv_conf_list.head)
{
aconf = ptr->data;
if(!aconf->hold)
sendto_one_numeric(source_p, RPL_STATSQLINE,
form_str(RPL_STATSQLINE),
'Q', aconf->port, aconf->host, aconf->passwd);
}
RB_RADIXTREE_FOREACH(aconf, &state, resv_tree)
{
if(!aconf->hold)
sendto_one_numeric(source_p, RPL_STATSQLINE,
form_str(RPL_STATSQLINE),
'Q', aconf->port, aconf->host, aconf->passwd);
}
}
static void
stats_ssld_foreach(void *data, pid_t pid, int cli_count, enum ssld_status status, const char *version)
{
struct Client *source_p = data;
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"S :%u %c %u :%s",
pid,
status == SSLD_DEAD ? 'D' : (status == SSLD_SHUTDOWN ? 'S' : 'A'),
cli_count,
version);
}
static void
stats_ssld(struct Client *source_p)
{
ssld_foreach_info(stats_ssld_foreach, source_p);
}
static void
stats_usage (struct Client *source_p)
{
#ifndef _WIN32
struct rusage rus;
time_t secs;
time_t rup;
#ifdef hz
# define hzz hz
#else
# ifdef HZ
# define hzz HZ
# else
int hzz = 1;
# endif
#endif
if(getrusage(RUSAGE_SELF, &rus) == -1)
{
sendto_one_notice(source_p, ":Getruseage error: %s.",
strerror(errno));
return;
}
secs = rus.ru_utime.tv_sec + rus.ru_stime.tv_sec;
if(0 == secs)
secs = 1;
rup = (rb_current_time() - startup_time) * hzz;
if(0 == rup)
rup = 1;
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"R :CPU Secs %d:%02d User %d:%02d System %d:%02d",
(int) (secs / 60), (int) (secs % 60),
(int) (rus.ru_utime.tv_sec / 60),
(int) (rus.ru_utime.tv_sec % 60),
(int) (rus.ru_stime.tv_sec / 60),
(int) (rus.ru_stime.tv_sec % 60));
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"R :RSS %ld ShMem %ld Data %ld Stack %ld",
rus.ru_maxrss, (rus.ru_ixrss / rup),
(rus.ru_idrss / rup), (rus.ru_isrss / rup));
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"R :Swaps %d Reclaims %d Faults %d",
(int) rus.ru_nswap, (int) rus.ru_minflt, (int) rus.ru_majflt);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"R :Block in %d out %d",
(int) rus.ru_inblock, (int) rus.ru_oublock);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"R :Msg Rcv %d Send %d",
(int) rus.ru_msgrcv, (int) rus.ru_msgsnd);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"R :Signals %d Context Vol. %d Invol %d",
(int) rus.ru_nsignals, (int) rus.ru_nvcsw,
(int) rus.ru_nivcsw);
#endif
}
static void
stats_tstats (struct Client *source_p)
{
struct Client *target_p;
struct ServerStatistics sp;
rb_dlink_node *ptr;
memcpy(&sp, &ServerStats, sizeof(struct ServerStatistics));
RB_DLINK_FOREACH(ptr, serv_list.head)
{
target_p = ptr->data;
sp.is_sbs += target_p->localClient->sendB;
sp.is_sbr += target_p->localClient->receiveB;
sp.is_sti += (unsigned long long)(rb_current_time() - target_p->localClient->firsttime);
sp.is_sv++;
}
RB_DLINK_FOREACH(ptr, lclient_list.head)
{
target_p = ptr->data;
sp.is_cbs += target_p->localClient->sendB;
sp.is_cbr += target_p->localClient->receiveB;
sp.is_cti += (unsigned long long)(rb_current_time() - target_p->localClient->firsttime);
sp.is_cl++;
}
RB_DLINK_FOREACH(ptr, unknown_list.head)
{
sp.is_ni++;
}
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"T :accepts %u refused %u", sp.is_ac, sp.is_ref);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"T :rejected %u delaying %lu",
sp.is_rej, delay_exit_length());
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"T :throttled refused %u throttle list size %lu", sp.is_thr, throttle_size());
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"T :nicks being delayed %lu",
get_nd_count());
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"T :unknown commands %u prefixes %u",
sp.is_unco, sp.is_unpf);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"T :nick collisions %u saves %u unknown closes %u",
sp.is_kill, sp.is_save, sp.is_ni);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"T :wrong direction %u empty %u",
sp.is_wrdi, sp.is_empt);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"T :numerics seen %u", sp.is_num);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"T :tgchange blocked msgs %u restricted addrs %lu",
sp.is_tgch, rb_dlink_list_length(&tgchange_list));
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"T :ratelimit blocked commands %u", sp.is_rl);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"T :auth successes %u fails %u",
sp.is_asuc, sp.is_abad);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"T :sasl successes %u fails %u",
sp.is_ssuc, sp.is_sbad);
sendto_one_numeric(source_p, RPL_STATSDEBUG, "T :Client Server");
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"T :connected %u %u", sp.is_cl, sp.is_sv);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"T :bytes sent %lluK %lluK",
sp.is_cbs / 1024,
sp.is_sbs / 1024);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"T :bytes recv %lluK %lluK",
sp.is_cbr / 1024,
sp.is_sbr / 1024);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"T :time connected %llu %llu",
sp.is_cti, sp.is_sti);
}
static void
stats_uptime (struct Client *source_p)
{
time_t now;
now = rb_current_time() - startup_time;
sendto_one_numeric(source_p, RPL_STATSUPTIME,
form_str (RPL_STATSUPTIME),
(int)(now / 86400), (int)((now / 3600) % 24),
(int)((now / 60) % 60), (int)(now % 60));
sendto_one_numeric(source_p, RPL_STATSCONN,
form_str (RPL_STATSCONN),
MaxConnectionCount, MaxClientCount,
Count.totalrestartcount);
}
struct shared_flags
{
int flag;
char letter;
};
static struct shared_flags shared_flagtable[] =
{
{ SHARED_PKLINE, 'K' },
{ SHARED_TKLINE, 'k' },
{ SHARED_UNKLINE, 'U' },
{ SHARED_PXLINE, 'X' },
{ SHARED_TXLINE, 'x' },
{ SHARED_UNXLINE, 'Y' },
{ SHARED_PRESV, 'Q' },
{ SHARED_TRESV, 'q' },
{ SHARED_UNRESV, 'R' },
{ SHARED_LOCOPS, 'L' },
{ SHARED_REHASH, 'H' },
{ SHARED_TDLINE, 'd' },
{ SHARED_PDLINE, 'D' },
{ SHARED_UNDLINE, 'E' },
{ SHARED_GRANT, 'G' },
{ SHARED_DIE, 'I' },
{ 0, '\0'}
};
static void
stats_shared (struct Client *source_p)
{
struct remote_conf *shared_p;
rb_dlink_node *ptr;
char buf[sizeof(shared_flagtable)/sizeof(shared_flagtable[0])];
char *p;
int i;
RB_DLINK_FOREACH(ptr, shared_conf_list.head)
{
shared_p = ptr->data;
p = buf;
*p++ = 'c';
for(i = 0; shared_flagtable[i].flag != 0; i++)
{
if(shared_p->flags & shared_flagtable[i].flag)
*p++ = shared_flagtable[i].letter;
}
*p = '\0';
sendto_one_numeric(source_p, RPL_STATSULINE,
form_str(RPL_STATSULINE),
shared_p->server, shared_p->username,
shared_p->host, buf);
}
RB_DLINK_FOREACH(ptr, cluster_conf_list.head)
{
shared_p = ptr->data;
p = buf;
*p++ = 'C';
for(i = 0; shared_flagtable[i].flag != 0; i++)
{
if(shared_p->flags & shared_flagtable[i].flag)
*p++ = shared_flagtable[i].letter;
}
*p = '\0';
sendto_one_numeric(source_p, RPL_STATSULINE,
form_str(RPL_STATSULINE),
shared_p->server, "*", "*", buf);
}
}
/* stats_servers()
*
* input - client pointer
* output - none
* side effects - client is shown lists of who connected servers
*/
static void
stats_servers (struct Client *source_p)
{
struct Client *target_p;
rb_dlink_node *ptr;
time_t seconds;
int days, hours, minutes;
int j = 0;
if(ConfigServerHide.flatten_links && !IsOper(source_p) &&
!IsExemptShide(source_p))
{
sendto_one_numeric(source_p, ERR_NOPRIVILEGES,
form_str (ERR_NOPRIVILEGES));
return;
}
RB_DLINK_FOREACH (ptr, serv_list.head)
{
target_p = ptr->data;
j++;
seconds = rb_current_time() - target_p->localClient->firsttime;
days = (int) (seconds / 86400);
seconds %= 86400;
hours = (int) (seconds / 3600);
seconds %= 3600;
minutes = (int) (seconds / 60);
seconds %= 60;
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"V :%s (%s!*@*) Idle: %d SendQ: %d "
"Connected: %d day%s, %d:%02d:%02d",
target_p->name,
(target_p->serv->by[0] ? target_p->serv->by : "Remote."),
(int) (rb_current_time() - target_p->localClient->lasttime),
(int) rb_linebuf_len (&target_p->localClient->buf_sendq),
days, (days == 1) ? "" : "s", hours, minutes,
(int) seconds);
}
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"V :%d Server(s)", j);
}
static void
stats_tgecos(struct Client *source_p)
{
struct ConfItem *aconf;
rb_dlink_node *ptr;
RB_DLINK_FOREACH(ptr, xline_conf_list.head)
{
aconf = ptr->data;
if(aconf->hold)
sendto_one_numeric(source_p, RPL_STATSXLINE,
form_str(RPL_STATSXLINE),
'x', aconf->port, aconf->host,
aconf->passwd);
}
}
static void
stats_gecos(struct Client *source_p)
{
struct ConfItem *aconf;
rb_dlink_node *ptr;
RB_DLINK_FOREACH(ptr, xline_conf_list.head)
{
aconf = ptr->data;
if(!aconf->hold)
sendto_one_numeric(source_p, RPL_STATSXLINE,
form_str(RPL_STATSXLINE),
'X', aconf->port, aconf->host,
aconf->passwd);
}
}
static void
stats_class(struct Client *source_p)
{
if(ConfigFileEntry.stats_y_oper_only && !IsOper(source_p))
sendto_one_numeric(source_p, ERR_NOPRIVILEGES,
form_str (ERR_NOPRIVILEGES));
else
report_classes(source_p);
}
static void
stats_memory (struct Client *source_p)
{
struct Client *target_p;
struct Channel *chptr;
rb_dlink_node *rb_dlink;
rb_dlink_node *ptr;
int channel_count = 0;
int local_client_conf_count = 0; /* local client conf links */
int users_counted = 0; /* user structs */
int channel_users = 0;
int channel_invites = 0;
int channel_bans = 0;
int channel_except = 0;
int channel_invex = 0;
int channel_quiets = 0;
int class_count = 0; /* classes */
int conf_count = 0; /* conf lines */
int users_invited_count = 0; /* users invited */
int user_channels = 0; /* users in channels */
int aways_counted = 0;
size_t number_servers_cached; /* number of servers cached by scache */
size_t channel_memory = 0;
size_t channel_ban_memory = 0;
size_t channel_except_memory = 0;
size_t channel_invex_memory = 0;
size_t channel_quiet_memory = 0;
size_t away_memory = 0; /* memory used by aways */
size_t ww = 0; /* whowas array count */
size_t wwm = 0; /* whowas array memory used */
size_t conf_memory = 0; /* memory used by conf lines */
size_t mem_servers_cached; /* memory used by scache */
size_t linebuf_count = 0;
size_t linebuf_memory_used = 0;
size_t total_channel_memory = 0;
size_t totww = 0;
size_t local_client_count = 0;
size_t local_client_memory_used = 0;
size_t remote_client_count = 0;
size_t remote_client_memory_used = 0;
size_t total_memory = 0;
whowas_memory_usage(&ww, &wwm);
RB_DLINK_FOREACH(ptr, global_client_list.head)
{
target_p = ptr->data;
if(MyConnect(target_p))
{
local_client_conf_count++;
}
if(target_p->user)
{
users_counted++;
users_invited_count += rb_dlink_list_length(&target_p->user->invited);
user_channels += rb_dlink_list_length(&target_p->user->channel);
if(target_p->user->away)
{
aways_counted++;
away_memory += (strlen(target_p->user->away) + 1);
}
}
}
/* Count up all channels, ban lists, except lists, Invex lists */
RB_DLINK_FOREACH(ptr, global_channel_list.head)
{
chptr = ptr->data;
channel_count++;
channel_memory += (strlen(chptr->chname) + sizeof(struct Channel));
channel_users += rb_dlink_list_length(&chptr->members);
channel_invites += rb_dlink_list_length(&chptr->invites);
RB_DLINK_FOREACH(rb_dlink, chptr->banlist.head)
{
channel_bans++;
channel_ban_memory += sizeof(rb_dlink_node) + sizeof(struct Ban);
}
RB_DLINK_FOREACH(rb_dlink, chptr->exceptlist.head)
{
channel_except++;
channel_except_memory += (sizeof(rb_dlink_node) + sizeof(struct Ban));
}
RB_DLINK_FOREACH(rb_dlink, chptr->invexlist.head)
{
channel_invex++;
channel_invex_memory += (sizeof(rb_dlink_node) + sizeof(struct Ban));
}
RB_DLINK_FOREACH(rb_dlink, chptr->quietlist.head)
{
channel_quiets++;
channel_quiet_memory += (sizeof(rb_dlink_node) + sizeof(struct Ban));
}
}
/* count up all classes */
class_count = rb_dlink_list_length(&class_list) + 1;
rb_count_rb_linebuf_memory(&linebuf_count, &linebuf_memory_used);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"z :Users %u(%lu) Invites %u(%lu)",
users_counted,
(unsigned long) users_counted * sizeof(struct User),
users_invited_count,
(unsigned long) users_invited_count * sizeof(rb_dlink_node));
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"z :User channels %u(%lu) Aways %u(%d)",
user_channels,
(unsigned long) user_channels * sizeof(rb_dlink_node),
aways_counted, (int) away_memory);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"z :Attached confs %u(%lu)",
local_client_conf_count,
(unsigned long) local_client_conf_count * sizeof(rb_dlink_node));
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"z :Conflines %u(%d)", conf_count, (int) conf_memory);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"z :Classes %u(%lu)",
class_count,
(unsigned long) class_count * sizeof(struct Class));
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"z :Channels %u(%d)",
channel_count, (int) channel_memory);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"z :Bans %u(%d) Exceptions %u(%d) Invex %u(%d) Quiets %u(%d)",
channel_bans, (int) channel_ban_memory,
channel_except, (int) channel_except_memory,
channel_invex, (int) channel_invex_memory,
channel_quiets, (int) channel_quiet_memory);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"z :Channel members %u(%lu) invite %u(%lu)",
channel_users,
(unsigned long) channel_users * sizeof(rb_dlink_node),
channel_invites,
(unsigned long) channel_invites * sizeof(rb_dlink_node));
total_channel_memory = channel_memory +
channel_ban_memory +
channel_users * sizeof(rb_dlink_node) + channel_invites * sizeof(rb_dlink_node);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"z :Whowas array %ld(%ld)",
(long)ww, (long)wwm);
totww = wwm;
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"z :Hash: client %u(%ld) chan %u(%ld)",
U_MAX, (long)(U_MAX * sizeof(rb_dlink_list)),
CH_MAX, (long)(CH_MAX * sizeof(rb_dlink_list)));
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"z :linebuf %ld(%ld)",
(long)linebuf_count, (long)linebuf_memory_used);
count_scache(&number_servers_cached, &mem_servers_cached);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"z :scache %ld(%ld)",
(long)number_servers_cached, (long)mem_servers_cached);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"z :hostname hash %d(%ld)",
HOST_MAX, (long)HOST_MAX * sizeof(rb_dlink_list));
total_memory = totww + total_channel_memory + conf_memory +
class_count * sizeof(struct Class);
total_memory += mem_servers_cached;
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"z :Total: whowas %d channel %d conf %d",
(int) totww, (int) total_channel_memory,
(int) conf_memory);
count_local_client_memory(&local_client_count, &local_client_memory_used);
total_memory += local_client_memory_used;
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"z :Local client Memory in use: %ld(%ld)",
(long)local_client_count, (long)local_client_memory_used);
count_remote_client_memory(&remote_client_count, &remote_client_memory_used);
total_memory += remote_client_memory_used;
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"z :Remote client Memory in use: %ld(%ld)",
(long)remote_client_count,
(long)remote_client_memory_used);
}
static void
stats_ziplinks (struct Client *source_p)
{
rb_dlink_node *ptr;
struct Client *target_p;
struct ZipStats *zipstats;
int sent_data = 0;
char buf[128], buf1[128];
RB_DLINK_FOREACH (ptr, serv_list.head)
{
target_p = ptr->data;
if(IsCapable (target_p, CAP_ZIP))
{
zipstats = target_p->localClient->zipstats;
sprintf(buf, "%.2f%%", zipstats->out_ratio);
sprintf(buf1, "%.2f%%", zipstats->in_ratio);
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"Z :ZipLinks stats for %s send[%s compression "
"(%llu kB data/%llu kB wire)] recv[%s compression "
"(%llu kB data/%llu kB wire)]",
target_p->name,
buf, zipstats->out >> 10,
zipstats->out_wire >> 10, buf1,
zipstats->in >> 10, zipstats->in_wire >> 10);
sent_data++;
}
}
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"Z :%u ziplink(s)", sent_data);
}
static void
stats_servlinks (struct Client *source_p)
{
static char Sformat[] = ":%s %d %s %s %u %u %u %u %u :%u %u %s";
long uptime, sendK, receiveK;
struct Client *target_p;
rb_dlink_node *ptr;
int j = 0;
char buf[128];
if(ConfigServerHide.flatten_links && !IsOper (source_p) &&
!IsExemptShide(source_p))
{
sendto_one_numeric(source_p, ERR_NOPRIVILEGES,
form_str (ERR_NOPRIVILEGES));
return;
}
sendK = receiveK = 0;
RB_DLINK_FOREACH (ptr, serv_list.head)
{
target_p = ptr->data;
j++;
sendK += target_p->localClient->sendK;
receiveK += target_p->localClient->receiveK;
sendto_one(source_p, Sformat,
get_id(&me, source_p), RPL_STATSLINKINFO, get_id(source_p, source_p),
target_p->name,
(int) rb_linebuf_len (&target_p->localClient->buf_sendq),
(int) target_p->localClient->sendM,
(int) target_p->localClient->sendK,
(int) target_p->localClient->receiveM,
(int) target_p->localClient->receiveK,
rb_current_time() - target_p->localClient->firsttime,
(rb_current_time() > target_p->localClient->lasttime) ?
(rb_current_time() - target_p->localClient->lasttime) : 0,
IsOper (source_p) ? show_capabilities (target_p) : "TS");
}
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"? :%u total server(s)", j);
snprintf(buf, sizeof buf, "%7.2f", _GMKv ((sendK)));
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"? :Sent total : %s %s",
buf, _GMKs (sendK));
snprintf(buf, sizeof buf, "%7.2f", _GMKv ((receiveK)));
sendto_one_numeric(source_p, RPL_STATSDEBUG,
"? :Recv total : %s %s",
buf, _GMKs (receiveK));
uptime = (rb_current_time() - startup_time);
snprintf(buf, sizeof buf, "%7.2f %s (%4.1f K/s)",
_GMKv (me.localClient->sendK),
_GMKs (me.localClient->sendK),
(float) ((float) me.localClient->sendK / (float) uptime));
sendto_one_numeric(source_p, RPL_STATSDEBUG, "? :Server send: %s", buf);
snprintf(buf, sizeof buf, "%7.2f %s (%4.1f K/s)",
_GMKv (me.localClient->receiveK),
_GMKs (me.localClient->receiveK),
(float) ((float) me.localClient->receiveK / (float) uptime));
sendto_one_numeric(source_p, RPL_STATSDEBUG, "? :Server recv: %s", buf);
}
static inline bool
stats_l_should_show_oper(struct Client *target_p)
{
return (!IsOperInvis(target_p));
}
static void
stats_ltrace(struct Client *source_p, int parc, const char *parv[])
{
bool doall = false;
bool wilds = false;
const char *name;
char statchar = parv[1][0];
/* this is def targeted at us somehow.. */
if(parc > 2 && !EmptyString(parv[2]))
{
/* directed at us generically? */
if(match(parv[2], me.name) ||
(!MyClient(source_p) && !irccmp(parv[2], me.id)))
{
name = me.name;
doall = true;
}
else
{
name = parv[2];
wilds = strchr(name, '*') || strchr(name, '?');
}
/* must be directed at a specific person thats not us */
if(!doall && !wilds)
{
struct Client *target_p;
if(MyClient(source_p))
target_p = find_named_person(name);
else
target_p = find_person(name);
if(target_p != NULL)
{
stats_spy(source_p, statchar, target_p->name);
stats_l_client(source_p, target_p, statchar);
}
else
sendto_one_numeric(source_p, ERR_NOSUCHSERVER,
form_str(ERR_NOSUCHSERVER),
name);
return;
}
}
else
{
name = me.name;
doall = true;
}
stats_spy(source_p, statchar, name);
if(doall)
{
/* local opers get everyone */
if(MyOper(source_p))
{
stats_l_list(source_p, name, doall, wilds, &unknown_list, statchar, NULL);
stats_l_list(source_p, name, doall, wilds, &lclient_list, statchar, NULL);
}
else
{
/* they still need themselves if theyre local.. */
if(MyClient(source_p))
stats_l_client(source_p, source_p, statchar);
stats_l_list(source_p, name, doall, wilds, &local_oper_list, statchar, stats_l_should_show_oper);
}
if (!ConfigServerHide.flatten_links || IsOper(source_p) ||
IsExemptShide(source_p))
stats_l_list(source_p, name, doall, wilds, &serv_list, statchar, NULL);
return;
}
/* ok, at this point theyre looking for a specific client whos on
* our server.. but it contains a wildcard. --fl
*/
stats_l_list(source_p, name, doall, wilds, &lclient_list, statchar, NULL);
return;
}
static void
stats_l_list(struct Client *source_p, const char *name, bool doall, bool wilds,
rb_dlink_list * list, char statchar, bool (*check_fn)(struct Client *target_p))
{
rb_dlink_node *ptr;
struct Client *target_p;
/* send information about connections which match. note, we
* dont need tests for IsInvisible(), because non-opers will
* never get here for normal clients --fl
*/
RB_DLINK_FOREACH(ptr, list->head)
{
target_p = ptr->data;
if(!doall && wilds && !match(name, target_p->name))
continue;
if (check_fn == NULL || check_fn(target_p))
stats_l_client(source_p, target_p, statchar);
}
}
void
stats_l_client(struct Client *source_p, struct Client *target_p,
char statchar)
{
if(IsAnyServer(target_p))
{
sendto_one_numeric(source_p, RPL_STATSLINKINFO, Lformat,
target_p->name,
(int) rb_linebuf_len(&target_p->localClient->buf_sendq),
(int) target_p->localClient->sendM,
(int) target_p->localClient->sendK,
(int) target_p->localClient->receiveM,
(int) target_p->localClient->receiveK,
rb_current_time() - target_p->localClient->firsttime,
(rb_current_time() > target_p->localClient->lasttime) ?
(rb_current_time() - target_p->localClient->lasttime) : 0,
IsOper(source_p) ? show_capabilities(target_p) : "-");
}
else
{
sendto_one_numeric(source_p, RPL_STATSLINKINFO, Lformat,
show_ip(source_p, target_p) ?
(IsUpper(statchar) ?
get_client_name(target_p, SHOW_IP) :
get_client_name(target_p, HIDE_IP)) :
get_client_name(target_p, MASK_IP),
(int) rb_linebuf_len(&target_p->localClient->buf_sendq),
(int) target_p->localClient->sendM,
(int) target_p->localClient->sendK,
(int) target_p->localClient->receiveM,
(int) target_p->localClient->receiveK,
rb_current_time() - target_p->localClient->firsttime,
(rb_current_time() > target_p->localClient->lasttime) ?
(rb_current_time() - target_p->localClient->lasttime) : 0,
"-");
}
}
static void
rb_dump_fd_callback(int fd, const char *desc, void *data)
{
struct Client *source_p = data;
sendto_one_numeric(source_p, RPL_STATSDEBUG, "F :fd %-3d desc '%s'", fd, desc);
}
static void
stats_comm(struct Client *source_p)
{
rb_dump_fd(rb_dump_fd_callback, source_p);
}
/*
* stats_spy
*
* inputs - pointer to client doing the /stats
* - char letter they are doing /stats on
* output - none
* side effects -
* This little helper function reports to opers if configured.
*/
static int
stats_spy(struct Client *source_p, char statchar, const char *name)
{
hook_data_int data;
data.client = source_p;
data.arg1 = name;
data.arg2 = (int) statchar;
data.result = 0;
call_hook(doing_stats_hook, &data);
return data.result;
}
/* stats_p_spy()
*
* input - pointer to client doing stats
* ouput -
* side effects - call hook doing_stats_p
*/
static void
stats_p_spy (struct Client *source_p)
{
hook_data data;
data.client = source_p;
data.arg1 = data.arg2 = NULL;
call_hook(doing_stats_p_hook, &data);
}