]>
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"
63 * @brief Report configuration lines and other statistics from this
65 * @version $Id: s_stats.c,v 1.44.2.1 2005/10/12 01:13:48 entrope Exp $
67 * Note: The info is reported in the order the server uses
68 * it--not reversed as in ircd.conf!
71 /* The statsinfo array should only be used in this file, but just TRY
72 * telling the compiler that you want to forward declare a static
73 * array without specifying a length, and see how it responds. So we
74 * forward declare it "extern".
76 extern struct StatDesc statsinfo
[];
78 /** Report items from #GlobalConfList.
79 * Uses sd->sd_funcdata as a filter for ConfItem::status.
80 * @param[in] sptr Client requesting statistics.
81 * @param[in] sd Stats descriptor for request.
82 * @param[in] param Extra parameter from user (ignored).
85 stats_configured_links(struct Client
*sptr
, const struct StatDesc
* sd
,
88 static char null
[] = "<NULL>";
90 unsigned short int port
;
92 char *host
, *pass
, *name
, *username
, *hub_limit
;
94 for (tmp
= GlobalConfList
; tmp
; tmp
= tmp
->next
)
96 if ((tmp
->status
& sd
->sd_funcdata
))
98 host
= BadPtr(tmp
->host
) ? null
: tmp
->host
;
99 pass
= BadPtr(tmp
->passwd
) ? null
: tmp
->passwd
;
100 name
= BadPtr(tmp
->name
) ? null
: tmp
->name
;
101 username
= BadPtr(tmp
->username
) ? null
: tmp
->username
;
102 hub_limit
= BadPtr(tmp
->hub_limit
) ? null
: tmp
->hub_limit
;
103 maximum
= tmp
->maximum
;
104 port
= tmp
->address
.port
;
106 if (tmp
->status
& CONF_UWORLD
)
107 send_reply(sptr
, RPL_STATSULINE
, host
);
108 else if (tmp
->status
& CONF_SERVER
)
109 send_reply(sptr
, RPL_STATSCLINE
, name
, port
, maximum
, hub_limit
, get_conf_class(tmp
));
110 else if (tmp
->status
& CONF_CLIENT
)
111 send_reply(sptr
, RPL_STATSILINE
,
112 (tmp
->host
? tmp
->host
: "*"), maximum
,
113 (name
[0] == ':' ? "0" : ""), (tmp
->name
? tmp
->name
: "*"),
114 port
, get_conf_class(tmp
));
115 else if (tmp
->status
& CONF_OPERATOR
)
116 send_reply(sptr
, RPL_STATSOLINE
,
117 ((FlagHas(&tmp
->privs_dirty
, PRIV_PROPAGATE
)
118 && FlagHas(&tmp
->privs
, PRIV_PROPAGATE
))
119 || (FlagHas(&tmp
->conn_class
->privs_dirty
, PRIV_PROPAGATE
)
120 && FlagHas(&tmp
->conn_class
->privs
, PRIV_PROPAGATE
)))
121 ? 'O' : 'o', username
, host
, name
, get_conf_class(tmp
));
126 /** Report connection rules from conf_get_crule_list().
127 * Uses sd->sd_funcdata as a filter for CRuleConf::type.
128 * @param[in] to Client requesting statistics.
129 * @param[in] sd Stats descriptor for request.
130 * @param[in] param Extra parameter from user (ignored).
133 stats_crule_list(struct Client
* to
, const struct StatDesc
*sd
,
136 const struct CRuleConf
* p
= conf_get_crule_list();
138 for ( ; p
; p
= p
->next
)
140 if (p
->type
& sd
->sd_funcdata
)
141 send_reply(to
, RPL_STATSDLINE
, (p
->type
& CRULE_ALL
? 'D' : 'd'), p
->hostmask
, p
->rule
);
145 /** Report active event engine name.
146 * @param[in] to Client requesting statistics.
147 * @param[in] sd Stats descriptor for request (ignored).
148 * @param[in] param Extra parameter from user (ignored).
151 stats_engine(struct Client
*to
, const struct StatDesc
*sd
, char *param
)
153 send_reply(to
, RPL_STATSENGINE
, engine_name());
156 /** Report client access lists.
157 * @param[in] to Client requesting statistics.
158 * @param[in] sd Stats descriptor for request.
159 * @param[in] param Filter for hostname or IP (NULL to show all).
162 stats_access(struct Client
*to
, const struct StatDesc
*sd
, char *param
)
164 struct ConfItem
*aconf
;
170 stats_configured_links(to
, sd
, param
);
174 wilds
= string_has_wildcards(param
);
176 for (aconf
= GlobalConfList
; aconf
; aconf
= aconf
->next
)
178 if (aconf
->status
!= CONF_CLIENT
)
180 if (wilds
? ((aconf
->host
&& !mmatch(aconf
->host
, param
))
181 || (aconf
->name
&& !mmatch(aconf
->name
, param
)))
182 : ((aconf
->host
&& !match(param
, aconf
->host
))
183 || (aconf
->name
&& !match(param
, aconf
->name
))))
185 send_reply(to
, RPL_STATSILINE
,
186 (aconf
->host
? aconf
->host
: "*"), aconf
->maximum
,
187 (aconf
->name
&& aconf
->name
[0] == ':' ? "0":""),
188 aconf
->name
? aconf
->name
: "*",
189 aconf
->address
.port
, get_conf_class(aconf
));
197 /** Report DenyConf entries.
198 * @param[in] to Client requesting list.
201 report_deny_list(struct Client
* to
)
203 const struct DenyConf
* p
= conf_get_deny_list();
204 for ( ; p
; p
= p
->next
)
205 send_reply(to
, RPL_STATSKLINE
, p
->bits
> 0 ? 'k' : 'K',
206 p
->usermask
? p
->usermask
: "*",
207 p
->hostmask
? p
->hostmask
: "*",
208 p
->message
? p
->message
: "(none)",
209 p
->realmask
? p
->realmask
: "*");
212 /** Report K/k-lines to a user.
213 * @param[in] sptr Client requesting statistics.
214 * @param[in] sd Stats descriptor for request (ignored).
215 * @param[in] mask Filter for hostmasks to show.
218 stats_klines(struct Client
*sptr
, const struct StatDesc
*sd
, char *mask
)
225 const struct DenyConf
* conf
;
233 need_more_params(sptr
, "STATS K");
235 report_deny_list(sptr
);
241 wilds
= string_has_wildcards(mask
);
244 if ((host
= strchr(mask
, '@')))
252 for (conf
= conf_get_deny_list(); conf
; conf
= conf
->next
)
254 /* Skip this block if the user is searching for a user-matching
255 * mask but the current Kill doesn't have a usermask, or if user
256 * is searching for a host-matching mask but the Kill has no
257 * hostmask, or if the user mask is specified and doesn't match,
258 * or if the host mask is specified and doesn't match.
260 if ((user
&& !conf
->usermask
)
261 || (host
&& !conf
->hostmask
)
262 || (user
&& conf
->usermask
264 ? mmatch(user
, conf
->usermask
)
265 : match(conf
->usermask
, user
)))
266 || (host
&& conf
->hostmask
268 ? mmatch(host
, conf
->hostmask
)
269 : match(conf
->hostmask
, host
))))
271 send_reply(sptr
, RPL_STATSKLINE
, conf
->bits
> 0 ? 'k' : 'K',
272 conf
->usermask
? conf
->usermask
: "*",
273 conf
->hostmask
? conf
->hostmask
: "*",
274 conf
->message
? conf
->message
: "(none)",
275 conf
->realmask
? conf
->realmask
: "*");
281 /** Report on servers and/or clients connected to the network.
282 * @param[in] sptr Client requesting statistics.
283 * @param[in] sd Stats descriptor for request (ignored).
284 * @param[in] name Filter for client names to show.
287 stats_links(struct Client
* sptr
, const struct StatDesc
* sd
, char* name
)
289 struct Client
*acptr
;
294 wilds
= string_has_wildcards(name
);
297 * Send info about connections which match, or all if the
298 * mask matches me.name. Only restrictions are on those who
299 * are invisible not being visible to 'foreigners' who use
300 * a wild card based search to list it.
302 send_reply(sptr
, SND_EXPLICIT
| RPL_STATSLINKINFO
, "Connection SendQ "
303 "SendM SendKBytes RcveM RcveKBytes :Open since");
304 for (i
= 0; i
<= HighestFd
; i
++)
306 if (!(acptr
= LocalClientArray
[i
]))
308 /* Don't return clients when this is a request for `all' */
309 if (!name
&& IsUser(acptr
))
311 /* Don't show invisible people to non opers unless they know the nick */
312 if (IsInvisible(acptr
) && (!name
|| wilds
) && !IsAnOper(acptr
) &&
315 /* Only show the ones that match the given mask - if any */
316 if (name
&& wilds
&& match(name
, cli_name(acptr
)))
318 /* Skip all that do not match the specific query */
319 if (!(!name
|| wilds
) && 0 != ircd_strcmp(name
, cli_name(acptr
)))
321 send_reply(sptr
, SND_EXPLICIT
| RPL_STATSLINKINFO
,
322 "%s %u %u %Lu %u %Lu :%Tu",
323 (*(cli_name(acptr
))) ? cli_name(acptr
) : "<unregistered>",
324 (int)MsgQLength(&(cli_sendQ(acptr
))), (int)cli_sendM(acptr
),
325 (cli_sendB(acptr
) >> 10), (int)cli_receiveM(acptr
),
326 (cli_receiveB(acptr
) >> 10), CurrentTime
- cli_firsttime(acptr
));
330 /** Report on loaded modules.
331 * @param[in] to Client requesting statistics.
332 * @param[in] sd Stats descriptor for request (ignored).
333 * @param[in] param Extra parameter from user (ignored).
336 stats_modules(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
338 crypt_mechs_t
* mechs
;
340 send_reply(to
, SND_EXPLICIT
| RPL_STATSLLINE
,
341 "Module Description Entry Point");
343 /* atm the only "modules" we have are the crypto mechanisms,
344 eventualy they'll be part of a global dl module list, for now
345 i'll just output data about them -- hikari */
347 if(crypt_mechs_root
== NULL
)
350 mechs
= crypt_mechs_root
->next
;
357 send_reply(to
, SND_EXPLICIT
| RPL_STATSLLINE
,
359 mechs
->mech
->shortname
, mechs
->mech
->description
,
360 mechs
->mech
->crypt_function
);
367 /** Report how many times each command has been used.
368 * @param[in] to Client requesting statistics.
369 * @param[in] sd Stats descriptor for request (ignored).
370 * @param[in] param Extra parameter from user (ignored).
373 stats_commands(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
375 struct Message
*mptr
;
377 for (mptr
= msgtab
; mptr
->cmd
; mptr
++)
379 send_reply(to
, RPL_STATSCOMMANDS
, mptr
->cmd
, mptr
->count
, mptr
->bytes
);
382 /** List channel quarantines.
383 * @param[in] to Client requesting statistics.
384 * @param[in] sd Stats descriptor for request (ignored).
385 * @param[in] param Filter for quarantined channel names.
388 stats_quarantine(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
392 for (qline
= GlobalQuarantineList
; qline
; qline
= qline
->next
)
394 if (param
&& match(param
, qline
->chname
)) /* narrow search */
396 send_reply(to
, RPL_STATSQLINE
, qline
->chname
, qline
->reason
);
401 stats_sline(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
407 send_reply(to
, SND_EXPLICIT
| RPL_TEXT
, "# Type Spoofhost Realhost Ident");
409 send_reply(to
, SND_EXPLICIT
| RPL_TEXT
, "# Type Spoofhost");
411 for (sline
= GlobalSList
; sline
; sline
= sline
->next
) {
412 if (param
&& match(param
, sline
->spoofhost
)) { /* narrow search */
416 if (!EmptyString(sline
->passwd
))
422 send_reply(to
, RPL_STATSSLINE
, (param
) ? y
: i
,
423 (EmptyString(sline
->passwd
)) ? "oper" : "user",
425 (EmptyString(sline
->realhost
)) ? "" : sline
->realhost
,
426 (EmptyString(sline
->username
)) ? "" : sline
->username
);
429 if (!EmptyString(sline
->passwd
)) {
430 send_reply(to
, RPL_STATSSLINE
, (param
) ? y
: i
, "user", sline
->spoofhost
,
438 /** List service pseudo-command mappings.
439 * @param[in] to Client requesting statistics.
440 * @param[in] sd Stats descriptor for request (ignored).
441 * @param[in] param Extra parameter from user (ignored).
444 stats_mapping(struct Client
*to
, const struct StatDesc
* sd
, char* param
)
448 send_reply(to
, RPL_STATSRLINE
, "Command", "Name", "Prepend", "Target");
449 for (map
= GlobalServiceMapList
; map
; map
= map
->next
) {
450 struct nick_host
*nh
;
451 for (nh
= map
->services
; nh
; nh
= nh
->next
) {
452 send_reply(to
, RPL_STATSRLINE
, map
->command
, map
->name
,
453 (map
->prepend
? map
->prepend
: "*"), nh
->nick
);
458 /** Report server uptime and maximum connection/client counts.
459 * @param[in] to Client requesting statistics.
460 * @param[in] sd Stats descriptor for request (ignored).
461 * @param[in] param Extra parameter from user (ignored).
464 stats_uptime(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
468 nowr
= CurrentTime
- cli_since(&me
);
469 send_reply(to
, RPL_STATSUPTIME
, nowr
/ 86400, (nowr
/ 3600) % 24,
470 (nowr
/ 60) % 60, nowr
% 60);
471 send_reply(to
, RPL_STATSCONN
, max_connection_count
, max_client_count
);
474 /** Verbosely report on servers connected to the network.
475 * If sd->sd_funcdata != 0, then display in a more human-friendly format.
476 * @param[in] sptr Client requesting statistics.
477 * @param[in] sd Stats descriptor for request.
478 * @param[in] param Filter for server names to display.
481 stats_servers_verbose(struct Client
* sptr
, const struct StatDesc
* sd
,
484 struct Client
*acptr
;
488 * lowercase 'v' is for human-readable,
489 * uppercase 'V' is for machine-readable
491 if (sd
->sd_funcdata
) {
492 send_reply(sptr
, SND_EXPLICIT
| RPL_STATSVERBOSE
,
493 "%-20s %-20s Flags Hops Numeric Lag RTT Up Down "
494 "Clients/Max Proto %-10s :Info", "Servername", "Uplink",
496 fmt
= "%-20s %-20s %c%c%c%c %4i %s %-4i %5i %4i %4i %4i %5i %5i P%-2i %Tu :%s";
498 fmt
= "%s %s %c%c%c%c %i %s %i %i %i %i %i %i %i P%i %Tu :%s";
501 for (acptr
= GlobalClientList
; acptr
; acptr
= cli_next(acptr
))
503 if (!IsServer(acptr
) && !IsMe(acptr
))
506 if (param
&& match(param
, cli_name(acptr
)))
508 send_reply(sptr
, SND_EXPLICIT
| RPL_STATSVERBOSE
, fmt
,
510 cli_name(cli_serv(acptr
)->up
),
511 IsBurst(acptr
) ? 'B' : '-',
512 IsBurstAck(acptr
) ? 'A' : '-',
513 IsHub(acptr
) ? 'H' : '-',
514 IsService(acptr
) ? 'S' : '-',
517 base64toint(cli_yxx(acptr
)),
518 cli_serv(acptr
)->lag
,
519 cli_serv(acptr
)->asll_rtt
,
520 cli_serv(acptr
)->asll_to
,
521 cli_serv(acptr
)->asll_from
,
522 (acptr
== &me
? UserStats
.local_clients
: cli_serv(acptr
)->clients
),
523 cli_serv(acptr
)->nn_mask
,
524 cli_serv(acptr
)->prot
,
525 cli_serv(acptr
)->timestamp
,
530 /** Display objects allocated (and total memory used by them) for
531 * several types of structures.
532 * @param[in] to Client requesting statistics.
533 * @param[in] sd Stats descriptor for request (ignored).
534 * @param[in] param Extra parameter from user (ignored).
537 stats_meminfo(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
539 extern void bans_send_meminfo(struct Client
*cptr
);
541 class_send_meminfo(to
);
542 bans_send_meminfo(to
);
543 send_listinfo(to
, 0);
546 /** Send a list of available statistics.
547 * @param[in] to Client requesting statistics.
548 * @param[in] sd Stats descriptor for request.
549 * @param[in] param Extra parameter from user (ignored).
552 stats_help(struct Client
* to
, const struct StatDesc
* sd
, char* param
)
554 struct StatDesc
*asd
;
556 /* only if it's my user */
558 for (asd
= statsinfo
; asd
->sd_name
; asd
++)
559 if (asd
!= sd
) /* don't send the help for us */
560 sendcmdto_one(&me
, CMD_NOTICE
, to
, "%C :%c (%s) - %s", to
, asd
->sd_c
,
561 asd
->sd_name
, asd
->sd_desc
);
564 /** Contains information about all statistics. */
565 struct StatDesc statsinfo
[] = {
566 { 'a', "nameservers", STAT_FLAG_OPERFEAT
|STAT_FLAG_LOCONLY
, FEAT_HIS_STATS_a
,
567 report_dns_servers
, 0,
569 { 'c', "connect", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_c
,
570 stats_configured_links
, CONF_SERVER
,
571 "Remote server connection lines." },
572 { 'd', "maskrules", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_d
,
573 stats_crule_list
, CRULE_MASK
,
574 "Dynamic routing configuration." },
575 { 'D', "crules", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_d
,
576 stats_crule_list
, CRULE_ALL
,
577 "Dynamic routing configuration." },
578 { 'e', "engine", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_e
,
580 "Report server event loop engine." },
581 { 'f', "features", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_f
,
583 "Feature settings." },
584 { 'g', "glines", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_g
,
586 "Global bans (G-lines)." },
587 { 'i', "access", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
), FEAT_HIS_STATS_i
,
588 stats_access
, CONF_CLIENT
,
589 "Connection authorization lines." },
590 { 'j', "histogram", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_j
,
592 "Message length histogram." },
593 { 'J', "jupes", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_J
,
596 { 'k', "klines", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
), FEAT_HIS_STATS_k
,
598 "Local bans (K-Lines)." },
599 { 'l', "links", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
| STAT_FLAG_CASESENS
),
602 "Current connections information." },
603 { 'L', "modules", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
),
606 "Dynamically loaded modules." },
607 { 'm', "commands", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_m
,
609 "Message usage information." },
610 { 'o', "operators", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_o
,
611 stats_configured_links
, CONF_OPERATOR
,
612 "Operator information." },
613 { 'p', "ports", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
), FEAT_HIS_STATS_p
,
615 "Listening ports." },
616 { 'q', "quarantines", (STAT_FLAG_OPERONLY
| STAT_FLAG_VARPARAM
), FEAT_HIS_STATS_q
,
618 "Quarantined channels list." },
619 { 'R', "mappings", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_R
,
621 "Service mappings." },
623 { 'r', "usage", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_r
,
625 "System resource usage (Debug only)." },
627 { 's', "spoofhosts", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
), FEAT_HIS_STATS_s
,
629 "Spoofed hosts information." },
630 { 'T', "motds", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_T
,
632 "Configured Message Of The Day files." },
633 { 't', "locals", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_t
,
635 "Local connection statistics (Total SND/RCV, etc)." },
636 { 'U', "uworld", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_U
,
637 stats_configured_links
, CONF_UWORLD
,
638 "Service server information." },
639 { 'u', "uptime", (STAT_FLAG_OPERFEAT
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_u
,
641 "Current uptime & highest connection count." },
642 { 'v', "vservers", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_v
,
643 stats_servers_verbose
, 1,
644 "Verbose server information." },
645 { 'V', "vserversmach", (STAT_FLAG_OPERFEAT
| STAT_FLAG_VARPARAM
| STAT_FLAG_CASESENS
), FEAT_HIS_STATS_v
,
646 stats_servers_verbose
, 0,
647 "Verbose server information." },
648 { 'w', "userload", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_w
,
650 "Userload statistics." },
651 { 'x', "memusage", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_x
,
653 "List usage information." },
654 { 'y', "classes", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_y
,
656 "Connection classes." },
657 { 'z', "memory", STAT_FLAG_OPERFEAT
, FEAT_HIS_STATS_z
,
659 "Memory/Structure allocation information." },
660 { '*', "help", STAT_FLAG_CASESENS
, FEAT_LAST_F
,
662 "Send help for stats." },
663 { '\0', 0, FEAT_LAST_F
, 0, 0, 0 }
666 /** Maps from characters to statistics descriptors.
667 * Statistics descriptors with no single-character alias are not included.
669 static struct StatDesc
*statsmap
[256];
670 /** Number of statistics descriptors. */
671 static int statscount
;
673 /** Compare two StatDesc structures by long name (StatDesc::sd_name).
674 * @param[in] a_ Pointer to a StatDesc.
675 * @param[in] b_ Pointer to a StatDesc.
676 * @return Less than, equal to, or greater than zero if \a a_ is
677 * lexicographically less than, equal to, or greater than \a b_.
680 stats_cmp(const void *a_
, const void *b_
)
682 const struct StatDesc
*a
= a_
;
683 const struct StatDesc
*b
= b_
;
684 return ircd_strcmp(a
->sd_name
, b
->sd_name
);
687 /** Compare a StatDesc's name against a string.
688 * @param[in] key Pointer to a null-terminated string.
689 * @param[in] sd_ Pointer to a StatDesc.
690 * @return Less than, equal to, or greater than zero if \a key is
691 * lexicographically less than, equal to, or greater than \a
695 stats_search(const void *key
, const void *sd_
)
697 const struct StatDesc
*sd
= sd_
;
698 return ircd_strcmp(key
, sd
->sd_name
);
701 /** Look up a stats handler. If name_or_char is just one character
702 * long, use that as a character index; otherwise, look it up by name
704 * @param[in] name_or_char Null-terminated string to look up.
705 * @return The statistics descriptor for \a name_or_char (NULL if none).
707 const struct StatDesc
*
708 stats_find(const char *name_or_char
)
710 if (!name_or_char
[1])
711 return statsmap
[name_or_char
[0] - CHAR_MIN
];
713 return bsearch(name_or_char
, statsinfo
, statscount
, sizeof(statsinfo
[0]), stats_search
);
716 /** Build statsmap from the statsinfo array. */
722 /* Count number of stats entries and sort them. */
723 for (statscount
= 0, sd
= statsinfo
; sd
->sd_name
; sd
++, statscount
++) {}
724 qsort(statsinfo
, statscount
, sizeof(statsinfo
[0]), stats_cmp
);
726 /* Build the mapping */
727 for (sd
= statsinfo
; sd
->sd_name
; sd
++)
731 else if (sd
->sd_flags
& STAT_FLAG_CASESENS
)
732 /* case sensitive character... */
733 statsmap
[sd
->sd_c
- CHAR_MIN
] = sd
;
736 /* case insensitive--make sure to put in two entries */
737 statsmap
[ToLower(sd
->sd_c
) - CHAR_MIN
] = sd
;
738 statsmap
[ToUpper(sd
->sd_c
) - CHAR_MIN
] = sd
;