]>
Commit | Line | Data |
---|---|---|
189935b1 | 1 | /* |
2 | * IRC - Internet Relay Chat, ircd/s_stats.c | |
3 | * Copyright (C) 2000 Joseph Bongaarts | |
4 | * | |
5 | * See file AUTHORS in IRC package for additional names of | |
6 | * the programmers. | |
7 | * | |
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) | |
11 | * any later version. | |
12 | * | |
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. | |
17 | * | |
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. | |
21 | */ | |
22 | #include "config.h" | |
23 | ||
24 | #include "class.h" | |
25 | #include "client.h" | |
26 | #include "gline.h" | |
27 | #include "hash.h" | |
28 | #include "ircd.h" | |
29 | #include "ircd_chattr.h" | |
30 | #include "ircd_events.h" | |
31 | #include "ircd_features.h" | |
32 | #include "ircd_crypt.h" | |
33 | #include "ircd_log.h" | |
34 | #include "ircd_reply.h" | |
35 | #include "ircd_string.h" | |
36 | #include "listener.h" | |
37 | #include "list.h" | |
38 | #include "match.h" | |
39 | #include "motd.h" | |
40 | #include "msg.h" | |
41 | #include "msgq.h" | |
42 | #include "numeric.h" | |
43 | #include "numnicks.h" | |
44 | #include "querycmds.h" | |
45 | #include "res.h" | |
46 | #include "s_bsd.h" | |
47 | #include "s_conf.h" | |
48 | #include "s_debug.h" | |
49 | #include "s_misc.h" | |
50 | #include "s_serv.h" | |
51 | #include "s_stats.h" | |
52 | #include "s_user.h" | |
53 | #include "send.h" | |
54 | #include "struct.h" | |
55 | #include "userload.h" | |
56 | ||
57 | #include <stdio.h> | |
58 | #include <stdlib.h> | |
59 | #include <string.h> | |
60 | #include <sys/time.h> | |
61 | ||
62 | /** @file | |
63 | * @brief Report configuration lines and other statistics from this | |
64 | * server. | |
65 | * @version $Id: s_stats.c,v 1.44.2.1 2005/10/12 01:13:48 entrope Exp $ | |
66 | * | |
67 | * Note: The info is reported in the order the server uses | |
68 | * it--not reversed as in ircd.conf! | |
69 | */ | |
70 | ||
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". | |
75 | */ | |
76 | extern struct StatDesc statsinfo[]; | |
77 | ||
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). | |
83 | */ | |
84 | static void | |
85 | stats_configured_links(struct Client *sptr, const struct StatDesc* sd, | |
86 | char* param) | |
87 | { | |
88 | static char null[] = "<NULL>"; | |
89 | struct ConfItem *tmp; | |
90 | unsigned short int port; | |
91 | int maximum; | |
92 | char *host, *pass, *name, *username, *hub_limit; | |
93 | ||
94 | for (tmp = GlobalConfList; tmp; tmp = tmp->next) | |
95 | { | |
96 | if ((tmp->status & sd->sd_funcdata)) | |
97 | { | |
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; | |
105 | ||
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)); | |
122 | } | |
123 | } | |
124 | } | |
125 | ||
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). | |
131 | */ | |
132 | static void | |
133 | stats_crule_list(struct Client* to, const struct StatDesc *sd, | |
134 | char *param) | |
135 | { | |
136 | const struct CRuleConf* p = conf_get_crule_list(); | |
137 | ||
138 | for ( ; p; p = p->next) | |
139 | { | |
140 | if (p->type & sd->sd_funcdata) | |
141 | send_reply(to, RPL_STATSDLINE, (p->type & CRULE_ALL ? 'D' : 'd'), p->hostmask, p->rule); | |
142 | } | |
143 | } | |
144 | ||
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). | |
149 | */ | |
150 | static void | |
151 | stats_engine(struct Client *to, const struct StatDesc *sd, char *param) | |
152 | { | |
153 | send_reply(to, RPL_STATSENGINE, engine_name()); | |
154 | } | |
155 | ||
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). | |
160 | */ | |
161 | static void | |
162 | stats_access(struct Client *to, const struct StatDesc *sd, char *param) | |
163 | { | |
164 | struct ConfItem *aconf; | |
165 | int wilds = 0; | |
166 | int count = 1000; | |
167 | ||
168 | if (!param) | |
169 | { | |
170 | stats_configured_links(to, sd, param); | |
171 | return; | |
172 | } | |
173 | ||
174 | wilds = string_has_wildcards(param); | |
175 | ||
176 | for (aconf = GlobalConfList; aconf; aconf = aconf->next) | |
177 | { | |
178 | if (aconf->status != CONF_CLIENT) | |
179 | continue; | |
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)))) | |
184 | { | |
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)); | |
190 | if (--count == 0) | |
191 | break; | |
192 | } | |
193 | } | |
194 | } | |
195 | ||
196 | ||
197 | /** Report DenyConf entries. | |
198 | * @param[in] to Client requesting list. | |
199 | */ | |
200 | static void | |
201 | report_deny_list(struct Client* to) | |
202 | { | |
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 : "*"); | |
210 | } | |
211 | ||
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. | |
216 | */ | |
217 | static void | |
218 | stats_klines(struct Client *sptr, const struct StatDesc *sd, char *mask) | |
219 | { | |
220 | int wilds = 0; | |
221 | int count = 3; | |
222 | int limit_query = 0; | |
223 | char *user = 0; | |
224 | char *host; | |
225 | const struct DenyConf* conf; | |
226 | ||
227 | if (!IsAnOper(sptr)) | |
228 | limit_query = 1; | |
229 | ||
230 | if (!mask) | |
231 | { | |
232 | if (limit_query) | |
233 | need_more_params(sptr, "STATS K"); | |
234 | else | |
235 | report_deny_list(sptr); | |
236 | return; | |
237 | } | |
238 | ||
239 | if (!limit_query) | |
240 | { | |
241 | wilds = string_has_wildcards(mask); | |
242 | count = 1000; | |
243 | } | |
244 | if ((host = strchr(mask, '@'))) | |
245 | { | |
246 | user = mask; | |
247 | *host++ = '\0'; | |
248 | } | |
249 | else | |
250 | host = mask; | |
251 | ||
252 | for (conf = conf_get_deny_list(); conf; conf = conf->next) | |
253 | { | |
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. | |
259 | */ | |
260 | if ((user && !conf->usermask) | |
261 | || (host && !conf->hostmask) | |
262 | || (user && conf->usermask | |
263 | && (wilds | |
264 | ? mmatch(user, conf->usermask) | |
265 | : match(conf->usermask, user))) | |
266 | || (host && conf->hostmask | |
267 | && (wilds | |
268 | ? mmatch(host, conf->hostmask) | |
269 | : match(conf->hostmask, host)))) | |
270 | continue; | |
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 : "*"); | |
276 | if (--count == 0) | |
277 | return; | |
278 | } | |
279 | } | |
280 | ||
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. | |
285 | */ | |
286 | static void | |
287 | stats_links(struct Client* sptr, const struct StatDesc* sd, char* name) | |
288 | { | |
289 | struct Client *acptr; | |
290 | int i; | |
291 | int wilds = 0; | |
292 | ||
293 | if (name) | |
294 | wilds = string_has_wildcards(name); | |
295 | ||
296 | /* | |
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. | |
301 | */ | |
302 | send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO, "Connection SendQ " | |
303 | "SendM SendKBytes RcveM RcveKBytes :Open since"); | |
304 | for (i = 0; i <= HighestFd; i++) | |
305 | { | |
306 | if (!(acptr = LocalClientArray[i])) | |
307 | continue; | |
308 | /* Don't return clients when this is a request for `all' */ | |
309 | if (!name && IsUser(acptr)) | |
310 | continue; | |
311 | /* Don't show invisible people to non opers unless they know the nick */ | |
312 | if (IsInvisible(acptr) && (!name || wilds) && !IsAnOper(acptr) && | |
313 | (acptr != sptr)) | |
314 | continue; | |
315 | /* Only show the ones that match the given mask - if any */ | |
316 | if (name && wilds && match(name, cli_name(acptr))) | |
317 | continue; | |
318 | /* Skip all that do not match the specific query */ | |
319 | if (!(!name || wilds) && 0 != ircd_strcmp(name, cli_name(acptr))) | |
320 | continue; | |
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)); | |
327 | } | |
328 | } | |
329 | ||
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). | |
334 | */ | |
335 | static void | |
336 | stats_modules(struct Client* to, const struct StatDesc* sd, char* param) | |
337 | { | |
338 | crypt_mechs_t* mechs; | |
339 | ||
340 | send_reply(to, SND_EXPLICIT | RPL_STATSLLINE, | |
341 | "Module Description Entry Point"); | |
342 | ||
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 */ | |
346 | ||
347 | if(crypt_mechs_root == NULL) | |
348 | return; | |
349 | ||
350 | mechs = crypt_mechs_root->next; | |
351 | ||
352 | for(;;) | |
353 | { | |
354 | if(mechs == NULL) | |
355 | return; | |
356 | ||
357 | send_reply(to, SND_EXPLICIT | RPL_STATSLLINE, | |
358 | "%s %s 0x%X", | |
359 | mechs->mech->shortname, mechs->mech->description, | |
360 | mechs->mech->crypt_function); | |
361 | ||
362 | mechs = mechs->next; | |
363 | } | |
364 | ||
365 | } | |
366 | ||
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). | |
371 | */ | |
372 | static void | |
373 | stats_commands(struct Client* to, const struct StatDesc* sd, char* param) | |
374 | { | |
375 | struct Message *mptr; | |
376 | ||
377 | for (mptr = msgtab; mptr->cmd; mptr++) | |
378 | if (mptr->count) | |
379 | send_reply(to, RPL_STATSCOMMANDS, mptr->cmd, mptr->count, mptr->bytes); | |
380 | } | |
381 | ||
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. | |
386 | */ | |
387 | static void | |
388 | stats_quarantine(struct Client* to, const struct StatDesc* sd, char* param) | |
389 | { | |
390 | struct qline *qline; | |
391 | ||
392 | for (qline = GlobalQuarantineList; qline; qline = qline->next) | |
393 | { | |
394 | if (param && match(param, qline->chname)) /* narrow search */ | |
395 | continue; | |
396 | send_reply(to, RPL_STATSQLINE, qline->chname, qline->reason); | |
397 | } | |
398 | } | |
399 | ||
400 | /** List service pseudo-command mappings. | |
401 | * @param[in] to Client requesting statistics. | |
402 | * @param[in] sd Stats descriptor for request (ignored). | |
403 | * @param[in] param Extra parameter from user (ignored). | |
404 | */ | |
405 | static void | |
406 | stats_mapping(struct Client *to, const struct StatDesc* sd, char* param) | |
407 | { | |
408 | struct s_map *map; | |
409 | ||
410 | send_reply(to, RPL_STATSRLINE, "Command", "Name", "Prepend", "Target"); | |
411 | for (map = GlobalServiceMapList; map; map = map->next) { | |
412 | struct nick_host *nh; | |
413 | for (nh = map->services; nh; nh = nh->next) { | |
414 | send_reply(to, RPL_STATSRLINE, map->command, map->name, | |
415 | (map->prepend ? map->prepend : "*"), nh->nick); | |
416 | } | |
417 | } | |
418 | } | |
419 | ||
420 | /** Report server uptime and maximum connection/client counts. | |
421 | * @param[in] to Client requesting statistics. | |
422 | * @param[in] sd Stats descriptor for request (ignored). | |
423 | * @param[in] param Extra parameter from user (ignored). | |
424 | */ | |
425 | static void | |
426 | stats_uptime(struct Client* to, const struct StatDesc* sd, char* param) | |
427 | { | |
428 | time_t nowr; | |
429 | ||
430 | nowr = CurrentTime - cli_since(&me); | |
431 | send_reply(to, RPL_STATSUPTIME, nowr / 86400, (nowr / 3600) % 24, | |
432 | (nowr / 60) % 60, nowr % 60); | |
433 | send_reply(to, RPL_STATSCONN, max_connection_count, max_client_count); | |
434 | } | |
435 | ||
436 | /** Verbosely report on servers connected to the network. | |
437 | * If sd->sd_funcdata != 0, then display in a more human-friendly format. | |
438 | * @param[in] sptr Client requesting statistics. | |
439 | * @param[in] sd Stats descriptor for request. | |
440 | * @param[in] param Filter for server names to display. | |
441 | */ | |
442 | static void | |
443 | stats_servers_verbose(struct Client* sptr, const struct StatDesc* sd, | |
444 | char* param) | |
445 | { | |
446 | struct Client *acptr; | |
447 | const char *fmt; | |
448 | ||
449 | /* | |
450 | * lowercase 'v' is for human-readable, | |
451 | * uppercase 'V' is for machine-readable | |
452 | */ | |
453 | if (sd->sd_funcdata) { | |
454 | send_reply(sptr, SND_EXPLICIT | RPL_STATSVERBOSE, | |
455 | "%-20s %-20s Flags Hops Numeric Lag RTT Up Down " | |
456 | "Clients/Max Proto %-10s :Info", "Servername", "Uplink", | |
457 | "LinkTS"); | |
458 | fmt = "%-20s %-20s %c%c%c%c %4i %s %-4i %5i %4i %4i %4i %5i %5i P%-2i %Tu :%s"; | |
459 | } else { | |
460 | fmt = "%s %s %c%c%c%c %i %s %i %i %i %i %i %i %i P%i %Tu :%s"; | |
461 | } | |
462 | ||
463 | for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) | |
464 | { | |
465 | if (!IsServer(acptr) && !IsMe(acptr)) | |
466 | continue; | |
467 | /* narrow search */ | |
468 | if (param && match(param, cli_name(acptr))) | |
469 | continue; | |
470 | send_reply(sptr, SND_EXPLICIT | RPL_STATSVERBOSE, fmt, | |
471 | cli_name(acptr), | |
472 | cli_name(cli_serv(acptr)->up), | |
473 | IsBurst(acptr) ? 'B' : '-', | |
474 | IsBurstAck(acptr) ? 'A' : '-', | |
475 | IsHub(acptr) ? 'H' : '-', | |
476 | IsService(acptr) ? 'S' : '-', | |
477 | cli_hopcount(acptr), | |
478 | NumServ(acptr), | |
479 | base64toint(cli_yxx(acptr)), | |
480 | cli_serv(acptr)->lag, | |
481 | cli_serv(acptr)->asll_rtt, | |
482 | cli_serv(acptr)->asll_to, | |
483 | cli_serv(acptr)->asll_from, | |
484 | (acptr == &me ? UserStats.local_clients : cli_serv(acptr)->clients), | |
485 | cli_serv(acptr)->nn_mask, | |
486 | cli_serv(acptr)->prot, | |
487 | cli_serv(acptr)->timestamp, | |
488 | cli_info(acptr)); | |
489 | } | |
490 | } | |
491 | ||
492 | /** Display objects allocated (and total memory used by them) for | |
493 | * several types of structures. | |
494 | * @param[in] to Client requesting statistics. | |
495 | * @param[in] sd Stats descriptor for request (ignored). | |
496 | * @param[in] param Extra parameter from user (ignored). | |
497 | */ | |
498 | static void | |
499 | stats_meminfo(struct Client* to, const struct StatDesc* sd, char* param) | |
500 | { | |
501 | extern void bans_send_meminfo(struct Client *cptr); | |
502 | ||
503 | class_send_meminfo(to); | |
504 | bans_send_meminfo(to); | |
505 | send_listinfo(to, 0); | |
506 | } | |
507 | ||
508 | /** Send a list of available statistics. | |
509 | * @param[in] to Client requesting statistics. | |
510 | * @param[in] sd Stats descriptor for request. | |
511 | * @param[in] param Extra parameter from user (ignored). | |
512 | */ | |
513 | static void | |
514 | stats_help(struct Client* to, const struct StatDesc* sd, char* param) | |
515 | { | |
516 | struct StatDesc *asd; | |
517 | ||
518 | /* only if it's my user */ | |
519 | if (MyUser(to)) | |
520 | for (asd = statsinfo; asd->sd_name; asd++) | |
521 | if (asd != sd) /* don't send the help for us */ | |
522 | sendcmdto_one(&me, CMD_NOTICE, to, "%C :%c (%s) - %s", to, asd->sd_c, | |
523 | asd->sd_name, asd->sd_desc); | |
524 | } | |
525 | ||
526 | /** Contains information about all statistics. */ | |
527 | struct StatDesc statsinfo[] = { | |
528 | { 'a', "nameservers", STAT_FLAG_OPERFEAT|STAT_FLAG_LOCONLY, FEAT_HIS_STATS_a, | |
529 | report_dns_servers, 0, | |
530 | "DNS servers." }, | |
531 | { 'c', "connect", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_c, | |
532 | stats_configured_links, CONF_SERVER, | |
533 | "Remote server connection lines." }, | |
534 | { 'd', "maskrules", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_d, | |
535 | stats_crule_list, CRULE_MASK, | |
536 | "Dynamic routing configuration." }, | |
537 | { 'D', "crules", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_d, | |
538 | stats_crule_list, CRULE_ALL, | |
539 | "Dynamic routing configuration." }, | |
540 | { 'e', "engine", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_e, | |
541 | stats_engine, 0, | |
542 | "Report server event loop engine." }, | |
543 | { 'f', "features", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_f, | |
544 | feature_report, 0, | |
545 | "Feature settings." }, | |
546 | { 'g', "glines", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_g, | |
547 | gline_stats, 0, | |
548 | "Global bans (G-lines)." }, | |
549 | { 'i', "access", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM), FEAT_HIS_STATS_i, | |
550 | stats_access, CONF_CLIENT, | |
551 | "Connection authorization lines." }, | |
552 | { 'j', "histogram", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_j, | |
553 | msgq_histogram, 0, | |
554 | "Message length histogram." }, | |
555 | { 'J', "jupes", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_J, | |
556 | stats_nickjupes, 0, | |
557 | "Nickname jupes." }, | |
558 | { 'k', "klines", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM), FEAT_HIS_STATS_k, | |
559 | stats_klines, 0, | |
560 | "Local bans (K-Lines)." }, | |
561 | { 'l', "links", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM | STAT_FLAG_CASESENS), | |
562 | FEAT_HIS_STATS_l, | |
563 | stats_links, 0, | |
564 | "Current connections information." }, | |
565 | { 'L', "modules", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), | |
566 | FEAT_HIS_STATS_L, | |
567 | stats_modules, 0, | |
568 | "Dynamically loaded modules." }, | |
569 | { 'm', "commands", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_m, | |
570 | stats_commands, 0, | |
571 | "Message usage information." }, | |
572 | { 'o', "operators", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_o, | |
573 | stats_configured_links, CONF_OPERATOR, | |
574 | "Operator information." }, | |
575 | { 'p', "ports", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM), FEAT_HIS_STATS_p, | |
576 | show_ports, 0, | |
577 | "Listening ports." }, | |
578 | { 'q', "quarantines", (STAT_FLAG_OPERONLY | STAT_FLAG_VARPARAM), FEAT_HIS_STATS_q, | |
579 | stats_quarantine, 0, | |
580 | "Quarantined channels list." }, | |
581 | { 'R', "mappings", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_R, | |
582 | stats_mapping, 0, | |
583 | "Service mappings." }, | |
584 | #ifdef DEBUGMODE | |
585 | { 'r', "usage", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_r, | |
586 | send_usage, 0, | |
587 | "System resource usage (Debug only)." }, | |
588 | #endif | |
589 | { 'T', "motds", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_T, | |
590 | motd_report, 0, | |
591 | "Configured Message Of The Day files." }, | |
592 | { 't', "locals", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_t, | |
593 | tstats, 0, | |
594 | "Local connection statistics (Total SND/RCV, etc)." }, | |
595 | { 'U', "uworld", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_U, | |
596 | stats_configured_links, CONF_UWORLD, | |
597 | "Service server information." }, | |
598 | { 'u', "uptime", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_u, | |
599 | stats_uptime, 0, | |
600 | "Current uptime & highest connection count." }, | |
601 | { 'v', "vservers", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM | STAT_FLAG_CASESENS), FEAT_HIS_STATS_v, | |
602 | stats_servers_verbose, 1, | |
603 | "Verbose server information." }, | |
604 | { 'V', "vserversmach", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM | STAT_FLAG_CASESENS), FEAT_HIS_STATS_v, | |
605 | stats_servers_verbose, 0, | |
606 | "Verbose server information." }, | |
607 | { 'w', "userload", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_w, | |
608 | calc_load, 0, | |
609 | "Userload statistics." }, | |
610 | { 'x', "memusage", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_x, | |
611 | stats_meminfo, 0, | |
612 | "List usage information." }, | |
613 | { 'y', "classes", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_y, | |
614 | report_classes, 0, | |
615 | "Connection classes." }, | |
616 | { 'z', "memory", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_z, | |
617 | count_memory, 0, | |
618 | "Memory/Structure allocation information." }, | |
619 | { '*', "help", STAT_FLAG_CASESENS, FEAT_LAST_F, | |
620 | stats_help, 0, | |
621 | "Send help for stats." }, | |
622 | { '\0', 0, FEAT_LAST_F, 0, 0, 0 } | |
623 | }; | |
624 | ||
625 | /** Maps from characters to statistics descriptors. | |
626 | * Statistics descriptors with no single-character alias are not included. | |
627 | */ | |
628 | static struct StatDesc *statsmap[256]; | |
629 | /** Number of statistics descriptors. */ | |
630 | static int statscount; | |
631 | ||
632 | /** Compare two StatDesc structures by long name (StatDesc::sd_name). | |
633 | * @param[in] a_ Pointer to a StatDesc. | |
634 | * @param[in] b_ Pointer to a StatDesc. | |
635 | * @return Less than, equal to, or greater than zero if \a a_ is | |
636 | * lexicographically less than, equal to, or greater than \a b_. | |
637 | */ | |
638 | static int | |
639 | stats_cmp(const void *a_, const void *b_) | |
640 | { | |
641 | const struct StatDesc *a = a_; | |
642 | const struct StatDesc *b = b_; | |
643 | return ircd_strcmp(a->sd_name, b->sd_name); | |
644 | } | |
645 | ||
646 | /** Compare a StatDesc's name against a string. | |
647 | * @param[in] key Pointer to a null-terminated string. | |
648 | * @param[in] sd_ Pointer to a StatDesc. | |
649 | * @return Less than, equal to, or greater than zero if \a key is | |
650 | * lexicographically less than, equal to, or greater than \a | |
651 | * sd_->sd_name. | |
652 | */ | |
653 | static int | |
654 | stats_search(const void *key, const void *sd_) | |
655 | { | |
656 | const struct StatDesc *sd = sd_; | |
657 | return ircd_strcmp(key, sd->sd_name); | |
658 | } | |
659 | ||
660 | /** Look up a stats handler. If name_or_char is just one character | |
661 | * long, use that as a character index; otherwise, look it up by name | |
662 | * in #statsinfo. | |
663 | * @param[in] name_or_char Null-terminated string to look up. | |
664 | * @return The statistics descriptor for \a name_or_char (NULL if none). | |
665 | */ | |
666 | const struct StatDesc * | |
667 | stats_find(const char *name_or_char) | |
668 | { | |
669 | if (!name_or_char[1]) | |
670 | return statsmap[name_or_char[0] - CHAR_MIN]; | |
671 | else | |
672 | return bsearch(name_or_char, statsinfo, statscount, sizeof(statsinfo[0]), stats_search); | |
673 | } | |
674 | ||
675 | /** Build statsmap from the statsinfo array. */ | |
676 | void | |
677 | stats_init(void) | |
678 | { | |
679 | struct StatDesc *sd; | |
680 | ||
681 | /* Count number of stats entries and sort them. */ | |
682 | for (statscount = 0, sd = statsinfo; sd->sd_name; sd++, statscount++) {} | |
683 | qsort(statsinfo, statscount, sizeof(statsinfo[0]), stats_cmp); | |
684 | ||
685 | /* Build the mapping */ | |
686 | for (sd = statsinfo; sd->sd_name; sd++) | |
687 | { | |
688 | if (!sd->sd_c) | |
689 | continue; | |
690 | else if (sd->sd_flags & STAT_FLAG_CASESENS) | |
691 | /* case sensitive character... */ | |
692 | statsmap[sd->sd_c - CHAR_MIN] = sd; | |
693 | else | |
694 | { | |
695 | /* case insensitive--make sure to put in two entries */ | |
696 | statsmap[ToLower(sd->sd_c) - CHAR_MIN] = sd; | |
697 | statsmap[ToUpper(sd->sd_c) - CHAR_MIN] = sd; | |
698 | } | |
699 | } | |
700 | } |