]>
jfr.im git - irc/quakenet/snircd.git/blob - ircd/s_stats.c
2 * IRC - Internet Relay Chat, ircd/s_stats.c
3 * Copyright (C) 2000 Joseph Bongaarts
5 * See file AUTHORS in IRC package for additional names of
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 1, or (at your option)
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include "ircd_chattr.h"
30 #include "ircd_events.h"
31 #include "ircd_features.h"
32 #include "ircd_crypt.h"
34 #include "ircd_reply.h"
35 #include "ircd_string.h"
44 #include "querycmds.h"
64 * @brief Report configuration lines and other statistics from this
66 * @version $Id: s_stats.c,v 1.44.2.4 2006/03/14 14:56:50 entrope Exp $
68 * Note: The info is reported in the order the server uses
69 * it--not reversed as in ircd.conf!
72 /* The statsinfo array should only be used in this file, but just TRY
73 * telling the compiler that you want to forward declare a static
74 * array without specifying a length, and see how it responds. So we
75 * forward declare it "extern".
77 extern struct StatDesc statsinfo
[];
79 /** Report items from #GlobalConfList.
80 * Uses sd->sd_funcdata as a filter for ConfItem::status.
81 * @param[in] sptr Client requesting statistics.
82 * @param[in] sd Stats descriptor for request.
83 * @param[in] param Extra parameter from user (ignored).
86 stats_configured_links(struct Client
*sptr
, const struct StatDesc
* sd
,
89 static char null
[] = "<NULL>";
91 unsigned short int port
;
93 char *host
, *pass
, *name
, *username
, *hub_limit
;
95 for (tmp
= GlobalConfList
; tmp
; tmp
= tmp
->next
)
97 if ((tmp
->status
& sd
->sd_funcdata
))
99 host
= BadPtr(tmp
->host
) ? null
: tmp
->host
;
100 pass
= BadPtr(tmp
->passwd
) ? null
: tmp
->passwd
;
101 name
= BadPtr(tmp
->name
) ? null
: tmp
->name
;
102 username
= BadPtr(tmp
->username
) ? null
: tmp
->username
;
103 hub_limit
= BadPtr(tmp
->hub_limit
) ? null
: tmp
->hub_limit
;
104 maximum
= tmp
->maximum
;
105 port
= tmp
->address
.port
;
107 if (tmp
->status
& CONF_UWORLD
)
108 send_reply(sptr
, RPL_STATSULINE
, host
);
109 else if (tmp
->status
& CONF_SERVER
)
110 send_reply(sptr
, RPL_STATSCLINE
, name
, port
, maximum
, hub_limit
, get_conf_class(tmp
));
111 else if (tmp
->status
& CONF_CLIENT
)
112 send_reply(sptr
, RPL_STATSILINE
,
113 (tmp
->username
? tmp
->username
: ""), (tmp
->username
? "@" : ""),
114 (tmp
->host
? tmp
->host
: "*"), maximum
,
115 (name
[0] == ':' ? "0" : ""), (tmp
->name
? tmp
->name
: "*"),
116 port
, get_conf_class(tmp
));
117 else if (tmp
->status
& CONF_OPERATOR
)
118 send_reply(sptr
, RPL_STATSOLINE
,
119 ((FlagHas(&tmp
->privs_dirty
, PRIV_PROPAGATE
)
120 && FlagHas(&tmp
->privs
, PRIV_PROPAGATE
))
121 || (FlagHas(&tmp
->conn_class
->privs_dirty
, PRIV_PROPAGATE
)
122 && FlagHas(&tmp
->conn_class
->privs
, PRIV_PROPAGATE
)))
123 ? 'O' : 'o', username
, host
, name
, get_conf_class(tmp
));
128 /** Report connection rules from conf_get_crule_list().
129 * Uses sd->sd_funcdata as a filter for CRuleConf::type.
130 * @param[in] to Client requesting statistics.
131 * @param[in] sd Stats descriptor for request.
132 * @param[in] param Extra parameter from user (ignored).
135 stats_crule_list(struct Client
* to
, const struct StatDesc
*sd
,
138 const struct CRuleConf
* p
= conf_get_crule_list();
140 for ( ; p
; p
= p
->next
)
142 if (p
->type
& sd
->sd_funcdata
)
143 send_reply(to
, RPL_STATSDLINE
, (p
->type
& CRULE_ALL
? 'D' : 'd'), p
->hostmask
, p
->rule
);
147 /** Report active event engine name.
148 * @param[in] to Client requesting statistics.
149 * @param[in] sd Stats descriptor for request (ignored).
150 * @param[in] param Extra parameter from user (ignored).
153 stats_engine(struct Client
*to
, const struct StatDesc
*sd
, char *param
)
155 send_reply(to
, RPL_STATSENGINE
, engine_name());
158 /** Report client access lists.
159 * @param[in] to Client requesting statistics.
160 * @param[in] sd Stats descriptor for request.
161 * @param[in] param Filter for hostname or IP (NULL to show all).
164 stats_access(struct Client
*to
, const struct StatDesc
*sd
, char *param
)
166 struct ConfItem
*aconf
;
172 stats_configured_links(to
, sd
, param
);
176 wilds
= string_has_wildcards(param
);
178 for (aconf
= GlobalConfList
; aconf
; aconf
= aconf
->next
)
180 if (aconf
->status
!= CONF_CLIENT
)
182 if (wilds
? ((aconf
->host
&& !mmatch(aconf
->host
, param
))
183 || (aconf
->name
&& !mmatch(aconf
->name
, param
)))
184 : ((aconf
->host
&& !match(param
, aconf
->host
))
185 || (aconf
->name
&& !match(param
, aconf
->name
))))
187 send_reply(to
, RPL_STATSILINE
,
188 (aconf
->username
? aconf
->username
: ""), (aconf
->username
? "@" : ""),
189 (aconf
->host
? aconf
->host
: "*"), aconf
->maximum
,
190 (aconf
->name
&& aconf
->name
[0] == ':' ? "0":""),
191 aconf
->name
? aconf
->name
: "*",
192 aconf
->address
.port
, get_conf_class(aconf
));
200 /** Report DenyConf entries.
201 * @param[in] to Client requesting list.
204 report_deny_list(struct Client
* to
)
206 const struct DenyConf
* p
= conf_get_deny_list();
207 for ( ; p
; p
= p
->next
)
208 send_reply(to
, RPL_STATSKLINE
, p
->bits
> 0 ? 'k' : 'K',
209 p
->usermask
? p
->usermask
: "*",
210 p
->hostmask
? p
->hostmask
: "*",
211 p
->message
? p
->message
: "(none)",
212 p
->realmask
? p
->realmask
: "*");
215 /** Report K/k-lines to a user.
216 * @param[in] sptr Client requesting statistics.
217 * @param[in] sd Stats descriptor for request (ignored).
218 * @param[in] mask Filter for hostmasks to show.
221 stats_klines(struct Client
*sptr
, const struct StatDesc
*sd
, char *mask
)
228 const struct DenyConf
* conf
;
236 need_more_params(sptr
, "STATS K");
238 report_deny_list(sptr
);
244 wilds
= string_has_wildcards(mask
);
247 if ((host
= strchr(mask
, '@')))
255 for (conf
= conf_get_deny_list(); conf
; conf
= conf
->next
)
257 /* Skip this block if the user is searching for a user-matching
258 * mask but the current Kill doesn't have a usermask, or if user
259 * is searching for a host-matching mask but the Kill has no
260 * hostmask, or if the user mask is specified and doesn't match,
261 * or if the host mask is specified and doesn't match.
263 if ((user
&& !conf
->usermask
)
264 || (host
&& !conf
->hostmask
)
265 || (user
&& conf
->usermask
267 ? mmatch(user
, conf
->usermask
)
268 : match(conf
->usermask
, user
)))
269 || (host
&& conf
->hostmask
271 ? mmatch(host
, conf
->hostmask
)
272 : match(conf
->hostmask
, host
))))
274 send_reply(sptr
, RPL_STATSKLINE
, conf
->bits
> 0 ? 'k' : 'K',
275 conf
->usermask
? conf
->usermask
: "*",
276 conf
->hostmask
? conf
->hostmask
: "*",
277 conf
->message
? conf
->message
: "(none)",
278 conf
->realmask
? conf
->realmask
: "*");
284 /** Report on servers and/or clients connected to the network.
285 * @param[in] sptr Client requesting statistics.
286 * @param[in] sd Stats descriptor for request (ignored).
287 * @param[in] name Filter for client names to show.
290 stats_links(struct Client
* sptr
, const struct StatDesc
* sd
, char* name
)
292 struct Client
*acptr
;
297 wilds
= string_has_wildcards(name
);
300 * Send info about connections which match, or all if the
301 * mask matches me.name. Only restrictions are on those who
302 * are invisible not being visible to 'foreigners' who use
303 * a wild card based search to list it.
305 send_reply(sptr
, SND_EXPLICIT
| RPL_STATSLINKINFO
, "Connection SendQ "
306 "SendM SendKBytes RcveM RcveKBytes :Open since");
307 for (i
= 0; i
<= HighestFd
; i
++)
309 if (!(acptr
= LocalClientArray
[i
]))
311 /* Don't return clients when this is a request for `all' */
312 if (!name
&& IsUser(acptr
))
314 /* Don't show invisible people to non opers unless they know the nick */
315 if (IsInvisible(acptr
) && (!name
|| wilds
) && !IsAnOper(acptr
) &&
318 /* Only show the ones that match the given mask - if any */
319 if (name
&& wilds
&& match(name
, cli_name(acptr
)))
321 /* Skip all that do not match the specific query */
322 if (!(!name
|| wilds
) && 0 != ircd_strcmp(name
, cli_name(acptr
)))
324 send_reply(sptr
, SND_EXPLICIT
| RPL_STATSLINKINFO
,
325 "%s %u %u %Lu %u %Lu :%Tu",
326 (*(cli_name(acptr
))) ? cli_name(acptr
) : "<unregistered>",
327 (int)MsgQLength(&(cli_sendQ(acptr
))), (int)cli_sendM(acptr
),
328 (cli_sendB(acptr
) >> 10), (int)cli_receiveM(acptr
),
329 (cli_receiveB(acptr
) >> 10), CurrentTime
- cli_firsttime(acptr
));
333 /** Report on loaded modules.
334 * @param[in] to Client requesting statistics.
335 * @param[in] sd Stats descriptor for request (ignored).
336 * @param[in] param Extra parameter from user (ignored).
339 stats_modules(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
341 crypt_mechs_t
* mechs
;
343 send_reply(to
, SND_EXPLICIT
| RPL_STATSLLINE
,
344 "Module Description Entry Point");
346 /* atm the only "modules" we have are the crypto mechanisms,
347 eventualy they'll be part of a global dl module list, for now
348 i'll just output data about them -- hikari */
350 if(crypt_mechs_root
== NULL
)
353 mechs
= crypt_mechs_root
->next
;
360 send_reply(to
, SND_EXPLICIT
| RPL_STATSLLINE
,
362 mechs
->mech
->shortname
, mechs
->mech
->description
,
363 mechs
->mech
->crypt_function
);
370 /** Report how many times each command has been used.
371 * @param[in] to Client requesting statistics.
372 * @param[in] sd Stats descriptor for request (ignored).
373 * @param[in] param Extra parameter from user (ignored).
376 stats_commands(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
378 struct Message
*mptr
;
380 for (mptr
= msgtab
; mptr
->cmd
; mptr
++)
382 send_reply(to
, RPL_STATSCOMMANDS
, mptr
->cmd
, mptr
->count
, mptr
->bytes
);
385 /** List channel quarantines.
386 * @param[in] to Client requesting statistics.
387 * @param[in] sd Stats descriptor for request (ignored).
388 * @param[in] param Filter for quarantined channel names.
391 stats_quarantine(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
395 for (qline
= GlobalQuarantineList
; qline
; qline
= qline
->next
)
397 if (param
&& match(param
, qline
->chname
)) /* narrow search */
399 send_reply(to
, RPL_STATSQLINE
, qline
->chname
, qline
->reason
);
404 stats_sline(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
410 send_reply(to
, SND_EXPLICIT
| RPL_TEXT
, "# Type Spoofhost Realhost Ident");
412 send_reply(to
, SND_EXPLICIT
| RPL_TEXT
, "# Type Spoofhost");
414 for (sline
= GlobalSList
; sline
; sline
= sline
->next
) {
415 if (param
&& match(param
, sline
->spoofhost
)) { /* narrow search */
419 if (!EmptyString(sline
->passwd
))
425 send_reply(to
, RPL_STATSSLINE
, (param
) ? y
: i
,
426 (EmptyString(sline
->passwd
)) ? "oper" : "user",
428 (EmptyString(sline
->realhost
)) ? "" : sline
->realhost
,
429 (EmptyString(sline
->username
)) ? "" : sline
->username
);
432 if (!EmptyString(sline
->passwd
)) {
433 send_reply(to
, RPL_STATSSLINE
, (param
) ? y
: i
, "user", sline
->spoofhost
,
441 /** List service pseudo-command mappings.
442 * @param[in] to Client requesting statistics.
443 * @param[in] sd Stats descriptor for request (ignored).
444 * @param[in] param Extra parameter from user (ignored).
447 stats_mapping(struct Client
*to
, const struct StatDesc
* sd
, char* param
)
451 send_reply(to
, RPL_STATSRLINE
, "Command", "Name", "Prepend", "Target");
452 for (map
= GlobalServiceMapList
; map
; map
= map
->next
) {
453 struct nick_host
*nh
;
454 for (nh
= map
->services
; nh
; nh
= nh
->next
) {
455 send_reply(to
, RPL_STATSRLINE
, map
->command
, map
->name
,
456 (map
->prepend
? map
->prepend
: "*"), nh
->nick
);
461 /** Report server uptime and maximum connection/client counts.
462 * @param[in] to Client requesting statistics.
463 * @param[in] sd Stats descriptor for request (ignored).
464 * @param[in] param Extra parameter from user (ignored).
467 stats_uptime(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
471 nowr
= CurrentTime
- cli_since(&me
);
472 send_reply(to
, RPL_STATSUPTIME
, nowr
/ 86400, (nowr
/ 3600) % 24,
473 (nowr
/ 60) % 60, nowr
% 60);
474 send_reply(to
, RPL_STATSCONN
, max_connection_count
, max_client_count
);
477 /** Verbosely report on servers connected to the network.
478 * If sd->sd_funcdata != 0, then display in a more human-friendly format.
479 * @param[in] sptr Client requesting statistics.
480 * @param[in] sd Stats descriptor for request.
481 * @param[in] param Filter for server names to display.
484 stats_servers_verbose(struct Client
* sptr
, const struct StatDesc
* sd
,
487 struct Client
*acptr
;
491 * lowercase 'v' is for human-readable,
492 * uppercase 'V' is for machine-readable
494 if (sd
->sd_funcdata
) {
495 send_reply(sptr
, SND_EXPLICIT
| RPL_STATSVERBOSE
,
496 "%-20s %-20s Flags Hops Numeric Lag RTT Up Down "
497 "Clients/Max Proto %-10s :Info", "Servername", "Uplink",
499 fmt
= "%-20s %-20s %c%c%c%c%c %4i %s %-4i %5i %4i %4i %4i %5i %5i P%-2i %Tu :%s";
501 fmt
= "%s %s %c%c%c%c%c %i %s %i %i %i %i %i %i %i P%i %Tu :%s";
504 for (acptr
= GlobalClientList
; acptr
; acptr
= cli_next(acptr
))
506 if (!IsServer(acptr
) && !IsMe(acptr
))
509 if (param
&& match(param
, cli_name(acptr
)))
511 send_reply(sptr
, SND_EXPLICIT
| RPL_STATSVERBOSE
, fmt
,
513 cli_name(cli_serv(acptr
)->up
),
514 IsBurst(acptr
) ? 'B' : '-',
515 IsBurstAck(acptr
) ? 'A' : '-',
516 IsHub(acptr
) ? 'H' : '-',
517 IsService(acptr
) ? 'S' : '-',
518 IsIPv6(acptr
) ? '6' : '-',
521 base64toint(cli_yxx(acptr
)),
522 cli_serv(acptr
)->lag
,
523 cli_serv(acptr
)->asll_rtt
,
524 cli_serv(acptr
)->asll_to
,
525 cli_serv(acptr
)->asll_from
,
526 (acptr
== &me
? UserStats
.local_clients
: cli_serv(acptr
)->clients
),
527 cli_serv(acptr
)->nn_mask
,
528 cli_serv(acptr
)->prot
,
529 cli_serv(acptr
)->timestamp
,
534 /** Display objects allocated (and total memory used by them) for
535 * several types of structures.
536 * @param[in] to Client requesting statistics.
537 * @param[in] sd Stats descriptor for request (ignored).
538 * @param[in] param Extra parameter from user (ignored).
541 stats_meminfo(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
543 extern void bans_send_meminfo(struct Client
*cptr
);
545 class_send_meminfo(to
);
546 bans_send_meminfo(to
);
547 send_listinfo(to
, 0);
550 /** Send a list of available statistics.
551 * @param[in] to Client requesting statistics.
552 * @param[in] sd Stats descriptor for request.
553 * @param[in] param Extra parameter from user (ignored).
556 stats_help(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
558 struct StatDesc
*asd
;
560 /* only if it's my user */
562 for (asd
= statsinfo
; asd
->sd_name
; asd
++)
563 if (asd
!= sd
) /* don't send the help for us */
564 sendcmdto_one(&me
, CMD_NOTICE
, to
, "%C :%c (%s) - %s", to
, asd
->sd_c
,
565 asd
->sd_name
, asd
->sd_desc
);
568 /** Contains information about all statistics. */
569 struct StatDesc statsinfo
[] = {
570 { 'a', "nameservers", STAT_FLAG_OPERFEAT
|STAT_FLAG_LOCONLY
, FEAT_HIS_STATS_a
,
571 report_dns_servers
, 0,
573 { 'c', "connect", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_c
,
574 stats_configured_links
, CONF_SERVER
,
575 "Remote server connection lines." },
576 { 'd', "maskrules", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_d
,
577 stats_crule_list
, CRULE_MASK
,
578 "Dynamic routing configuration." },
579 { 'D', "crules", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_d
,
580 stats_crule_list
, CRULE_ALL
,
581 "Dynamic routing configuration." },
582 { 'e', "engine", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_e
,
584 "Report server event loop engine." },
585 { 'f', "features", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_f
,
587 "Feature settings." },
588 { 'g', "glines", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_g
,
590 "Global bans (G-lines)." },
591 { 'i', "access", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
), FEAT_HIS_STATS_i
,
592 stats_access
, CONF_CLIENT
,
593 "Connection authorization lines." },
594 { 'j', "histogram", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_j
,
596 "Message length histogram." },
597 { 'J', "jupes", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_J
,
600 { 'k', "klines", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
), FEAT_HIS_STATS_k
,
602 "Local bans (K-Lines)." },
603 { 'l', "links", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
| STAT_FLAG_CASESENS
),
606 "Current connections information." },
607 { 'L', "modules", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
),
610 "Dynamically loaded modules." },
611 { 'm', "commands", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_m
,
613 "Message usage information." },
614 { 'o', "operators", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_o
,
615 stats_configured_links
, CONF_OPERATOR
,
616 "Operator information." },
617 { 'p', "ports", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
), FEAT_HIS_STATS_p
,
619 "Listening ports." },
620 { 'q', "quarantines", (STAT_FLAG_OPERONLY
| STAT_FLAG_VARPARAM
), FEAT_HIS_STATS_q
,
622 "Quarantined channels list." },
623 { 'R', "mappings", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_R
,
625 "Service mappings." },
627 { 'r', "usage", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_r
,
629 "System resource usage (Debug only)." },
631 { 's', "spoofhosts", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
), FEAT_HIS_STATS_s
,
633 "Spoofed hosts information." },
634 { 'T', "motds", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_T
,
636 "Configured Message Of The Day files." },
637 { 't', "locals", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_t
,
639 "Local connection statistics (Total SND/RCV, etc)." },
640 { 'U', "uworld", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_U
,
641 stats_configured_links
, CONF_UWORLD
,
642 "Service server information." },
643 { 'u', "uptime", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_u
,
645 "Current uptime & highest connection count." },
646 { 'v', "vservers", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_v
,
647 stats_servers_verbose
, 1,
648 "Verbose server information." },
649 { 'V', "vserversmach", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_v
,
650 stats_servers_verbose
, 0,
651 "Verbose server information." },
652 { 'w', "userload", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_w
,
654 "Userload statistics." },
655 { 'x', "memusage", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_x
,
657 "List usage information." },
658 { 'y', "classes", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_y
,
660 "Connection classes." },
661 { 'z', "memory", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_z
,
663 "Memory/Structure allocation information." },
664 { ' ', "iauth", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_IAUTH
,
665 report_iauth_stats
, 0,
666 "IAuth statistics." },
667 { ' ', "iauthconf", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_IAUTH
,
668 report_iauth_conf
, 0,
669 "IAuth configuration." },
670 { '*', "help", STAT_FLAG_CASESENS
, FEAT_LAST_F
,
672 "Send help for stats." },
673 { '\0', 0, FEAT_LAST_F
, 0, 0, 0 }
676 /** Maps from characters to statistics descriptors.
677 * Statistics descriptors with no single-character alias are not included.
679 static struct StatDesc
*statsmap
[256];
680 /** Number of statistics descriptors. */
681 static int statscount
;
683 /** Compare two StatDesc structures by long name (StatDesc::sd_name).
684 * @param[in] a_ Pointer to a StatDesc.
685 * @param[in] b_ Pointer to a StatDesc.
686 * @return Less than, equal to, or greater than zero if \a a_ is
687 * lexicographically less than, equal to, or greater than \a b_.
690 stats_cmp(const void *a_
, const void *b_
)
692 const struct StatDesc
*a
= a_
;
693 const struct StatDesc
*b
= b_
;
694 return ircd_strcmp(a
->sd_name
, b
->sd_name
);
697 /** Compare a StatDesc's name against a string.
698 * @param[in] key Pointer to a null-terminated string.
699 * @param[in] sd_ Pointer to a StatDesc.
700 * @return Less than, equal to, or greater than zero if \a key is
701 * lexicographically less than, equal to, or greater than \a
705 stats_search(const void *key
, const void *sd_
)
707 const struct StatDesc
*sd
= sd_
;
708 return ircd_strcmp(key
, sd
->sd_name
);
711 /** Look up a stats handler. If name_or_char is just one character
712 * long, use that as a character index; otherwise, look it up by name
714 * @param[in] name_or_char Null-terminated string to look up.
715 * @return The statistics descriptor for \a name_or_char (NULL if none).
717 const struct StatDesc
*
718 stats_find(const char *name_or_char
)
720 if (!name_or_char
[1])
721 return statsmap
[name_or_char
[0] - CHAR_MIN
];
723 return bsearch(name_or_char
, statsinfo
, statscount
, sizeof(statsinfo
[0]), stats_search
);
726 /** Build statsmap from the statsinfo array. */
732 /* Count number of stats entries and sort them. */
733 for (statscount
= 0, sd
= statsinfo
; sd
->sd_name
; sd
++, statscount
++) {}
734 qsort(statsinfo
, statscount
, sizeof(statsinfo
[0]), stats_cmp
);
736 /* Build the mapping */
737 for (sd
= statsinfo
; sd
->sd_name
; sd
++)
741 else if (sd
->sd_flags
& STAT_FLAG_CASESENS
)
742 /* case sensitive character... */
743 statsmap
[sd
->sd_c
- CHAR_MIN
] = sd
;
746 /* case insensitive--make sure to put in two entries */
747 statsmap
[ToLower(sd
->sd_c
) - CHAR_MIN
] = sd
;
748 statsmap
[ToUpper(sd
->sd_c
) - CHAR_MIN
] = sd
;