]>
Commit | Line | Data |
---|---|---|
212380e3 AC |
1 | /* |
2 | * ircd-ratbox: an advanced Internet Relay Chat Daemon(ircd). | |
3 | * m_stats.c: Sends the user statistics or config information. | |
4 | * | |
5 | * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center | |
6 | * Copyright (C) 1996-2002 Hybrid Development Team | |
7 | * Copyright (C) 2002-2005 ircd-ratbox development team | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program; if not, write to the Free Software | |
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |
22 | * USA | |
212380e3 AC |
23 | */ |
24 | ||
25 | #include "stdinc.h" | |
212380e3 AC |
26 | #include "class.h" /* report_classes */ |
27 | #include "client.h" /* Client */ | |
4562c604 | 28 | #include "match.h" |
212380e3 AC |
29 | #include "ircd.h" /* me */ |
30 | #include "listener.h" /* show_ports */ | |
212380e3 AC |
31 | #include "msg.h" /* Message */ |
32 | #include "hostmask.h" /* report_mtrie_conf_links */ | |
33 | #include "numeric.h" /* ERR_xxx */ | |
34 | #include "scache.h" /* list_scache */ | |
35 | #include "send.h" /* sendto_one */ | |
212380e3 AC |
36 | #include "s_conf.h" /* ConfItem */ |
37 | #include "s_serv.h" /* hunt_server */ | |
47adde3d | 38 | #include "s_stats.h" |
212380e3 | 39 | #include "s_user.h" /* show_opers */ |
212380e3 AC |
40 | #include "parse.h" |
41 | #include "modules.h" | |
42 | #include "hook.h" | |
43 | #include "s_newconf.h" | |
44 | #include "hash.h" | |
47adde3d VY |
45 | #include "reject.h" |
46 | #include "whowas.h" | |
a4bf26dd | 47 | #include "rb_radixtree.h" |
eb1b303d | 48 | #include "sslproc.h" |
169a1c35 | 49 | #include "s_assert.h" |
212380e3 | 50 | |
3abc337f AW |
51 | static const char stats_desc[] = |
52 | "Provides the STATS command to inspect various server/network information"; | |
212380e3 | 53 | |
3c7d6fcc | 54 | static void m_stats (struct MsgBuf *, struct Client *, struct Client *, int, const char **); |
eeabf33a | 55 | |
212380e3 | 56 | struct Message stats_msgtab = { |
788e2d59 | 57 | "STATS", false, false, false, false, |
212380e3 AC |
58 | {mg_unreg, {m_stats, 2}, {m_stats, 3}, mg_ignore, mg_ignore, {m_stats, 2}} |
59 | }; | |
60 | ||
61 | int doing_stats_hook; | |
62 | int doing_stats_p_hook; | |
01fb744c | 63 | int doing_stats_show_idle_hook; |
212380e3 AC |
64 | |
65 | mapi_clist_av1 stats_clist[] = { &stats_msgtab, NULL }; | |
66 | mapi_hlist_av1 stats_hlist[] = { | |
67 | { "doing_stats", &doing_stats_hook }, | |
68 | { "doing_stats_p", &doing_stats_p_hook }, | |
01fb744c | 69 | { "doing_stats_show_idle", &doing_stats_show_idle_hook }, |
212380e3 AC |
70 | { NULL, NULL } |
71 | }; | |
72 | ||
3abc337f | 73 | DECLARE_MODULE_AV2(stats, NULL, NULL, stats_clist, stats_hlist, NULL, NULL, NULL, stats_desc); |
212380e3 AC |
74 | |
75 | const char *Lformat = "%s %u %u %u %u %u :%u %u %s"; | |
76 | ||
3c7d6fcc | 77 | static void stats_l_list(struct Client *s, const char *, bool, bool, rb_dlink_list *, char, |
24652f2b | 78 | bool (*check_fn)(struct Client *source_p, struct Client *target_p)); |
212380e3 AC |
79 | static void stats_l_client(struct Client *source_p, struct Client *target_p, |
80 | char statchar); | |
81 | ||
ea2d2700 | 82 | static int stats_spy(struct Client *, char, const char *); |
212380e3 AC |
83 | static void stats_p_spy(struct Client *); |
84 | ||
788e2d59 EM |
85 | typedef void (*handler_t)(struct Client *source_p); |
86 | typedef void (*handler_parv_t)(struct Client *source_p, int parc, const char *parv[]); | |
87 | ||
963c3faa | 88 | struct stats_cmd |
212380e3 | 89 | { |
788e2d59 EM |
90 | union |
91 | { | |
92 | handler_t handler; | |
93 | handler_parv_t handler_parv; | |
94 | }; | |
326ecd96 | 95 | const char *need_priv; |
788e2d59 | 96 | bool need_parv; |
788e2d59 | 97 | bool need_admin; |
212380e3 AC |
98 | }; |
99 | ||
8a26cd19 | 100 | static void stats_dns_servers(struct Client *); |
212380e3 AC |
101 | static void stats_delay(struct Client *); |
102 | static void stats_hash(struct Client *); | |
103 | static void stats_connect(struct Client *); | |
104 | static void stats_tdeny(struct Client *); | |
105 | static void stats_deny(struct Client *); | |
106 | static void stats_exempt(struct Client *); | |
107 | static void stats_events(struct Client *); | |
416d868e | 108 | static void stats_prop_klines(struct Client *); |
212380e3 AC |
109 | static void stats_auth(struct Client *); |
110 | static void stats_tklines(struct Client *); | |
111 | static void stats_klines(struct Client *); | |
112 | static void stats_messages(struct Client *); | |
113 | static void stats_dnsbl(struct Client *); | |
114 | static void stats_oper(struct Client *); | |
3a177354 | 115 | static void stats_privset(struct Client *); |
212380e3 AC |
116 | static void stats_operedup(struct Client *); |
117 | static void stats_ports(struct Client *); | |
118 | static void stats_tresv(struct Client *); | |
119 | static void stats_resv(struct Client *); | |
8f0dd52c | 120 | static void stats_secure(struct Client *); |
eb1b303d | 121 | static void stats_ssld(struct Client *); |
212380e3 AC |
122 | static void stats_usage(struct Client *); |
123 | static void stats_tstats(struct Client *); | |
124 | static void stats_uptime(struct Client *); | |
212380e3 AC |
125 | static void stats_servers(struct Client *); |
126 | static void stats_tgecos(struct Client *); | |
127 | static void stats_gecos(struct Client *); | |
128 | static void stats_class(struct Client *); | |
129 | static void stats_memory(struct Client *); | |
130 | static void stats_servlinks(struct Client *); | |
131 | static void stats_ltrace(struct Client *, int, const char **); | |
a235e410 | 132 | static void stats_comm(struct Client *); |
ac37f16a AC |
133 | static void stats_capability(struct Client *); |
134 | ||
326ecd96 EK |
135 | #define HANDLER_NORM(fn, admin, priv) \ |
136 | { { .handler = fn }, .need_parv = false, .need_priv = priv, .need_admin = admin } | |
137 | #define HANDLER_PARV(fn, admin, priv) \ | |
138 | { { .handler_parv = fn }, .need_parv = true, .need_priv = priv, .need_admin = admin } | |
95b03246 | 139 | |
212380e3 | 140 | /* This table contains the possible stats items, in order: |
963c3faa EM |
141 | * stats letter, function to call, operonly? adminonly? --fl_ |
142 | * | |
143 | * Previously in this table letters were a column. I fixed it to use modern | |
788e2d59 | 144 | * C initalisers so we don't have to iterate anymore |
963c3faa | 145 | * --Elizafox |
212380e3 | 146 | */ |
6f39a80e | 147 | static struct stats_cmd stats_cmd_table[256] = { |
326ecd96 EK |
148 | /* letter handler admin priv */ |
149 | ['a'] = HANDLER_NORM(stats_dns_servers, true, NULL), | |
150 | ['A'] = HANDLER_NORM(stats_dns_servers, true, NULL), | |
151 | ['b'] = HANDLER_NORM(stats_delay, true, NULL), | |
152 | ['B'] = HANDLER_NORM(stats_hash, true, NULL), | |
153 | ['c'] = HANDLER_NORM(stats_connect, false, NULL), | |
154 | ['C'] = HANDLER_NORM(stats_capability, false, "oper:general"), | |
155 | ['d'] = HANDLER_NORM(stats_tdeny, false, "oper:general"), | |
156 | ['D'] = HANDLER_NORM(stats_deny, false, "oper:general"), | |
157 | ['e'] = HANDLER_NORM(stats_exempt, false, "oper:general"), | |
158 | ['E'] = HANDLER_NORM(stats_events, true, NULL), | |
159 | ['f'] = HANDLER_NORM(stats_comm, true, NULL), | |
160 | ['F'] = HANDLER_NORM(stats_comm, true, NULL), | |
161 | ['g'] = HANDLER_NORM(stats_prop_klines, false, "oper:general"), | |
326ecd96 EK |
162 | ['i'] = HANDLER_NORM(stats_auth, false, NULL), |
163 | ['I'] = HANDLER_NORM(stats_auth, false, NULL), | |
164 | ['k'] = HANDLER_NORM(stats_tklines, false, NULL), | |
165 | ['K'] = HANDLER_NORM(stats_klines, false, NULL), | |
166 | ['l'] = HANDLER_PARV(stats_ltrace, false, NULL), | |
167 | ['L'] = HANDLER_PARV(stats_ltrace, false, NULL), | |
168 | ['m'] = HANDLER_NORM(stats_messages, false, NULL), | |
169 | ['M'] = HANDLER_NORM(stats_messages, false, NULL), | |
170 | ['n'] = HANDLER_NORM(stats_dnsbl, false, NULL), | |
171 | ['o'] = HANDLER_NORM(stats_oper, false, NULL), | |
b549e8e8 | 172 | ['O'] = HANDLER_NORM(stats_privset, false, "oper:privs"), |
326ecd96 EK |
173 | ['p'] = HANDLER_NORM(stats_operedup, false, NULL), |
174 | ['P'] = HANDLER_NORM(stats_ports, false, NULL), | |
175 | ['q'] = HANDLER_NORM(stats_tresv, false, "oper:general"), | |
176 | ['Q'] = HANDLER_NORM(stats_resv, false, "oper:general"), | |
177 | ['r'] = HANDLER_NORM(stats_usage, false, "oper:general"), | |
178 | ['R'] = HANDLER_NORM(stats_usage, false, "oper:general"), | |
8f0dd52c | 179 | ['s'] = HANDLER_NORM(stats_secure, false, "oper:general"), |
326ecd96 EK |
180 | ['S'] = HANDLER_NORM(stats_ssld, true, NULL), |
181 | ['t'] = HANDLER_NORM(stats_tstats, false, "oper:general"), | |
182 | ['T'] = HANDLER_NORM(stats_tstats, false, "oper:general"), | |
183 | ['u'] = HANDLER_NORM(stats_uptime, false, NULL), | |
326ecd96 EK |
184 | ['v'] = HANDLER_NORM(stats_servers, false, NULL), |
185 | ['V'] = HANDLER_NORM(stats_servers, false, NULL), | |
186 | ['x'] = HANDLER_NORM(stats_tgecos, false, "oper:general"), | |
187 | ['X'] = HANDLER_NORM(stats_gecos, false, "oper:general"), | |
188 | ['y'] = HANDLER_NORM(stats_class, false, NULL), | |
189 | ['Y'] = HANDLER_NORM(stats_class, false, NULL), | |
190 | ['z'] = HANDLER_NORM(stats_memory, false, "oper:general"), | |
326ecd96 | 191 | ['?'] = HANDLER_NORM(stats_servlinks, false, NULL), |
212380e3 AC |
192 | }; |
193 | ||
194 | /* | |
195 | * m_stats by fl_ | |
963c3faa | 196 | * Modified heavily by Elizafox |
212380e3 AC |
197 | * parv[1] = stat letter/command |
198 | * parv[2] = (if present) server/mask in stats L, or target | |
199 | * | |
200 | * This will search the tables for the appropriate stats letter, | |
55abcbb2 | 201 | * if found execute it. |
212380e3 | 202 | */ |
3c7d6fcc | 203 | static void |
428ca87b | 204 | m_stats(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) |
212380e3 AC |
205 | { |
206 | static time_t last_used = 0; | |
963c3faa | 207 | struct stats_cmd *cmd; |
e23126c8 | 208 | unsigned char statchar; |
ea2d2700 | 209 | int did_stats = 0; |
212380e3 AC |
210 | |
211 | statchar = parv[1][0]; | |
212 | ||
d4f7eb4c | 213 | if(MyClient(source_p) && !IsOperGeneral(source_p) && parc > 2) |
212380e3 AC |
214 | { |
215 | /* Check the user is actually allowed to do /stats, and isnt flooding */ | |
e3354945 | 216 | if((last_used + ConfigFileEntry.pace_wait) > rb_current_time()) |
212380e3 AC |
217 | { |
218 | /* safe enough to give this on a local connect only */ | |
219 | sendto_one(source_p, form_str(RPL_LOAD2HI), | |
220 | me.name, source_p->name, "STATS"); | |
55abcbb2 | 221 | sendto_one_numeric(source_p, RPL_ENDOFSTATS, |
212380e3 | 222 | form_str(RPL_ENDOFSTATS), statchar); |
3c7d6fcc | 223 | return; |
212380e3 AC |
224 | } |
225 | else | |
e3354945 | 226 | last_used = rb_current_time(); |
212380e3 AC |
227 | } |
228 | ||
e902e169 | 229 | if(hunt_server(client_p, source_p, ":%s STATS %s :%s", 2, parc, parv) != HUNTED_ISME) |
3c7d6fcc | 230 | return; |
212380e3 | 231 | |
788e2d59 EM |
232 | if(tolower(statchar) != 'l') |
233 | /* FIXME */ | |
ea2d2700 AC |
234 | did_stats = stats_spy(source_p, statchar, NULL); |
235 | ||
236 | /* if did_stats is true, a module grabbed this STATS request */ | |
963c3faa | 237 | if(did_stats) |
ea2d2700 | 238 | goto stats_out; |
212380e3 | 239 | |
963c3faa EM |
240 | /* Look up */ |
241 | cmd = &stats_cmd_table[statchar]; | |
242 | if(cmd->handler != NULL) | |
212380e3 | 243 | { |
963c3faa | 244 | /* The stats table says what privs are needed, so check --fl_ */ |
326ecd96 | 245 | const char *missing_priv = NULL; |
963c3faa | 246 | if(cmd->need_admin && !IsOperAdmin(source_p)) |
326ecd96 EK |
247 | missing_priv = "admin"; |
248 | else if(cmd->need_priv && !HasPrivilege(source_p, cmd->need_priv)) | |
249 | missing_priv = cmd->need_priv; | |
250 | ||
251 | if(missing_priv != NULL) | |
212380e3 | 252 | { |
326ecd96 EK |
253 | if(!IsOper(source_p)) |
254 | { | |
255 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
256 | form_str(ERR_NOPRIVILEGES)); | |
257 | } | |
258 | else | |
259 | { | |
260 | if(!strncmp(missing_priv, "oper:", 5)) | |
261 | missing_priv += 5; | |
262 | sendto_one(source_p, form_str(ERR_NOPRIVS), | |
263 | me.name, source_p->name, missing_priv); | |
264 | } | |
963c3faa EM |
265 | goto stats_out; |
266 | } | |
267 | ||
788e2d59 EM |
268 | if(cmd->need_parv) |
269 | cmd->handler_parv(source_p, parc, parv); | |
270 | else | |
271 | cmd->handler(source_p); | |
212380e3 AC |
272 | } |
273 | ||
ea2d2700 | 274 | stats_out: |
212380e3 | 275 | /* Send the end of stats notice, and the stats_spy */ |
55abcbb2 | 276 | sendto_one_numeric(source_p, RPL_ENDOFSTATS, |
212380e3 | 277 | form_str(RPL_ENDOFSTATS), statchar); |
212380e3 AC |
278 | } |
279 | ||
280 | static void | |
8a26cd19 | 281 | stats_dns_servers (struct Client *source_p) |
212380e3 | 282 | { |
55799c6b EM |
283 | rb_dlink_node *n; |
284 | ||
285 | RB_DLINK_FOREACH(n, nameservers.head) | |
286 | { | |
67ab06dd | 287 | sendto_one_numeric(source_p, RPL_STATSDEBUG, "A :%s", (char *)n->data); |
55799c6b | 288 | } |
212380e3 AC |
289 | } |
290 | ||
291 | static void | |
292 | stats_delay(struct Client *source_p) | |
293 | { | |
294 | struct nd_entry *nd; | |
4177311e | 295 | rb_dictionary_iter iter; |
212380e3 | 296 | |
56f84ded | 297 | RB_DICTIONARY_FOREACH(nd, &iter, nd_dict) |
212380e3 | 298 | { |
e4b9c8e1 | 299 | sendto_one_notice(source_p, ":Delaying: %s for %ld", |
212380e3 AC |
300 | nd->name, (long) nd->expire); |
301 | } | |
212380e3 AC |
302 | } |
303 | ||
21d5a11c AC |
304 | static void |
305 | stats_hash_cb(const char *buf, void *client_p) | |
306 | { | |
307 | sendto_one_numeric(client_p, RPL_STATSDEBUG, "B :%s", buf); | |
308 | } | |
309 | ||
212380e3 AC |
310 | static void |
311 | stats_hash(struct Client *source_p) | |
312 | { | |
8dacf9e9 AC |
313 | sendto_one_numeric(source_p, RPL_STATSDEBUG, "B :%-30s %-15s %-10s %-10s %-10s %-10s", |
314 | "NAME", "TYPE", "OBJECTS", "DEPTH SUM", "AVG DEPTH", "MAX DEPTH"); | |
21d5a11c | 315 | |
a4bf26dd EM |
316 | rb_dictionary_stats_walk(stats_hash_cb, source_p); |
317 | rb_radixtree_stats_walk(stats_hash_cb, source_p); | |
212380e3 AC |
318 | } |
319 | ||
320 | static void | |
321 | stats_connect(struct Client *source_p) | |
322 | { | |
6003ce76 | 323 | static char buf[BUFSIZE]; |
212380e3 AC |
324 | struct server_conf *server_p; |
325 | char *s; | |
5b96d9a6 | 326 | rb_dlink_node *ptr; |
212380e3 | 327 | |
55abcbb2 | 328 | if((ConfigFileEntry.stats_c_oper_only || |
212380e3 | 329 | (ConfigServerHide.flatten_links && !IsExemptShide(source_p))) && |
d4f7eb4c | 330 | !IsOperGeneral(source_p)) |
212380e3 AC |
331 | { |
332 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
333 | form_str(ERR_NOPRIVILEGES)); | |
334 | return; | |
335 | } | |
336 | ||
5b96d9a6 | 337 | RB_DLINK_FOREACH(ptr, server_conf_list.head) |
212380e3 AC |
338 | { |
339 | server_p = ptr->data; | |
340 | ||
341 | if(ServerConfIllegal(server_p)) | |
342 | continue; | |
343 | ||
212380e3 AC |
344 | s = buf; |
345 | ||
d4f7eb4c | 346 | if(IsOperGeneral(source_p)) |
212380e3 AC |
347 | { |
348 | if(ServerConfAutoconn(server_p)) | |
349 | *s++ = 'A'; | |
6003ce76 SA |
350 | if(ServerConfSCTP(server_p)) |
351 | *s++ = 'M'; | |
8bd5767b | 352 | if(ServerConfSSL(server_p)) |
f53ed7f8 | 353 | *s++ = 'S'; |
212380e3 AC |
354 | if(ServerConfTb(server_p)) |
355 | *s++ = 'T'; | |
212380e3 AC |
356 | } |
357 | ||
f76ca178 | 358 | if(s == buf) |
212380e3 AC |
359 | *s++ = '*'; |
360 | ||
361 | *s = '\0'; | |
362 | ||
55abcbb2 | 363 | sendto_one_numeric(source_p, RPL_STATSCLINE, |
212380e3 | 364 | form_str(RPL_STATSCLINE), |
55abcbb2 | 365 | "*@127.0.0.1", |
212380e3 | 366 | buf, server_p->name, |
30857341 SA |
367 | server_p->port, server_p->class_name, |
368 | server_p->certfp ? server_p->certfp : "*"); | |
212380e3 AC |
369 | } |
370 | } | |
371 | ||
372 | /* stats_tdeny() | |
373 | * | |
374 | * input - client to report to | |
375 | * output - none | |
376 | * side effects - client is given temp dline list. | |
377 | */ | |
378 | static void | |
379 | stats_tdeny (struct Client *source_p) | |
380 | { | |
381 | char *host, *pass, *user, *oper_reason; | |
382 | struct AddressRec *arec; | |
383 | struct ConfItem *aconf; | |
384 | int i; | |
385 | ||
386 | for (i = 0; i < ATABLE_SIZE; i++) | |
387 | { | |
388 | for (arec = atable[i]; arec; arec = arec->next) | |
389 | { | |
390 | if(arec->type == CONF_DLINE) | |
391 | { | |
392 | aconf = arec->aconf; | |
393 | ||
394 | if(!(aconf->flags & CONF_FLAGS_TEMPORARY)) | |
395 | continue; | |
396 | ||
397 | get_printable_kline(source_p, aconf, &host, &pass, &user, &oper_reason); | |
398 | ||
55abcbb2 | 399 | sendto_one_numeric(source_p, RPL_STATSDLINE, |
212380e3 AC |
400 | form_str (RPL_STATSDLINE), |
401 | 'd', host, pass, | |
402 | oper_reason ? "|" : "", | |
403 | oper_reason ? oper_reason : ""); | |
404 | } | |
405 | } | |
406 | } | |
407 | } | |
408 | ||
409 | /* stats_deny() | |
410 | * | |
411 | * input - client to report to | |
412 | * output - none | |
413 | * side effects - client is given dline list. | |
414 | */ | |
415 | static void | |
416 | stats_deny (struct Client *source_p) | |
417 | { | |
418 | char *host, *pass, *user, *oper_reason; | |
419 | struct AddressRec *arec; | |
420 | struct ConfItem *aconf; | |
421 | int i; | |
422 | ||
423 | for (i = 0; i < ATABLE_SIZE; i++) | |
424 | { | |
425 | for (arec = atable[i]; arec; arec = arec->next) | |
426 | { | |
427 | if(arec->type == CONF_DLINE) | |
428 | { | |
429 | aconf = arec->aconf; | |
430 | ||
431 | if(aconf->flags & CONF_FLAGS_TEMPORARY) | |
432 | continue; | |
433 | ||
434 | get_printable_kline(source_p, aconf, &host, &pass, &user, &oper_reason); | |
435 | ||
55abcbb2 | 436 | sendto_one_numeric(source_p, RPL_STATSDLINE, |
212380e3 AC |
437 | form_str (RPL_STATSDLINE), |
438 | 'D', host, pass, | |
439 | oper_reason ? "|" : "", | |
440 | oper_reason ? oper_reason : ""); | |
441 | } | |
442 | } | |
443 | } | |
444 | } | |
445 | ||
446 | ||
447 | /* stats_exempt() | |
448 | * | |
449 | * input - client to report to | |
450 | * output - none | |
451 | * side effects - client is given list of exempt blocks | |
452 | */ | |
453 | static void | |
454 | stats_exempt(struct Client *source_p) | |
455 | { | |
48a06ae3 | 456 | char *name, *host, *user, *classname, *desc; |
29c92cf9 | 457 | const char *pass; |
212380e3 AC |
458 | struct AddressRec *arec; |
459 | struct ConfItem *aconf; | |
460 | int i, port; | |
461 | ||
462 | if(ConfigFileEntry.stats_e_disabled) | |
463 | { | |
22f2f68a JT |
464 | sendto_one_numeric(source_p, ERR_DISABLED, |
465 | form_str(ERR_DISABLED), "STATS e"); | |
212380e3 AC |
466 | return; |
467 | } | |
468 | ||
469 | for (i = 0; i < ATABLE_SIZE; i++) | |
470 | { | |
471 | for (arec = atable[i]; arec; arec = arec->next) | |
472 | { | |
473 | if(arec->type == CONF_EXEMPTDLINE) | |
474 | { | |
475 | aconf = arec->aconf; | |
476 | get_printable_conf (aconf, &name, &host, &pass, | |
48a06ae3 | 477 | &user, &port, &classname, &desc); |
212380e3 | 478 | |
55abcbb2 | 479 | sendto_one_numeric(source_p, RPL_STATSDLINE, |
212380e3 AC |
480 | form_str(RPL_STATSDLINE), |
481 | 'e', host, pass, "", ""); | |
482 | } | |
483 | } | |
8f0dd52c EK |
484 | } |
485 | } | |
212380e3 AC |
486 | |
487 | ||
f237e31a JT |
488 | static void |
489 | stats_events_cb(char *str, void *ptr) | |
490 | { | |
491 | sendto_one_numeric(ptr, RPL_STATSDEBUG, "E :%s", str); | |
492 | } | |
493 | ||
494 | static void | |
495 | stats_events (struct Client *source_p) | |
496 | { | |
497 | rb_dump_events(stats_events_cb, source_p); | |
212380e3 AC |
498 | } |
499 | ||
416d868e JT |
500 | static void |
501 | stats_prop_klines(struct Client *source_p) | |
502 | { | |
503 | struct ConfItem *aconf; | |
416d868e | 504 | char *user, *host, *pass, *oper_reason; |
ce376a21 | 505 | rb_dictionary_iter state; |
416d868e | 506 | |
ce376a21 | 507 | RB_DICTIONARY_FOREACH(aconf, &state, prop_bans_dict) |
416d868e | 508 | { |
416d868e | 509 | /* Skip non-klines and deactivated klines. */ |
ce376a21 | 510 | if (aconf->status != CONF_KILL) |
416d868e JT |
511 | continue; |
512 | ||
55abcbb2 | 513 | get_printable_kline(source_p, aconf, &host, &pass, |
416d868e JT |
514 | &user, &oper_reason); |
515 | ||
516 | sendto_one_numeric(source_p, RPL_STATSKLINE, | |
517 | form_str(RPL_STATSKLINE), | |
518 | 'g', host, user, pass, | |
519 | oper_reason ? "|" : "", | |
520 | oper_reason ? oper_reason : ""); | |
521 | } | |
522 | } | |
523 | ||
212380e3 AC |
524 | static void |
525 | stats_auth (struct Client *source_p) | |
526 | { | |
527 | /* Oper only, if unopered, return ERR_NOPRIVS */ | |
d4f7eb4c | 528 | if((ConfigFileEntry.stats_i_oper_only == 2) && !IsOperGeneral (source_p)) |
212380e3 AC |
529 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, |
530 | form_str (ERR_NOPRIVILEGES)); | |
531 | ||
532 | /* If unopered, Only return matching auth blocks */ | |
d4f7eb4c | 533 | else if((ConfigFileEntry.stats_i_oper_only == 1) && !IsOperGeneral (source_p)) |
212380e3 AC |
534 | { |
535 | struct ConfItem *aconf; | |
48a06ae3 | 536 | char *name, *host, *user, *classname, *desc; |
29c92cf9 | 537 | const char *pass = "*"; |
212380e3 AC |
538 | int port; |
539 | ||
540 | if(MyConnect (source_p)) | |
541 | aconf = find_conf_by_address (source_p->host, source_p->sockhost, NULL, | |
542 | (struct sockaddr *)&source_p->localClient->ip, | |
543 | CONF_CLIENT, | |
2d77d121 | 544 | GET_SS_FAMILY(&source_p->localClient->ip), |
40c1fd47 | 545 | source_p->username, NULL); |
212380e3 AC |
546 | else |
547 | aconf = find_conf_by_address (source_p->host, NULL, NULL, NULL, CONF_CLIENT, | |
40c1fd47 | 548 | 0, source_p->username, NULL); |
212380e3 AC |
549 | |
550 | if(aconf == NULL) | |
551 | return; | |
552 | ||
48a06ae3 | 553 | get_printable_conf (aconf, &name, &host, &pass, &user, &port, &classname, &desc); |
40c1fd47 VY |
554 | if(!EmptyString(aconf->spasswd)) |
555 | pass = aconf->spasswd; | |
212380e3 AC |
556 | |
557 | sendto_one_numeric(source_p, RPL_STATSILINE, form_str(RPL_STATSILINE), | |
40c1fd47 | 558 | name, pass, show_iline_prefix(source_p, aconf, user), |
48a06ae3 | 559 | host, port, classname, desc); |
212380e3 AC |
560 | } |
561 | ||
562 | /* Theyre opered, or allowed to see all auth blocks */ | |
563 | else | |
564 | report_auth (source_p); | |
565 | } | |
566 | ||
567 | ||
568 | static void | |
569 | stats_tklines(struct Client *source_p) | |
570 | { | |
571 | /* Oper only, if unopered, return ERR_NOPRIVS */ | |
d4f7eb4c | 572 | if((ConfigFileEntry.stats_k_oper_only == 2) && !IsOperGeneral (source_p)) |
212380e3 AC |
573 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, |
574 | form_str (ERR_NOPRIVILEGES)); | |
575 | ||
576 | /* If unopered, Only return matching klines */ | |
d4f7eb4c | 577 | else if((ConfigFileEntry.stats_k_oper_only == 1) && !IsOperGeneral (source_p)) |
212380e3 AC |
578 | { |
579 | struct ConfItem *aconf; | |
580 | char *host, *pass, *user, *oper_reason; | |
581 | ||
582 | if(MyConnect (source_p)) | |
583 | aconf = find_conf_by_address (source_p->host, source_p->sockhost, NULL, | |
584 | (struct sockaddr *)&source_p->localClient->ip, | |
585 | CONF_KILL, | |
2d77d121 | 586 | GET_SS_FAMILY(&source_p->localClient->ip), |
40c1fd47 | 587 | source_p->username, NULL); |
212380e3 AC |
588 | else |
589 | aconf = find_conf_by_address (source_p->host, NULL, NULL, NULL, CONF_KILL, | |
40c1fd47 | 590 | 0, source_p->username, NULL); |
212380e3 AC |
591 | |
592 | if(aconf == NULL) | |
593 | return; | |
594 | ||
595 | /* dont report a permanent kline as a tkline */ | |
596 | if((aconf->flags & CONF_FLAGS_TEMPORARY) == 0) | |
597 | return; | |
598 | ||
599 | get_printable_kline(source_p, aconf, &host, &pass, &user, &oper_reason); | |
600 | ||
55abcbb2 | 601 | sendto_one_numeric(source_p, RPL_STATSKLINE, |
d8f0b5d7 | 602 | form_str(RPL_STATSKLINE), (aconf->flags & CONF_FLAGS_TEMPORARY) ? 'k' : 'K', |
0a621c4b | 603 | host, user, pass, oper_reason ? "|" : "", |
212380e3 AC |
604 | oper_reason ? oper_reason : ""); |
605 | } | |
606 | /* Theyre opered, or allowed to see all klines */ | |
607 | else | |
608 | { | |
609 | struct ConfItem *aconf; | |
5b96d9a6 | 610 | rb_dlink_node *ptr; |
212380e3 AC |
611 | int i; |
612 | char *user, *host, *pass, *oper_reason; | |
613 | ||
614 | for(i = 0; i < LAST_TEMP_TYPE; i++) | |
615 | { | |
5b96d9a6 | 616 | RB_DLINK_FOREACH(ptr, temp_klines[i].head) |
212380e3 AC |
617 | { |
618 | aconf = ptr->data; | |
619 | ||
55abcbb2 | 620 | get_printable_kline(source_p, aconf, &host, &pass, |
212380e3 AC |
621 | &user, &oper_reason); |
622 | ||
623 | sendto_one_numeric(source_p, RPL_STATSKLINE, | |
624 | form_str(RPL_STATSKLINE), | |
625 | 'k', host, user, pass, | |
626 | oper_reason ? "|" : "", | |
627 | oper_reason ? oper_reason : ""); | |
628 | } | |
629 | } | |
630 | } | |
631 | } | |
632 | ||
34c10ca8 EM |
633 | /* report_Klines() |
634 | * | |
635 | * inputs - Client to report to, mask | |
636 | * outputs - | |
637 | * side effects - Reports configured K-lines to client_p. | |
638 | */ | |
639 | static void | |
640 | report_Klines(struct Client *source_p) | |
641 | { | |
642 | char *host, *pass, *user, *oper_reason; | |
643 | struct AddressRec *arec; | |
644 | struct ConfItem *aconf = NULL; | |
645 | int i; | |
646 | ||
647 | for (i = 0; i < ATABLE_SIZE; i++) | |
648 | { | |
649 | for (arec = atable[i]; arec; arec = arec->next) | |
650 | { | |
651 | if(arec->type == CONF_KILL) | |
652 | { | |
653 | aconf = arec->aconf; | |
654 | ||
655 | /* its a tempkline, theyre reported elsewhere */ | |
656 | if(aconf->flags & CONF_FLAGS_TEMPORARY) | |
657 | continue; | |
658 | ||
659 | get_printable_kline(source_p, aconf, &host, &pass, &user, &oper_reason); | |
660 | sendto_one_numeric(source_p, RPL_STATSKLINE, | |
661 | form_str(RPL_STATSKLINE), | |
662 | 'K', host, user, pass, | |
663 | oper_reason ? "|" : "", | |
664 | oper_reason ? oper_reason : ""); | |
665 | } | |
666 | } | |
667 | } | |
668 | } | |
669 | ||
212380e3 AC |
670 | static void |
671 | stats_klines(struct Client *source_p) | |
672 | { | |
673 | /* Oper only, if unopered, return ERR_NOPRIVS */ | |
d4f7eb4c | 674 | if((ConfigFileEntry.stats_k_oper_only == 2) && !IsOperGeneral (source_p)) |
212380e3 AC |
675 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, |
676 | form_str (ERR_NOPRIVILEGES)); | |
677 | ||
678 | /* If unopered, Only return matching klines */ | |
d4f7eb4c | 679 | else if((ConfigFileEntry.stats_k_oper_only == 1) && !IsOperGeneral (source_p)) |
212380e3 AC |
680 | { |
681 | struct ConfItem *aconf; | |
682 | char *host, *pass, *user, *oper_reason; | |
683 | ||
684 | /* search for a kline */ | |
685 | if(MyConnect (source_p)) | |
686 | aconf = find_conf_by_address (source_p->host, source_p->sockhost, NULL, | |
687 | (struct sockaddr *)&source_p->localClient->ip, | |
688 | CONF_KILL, | |
2d77d121 | 689 | GET_SS_FAMILY(&source_p->localClient->ip), |
40c1fd47 | 690 | source_p->username, NULL); |
212380e3 AC |
691 | else |
692 | aconf = find_conf_by_address (source_p->host, NULL, NULL, NULL, CONF_KILL, | |
40c1fd47 | 693 | 0, source_p->username, NULL); |
212380e3 AC |
694 | |
695 | if(aconf == NULL) | |
696 | return; | |
697 | ||
212380e3 AC |
698 | get_printable_kline(source_p, aconf, &host, &pass, &user, &oper_reason); |
699 | ||
700 | sendto_one_numeric(source_p, RPL_STATSKLINE, form_str(RPL_STATSKLINE), | |
d8f0b5d7 | 701 | (aconf->flags & CONF_FLAGS_TEMPORARY) ? 'k' : 'K', |
0a621c4b | 702 | host, user, pass, oper_reason ? "|" : "", |
212380e3 AC |
703 | oper_reason ? oper_reason : ""); |
704 | } | |
705 | /* Theyre opered, or allowed to see all klines */ | |
706 | else | |
707 | report_Klines (source_p); | |
708 | } | |
709 | ||
710 | static void | |
711 | stats_messages(struct Client *source_p) | |
712 | { | |
4177311e | 713 | rb_dictionary_iter iter; |
e8f1c19e | 714 | struct Message *msg; |
e8f1c19e | 715 | |
56f84ded | 716 | RB_DICTIONARY_FOREACH(msg, &iter, cmd_dict) |
e8f1c19e EM |
717 | { |
718 | s_assert(msg->cmd != NULL); | |
719 | sendto_one_numeric(source_p, RPL_STATSCOMMANDS, | |
720 | form_str(RPL_STATSCOMMANDS), | |
721 | msg->cmd, msg->count, | |
722 | msg->bytes, msg->rcount); | |
723 | } | |
212380e3 AC |
724 | } |
725 | ||
726 | static void | |
727 | stats_dnsbl(struct Client *source_p) | |
728 | { | |
d3f6b808 | 729 | rb_dictionary_iter iter; |
fbc97166 | 730 | struct DNSBLEntry *entry; |
212380e3 | 731 | |
3321eef4 | 732 | if(dnsbl_stats == NULL) |
896370cc SA |
733 | return; |
734 | ||
fbc97166 | 735 | RB_DICTIONARY_FOREACH(entry, &iter, dnsbl_stats) |
212380e3 | 736 | { |
212380e3 | 737 | /* use RPL_STATSDEBUG for now -- jilles */ |
d3f6b808 | 738 | sendto_one_numeric(source_p, RPL_STATSDEBUG, "n :%d %s", |
fbc97166 | 739 | entry->hits, entry->host); |
212380e3 AC |
740 | } |
741 | } | |
742 | ||
743 | static void | |
744 | stats_oper(struct Client *source_p) | |
745 | { | |
746 | struct oper_conf *oper_p; | |
5b96d9a6 | 747 | rb_dlink_node *ptr; |
212380e3 | 748 | |
d4f7eb4c | 749 | if(!IsOperGeneral(source_p) && ConfigFileEntry.stats_o_oper_only) |
212380e3 AC |
750 | { |
751 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
752 | form_str (ERR_NOPRIVILEGES)); | |
753 | return; | |
754 | } | |
755 | ||
5b96d9a6 | 756 | RB_DLINK_FOREACH(ptr, oper_conf_list.head) |
212380e3 AC |
757 | { |
758 | oper_p = ptr->data; | |
55abcbb2 KB |
759 | |
760 | sendto_one_numeric(source_p, RPL_STATSOLINE, | |
212380e3 AC |
761 | form_str(RPL_STATSOLINE), |
762 | oper_p->username, oper_p->host, oper_p->name, | |
6d5be11f | 763 | HasPrivilege(source_p, "oper:privs") ? oper_p->privset->name : "0", "-1"); |
212380e3 AC |
764 | } |
765 | } | |
766 | ||
ac37f16a AC |
767 | static void |
768 | stats_capability_walk(const char *line, void *data) | |
769 | { | |
770 | struct Client *client_p = data; | |
771 | ||
772 | sendto_one_numeric(client_p, RPL_STATSDEBUG, "C :%s", line); | |
773 | } | |
774 | ||
775 | static void | |
776 | stats_capability(struct Client *client_p) | |
777 | { | |
778 | capability_index_stats(stats_capability_walk, client_p); | |
779 | } | |
780 | ||
3a177354 JT |
781 | static void |
782 | stats_privset(struct Client *source_p) | |
783 | { | |
784 | privilegeset_report(source_p); | |
785 | } | |
212380e3 AC |
786 | |
787 | /* stats_operedup() | |
788 | * | |
789 | * input - client pointer | |
790 | * output - none | |
791 | * side effects - client is shown a list of active opers | |
792 | */ | |
793 | static void | |
794 | stats_operedup (struct Client *source_p) | |
795 | { | |
796 | struct Client *target_p; | |
5b96d9a6 | 797 | rb_dlink_node *oper_ptr; |
212380e3 AC |
798 | unsigned int count = 0; |
799 | ||
5b96d9a6 | 800 | RB_DLINK_FOREACH (oper_ptr, oper_list.head) |
212380e3 AC |
801 | { |
802 | target_p = oper_ptr->data; | |
803 | ||
1123eefc | 804 | if(!SeesOper(target_p, source_p)) |
212380e3 AC |
805 | continue; |
806 | ||
c127b45b | 807 | if(target_p->user->away) |
212380e3 AC |
808 | continue; |
809 | ||
810 | count++; | |
811 | ||
812 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
813 | "p :%s (%s@%s)", | |
55abcbb2 | 814 | target_p->name, target_p->username, |
212380e3 AC |
815 | target_p->host); |
816 | } | |
817 | ||
818 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
819 | "p :%u staff members", count); | |
820 | ||
821 | stats_p_spy (source_p); | |
822 | } | |
823 | ||
824 | static void | |
825 | stats_ports (struct Client *source_p) | |
826 | { | |
d4f7eb4c | 827 | if(!IsOperGeneral (source_p) && ConfigFileEntry.stats_P_oper_only) |
212380e3 AC |
828 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, |
829 | form_str (ERR_NOPRIVILEGES)); | |
830 | else | |
831 | show_ports (source_p); | |
832 | } | |
833 | ||
834 | static void | |
835 | stats_tresv(struct Client *source_p) | |
836 | { | |
837 | struct ConfItem *aconf; | |
2fc6772e | 838 | rb_radixtree_iteration_state state; |
5b96d9a6 | 839 | rb_dlink_node *ptr; |
212380e3 | 840 | |
5b96d9a6 | 841 | RB_DLINK_FOREACH(ptr, resv_conf_list.head) |
212380e3 AC |
842 | { |
843 | aconf = ptr->data; | |
844 | if(aconf->hold) | |
55abcbb2 | 845 | sendto_one_numeric(source_p, RPL_STATSQLINE, |
212380e3 | 846 | form_str(RPL_STATSQLINE), |
70ea02eb | 847 | 'q', aconf->port, aconf->host, aconf->passwd); |
212380e3 AC |
848 | } |
849 | ||
a4bf26dd | 850 | RB_RADIXTREE_FOREACH(aconf, &state, resv_tree) |
212380e3 | 851 | { |
212380e3 | 852 | if(aconf->hold) |
55abcbb2 | 853 | sendto_one_numeric(source_p, RPL_STATSQLINE, |
212380e3 | 854 | form_str(RPL_STATSQLINE), |
70ea02eb | 855 | 'q', aconf->port, aconf->host, aconf->passwd); |
212380e3 | 856 | } |
212380e3 AC |
857 | } |
858 | ||
859 | ||
860 | static void | |
861 | stats_resv(struct Client *source_p) | |
862 | { | |
863 | struct ConfItem *aconf; | |
2fc6772e | 864 | rb_radixtree_iteration_state state; |
5b96d9a6 | 865 | rb_dlink_node *ptr; |
212380e3 | 866 | |
5b96d9a6 | 867 | RB_DLINK_FOREACH(ptr, resv_conf_list.head) |
212380e3 AC |
868 | { |
869 | aconf = ptr->data; | |
870 | if(!aconf->hold) | |
55abcbb2 | 871 | sendto_one_numeric(source_p, RPL_STATSQLINE, |
212380e3 | 872 | form_str(RPL_STATSQLINE), |
23959371 | 873 | 'Q', aconf->port, aconf->host, aconf->passwd); |
212380e3 AC |
874 | } |
875 | ||
a4bf26dd | 876 | RB_RADIXTREE_FOREACH(aconf, &state, resv_tree) |
212380e3 | 877 | { |
212380e3 | 878 | if(!aconf->hold) |
55abcbb2 | 879 | sendto_one_numeric(source_p, RPL_STATSQLINE, |
212380e3 | 880 | form_str(RPL_STATSQLINE), |
23959371 | 881 | 'Q', aconf->port, aconf->host, aconf->passwd); |
212380e3 | 882 | } |
212380e3 AC |
883 | } |
884 | ||
8f0dd52c EK |
885 | static void |
886 | stats_secure(struct Client *source_p) | |
887 | { | |
888 | struct AddressRec *arec; | |
889 | struct ConfItem *aconf; | |
890 | size_t i; | |
891 | ||
892 | for (i = 0; i < ATABLE_SIZE; i++) | |
893 | { | |
894 | for (arec = atable[i]; arec; arec = arec->next) | |
895 | { | |
896 | if(arec->type == CONF_SECURE) | |
897 | { | |
898 | aconf = arec->aconf; | |
899 | sendto_one_numeric(source_p, RPL_STATSDEBUG, "s :%s", aconf->host); | |
900 | } | |
901 | } | |
902 | } | |
903 | } | |
904 | ||
eb1b303d | 905 | static void |
e9ffc3c1 | 906 | stats_ssld_foreach(void *data, pid_t pid, int cli_count, enum ssld_status status, const char *version) |
eb1b303d SA |
907 | { |
908 | struct Client *source_p = data; | |
909 | ||
910 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
62f6351d J |
911 | "S :%ld %c %u :%s", |
912 | (long)pid, | |
eb1b303d | 913 | status == SSLD_DEAD ? 'D' : (status == SSLD_SHUTDOWN ? 'S' : 'A'), |
e9ffc3c1 SA |
914 | cli_count, |
915 | version); | |
eb1b303d SA |
916 | } |
917 | ||
918 | static void | |
919 | stats_ssld(struct Client *source_p) | |
920 | { | |
921 | ssld_foreach_info(stats_ssld_foreach, source_p); | |
922 | } | |
923 | ||
212380e3 AC |
924 | static void |
925 | stats_usage (struct Client *source_p) | |
926 | { | |
927 | struct rusage rus; | |
928 | time_t secs; | |
929 | time_t rup; | |
930 | #ifdef hz | |
931 | # define hzz hz | |
932 | #else | |
933 | # ifdef HZ | |
934 | # define hzz HZ | |
935 | # else | |
936 | int hzz = 1; | |
937 | # endif | |
938 | #endif | |
939 | ||
940 | if(getrusage(RUSAGE_SELF, &rus) == -1) | |
941 | { | |
942 | sendto_one_notice(source_p, ":Getruseage error: %s.", | |
943 | strerror(errno)); | |
944 | return; | |
945 | } | |
946 | secs = rus.ru_utime.tv_sec + rus.ru_stime.tv_sec; | |
947 | if(0 == secs) | |
948 | secs = 1; | |
949 | ||
e3354945 | 950 | rup = (rb_current_time() - startup_time) * hzz; |
212380e3 AC |
951 | if(0 == rup) |
952 | rup = 1; | |
55abcbb2 | 953 | |
212380e3 | 954 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
e4ce3b54 | 955 | "R :CPU Secs %d:%02d User %d:%02d System %d:%02d", |
212380e3 AC |
956 | (int) (secs / 60), (int) (secs % 60), |
957 | (int) (rus.ru_utime.tv_sec / 60), | |
958 | (int) (rus.ru_utime.tv_sec % 60), | |
55abcbb2 | 959 | (int) (rus.ru_stime.tv_sec / 60), |
212380e3 AC |
960 | (int) (rus.ru_stime.tv_sec % 60)); |
961 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
962 | "R :RSS %ld ShMem %ld Data %ld Stack %ld", | |
5c01fc8b MM |
963 | rus.ru_maxrss, (long)(rus.ru_ixrss / rup), |
964 | (long)(rus.ru_idrss / rup), (long)(rus.ru_isrss / rup)); | |
212380e3 AC |
965 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
966 | "R :Swaps %d Reclaims %d Faults %d", | |
967 | (int) rus.ru_nswap, (int) rus.ru_minflt, (int) rus.ru_majflt); | |
968 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
969 | "R :Block in %d out %d", | |
970 | (int) rus.ru_inblock, (int) rus.ru_oublock); | |
971 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
972 | "R :Msg Rcv %d Send %d", | |
973 | (int) rus.ru_msgrcv, (int) rus.ru_msgsnd); | |
974 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
975 | "R :Signals %d Context Vol. %d Invol %d", | |
55abcbb2 | 976 | (int) rus.ru_nsignals, (int) rus.ru_nvcsw, |
212380e3 AC |
977 | (int) rus.ru_nivcsw); |
978 | } | |
979 | ||
54ac8b60 VY |
980 | static void |
981 | stats_tstats (struct Client *source_p) | |
982 | { | |
47adde3d VY |
983 | struct Client *target_p; |
984 | struct ServerStatistics sp; | |
985 | rb_dlink_node *ptr; | |
986 | ||
987 | memcpy(&sp, &ServerStats, sizeof(struct ServerStatistics)); | |
988 | ||
8bd5767b JT |
989 | RB_DLINK_FOREACH(ptr, serv_list.head) |
990 | { | |
991 | target_p = ptr->data; | |
992 | ||
993 | sp.is_sbs += target_p->localClient->sendB; | |
994 | sp.is_sbr += target_p->localClient->receiveB; | |
679ccbe5 | 995 | sp.is_sti += (unsigned long long)(rb_current_time() - target_p->localClient->firsttime); |
8bd5767b JT |
996 | sp.is_sv++; |
997 | } | |
998 | ||
999 | RB_DLINK_FOREACH(ptr, lclient_list.head) | |
1000 | { | |
1001 | target_p = ptr->data; | |
1002 | ||
1003 | sp.is_cbs += target_p->localClient->sendB; | |
1004 | sp.is_cbr += target_p->localClient->receiveB; | |
679ccbe5 | 1005 | sp.is_cti += (unsigned long long)(rb_current_time() - target_p->localClient->firsttime); |
8bd5767b | 1006 | sp.is_cl++; |
47adde3d VY |
1007 | } |
1008 | ||
1009 | RB_DLINK_FOREACH(ptr, unknown_list.head) | |
1010 | { | |
1011 | sp.is_ni++; | |
1012 | } | |
1013 | ||
1014 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1015 | "T :accepts %u refused %u", sp.is_ac, sp.is_ref); | |
1016 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
55abcbb2 | 1017 | "T :rejected %u delaying %lu", |
43946961 | 1018 | sp.is_rej, delay_exit_length()); |
ae09cb7d JT |
1019 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
1020 | "T :throttled refused %u throttle list size %lu", sp.is_thr, throttle_size()); | |
47adde3d VY |
1021 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
1022 | "T :nicks being delayed %lu", | |
1023 | get_nd_count()); | |
1024 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1025 | "T :unknown commands %u prefixes %u", | |
1026 | sp.is_unco, sp.is_unpf); | |
1027 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1028 | "T :nick collisions %u saves %u unknown closes %u", | |
1029 | sp.is_kill, sp.is_save, sp.is_ni); | |
1030 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
55abcbb2 | 1031 | "T :wrong direction %u empty %u", |
47adde3d VY |
1032 | sp.is_wrdi, sp.is_empt); |
1033 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1034 | "T :numerics seen %u", sp.is_num); | |
1035 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1036 | "T :tgchange blocked msgs %u restricted addrs %lu", | |
1037 | sp.is_tgch, rb_dlink_list_length(&tgchange_list)); | |
e88a1f1b KB |
1038 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
1039 | "T :ratelimit blocked commands %u", sp.is_rl); | |
47adde3d VY |
1040 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
1041 | "T :auth successes %u fails %u", | |
1042 | sp.is_asuc, sp.is_abad); | |
1043 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1044 | "T :sasl successes %u fails %u", | |
1045 | sp.is_ssuc, sp.is_sbad); | |
1046 | sendto_one_numeric(source_p, RPL_STATSDEBUG, "T :Client Server"); | |
1047 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1048 | "T :connected %u %u", sp.is_cl, sp.is_sv); | |
8bd5767b JT |
1049 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
1050 | "T :bytes sent %lluK %lluK", | |
55abcbb2 | 1051 | sp.is_cbs / 1024, |
8bd5767b JT |
1052 | sp.is_sbs / 1024); |
1053 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1054 | "T :bytes recv %lluK %lluK", | |
55abcbb2 | 1055 | sp.is_cbr / 1024, |
7685dd09 | 1056 | sp.is_sbr / 1024); |
8bd5767b | 1057 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
679ccbe5 AS |
1058 | "T :time connected %llu %llu", |
1059 | sp.is_cti, sp.is_sti); | |
212380e3 AC |
1060 | } |
1061 | ||
1062 | static void | |
1063 | stats_uptime (struct Client *source_p) | |
1064 | { | |
1065 | time_t now; | |
1066 | ||
e3354945 | 1067 | now = rb_current_time() - startup_time; |
55abcbb2 | 1068 | sendto_one_numeric(source_p, RPL_STATSUPTIME, |
212380e3 | 1069 | form_str (RPL_STATSUPTIME), |
77910830 JT |
1070 | (int)(now / 86400), (int)((now / 3600) % 24), |
1071 | (int)((now / 60) % 60), (int)(now % 60)); | |
212380e3 AC |
1072 | sendto_one_numeric(source_p, RPL_STATSCONN, |
1073 | form_str (RPL_STATSCONN), | |
55abcbb2 | 1074 | MaxConnectionCount, MaxClientCount, |
212380e3 AC |
1075 | Count.totalrestartcount); |
1076 | } | |
1077 | ||
212380e3 AC |
1078 | |
1079 | /* stats_servers() | |
1080 | * | |
1081 | * input - client pointer | |
1082 | * output - none | |
1083 | * side effects - client is shown lists of who connected servers | |
1084 | */ | |
1085 | static void | |
1086 | stats_servers (struct Client *source_p) | |
1087 | { | |
1088 | struct Client *target_p; | |
5b96d9a6 | 1089 | rb_dlink_node *ptr; |
212380e3 AC |
1090 | time_t seconds; |
1091 | int days, hours, minutes; | |
1092 | int j = 0; | |
1093 | ||
d4f7eb4c | 1094 | if(ConfigServerHide.flatten_links && !IsOperGeneral(source_p) && |
212380e3 AC |
1095 | !IsExemptShide(source_p)) |
1096 | { | |
1097 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
1098 | form_str (ERR_NOPRIVILEGES)); | |
1099 | return; | |
1100 | } | |
1101 | ||
5b96d9a6 | 1102 | RB_DLINK_FOREACH (ptr, serv_list.head) |
212380e3 AC |
1103 | { |
1104 | target_p = ptr->data; | |
1105 | ||
1106 | j++; | |
e3354945 | 1107 | seconds = rb_current_time() - target_p->localClient->firsttime; |
212380e3 AC |
1108 | |
1109 | days = (int) (seconds / 86400); | |
1110 | seconds %= 86400; | |
1111 | hours = (int) (seconds / 3600); | |
1112 | seconds %= 3600; | |
1113 | minutes = (int) (seconds / 60); | |
1114 | seconds %= 60; | |
1115 | ||
1116 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1117 | "V :%s (%s!*@*) Idle: %d SendQ: %d " | |
1118 | "Connected: %d day%s, %d:%02d:%02d", | |
1119 | target_p->name, | |
1120 | (target_p->serv->by[0] ? target_p->serv->by : "Remote."), | |
e3354945 | 1121 | (int) (rb_current_time() - target_p->localClient->lasttime), |
e8e79621 | 1122 | (int) rb_linebuf_len (&target_p->localClient->buf_sendq), |
55abcbb2 | 1123 | days, (days == 1) ? "" : "s", hours, minutes, |
212380e3 AC |
1124 | (int) seconds); |
1125 | } | |
1126 | ||
1127 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1128 | "V :%d Server(s)", j); | |
1129 | } | |
1130 | ||
1131 | static void | |
1132 | stats_tgecos(struct Client *source_p) | |
1133 | { | |
1134 | struct ConfItem *aconf; | |
5b96d9a6 | 1135 | rb_dlink_node *ptr; |
212380e3 | 1136 | |
5b96d9a6 | 1137 | RB_DLINK_FOREACH(ptr, xline_conf_list.head) |
212380e3 AC |
1138 | { |
1139 | aconf = ptr->data; | |
1140 | ||
1141 | if(aconf->hold) | |
1142 | sendto_one_numeric(source_p, RPL_STATSXLINE, | |
1143 | form_str(RPL_STATSXLINE), | |
23959371 | 1144 | 'x', aconf->port, aconf->host, |
212380e3 AC |
1145 | aconf->passwd); |
1146 | } | |
1147 | } | |
1148 | ||
1149 | static void | |
1150 | stats_gecos(struct Client *source_p) | |
1151 | { | |
1152 | struct ConfItem *aconf; | |
5b96d9a6 | 1153 | rb_dlink_node *ptr; |
212380e3 | 1154 | |
5b96d9a6 | 1155 | RB_DLINK_FOREACH(ptr, xline_conf_list.head) |
212380e3 AC |
1156 | { |
1157 | aconf = ptr->data; | |
1158 | ||
1159 | if(!aconf->hold) | |
1160 | sendto_one_numeric(source_p, RPL_STATSXLINE, | |
1161 | form_str(RPL_STATSXLINE), | |
55abcbb2 | 1162 | 'X', aconf->port, aconf->host, |
212380e3 AC |
1163 | aconf->passwd); |
1164 | } | |
1165 | } | |
1166 | ||
1167 | static void | |
1168 | stats_class(struct Client *source_p) | |
1169 | { | |
d4f7eb4c | 1170 | if(ConfigFileEntry.stats_y_oper_only && !IsOperGeneral(source_p)) |
212380e3 AC |
1171 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, |
1172 | form_str (ERR_NOPRIVILEGES)); | |
1173 | else | |
1174 | report_classes(source_p); | |
1175 | } | |
1176 | ||
1177 | static void | |
1178 | stats_memory (struct Client *source_p) | |
1179 | { | |
47adde3d VY |
1180 | struct Client *target_p; |
1181 | struct Channel *chptr; | |
47adde3d VY |
1182 | rb_dlink_node *rb_dlink; |
1183 | rb_dlink_node *ptr; | |
1184 | int channel_count = 0; | |
1185 | int local_client_conf_count = 0; /* local client conf links */ | |
1186 | int users_counted = 0; /* user structs */ | |
1187 | ||
1188 | int channel_users = 0; | |
1189 | int channel_invites = 0; | |
1190 | int channel_bans = 0; | |
1191 | int channel_except = 0; | |
1192 | int channel_invex = 0; | |
1193 | int channel_quiets = 0; | |
1194 | ||
1195 | int class_count = 0; /* classes */ | |
1196 | int conf_count = 0; /* conf lines */ | |
1197 | int users_invited_count = 0; /* users invited */ | |
1198 | int user_channels = 0; /* users in channels */ | |
c127b45b | 1199 | int aways_counted = 0; |
47adde3d VY |
1200 | size_t number_servers_cached; /* number of servers cached by scache */ |
1201 | ||
1202 | size_t channel_memory = 0; | |
1203 | size_t channel_ban_memory = 0; | |
1204 | size_t channel_except_memory = 0; | |
1205 | size_t channel_invex_memory = 0; | |
1206 | size_t channel_quiet_memory = 0; | |
1207 | ||
c127b45b | 1208 | size_t away_memory = 0; /* memory used by aways */ |
47adde3d VY |
1209 | size_t ww = 0; /* whowas array count */ |
1210 | size_t wwm = 0; /* whowas array memory used */ | |
1211 | size_t conf_memory = 0; /* memory used by conf lines */ | |
1212 | size_t mem_servers_cached; /* memory used by scache */ | |
1213 | ||
1214 | size_t linebuf_count = 0; | |
1215 | size_t linebuf_memory_used = 0; | |
1216 | ||
1217 | size_t total_channel_memory = 0; | |
1218 | size_t totww = 0; | |
1219 | ||
1220 | size_t local_client_count = 0; | |
1221 | size_t local_client_memory_used = 0; | |
1222 | ||
1223 | size_t remote_client_count = 0; | |
1224 | size_t remote_client_memory_used = 0; | |
1225 | ||
1226 | size_t total_memory = 0; | |
1227 | ||
b47f8a4f | 1228 | whowas_memory_usage(&ww, &wwm); |
47adde3d VY |
1229 | |
1230 | RB_DLINK_FOREACH(ptr, global_client_list.head) | |
1231 | { | |
1232 | target_p = ptr->data; | |
1233 | if(MyConnect(target_p)) | |
1234 | { | |
1235 | local_client_conf_count++; | |
1236 | } | |
1237 | ||
1238 | if(target_p->user) | |
1239 | { | |
1240 | users_counted++; | |
1241 | users_invited_count += rb_dlink_list_length(&target_p->user->invited); | |
1242 | user_channels += rb_dlink_list_length(&target_p->user->channel); | |
c127b45b SB |
1243 | if(target_p->user->away) |
1244 | { | |
1245 | aways_counted++; | |
1246 | away_memory += (strlen(target_p->user->away) + 1); | |
1247 | } | |
47adde3d VY |
1248 | } |
1249 | } | |
1250 | ||
1251 | /* Count up all channels, ban lists, except lists, Invex lists */ | |
1252 | RB_DLINK_FOREACH(ptr, global_channel_list.head) | |
1253 | { | |
1254 | chptr = ptr->data; | |
1255 | channel_count++; | |
1256 | channel_memory += (strlen(chptr->chname) + sizeof(struct Channel)); | |
1257 | ||
1258 | channel_users += rb_dlink_list_length(&chptr->members); | |
1259 | channel_invites += rb_dlink_list_length(&chptr->invites); | |
1260 | ||
1261 | RB_DLINK_FOREACH(rb_dlink, chptr->banlist.head) | |
1262 | { | |
47adde3d VY |
1263 | channel_bans++; |
1264 | ||
1265 | channel_ban_memory += sizeof(rb_dlink_node) + sizeof(struct Ban); | |
1266 | } | |
1267 | ||
1268 | RB_DLINK_FOREACH(rb_dlink, chptr->exceptlist.head) | |
1269 | { | |
47adde3d VY |
1270 | channel_except++; |
1271 | ||
1272 | channel_except_memory += (sizeof(rb_dlink_node) + sizeof(struct Ban)); | |
1273 | } | |
1274 | ||
1275 | RB_DLINK_FOREACH(rb_dlink, chptr->invexlist.head) | |
1276 | { | |
47adde3d VY |
1277 | channel_invex++; |
1278 | ||
1279 | channel_invex_memory += (sizeof(rb_dlink_node) + sizeof(struct Ban)); | |
1280 | } | |
1281 | ||
1282 | RB_DLINK_FOREACH(rb_dlink, chptr->quietlist.head) | |
1283 | { | |
47adde3d VY |
1284 | channel_quiets++; |
1285 | ||
1286 | channel_quiet_memory += (sizeof(rb_dlink_node) + sizeof(struct Ban)); | |
1287 | } | |
1288 | } | |
1289 | ||
1290 | /* count up all classes */ | |
1291 | ||
1292 | class_count = rb_dlink_list_length(&class_list) + 1; | |
1293 | ||
1294 | rb_count_rb_linebuf_memory(&linebuf_count, &linebuf_memory_used); | |
1295 | ||
1296 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1297 | "z :Users %u(%lu) Invites %u(%lu)", | |
1298 | users_counted, | |
1299 | (unsigned long) users_counted * sizeof(struct User), | |
55abcbb2 | 1300 | users_invited_count, |
47adde3d VY |
1301 | (unsigned long) users_invited_count * sizeof(rb_dlink_node)); |
1302 | ||
1303 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c | 1304 | "z :User channels %u(%lu) Aways %u(%zu)", |
47adde3d | 1305 | user_channels, |
c127b45b | 1306 | (unsigned long) user_channels * sizeof(rb_dlink_node), |
689afc7c | 1307 | aways_counted, away_memory); |
47adde3d VY |
1308 | |
1309 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1310 | "z :Attached confs %u(%lu)", | |
1311 | local_client_conf_count, | |
1312 | (unsigned long) local_client_conf_count * sizeof(rb_dlink_node)); | |
1313 | ||
1314 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c | 1315 | "z :Conflines %u(%zu)", conf_count, conf_memory); |
47adde3d VY |
1316 | |
1317 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1318 | "z :Classes %u(%lu)", | |
55abcbb2 | 1319 | class_count, |
47adde3d VY |
1320 | (unsigned long) class_count * sizeof(struct Class)); |
1321 | ||
1322 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1323 | "z :Channels %u(%zu)", |
1324 | channel_count, channel_memory); | |
47adde3d VY |
1325 | |
1326 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1327 | "z :Bans %u(%zu) Exceptions %u(%zu) Invex %u(%zu) Quiets %u(%zu)", |
1328 | channel_bans, channel_ban_memory, | |
1329 | channel_except, channel_except_memory, | |
1330 | channel_invex, channel_invex_memory, | |
1331 | channel_quiets, channel_quiet_memory); | |
47adde3d VY |
1332 | |
1333 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1334 | "z :Channel members %u(%lu) invite %u(%lu)", | |
1335 | channel_users, | |
1336 | (unsigned long) channel_users * sizeof(rb_dlink_node), | |
55abcbb2 | 1337 | channel_invites, |
47adde3d VY |
1338 | (unsigned long) channel_invites * sizeof(rb_dlink_node)); |
1339 | ||
1340 | total_channel_memory = channel_memory + | |
1341 | channel_ban_memory + | |
1342 | channel_users * sizeof(rb_dlink_node) + channel_invites * sizeof(rb_dlink_node); | |
1343 | ||
1344 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1345 | "z :Whowas array %zu(%zu)", |
1346 | ww, wwm); | |
47adde3d VY |
1347 | |
1348 | totww = wwm; | |
1349 | ||
1350 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1351 | "z :Hash: client %u(%lu) chan %u(%lu)", |
1352 | U_MAX, (unsigned long)(U_MAX * sizeof(rb_dlink_list)), | |
1353 | CH_MAX, (unsigned long)(CH_MAX * sizeof(rb_dlink_list))); | |
47adde3d VY |
1354 | |
1355 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1356 | "z :linebuf %zu(%zu)", |
1357 | linebuf_count, linebuf_memory_used); | |
47adde3d VY |
1358 | |
1359 | count_scache(&number_servers_cached, &mem_servers_cached); | |
1360 | ||
1361 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1362 | "z :scache %zu(%zu)", |
1363 | number_servers_cached, mem_servers_cached); | |
47adde3d VY |
1364 | |
1365 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1366 | "z :hostname hash %d(%lu)", |
1367 | HOST_MAX, (unsigned long)HOST_MAX * sizeof(rb_dlink_list)); | |
47adde3d VY |
1368 | |
1369 | total_memory = totww + total_channel_memory + conf_memory + | |
1370 | class_count * sizeof(struct Class); | |
1371 | ||
1372 | total_memory += mem_servers_cached; | |
1373 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1374 | "z :Total: whowas %zu channel %zu conf %zu", |
1375 | totww, total_channel_memory, | |
1376 | conf_memory); | |
47adde3d VY |
1377 | |
1378 | count_local_client_memory(&local_client_count, &local_client_memory_used); | |
1379 | total_memory += local_client_memory_used; | |
1380 | ||
1381 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1382 | "z :Local client Memory in use: %zu(%zu)", |
1383 | local_client_count, local_client_memory_used); | |
47adde3d VY |
1384 | |
1385 | ||
1386 | count_remote_client_memory(&remote_client_count, &remote_client_memory_used); | |
1387 | total_memory += remote_client_memory_used; | |
1388 | ||
1389 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1390 | "z :Remote client Memory in use: %zu(%zu)", |
1391 | remote_client_count, | |
1392 | remote_client_memory_used); | |
7c5b4dbb DF |
1393 | |
1394 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1395 | "z :TOTAL: %zu", | |
1396 | total_memory); | |
212380e3 AC |
1397 | } |
1398 | ||
212380e3 AC |
1399 | static void |
1400 | stats_servlinks (struct Client *source_p) | |
1401 | { | |
1402 | static char Sformat[] = ":%s %d %s %s %u %u %u %u %u :%u %u %s"; | |
1403 | long uptime, sendK, receiveK; | |
1404 | struct Client *target_p; | |
5b96d9a6 | 1405 | rb_dlink_node *ptr; |
212380e3 | 1406 | int j = 0; |
70b72a07 | 1407 | char buf[128]; |
212380e3 | 1408 | |
d4f7eb4c | 1409 | if(ConfigServerHide.flatten_links && !IsOperGeneral (source_p) && |
212380e3 AC |
1410 | !IsExemptShide(source_p)) |
1411 | { | |
1412 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
1413 | form_str (ERR_NOPRIVILEGES)); | |
1414 | return; | |
1415 | } | |
1416 | ||
1417 | sendK = receiveK = 0; | |
1418 | ||
5b96d9a6 | 1419 | RB_DLINK_FOREACH (ptr, serv_list.head) |
212380e3 AC |
1420 | { |
1421 | target_p = ptr->data; | |
1422 | ||
1423 | j++; | |
1424 | sendK += target_p->localClient->sendK; | |
1425 | receiveK += target_p->localClient->receiveK; | |
1426 | ||
1427 | sendto_one(source_p, Sformat, | |
1428 | get_id(&me, source_p), RPL_STATSLINKINFO, get_id(source_p, source_p), | |
b3ebc7ab | 1429 | target_p->name, |
d269d0b6 | 1430 | (int) rb_linebuf_len (&target_p->localClient->buf_sendq), |
212380e3 AC |
1431 | (int) target_p->localClient->sendM, |
1432 | (int) target_p->localClient->sendK, | |
1433 | (int) target_p->localClient->receiveM, | |
1434 | (int) target_p->localClient->receiveK, | |
e3354945 | 1435 | rb_current_time() - target_p->localClient->firsttime, |
55abcbb2 | 1436 | (rb_current_time() > target_p->localClient->lasttime) ? |
e3354945 | 1437 | (rb_current_time() - target_p->localClient->lasttime) : 0, |
d4f7eb4c | 1438 | IsOperGeneral (source_p) ? show_capabilities (target_p) : "TS"); |
212380e3 AC |
1439 | } |
1440 | ||
1441 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1442 | "? :%u total server(s)", j); | |
1443 | ||
70b72a07 | 1444 | snprintf(buf, sizeof buf, "%7.2f", _GMKv ((sendK))); |
212380e3 | 1445 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
70b72a07 JT |
1446 | "? :Sent total : %s %s", |
1447 | buf, _GMKs (sendK)); | |
1448 | snprintf(buf, sizeof buf, "%7.2f", _GMKv ((receiveK))); | |
212380e3 | 1449 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
70b72a07 JT |
1450 | "? :Recv total : %s %s", |
1451 | buf, _GMKs (receiveK)); | |
212380e3 | 1452 | |
e3354945 | 1453 | uptime = (rb_current_time() - startup_time); |
70b72a07 | 1454 | snprintf(buf, sizeof buf, "%7.2f %s (%4.1f K/s)", |
55abcbb2 | 1455 | _GMKv (me.localClient->sendK), |
212380e3 AC |
1456 | _GMKs (me.localClient->sendK), |
1457 | (float) ((float) me.localClient->sendK / (float) uptime)); | |
70b72a07 JT |
1458 | sendto_one_numeric(source_p, RPL_STATSDEBUG, "? :Server send: %s", buf); |
1459 | snprintf(buf, sizeof buf, "%7.2f %s (%4.1f K/s)", | |
212380e3 AC |
1460 | _GMKv (me.localClient->receiveK), |
1461 | _GMKs (me.localClient->receiveK), | |
1462 | (float) ((float) me.localClient->receiveK / (float) uptime)); | |
70b72a07 | 1463 | sendto_one_numeric(source_p, RPL_STATSDEBUG, "? :Server recv: %s", buf); |
212380e3 AC |
1464 | } |
1465 | ||
3c7d6fcc | 1466 | static inline bool |
24652f2b | 1467 | stats_l_should_show_oper(struct Client *source_p, struct Client *target_p) |
4727c0f5 | 1468 | { |
24652f2b | 1469 | return SeesOper(target_p, source_p); |
4727c0f5 AC |
1470 | } |
1471 | ||
212380e3 AC |
1472 | static void |
1473 | stats_ltrace(struct Client *source_p, int parc, const char *parv[]) | |
1474 | { | |
3c7d6fcc EM |
1475 | bool doall = false; |
1476 | bool wilds = false; | |
212380e3 AC |
1477 | const char *name; |
1478 | char statchar = parv[1][0]; | |
1479 | ||
63ab1dd6 EK |
1480 | if (ConfigFileEntry.stats_l_oper_only == STATS_L_OPER_ONLY_YES && !IsOperGeneral(source_p)) |
1481 | { | |
1482 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, form_str(ERR_NOPRIVILEGES)); | |
1483 | return; | |
1484 | } | |
1485 | ||
212380e3 | 1486 | /* this is def targeted at us somehow.. */ |
63ab1dd6 | 1487 | if (parc > 2 && !EmptyString(parv[2])) |
212380e3 AC |
1488 | { |
1489 | /* directed at us generically? */ | |
63ab1dd6 | 1490 | if (match(parv[2], me.name) || (!MyClient(source_p) && !irccmp(parv[2], me.id))) |
212380e3 AC |
1491 | { |
1492 | name = me.name; | |
3c7d6fcc | 1493 | doall = true; |
212380e3 AC |
1494 | } |
1495 | else | |
1496 | { | |
1497 | name = parv[2]; | |
1498 | wilds = strchr(name, '*') || strchr(name, '?'); | |
1499 | } | |
1500 | ||
1501 | /* must be directed at a specific person thats not us */ | |
63ab1dd6 | 1502 | if (!doall && !wilds) |
212380e3 AC |
1503 | { |
1504 | struct Client *target_p; | |
1505 | ||
63ab1dd6 | 1506 | if (MyClient(source_p)) |
212380e3 AC |
1507 | target_p = find_named_person(name); |
1508 | else | |
1509 | target_p = find_person(name); | |
1510 | ||
63ab1dd6 EK |
1511 | if (target_p != source_p && ConfigFileEntry.stats_l_oper_only != STATS_L_OPER_ONLY_NO |
1512 | && !IsOperGeneral(source_p)) | |
1513 | { | |
1514 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, form_str(ERR_NOPRIVILEGES)); | |
1515 | } | |
1516 | else if (target_p != NULL) | |
212380e3 AC |
1517 | { |
1518 | stats_spy(source_p, statchar, target_p->name); | |
1519 | stats_l_client(source_p, target_p, statchar); | |
1520 | } | |
1521 | else | |
63ab1dd6 | 1522 | { |
212380e3 AC |
1523 | sendto_one_numeric(source_p, ERR_NOSUCHSERVER, |
1524 | form_str(ERR_NOSUCHSERVER), | |
1525 | name); | |
63ab1dd6 | 1526 | } |
212380e3 AC |
1527 | |
1528 | return; | |
1529 | } | |
1530 | } | |
1531 | else | |
1532 | { | |
1533 | name = me.name; | |
3c7d6fcc | 1534 | doall = true; |
212380e3 AC |
1535 | } |
1536 | ||
1537 | stats_spy(source_p, statchar, name); | |
1538 | ||
63ab1dd6 EK |
1539 | if (ConfigFileEntry.stats_l_oper_only != STATS_L_OPER_ONLY_NO && !IsOperGeneral(source_p)) |
1540 | { | |
1541 | if (doall && MyClient(source_p)) | |
1542 | stats_l_client(source_p, source_p, statchar); | |
1543 | else | |
1544 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, form_str(ERR_NOPRIVILEGES)); | |
1545 | return; | |
1546 | } | |
1547 | ||
1548 | if (doall) | |
212380e3 AC |
1549 | { |
1550 | /* local opers get everyone */ | |
1551 | if(MyOper(source_p)) | |
1552 | { | |
e82bda18 AC |
1553 | stats_l_list(source_p, name, doall, wilds, &unknown_list, statchar, NULL); |
1554 | stats_l_list(source_p, name, doall, wilds, &lclient_list, statchar, NULL); | |
212380e3 AC |
1555 | } |
1556 | else | |
1557 | { | |
1558 | /* they still need themselves if theyre local.. */ | |
1559 | if(MyClient(source_p)) | |
1560 | stats_l_client(source_p, source_p, statchar); | |
1561 | ||
4727c0f5 | 1562 | stats_l_list(source_p, name, doall, wilds, &local_oper_list, statchar, stats_l_should_show_oper); |
212380e3 AC |
1563 | } |
1564 | ||
d4f7eb4c | 1565 | if (!ConfigServerHide.flatten_links || IsOperGeneral(source_p) || |
212380e3 | 1566 | IsExemptShide(source_p)) |
e82bda18 | 1567 | stats_l_list(source_p, name, doall, wilds, &serv_list, statchar, NULL); |
212380e3 AC |
1568 | |
1569 | return; | |
1570 | } | |
1571 | ||
1572 | /* ok, at this point theyre looking for a specific client whos on | |
1573 | * our server.. but it contains a wildcard. --fl | |
1574 | */ | |
e82bda18 | 1575 | stats_l_list(source_p, name, doall, wilds, &lclient_list, statchar, NULL); |
212380e3 AC |
1576 | |
1577 | return; | |
1578 | } | |
1579 | ||
212380e3 | 1580 | static void |
3c7d6fcc | 1581 | stats_l_list(struct Client *source_p, const char *name, bool doall, bool wilds, |
24652f2b | 1582 | rb_dlink_list * list, char statchar, bool (*check_fn)(struct Client *source_p, struct Client *target_p)) |
212380e3 | 1583 | { |
5b96d9a6 | 1584 | rb_dlink_node *ptr; |
212380e3 AC |
1585 | struct Client *target_p; |
1586 | ||
1587 | /* send information about connections which match. note, we | |
1588 | * dont need tests for IsInvisible(), because non-opers will | |
1589 | * never get here for normal clients --fl | |
1590 | */ | |
5b96d9a6 | 1591 | RB_DLINK_FOREACH(ptr, list->head) |
212380e3 AC |
1592 | { |
1593 | target_p = ptr->data; | |
1594 | ||
1595 | if(!doall && wilds && !match(name, target_p->name)) | |
1596 | continue; | |
1597 | ||
24652f2b | 1598 | if (check_fn == NULL || check_fn(source_p, target_p)) |
e82bda18 | 1599 | stats_l_client(source_p, target_p, statchar); |
212380e3 AC |
1600 | } |
1601 | } | |
1602 | ||
1603 | void | |
1604 | stats_l_client(struct Client *source_p, struct Client *target_p, | |
1605 | char statchar) | |
1606 | { | |
1607 | if(IsAnyServer(target_p)) | |
1608 | { | |
1609 | sendto_one_numeric(source_p, RPL_STATSLINKINFO, Lformat, | |
b3ebc7ab | 1610 | target_p->name, |
d269d0b6 | 1611 | (int) rb_linebuf_len(&target_p->localClient->buf_sendq), |
212380e3 AC |
1612 | (int) target_p->localClient->sendM, |
1613 | (int) target_p->localClient->sendK, | |
1614 | (int) target_p->localClient->receiveM, | |
1615 | (int) target_p->localClient->receiveK, | |
e3354945 | 1616 | rb_current_time() - target_p->localClient->firsttime, |
55abcbb2 | 1617 | (rb_current_time() > target_p->localClient->lasttime) ? |
e3354945 | 1618 | (rb_current_time() - target_p->localClient->lasttime) : 0, |
d4f7eb4c | 1619 | IsOperGeneral(source_p) ? show_capabilities(target_p) : "-"); |
212380e3 AC |
1620 | } |
1621 | ||
1622 | else | |
1623 | { | |
01fb744c DS |
1624 | /* fire the doing_stats_show_idle hook to allow modules to tell us whether to show the idle time */ |
1625 | hook_data_client_approval hdata_showidle; | |
1626 | ||
1627 | hdata_showidle.client = source_p; | |
1628 | hdata_showidle.target = target_p; | |
1629 | hdata_showidle.approved = WHOIS_IDLE_SHOW; | |
1630 | ||
1631 | call_hook(doing_stats_show_idle_hook, &hdata_showidle); | |
212380e3 AC |
1632 | sendto_one_numeric(source_p, RPL_STATSLINKINFO, Lformat, |
1633 | show_ip(source_p, target_p) ? | |
1634 | (IsUpper(statchar) ? | |
1635 | get_client_name(target_p, SHOW_IP) : | |
1636 | get_client_name(target_p, HIDE_IP)) : | |
1637 | get_client_name(target_p, MASK_IP), | |
01fb744c DS |
1638 | hdata_showidle.approved ? (int) rb_linebuf_len(&target_p->localClient->buf_sendq) : 0, |
1639 | hdata_showidle.approved ? (int) target_p->localClient->sendM : 0, | |
1640 | hdata_showidle.approved ? (int) target_p->localClient->sendK : 0, | |
1641 | hdata_showidle.approved ? (int) target_p->localClient->receiveM : 0, | |
1642 | hdata_showidle.approved ? (int) target_p->localClient->receiveK : 0, | |
e3354945 | 1643 | rb_current_time() - target_p->localClient->firsttime, |
01fb744c | 1644 | (rb_current_time() > target_p->localClient->lasttime) && hdata_showidle.approved ? |
e3354945 | 1645 | (rb_current_time() - target_p->localClient->lasttime) : 0, |
212380e3 AC |
1646 | "-"); |
1647 | } | |
1648 | } | |
1649 | ||
f237e31a JT |
1650 | static void |
1651 | rb_dump_fd_callback(int fd, const char *desc, void *data) | |
1652 | { | |
1653 | struct Client *source_p = data; | |
1654 | sendto_one_numeric(source_p, RPL_STATSDEBUG, "F :fd %-3d desc '%s'", fd, desc); | |
1655 | } | |
1656 | ||
1657 | static void | |
1658 | stats_comm(struct Client *source_p) | |
1659 | { | |
1660 | rb_dump_fd(rb_dump_fd_callback, source_p); | |
a235e410 VY |
1661 | } |
1662 | ||
212380e3 AC |
1663 | /* |
1664 | * stats_spy | |
1665 | * | |
1666 | * inputs - pointer to client doing the /stats | |
1667 | * - char letter they are doing /stats on | |
1668 | * output - none | |
1669 | * side effects - | |
1670 | * This little helper function reports to opers if configured. | |
212380e3 | 1671 | */ |
ea2d2700 | 1672 | static int |
212380e3 AC |
1673 | stats_spy(struct Client *source_p, char statchar, const char *name) |
1674 | { | |
1675 | hook_data_int data; | |
1676 | ||
1677 | data.client = source_p; | |
1678 | data.arg1 = name; | |
1679 | data.arg2 = (int) statchar; | |
ea2d2700 | 1680 | data.result = 0; |
212380e3 AC |
1681 | |
1682 | call_hook(doing_stats_hook, &data); | |
ea2d2700 AC |
1683 | |
1684 | return data.result; | |
212380e3 AC |
1685 | } |
1686 | ||
1687 | /* stats_p_spy() | |
1688 | * | |
1689 | * input - pointer to client doing stats | |
1690 | * ouput - | |
1691 | * side effects - call hook doing_stats_p | |
1692 | */ | |
1693 | static void | |
1694 | stats_p_spy (struct Client *source_p) | |
1695 | { | |
1696 | hook_data data; | |
1697 | ||
1698 | data.client = source_p; | |
1699 | data.arg1 = data.arg2 = NULL; | |
1700 | ||
1701 | call_hook(doing_stats_p_hook, &data); | |
1702 | } |