]>
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'; | |
356 | if(ServerConfCompressed(server_p)) | |
357 | *s++ = 'Z'; | |
358 | } | |
359 | ||
f76ca178 | 360 | if(s == buf) |
212380e3 AC |
361 | *s++ = '*'; |
362 | ||
363 | *s = '\0'; | |
364 | ||
55abcbb2 | 365 | sendto_one_numeric(source_p, RPL_STATSCLINE, |
212380e3 | 366 | form_str(RPL_STATSCLINE), |
55abcbb2 | 367 | "*@127.0.0.1", |
212380e3 | 368 | buf, server_p->name, |
30857341 SA |
369 | server_p->port, server_p->class_name, |
370 | server_p->certfp ? server_p->certfp : "*"); | |
212380e3 AC |
371 | } |
372 | } | |
373 | ||
374 | /* stats_tdeny() | |
375 | * | |
376 | * input - client to report to | |
377 | * output - none | |
378 | * side effects - client is given temp dline list. | |
379 | */ | |
380 | static void | |
381 | stats_tdeny (struct Client *source_p) | |
382 | { | |
383 | char *host, *pass, *user, *oper_reason; | |
384 | struct AddressRec *arec; | |
385 | struct ConfItem *aconf; | |
386 | int i; | |
387 | ||
388 | for (i = 0; i < ATABLE_SIZE; i++) | |
389 | { | |
390 | for (arec = atable[i]; arec; arec = arec->next) | |
391 | { | |
392 | if(arec->type == CONF_DLINE) | |
393 | { | |
394 | aconf = arec->aconf; | |
395 | ||
396 | if(!(aconf->flags & CONF_FLAGS_TEMPORARY)) | |
397 | continue; | |
398 | ||
399 | get_printable_kline(source_p, aconf, &host, &pass, &user, &oper_reason); | |
400 | ||
55abcbb2 | 401 | sendto_one_numeric(source_p, RPL_STATSDLINE, |
212380e3 AC |
402 | form_str (RPL_STATSDLINE), |
403 | 'd', host, pass, | |
404 | oper_reason ? "|" : "", | |
405 | oper_reason ? oper_reason : ""); | |
406 | } | |
407 | } | |
408 | } | |
409 | } | |
410 | ||
411 | /* stats_deny() | |
412 | * | |
413 | * input - client to report to | |
414 | * output - none | |
415 | * side effects - client is given dline list. | |
416 | */ | |
417 | static void | |
418 | stats_deny (struct Client *source_p) | |
419 | { | |
420 | char *host, *pass, *user, *oper_reason; | |
421 | struct AddressRec *arec; | |
422 | struct ConfItem *aconf; | |
423 | int i; | |
424 | ||
425 | for (i = 0; i < ATABLE_SIZE; i++) | |
426 | { | |
427 | for (arec = atable[i]; arec; arec = arec->next) | |
428 | { | |
429 | if(arec->type == CONF_DLINE) | |
430 | { | |
431 | aconf = arec->aconf; | |
432 | ||
433 | if(aconf->flags & CONF_FLAGS_TEMPORARY) | |
434 | continue; | |
435 | ||
436 | get_printable_kline(source_p, aconf, &host, &pass, &user, &oper_reason); | |
437 | ||
55abcbb2 | 438 | sendto_one_numeric(source_p, RPL_STATSDLINE, |
212380e3 AC |
439 | form_str (RPL_STATSDLINE), |
440 | 'D', host, pass, | |
441 | oper_reason ? "|" : "", | |
442 | oper_reason ? oper_reason : ""); | |
443 | } | |
444 | } | |
445 | } | |
446 | } | |
447 | ||
448 | ||
449 | /* stats_exempt() | |
450 | * | |
451 | * input - client to report to | |
452 | * output - none | |
453 | * side effects - client is given list of exempt blocks | |
454 | */ | |
455 | static void | |
456 | stats_exempt(struct Client *source_p) | |
457 | { | |
48a06ae3 | 458 | char *name, *host, *user, *classname, *desc; |
29c92cf9 | 459 | const char *pass; |
212380e3 AC |
460 | struct AddressRec *arec; |
461 | struct ConfItem *aconf; | |
462 | int i, port; | |
463 | ||
464 | if(ConfigFileEntry.stats_e_disabled) | |
465 | { | |
22f2f68a JT |
466 | sendto_one_numeric(source_p, ERR_DISABLED, |
467 | form_str(ERR_DISABLED), "STATS e"); | |
212380e3 AC |
468 | return; |
469 | } | |
470 | ||
471 | for (i = 0; i < ATABLE_SIZE; i++) | |
472 | { | |
473 | for (arec = atable[i]; arec; arec = arec->next) | |
474 | { | |
475 | if(arec->type == CONF_EXEMPTDLINE) | |
476 | { | |
477 | aconf = arec->aconf; | |
478 | get_printable_conf (aconf, &name, &host, &pass, | |
48a06ae3 | 479 | &user, &port, &classname, &desc); |
212380e3 | 480 | |
55abcbb2 | 481 | sendto_one_numeric(source_p, RPL_STATSDLINE, |
212380e3 AC |
482 | form_str(RPL_STATSDLINE), |
483 | 'e', host, pass, "", ""); | |
484 | } | |
485 | } | |
8f0dd52c EK |
486 | } |
487 | } | |
212380e3 AC |
488 | |
489 | ||
f237e31a JT |
490 | static void |
491 | stats_events_cb(char *str, void *ptr) | |
492 | { | |
493 | sendto_one_numeric(ptr, RPL_STATSDEBUG, "E :%s", str); | |
494 | } | |
495 | ||
496 | static void | |
497 | stats_events (struct Client *source_p) | |
498 | { | |
499 | rb_dump_events(stats_events_cb, source_p); | |
212380e3 AC |
500 | } |
501 | ||
416d868e JT |
502 | static void |
503 | stats_prop_klines(struct Client *source_p) | |
504 | { | |
505 | struct ConfItem *aconf; | |
416d868e | 506 | char *user, *host, *pass, *oper_reason; |
ce376a21 | 507 | rb_dictionary_iter state; |
416d868e | 508 | |
ce376a21 | 509 | RB_DICTIONARY_FOREACH(aconf, &state, prop_bans_dict) |
416d868e | 510 | { |
416d868e | 511 | /* Skip non-klines and deactivated klines. */ |
ce376a21 | 512 | if (aconf->status != CONF_KILL) |
416d868e JT |
513 | continue; |
514 | ||
55abcbb2 | 515 | get_printable_kline(source_p, aconf, &host, &pass, |
416d868e JT |
516 | &user, &oper_reason); |
517 | ||
518 | sendto_one_numeric(source_p, RPL_STATSKLINE, | |
519 | form_str(RPL_STATSKLINE), | |
520 | 'g', host, user, pass, | |
521 | oper_reason ? "|" : "", | |
522 | oper_reason ? oper_reason : ""); | |
523 | } | |
524 | } | |
525 | ||
212380e3 AC |
526 | static void |
527 | stats_auth (struct Client *source_p) | |
528 | { | |
529 | /* Oper only, if unopered, return ERR_NOPRIVS */ | |
d4f7eb4c | 530 | if((ConfigFileEntry.stats_i_oper_only == 2) && !IsOperGeneral (source_p)) |
212380e3 AC |
531 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, |
532 | form_str (ERR_NOPRIVILEGES)); | |
533 | ||
534 | /* If unopered, Only return matching auth blocks */ | |
d4f7eb4c | 535 | else if((ConfigFileEntry.stats_i_oper_only == 1) && !IsOperGeneral (source_p)) |
212380e3 AC |
536 | { |
537 | struct ConfItem *aconf; | |
48a06ae3 | 538 | char *name, *host, *user, *classname, *desc; |
29c92cf9 | 539 | const char *pass = "*"; |
212380e3 AC |
540 | int port; |
541 | ||
542 | if(MyConnect (source_p)) | |
543 | aconf = find_conf_by_address (source_p->host, source_p->sockhost, NULL, | |
544 | (struct sockaddr *)&source_p->localClient->ip, | |
545 | CONF_CLIENT, | |
2d77d121 | 546 | GET_SS_FAMILY(&source_p->localClient->ip), |
40c1fd47 | 547 | source_p->username, NULL); |
212380e3 AC |
548 | else |
549 | aconf = find_conf_by_address (source_p->host, NULL, NULL, NULL, CONF_CLIENT, | |
40c1fd47 | 550 | 0, source_p->username, NULL); |
212380e3 AC |
551 | |
552 | if(aconf == NULL) | |
553 | return; | |
554 | ||
48a06ae3 | 555 | get_printable_conf (aconf, &name, &host, &pass, &user, &port, &classname, &desc); |
40c1fd47 VY |
556 | if(!EmptyString(aconf->spasswd)) |
557 | pass = aconf->spasswd; | |
212380e3 AC |
558 | |
559 | sendto_one_numeric(source_p, RPL_STATSILINE, form_str(RPL_STATSILINE), | |
40c1fd47 | 560 | name, pass, show_iline_prefix(source_p, aconf, user), |
48a06ae3 | 561 | host, port, classname, desc); |
212380e3 AC |
562 | } |
563 | ||
564 | /* Theyre opered, or allowed to see all auth blocks */ | |
565 | else | |
566 | report_auth (source_p); | |
567 | } | |
568 | ||
569 | ||
570 | static void | |
571 | stats_tklines(struct Client *source_p) | |
572 | { | |
573 | /* Oper only, if unopered, return ERR_NOPRIVS */ | |
d4f7eb4c | 574 | if((ConfigFileEntry.stats_k_oper_only == 2) && !IsOperGeneral (source_p)) |
212380e3 AC |
575 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, |
576 | form_str (ERR_NOPRIVILEGES)); | |
577 | ||
578 | /* If unopered, Only return matching klines */ | |
d4f7eb4c | 579 | else if((ConfigFileEntry.stats_k_oper_only == 1) && !IsOperGeneral (source_p)) |
212380e3 AC |
580 | { |
581 | struct ConfItem *aconf; | |
582 | char *host, *pass, *user, *oper_reason; | |
583 | ||
584 | if(MyConnect (source_p)) | |
585 | aconf = find_conf_by_address (source_p->host, source_p->sockhost, NULL, | |
586 | (struct sockaddr *)&source_p->localClient->ip, | |
587 | CONF_KILL, | |
2d77d121 | 588 | GET_SS_FAMILY(&source_p->localClient->ip), |
40c1fd47 | 589 | source_p->username, NULL); |
212380e3 AC |
590 | else |
591 | aconf = find_conf_by_address (source_p->host, NULL, NULL, NULL, CONF_KILL, | |
40c1fd47 | 592 | 0, source_p->username, NULL); |
212380e3 AC |
593 | |
594 | if(aconf == NULL) | |
595 | return; | |
596 | ||
597 | /* dont report a permanent kline as a tkline */ | |
598 | if((aconf->flags & CONF_FLAGS_TEMPORARY) == 0) | |
599 | return; | |
600 | ||
601 | get_printable_kline(source_p, aconf, &host, &pass, &user, &oper_reason); | |
602 | ||
55abcbb2 | 603 | sendto_one_numeric(source_p, RPL_STATSKLINE, |
d8f0b5d7 | 604 | form_str(RPL_STATSKLINE), (aconf->flags & CONF_FLAGS_TEMPORARY) ? 'k' : 'K', |
0a621c4b | 605 | host, user, pass, oper_reason ? "|" : "", |
212380e3 AC |
606 | oper_reason ? oper_reason : ""); |
607 | } | |
608 | /* Theyre opered, or allowed to see all klines */ | |
609 | else | |
610 | { | |
611 | struct ConfItem *aconf; | |
5b96d9a6 | 612 | rb_dlink_node *ptr; |
212380e3 AC |
613 | int i; |
614 | char *user, *host, *pass, *oper_reason; | |
615 | ||
616 | for(i = 0; i < LAST_TEMP_TYPE; i++) | |
617 | { | |
5b96d9a6 | 618 | RB_DLINK_FOREACH(ptr, temp_klines[i].head) |
212380e3 AC |
619 | { |
620 | aconf = ptr->data; | |
621 | ||
55abcbb2 | 622 | get_printable_kline(source_p, aconf, &host, &pass, |
212380e3 AC |
623 | &user, &oper_reason); |
624 | ||
625 | sendto_one_numeric(source_p, RPL_STATSKLINE, | |
626 | form_str(RPL_STATSKLINE), | |
627 | 'k', host, user, pass, | |
628 | oper_reason ? "|" : "", | |
629 | oper_reason ? oper_reason : ""); | |
630 | } | |
631 | } | |
632 | } | |
633 | } | |
634 | ||
34c10ca8 EM |
635 | /* report_Klines() |
636 | * | |
637 | * inputs - Client to report to, mask | |
638 | * outputs - | |
639 | * side effects - Reports configured K-lines to client_p. | |
640 | */ | |
641 | static void | |
642 | report_Klines(struct Client *source_p) | |
643 | { | |
644 | char *host, *pass, *user, *oper_reason; | |
645 | struct AddressRec *arec; | |
646 | struct ConfItem *aconf = NULL; | |
647 | int i; | |
648 | ||
649 | for (i = 0; i < ATABLE_SIZE; i++) | |
650 | { | |
651 | for (arec = atable[i]; arec; arec = arec->next) | |
652 | { | |
653 | if(arec->type == CONF_KILL) | |
654 | { | |
655 | aconf = arec->aconf; | |
656 | ||
657 | /* its a tempkline, theyre reported elsewhere */ | |
658 | if(aconf->flags & CONF_FLAGS_TEMPORARY) | |
659 | continue; | |
660 | ||
661 | get_printable_kline(source_p, aconf, &host, &pass, &user, &oper_reason); | |
662 | sendto_one_numeric(source_p, RPL_STATSKLINE, | |
663 | form_str(RPL_STATSKLINE), | |
664 | 'K', host, user, pass, | |
665 | oper_reason ? "|" : "", | |
666 | oper_reason ? oper_reason : ""); | |
667 | } | |
668 | } | |
669 | } | |
670 | } | |
671 | ||
212380e3 AC |
672 | static void |
673 | stats_klines(struct Client *source_p) | |
674 | { | |
675 | /* Oper only, if unopered, return ERR_NOPRIVS */ | |
d4f7eb4c | 676 | if((ConfigFileEntry.stats_k_oper_only == 2) && !IsOperGeneral (source_p)) |
212380e3 AC |
677 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, |
678 | form_str (ERR_NOPRIVILEGES)); | |
679 | ||
680 | /* If unopered, Only return matching klines */ | |
d4f7eb4c | 681 | else if((ConfigFileEntry.stats_k_oper_only == 1) && !IsOperGeneral (source_p)) |
212380e3 AC |
682 | { |
683 | struct ConfItem *aconf; | |
684 | char *host, *pass, *user, *oper_reason; | |
685 | ||
686 | /* search for a kline */ | |
687 | if(MyConnect (source_p)) | |
688 | aconf = find_conf_by_address (source_p->host, source_p->sockhost, NULL, | |
689 | (struct sockaddr *)&source_p->localClient->ip, | |
690 | CONF_KILL, | |
2d77d121 | 691 | GET_SS_FAMILY(&source_p->localClient->ip), |
40c1fd47 | 692 | source_p->username, NULL); |
212380e3 AC |
693 | else |
694 | aconf = find_conf_by_address (source_p->host, NULL, NULL, NULL, CONF_KILL, | |
40c1fd47 | 695 | 0, source_p->username, NULL); |
212380e3 AC |
696 | |
697 | if(aconf == NULL) | |
698 | return; | |
699 | ||
212380e3 AC |
700 | get_printable_kline(source_p, aconf, &host, &pass, &user, &oper_reason); |
701 | ||
702 | sendto_one_numeric(source_p, RPL_STATSKLINE, form_str(RPL_STATSKLINE), | |
d8f0b5d7 | 703 | (aconf->flags & CONF_FLAGS_TEMPORARY) ? 'k' : 'K', |
0a621c4b | 704 | host, user, pass, oper_reason ? "|" : "", |
212380e3 AC |
705 | oper_reason ? oper_reason : ""); |
706 | } | |
707 | /* Theyre opered, or allowed to see all klines */ | |
708 | else | |
709 | report_Klines (source_p); | |
710 | } | |
711 | ||
712 | static void | |
713 | stats_messages(struct Client *source_p) | |
714 | { | |
4177311e | 715 | rb_dictionary_iter iter; |
e8f1c19e | 716 | struct Message *msg; |
e8f1c19e | 717 | |
56f84ded | 718 | RB_DICTIONARY_FOREACH(msg, &iter, cmd_dict) |
e8f1c19e EM |
719 | { |
720 | s_assert(msg->cmd != NULL); | |
721 | sendto_one_numeric(source_p, RPL_STATSCOMMANDS, | |
722 | form_str(RPL_STATSCOMMANDS), | |
723 | msg->cmd, msg->count, | |
724 | msg->bytes, msg->rcount); | |
725 | } | |
212380e3 AC |
726 | } |
727 | ||
728 | static void | |
729 | stats_dnsbl(struct Client *source_p) | |
730 | { | |
d3f6b808 | 731 | rb_dictionary_iter iter; |
3321eef4 | 732 | struct DNSBLEntryStats *stats; |
212380e3 | 733 | |
3321eef4 | 734 | if(dnsbl_stats == NULL) |
896370cc SA |
735 | return; |
736 | ||
3321eef4 | 737 | RB_DICTIONARY_FOREACH(stats, &iter, dnsbl_stats) |
212380e3 | 738 | { |
212380e3 | 739 | /* use RPL_STATSDEBUG for now -- jilles */ |
d3f6b808 EM |
740 | sendto_one_numeric(source_p, RPL_STATSDEBUG, "n :%d %s", |
741 | stats->hits, (const char *)iter.cur->key); | |
212380e3 AC |
742 | } |
743 | } | |
744 | ||
745 | static void | |
746 | stats_oper(struct Client *source_p) | |
747 | { | |
748 | struct oper_conf *oper_p; | |
5b96d9a6 | 749 | rb_dlink_node *ptr; |
212380e3 | 750 | |
d4f7eb4c | 751 | if(!IsOperGeneral(source_p) && ConfigFileEntry.stats_o_oper_only) |
212380e3 AC |
752 | { |
753 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
754 | form_str (ERR_NOPRIVILEGES)); | |
755 | return; | |
756 | } | |
757 | ||
5b96d9a6 | 758 | RB_DLINK_FOREACH(ptr, oper_conf_list.head) |
212380e3 AC |
759 | { |
760 | oper_p = ptr->data; | |
55abcbb2 KB |
761 | |
762 | sendto_one_numeric(source_p, RPL_STATSOLINE, | |
212380e3 AC |
763 | form_str(RPL_STATSOLINE), |
764 | oper_p->username, oper_p->host, oper_p->name, | |
6d5be11f | 765 | HasPrivilege(source_p, "oper:privs") ? oper_p->privset->name : "0", "-1"); |
212380e3 AC |
766 | } |
767 | } | |
768 | ||
ac37f16a AC |
769 | static void |
770 | stats_capability_walk(const char *line, void *data) | |
771 | { | |
772 | struct Client *client_p = data; | |
773 | ||
774 | sendto_one_numeric(client_p, RPL_STATSDEBUG, "C :%s", line); | |
775 | } | |
776 | ||
777 | static void | |
778 | stats_capability(struct Client *client_p) | |
779 | { | |
780 | capability_index_stats(stats_capability_walk, client_p); | |
781 | } | |
782 | ||
3a177354 JT |
783 | static void |
784 | stats_privset(struct Client *source_p) | |
785 | { | |
786 | privilegeset_report(source_p); | |
787 | } | |
212380e3 AC |
788 | |
789 | /* stats_operedup() | |
790 | * | |
791 | * input - client pointer | |
792 | * output - none | |
793 | * side effects - client is shown a list of active opers | |
794 | */ | |
795 | static void | |
796 | stats_operedup (struct Client *source_p) | |
797 | { | |
798 | struct Client *target_p; | |
5b96d9a6 | 799 | rb_dlink_node *oper_ptr; |
212380e3 AC |
800 | unsigned int count = 0; |
801 | ||
5b96d9a6 | 802 | RB_DLINK_FOREACH (oper_ptr, oper_list.head) |
212380e3 AC |
803 | { |
804 | target_p = oper_ptr->data; | |
805 | ||
1123eefc | 806 | if(!SeesOper(target_p, source_p)) |
212380e3 AC |
807 | continue; |
808 | ||
c127b45b | 809 | if(target_p->user->away) |
212380e3 AC |
810 | continue; |
811 | ||
812 | count++; | |
813 | ||
814 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
815 | "p :%s (%s@%s)", | |
55abcbb2 | 816 | target_p->name, target_p->username, |
212380e3 AC |
817 | target_p->host); |
818 | } | |
819 | ||
820 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
821 | "p :%u staff members", count); | |
822 | ||
823 | stats_p_spy (source_p); | |
824 | } | |
825 | ||
826 | static void | |
827 | stats_ports (struct Client *source_p) | |
828 | { | |
d4f7eb4c | 829 | if(!IsOperGeneral (source_p) && ConfigFileEntry.stats_P_oper_only) |
212380e3 AC |
830 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, |
831 | form_str (ERR_NOPRIVILEGES)); | |
832 | else | |
833 | show_ports (source_p); | |
834 | } | |
835 | ||
836 | static void | |
837 | stats_tresv(struct Client *source_p) | |
838 | { | |
839 | struct ConfItem *aconf; | |
2fc6772e | 840 | rb_radixtree_iteration_state state; |
5b96d9a6 | 841 | rb_dlink_node *ptr; |
212380e3 | 842 | |
5b96d9a6 | 843 | RB_DLINK_FOREACH(ptr, resv_conf_list.head) |
212380e3 AC |
844 | { |
845 | aconf = ptr->data; | |
846 | if(aconf->hold) | |
55abcbb2 | 847 | sendto_one_numeric(source_p, RPL_STATSQLINE, |
212380e3 | 848 | form_str(RPL_STATSQLINE), |
70ea02eb | 849 | 'q', aconf->port, aconf->host, aconf->passwd); |
212380e3 AC |
850 | } |
851 | ||
a4bf26dd | 852 | RB_RADIXTREE_FOREACH(aconf, &state, resv_tree) |
212380e3 | 853 | { |
212380e3 | 854 | if(aconf->hold) |
55abcbb2 | 855 | sendto_one_numeric(source_p, RPL_STATSQLINE, |
212380e3 | 856 | form_str(RPL_STATSQLINE), |
70ea02eb | 857 | 'q', aconf->port, aconf->host, aconf->passwd); |
212380e3 | 858 | } |
212380e3 AC |
859 | } |
860 | ||
861 | ||
862 | static void | |
863 | stats_resv(struct Client *source_p) | |
864 | { | |
865 | struct ConfItem *aconf; | |
2fc6772e | 866 | rb_radixtree_iteration_state state; |
5b96d9a6 | 867 | rb_dlink_node *ptr; |
212380e3 | 868 | |
5b96d9a6 | 869 | RB_DLINK_FOREACH(ptr, resv_conf_list.head) |
212380e3 AC |
870 | { |
871 | aconf = ptr->data; | |
872 | if(!aconf->hold) | |
55abcbb2 | 873 | sendto_one_numeric(source_p, RPL_STATSQLINE, |
212380e3 | 874 | form_str(RPL_STATSQLINE), |
23959371 | 875 | 'Q', aconf->port, aconf->host, aconf->passwd); |
212380e3 AC |
876 | } |
877 | ||
a4bf26dd | 878 | RB_RADIXTREE_FOREACH(aconf, &state, resv_tree) |
212380e3 | 879 | { |
212380e3 | 880 | if(!aconf->hold) |
55abcbb2 | 881 | sendto_one_numeric(source_p, RPL_STATSQLINE, |
212380e3 | 882 | form_str(RPL_STATSQLINE), |
23959371 | 883 | 'Q', aconf->port, aconf->host, aconf->passwd); |
212380e3 | 884 | } |
212380e3 AC |
885 | } |
886 | ||
8f0dd52c EK |
887 | static void |
888 | stats_secure(struct Client *source_p) | |
889 | { | |
890 | struct AddressRec *arec; | |
891 | struct ConfItem *aconf; | |
892 | size_t i; | |
893 | ||
894 | for (i = 0; i < ATABLE_SIZE; i++) | |
895 | { | |
896 | for (arec = atable[i]; arec; arec = arec->next) | |
897 | { | |
898 | if(arec->type == CONF_SECURE) | |
899 | { | |
900 | aconf = arec->aconf; | |
901 | sendto_one_numeric(source_p, RPL_STATSDEBUG, "s :%s", aconf->host); | |
902 | } | |
903 | } | |
904 | } | |
905 | } | |
906 | ||
eb1b303d | 907 | static void |
e9ffc3c1 | 908 | stats_ssld_foreach(void *data, pid_t pid, int cli_count, enum ssld_status status, const char *version) |
eb1b303d SA |
909 | { |
910 | struct Client *source_p = data; | |
911 | ||
912 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
62f6351d J |
913 | "S :%ld %c %u :%s", |
914 | (long)pid, | |
eb1b303d | 915 | status == SSLD_DEAD ? 'D' : (status == SSLD_SHUTDOWN ? 'S' : 'A'), |
e9ffc3c1 SA |
916 | cli_count, |
917 | version); | |
eb1b303d SA |
918 | } |
919 | ||
920 | static void | |
921 | stats_ssld(struct Client *source_p) | |
922 | { | |
923 | ssld_foreach_info(stats_ssld_foreach, source_p); | |
924 | } | |
925 | ||
212380e3 AC |
926 | static void |
927 | stats_usage (struct Client *source_p) | |
928 | { | |
929 | struct rusage rus; | |
930 | time_t secs; | |
931 | time_t rup; | |
932 | #ifdef hz | |
933 | # define hzz hz | |
934 | #else | |
935 | # ifdef HZ | |
936 | # define hzz HZ | |
937 | # else | |
938 | int hzz = 1; | |
939 | # endif | |
940 | #endif | |
941 | ||
942 | if(getrusage(RUSAGE_SELF, &rus) == -1) | |
943 | { | |
944 | sendto_one_notice(source_p, ":Getruseage error: %s.", | |
945 | strerror(errno)); | |
946 | return; | |
947 | } | |
948 | secs = rus.ru_utime.tv_sec + rus.ru_stime.tv_sec; | |
949 | if(0 == secs) | |
950 | secs = 1; | |
951 | ||
e3354945 | 952 | rup = (rb_current_time() - startup_time) * hzz; |
212380e3 AC |
953 | if(0 == rup) |
954 | rup = 1; | |
55abcbb2 | 955 | |
212380e3 | 956 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
e4ce3b54 | 957 | "R :CPU Secs %d:%02d User %d:%02d System %d:%02d", |
212380e3 AC |
958 | (int) (secs / 60), (int) (secs % 60), |
959 | (int) (rus.ru_utime.tv_sec / 60), | |
960 | (int) (rus.ru_utime.tv_sec % 60), | |
55abcbb2 | 961 | (int) (rus.ru_stime.tv_sec / 60), |
212380e3 AC |
962 | (int) (rus.ru_stime.tv_sec % 60)); |
963 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
964 | "R :RSS %ld ShMem %ld Data %ld Stack %ld", | |
5c01fc8b MM |
965 | rus.ru_maxrss, (long)(rus.ru_ixrss / rup), |
966 | (long)(rus.ru_idrss / rup), (long)(rus.ru_isrss / rup)); | |
212380e3 AC |
967 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
968 | "R :Swaps %d Reclaims %d Faults %d", | |
969 | (int) rus.ru_nswap, (int) rus.ru_minflt, (int) rus.ru_majflt); | |
970 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
971 | "R :Block in %d out %d", | |
972 | (int) rus.ru_inblock, (int) rus.ru_oublock); | |
973 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
974 | "R :Msg Rcv %d Send %d", | |
975 | (int) rus.ru_msgrcv, (int) rus.ru_msgsnd); | |
976 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
977 | "R :Signals %d Context Vol. %d Invol %d", | |
55abcbb2 | 978 | (int) rus.ru_nsignals, (int) rus.ru_nvcsw, |
212380e3 AC |
979 | (int) rus.ru_nivcsw); |
980 | } | |
981 | ||
54ac8b60 VY |
982 | static void |
983 | stats_tstats (struct Client *source_p) | |
984 | { | |
47adde3d VY |
985 | struct Client *target_p; |
986 | struct ServerStatistics sp; | |
987 | rb_dlink_node *ptr; | |
988 | ||
989 | memcpy(&sp, &ServerStats, sizeof(struct ServerStatistics)); | |
990 | ||
8bd5767b JT |
991 | RB_DLINK_FOREACH(ptr, serv_list.head) |
992 | { | |
993 | target_p = ptr->data; | |
994 | ||
995 | sp.is_sbs += target_p->localClient->sendB; | |
996 | sp.is_sbr += target_p->localClient->receiveB; | |
679ccbe5 | 997 | sp.is_sti += (unsigned long long)(rb_current_time() - target_p->localClient->firsttime); |
8bd5767b JT |
998 | sp.is_sv++; |
999 | } | |
1000 | ||
1001 | RB_DLINK_FOREACH(ptr, lclient_list.head) | |
1002 | { | |
1003 | target_p = ptr->data; | |
1004 | ||
1005 | sp.is_cbs += target_p->localClient->sendB; | |
1006 | sp.is_cbr += target_p->localClient->receiveB; | |
679ccbe5 | 1007 | sp.is_cti += (unsigned long long)(rb_current_time() - target_p->localClient->firsttime); |
8bd5767b | 1008 | sp.is_cl++; |
47adde3d VY |
1009 | } |
1010 | ||
1011 | RB_DLINK_FOREACH(ptr, unknown_list.head) | |
1012 | { | |
1013 | sp.is_ni++; | |
1014 | } | |
1015 | ||
1016 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1017 | "T :accepts %u refused %u", sp.is_ac, sp.is_ref); | |
1018 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
55abcbb2 | 1019 | "T :rejected %u delaying %lu", |
43946961 | 1020 | sp.is_rej, delay_exit_length()); |
ae09cb7d JT |
1021 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
1022 | "T :throttled refused %u throttle list size %lu", sp.is_thr, throttle_size()); | |
47adde3d VY |
1023 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
1024 | "T :nicks being delayed %lu", | |
1025 | get_nd_count()); | |
1026 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1027 | "T :unknown commands %u prefixes %u", | |
1028 | sp.is_unco, sp.is_unpf); | |
1029 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1030 | "T :nick collisions %u saves %u unknown closes %u", | |
1031 | sp.is_kill, sp.is_save, sp.is_ni); | |
1032 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
55abcbb2 | 1033 | "T :wrong direction %u empty %u", |
47adde3d VY |
1034 | sp.is_wrdi, sp.is_empt); |
1035 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1036 | "T :numerics seen %u", sp.is_num); | |
1037 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1038 | "T :tgchange blocked msgs %u restricted addrs %lu", | |
1039 | sp.is_tgch, rb_dlink_list_length(&tgchange_list)); | |
e88a1f1b KB |
1040 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
1041 | "T :ratelimit blocked commands %u", sp.is_rl); | |
47adde3d VY |
1042 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
1043 | "T :auth successes %u fails %u", | |
1044 | sp.is_asuc, sp.is_abad); | |
1045 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1046 | "T :sasl successes %u fails %u", | |
1047 | sp.is_ssuc, sp.is_sbad); | |
1048 | sendto_one_numeric(source_p, RPL_STATSDEBUG, "T :Client Server"); | |
1049 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1050 | "T :connected %u %u", sp.is_cl, sp.is_sv); | |
8bd5767b JT |
1051 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
1052 | "T :bytes sent %lluK %lluK", | |
55abcbb2 | 1053 | sp.is_cbs / 1024, |
8bd5767b JT |
1054 | sp.is_sbs / 1024); |
1055 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1056 | "T :bytes recv %lluK %lluK", | |
55abcbb2 | 1057 | sp.is_cbr / 1024, |
7685dd09 | 1058 | sp.is_sbr / 1024); |
8bd5767b | 1059 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
679ccbe5 AS |
1060 | "T :time connected %llu %llu", |
1061 | sp.is_cti, sp.is_sti); | |
212380e3 AC |
1062 | } |
1063 | ||
1064 | static void | |
1065 | stats_uptime (struct Client *source_p) | |
1066 | { | |
1067 | time_t now; | |
1068 | ||
e3354945 | 1069 | now = rb_current_time() - startup_time; |
55abcbb2 | 1070 | sendto_one_numeric(source_p, RPL_STATSUPTIME, |
212380e3 | 1071 | form_str (RPL_STATSUPTIME), |
77910830 JT |
1072 | (int)(now / 86400), (int)((now / 3600) % 24), |
1073 | (int)((now / 60) % 60), (int)(now % 60)); | |
212380e3 AC |
1074 | sendto_one_numeric(source_p, RPL_STATSCONN, |
1075 | form_str (RPL_STATSCONN), | |
55abcbb2 | 1076 | MaxConnectionCount, MaxClientCount, |
212380e3 AC |
1077 | Count.totalrestartcount); |
1078 | } | |
1079 | ||
212380e3 AC |
1080 | |
1081 | /* stats_servers() | |
1082 | * | |
1083 | * input - client pointer | |
1084 | * output - none | |
1085 | * side effects - client is shown lists of who connected servers | |
1086 | */ | |
1087 | static void | |
1088 | stats_servers (struct Client *source_p) | |
1089 | { | |
1090 | struct Client *target_p; | |
5b96d9a6 | 1091 | rb_dlink_node *ptr; |
212380e3 AC |
1092 | time_t seconds; |
1093 | int days, hours, minutes; | |
1094 | int j = 0; | |
1095 | ||
d4f7eb4c | 1096 | if(ConfigServerHide.flatten_links && !IsOperGeneral(source_p) && |
212380e3 AC |
1097 | !IsExemptShide(source_p)) |
1098 | { | |
1099 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
1100 | form_str (ERR_NOPRIVILEGES)); | |
1101 | return; | |
1102 | } | |
1103 | ||
5b96d9a6 | 1104 | RB_DLINK_FOREACH (ptr, serv_list.head) |
212380e3 AC |
1105 | { |
1106 | target_p = ptr->data; | |
1107 | ||
1108 | j++; | |
e3354945 | 1109 | seconds = rb_current_time() - target_p->localClient->firsttime; |
212380e3 AC |
1110 | |
1111 | days = (int) (seconds / 86400); | |
1112 | seconds %= 86400; | |
1113 | hours = (int) (seconds / 3600); | |
1114 | seconds %= 3600; | |
1115 | minutes = (int) (seconds / 60); | |
1116 | seconds %= 60; | |
1117 | ||
1118 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1119 | "V :%s (%s!*@*) Idle: %d SendQ: %d " | |
1120 | "Connected: %d day%s, %d:%02d:%02d", | |
1121 | target_p->name, | |
1122 | (target_p->serv->by[0] ? target_p->serv->by : "Remote."), | |
e3354945 | 1123 | (int) (rb_current_time() - target_p->localClient->lasttime), |
e8e79621 | 1124 | (int) rb_linebuf_len (&target_p->localClient->buf_sendq), |
55abcbb2 | 1125 | days, (days == 1) ? "" : "s", hours, minutes, |
212380e3 AC |
1126 | (int) seconds); |
1127 | } | |
1128 | ||
1129 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1130 | "V :%d Server(s)", j); | |
1131 | } | |
1132 | ||
1133 | static void | |
1134 | stats_tgecos(struct Client *source_p) | |
1135 | { | |
1136 | struct ConfItem *aconf; | |
5b96d9a6 | 1137 | rb_dlink_node *ptr; |
212380e3 | 1138 | |
5b96d9a6 | 1139 | RB_DLINK_FOREACH(ptr, xline_conf_list.head) |
212380e3 AC |
1140 | { |
1141 | aconf = ptr->data; | |
1142 | ||
1143 | if(aconf->hold) | |
1144 | sendto_one_numeric(source_p, RPL_STATSXLINE, | |
1145 | form_str(RPL_STATSXLINE), | |
23959371 | 1146 | 'x', aconf->port, aconf->host, |
212380e3 AC |
1147 | aconf->passwd); |
1148 | } | |
1149 | } | |
1150 | ||
1151 | static void | |
1152 | stats_gecos(struct Client *source_p) | |
1153 | { | |
1154 | struct ConfItem *aconf; | |
5b96d9a6 | 1155 | rb_dlink_node *ptr; |
212380e3 | 1156 | |
5b96d9a6 | 1157 | RB_DLINK_FOREACH(ptr, xline_conf_list.head) |
212380e3 AC |
1158 | { |
1159 | aconf = ptr->data; | |
1160 | ||
1161 | if(!aconf->hold) | |
1162 | sendto_one_numeric(source_p, RPL_STATSXLINE, | |
1163 | form_str(RPL_STATSXLINE), | |
55abcbb2 | 1164 | 'X', aconf->port, aconf->host, |
212380e3 AC |
1165 | aconf->passwd); |
1166 | } | |
1167 | } | |
1168 | ||
1169 | static void | |
1170 | stats_class(struct Client *source_p) | |
1171 | { | |
d4f7eb4c | 1172 | if(ConfigFileEntry.stats_y_oper_only && !IsOperGeneral(source_p)) |
212380e3 AC |
1173 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, |
1174 | form_str (ERR_NOPRIVILEGES)); | |
1175 | else | |
1176 | report_classes(source_p); | |
1177 | } | |
1178 | ||
1179 | static void | |
1180 | stats_memory (struct Client *source_p) | |
1181 | { | |
47adde3d VY |
1182 | struct Client *target_p; |
1183 | struct Channel *chptr; | |
47adde3d VY |
1184 | rb_dlink_node *rb_dlink; |
1185 | rb_dlink_node *ptr; | |
1186 | int channel_count = 0; | |
1187 | int local_client_conf_count = 0; /* local client conf links */ | |
1188 | int users_counted = 0; /* user structs */ | |
1189 | ||
1190 | int channel_users = 0; | |
1191 | int channel_invites = 0; | |
1192 | int channel_bans = 0; | |
1193 | int channel_except = 0; | |
1194 | int channel_invex = 0; | |
1195 | int channel_quiets = 0; | |
1196 | ||
1197 | int class_count = 0; /* classes */ | |
1198 | int conf_count = 0; /* conf lines */ | |
1199 | int users_invited_count = 0; /* users invited */ | |
1200 | int user_channels = 0; /* users in channels */ | |
c127b45b | 1201 | int aways_counted = 0; |
47adde3d VY |
1202 | size_t number_servers_cached; /* number of servers cached by scache */ |
1203 | ||
1204 | size_t channel_memory = 0; | |
1205 | size_t channel_ban_memory = 0; | |
1206 | size_t channel_except_memory = 0; | |
1207 | size_t channel_invex_memory = 0; | |
1208 | size_t channel_quiet_memory = 0; | |
1209 | ||
c127b45b | 1210 | size_t away_memory = 0; /* memory used by aways */ |
47adde3d VY |
1211 | size_t ww = 0; /* whowas array count */ |
1212 | size_t wwm = 0; /* whowas array memory used */ | |
1213 | size_t conf_memory = 0; /* memory used by conf lines */ | |
1214 | size_t mem_servers_cached; /* memory used by scache */ | |
1215 | ||
1216 | size_t linebuf_count = 0; | |
1217 | size_t linebuf_memory_used = 0; | |
1218 | ||
1219 | size_t total_channel_memory = 0; | |
1220 | size_t totww = 0; | |
1221 | ||
1222 | size_t local_client_count = 0; | |
1223 | size_t local_client_memory_used = 0; | |
1224 | ||
1225 | size_t remote_client_count = 0; | |
1226 | size_t remote_client_memory_used = 0; | |
1227 | ||
1228 | size_t total_memory = 0; | |
1229 | ||
b47f8a4f | 1230 | whowas_memory_usage(&ww, &wwm); |
47adde3d VY |
1231 | |
1232 | RB_DLINK_FOREACH(ptr, global_client_list.head) | |
1233 | { | |
1234 | target_p = ptr->data; | |
1235 | if(MyConnect(target_p)) | |
1236 | { | |
1237 | local_client_conf_count++; | |
1238 | } | |
1239 | ||
1240 | if(target_p->user) | |
1241 | { | |
1242 | users_counted++; | |
1243 | users_invited_count += rb_dlink_list_length(&target_p->user->invited); | |
1244 | user_channels += rb_dlink_list_length(&target_p->user->channel); | |
c127b45b SB |
1245 | if(target_p->user->away) |
1246 | { | |
1247 | aways_counted++; | |
1248 | away_memory += (strlen(target_p->user->away) + 1); | |
1249 | } | |
47adde3d VY |
1250 | } |
1251 | } | |
1252 | ||
1253 | /* Count up all channels, ban lists, except lists, Invex lists */ | |
1254 | RB_DLINK_FOREACH(ptr, global_channel_list.head) | |
1255 | { | |
1256 | chptr = ptr->data; | |
1257 | channel_count++; | |
1258 | channel_memory += (strlen(chptr->chname) + sizeof(struct Channel)); | |
1259 | ||
1260 | channel_users += rb_dlink_list_length(&chptr->members); | |
1261 | channel_invites += rb_dlink_list_length(&chptr->invites); | |
1262 | ||
1263 | RB_DLINK_FOREACH(rb_dlink, chptr->banlist.head) | |
1264 | { | |
47adde3d VY |
1265 | channel_bans++; |
1266 | ||
1267 | channel_ban_memory += sizeof(rb_dlink_node) + sizeof(struct Ban); | |
1268 | } | |
1269 | ||
1270 | RB_DLINK_FOREACH(rb_dlink, chptr->exceptlist.head) | |
1271 | { | |
47adde3d VY |
1272 | channel_except++; |
1273 | ||
1274 | channel_except_memory += (sizeof(rb_dlink_node) + sizeof(struct Ban)); | |
1275 | } | |
1276 | ||
1277 | RB_DLINK_FOREACH(rb_dlink, chptr->invexlist.head) | |
1278 | { | |
47adde3d VY |
1279 | channel_invex++; |
1280 | ||
1281 | channel_invex_memory += (sizeof(rb_dlink_node) + sizeof(struct Ban)); | |
1282 | } | |
1283 | ||
1284 | RB_DLINK_FOREACH(rb_dlink, chptr->quietlist.head) | |
1285 | { | |
47adde3d VY |
1286 | channel_quiets++; |
1287 | ||
1288 | channel_quiet_memory += (sizeof(rb_dlink_node) + sizeof(struct Ban)); | |
1289 | } | |
1290 | } | |
1291 | ||
1292 | /* count up all classes */ | |
1293 | ||
1294 | class_count = rb_dlink_list_length(&class_list) + 1; | |
1295 | ||
1296 | rb_count_rb_linebuf_memory(&linebuf_count, &linebuf_memory_used); | |
1297 | ||
1298 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1299 | "z :Users %u(%lu) Invites %u(%lu)", | |
1300 | users_counted, | |
1301 | (unsigned long) users_counted * sizeof(struct User), | |
55abcbb2 | 1302 | users_invited_count, |
47adde3d VY |
1303 | (unsigned long) users_invited_count * sizeof(rb_dlink_node)); |
1304 | ||
1305 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c | 1306 | "z :User channels %u(%lu) Aways %u(%zu)", |
47adde3d | 1307 | user_channels, |
c127b45b | 1308 | (unsigned long) user_channels * sizeof(rb_dlink_node), |
689afc7c | 1309 | aways_counted, away_memory); |
47adde3d VY |
1310 | |
1311 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1312 | "z :Attached confs %u(%lu)", | |
1313 | local_client_conf_count, | |
1314 | (unsigned long) local_client_conf_count * sizeof(rb_dlink_node)); | |
1315 | ||
1316 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c | 1317 | "z :Conflines %u(%zu)", conf_count, conf_memory); |
47adde3d VY |
1318 | |
1319 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1320 | "z :Classes %u(%lu)", | |
55abcbb2 | 1321 | class_count, |
47adde3d VY |
1322 | (unsigned long) class_count * sizeof(struct Class)); |
1323 | ||
1324 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1325 | "z :Channels %u(%zu)", |
1326 | channel_count, channel_memory); | |
47adde3d VY |
1327 | |
1328 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1329 | "z :Bans %u(%zu) Exceptions %u(%zu) Invex %u(%zu) Quiets %u(%zu)", |
1330 | channel_bans, channel_ban_memory, | |
1331 | channel_except, channel_except_memory, | |
1332 | channel_invex, channel_invex_memory, | |
1333 | channel_quiets, channel_quiet_memory); | |
47adde3d VY |
1334 | |
1335 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1336 | "z :Channel members %u(%lu) invite %u(%lu)", | |
1337 | channel_users, | |
1338 | (unsigned long) channel_users * sizeof(rb_dlink_node), | |
55abcbb2 | 1339 | channel_invites, |
47adde3d VY |
1340 | (unsigned long) channel_invites * sizeof(rb_dlink_node)); |
1341 | ||
1342 | total_channel_memory = channel_memory + | |
1343 | channel_ban_memory + | |
1344 | channel_users * sizeof(rb_dlink_node) + channel_invites * sizeof(rb_dlink_node); | |
1345 | ||
1346 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1347 | "z :Whowas array %zu(%zu)", |
1348 | ww, wwm); | |
47adde3d VY |
1349 | |
1350 | totww = wwm; | |
1351 | ||
1352 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1353 | "z :Hash: client %u(%lu) chan %u(%lu)", |
1354 | U_MAX, (unsigned long)(U_MAX * sizeof(rb_dlink_list)), | |
1355 | CH_MAX, (unsigned long)(CH_MAX * sizeof(rb_dlink_list))); | |
47adde3d VY |
1356 | |
1357 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1358 | "z :linebuf %zu(%zu)", |
1359 | linebuf_count, linebuf_memory_used); | |
47adde3d VY |
1360 | |
1361 | count_scache(&number_servers_cached, &mem_servers_cached); | |
1362 | ||
1363 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1364 | "z :scache %zu(%zu)", |
1365 | number_servers_cached, mem_servers_cached); | |
47adde3d VY |
1366 | |
1367 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1368 | "z :hostname hash %d(%lu)", |
1369 | HOST_MAX, (unsigned long)HOST_MAX * sizeof(rb_dlink_list)); | |
47adde3d VY |
1370 | |
1371 | total_memory = totww + total_channel_memory + conf_memory + | |
1372 | class_count * sizeof(struct Class); | |
1373 | ||
1374 | total_memory += mem_servers_cached; | |
1375 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1376 | "z :Total: whowas %zu channel %zu conf %zu", |
1377 | totww, total_channel_memory, | |
1378 | conf_memory); | |
47adde3d VY |
1379 | |
1380 | count_local_client_memory(&local_client_count, &local_client_memory_used); | |
1381 | total_memory += local_client_memory_used; | |
1382 | ||
1383 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1384 | "z :Local client Memory in use: %zu(%zu)", |
1385 | local_client_count, local_client_memory_used); | |
47adde3d VY |
1386 | |
1387 | ||
1388 | count_remote_client_memory(&remote_client_count, &remote_client_memory_used); | |
1389 | total_memory += remote_client_memory_used; | |
1390 | ||
1391 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
689afc7c DF |
1392 | "z :Remote client Memory in use: %zu(%zu)", |
1393 | remote_client_count, | |
1394 | remote_client_memory_used); | |
7c5b4dbb DF |
1395 | |
1396 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1397 | "z :TOTAL: %zu", | |
1398 | total_memory); | |
212380e3 AC |
1399 | } |
1400 | ||
212380e3 AC |
1401 | static void |
1402 | stats_servlinks (struct Client *source_p) | |
1403 | { | |
1404 | static char Sformat[] = ":%s %d %s %s %u %u %u %u %u :%u %u %s"; | |
1405 | long uptime, sendK, receiveK; | |
1406 | struct Client *target_p; | |
5b96d9a6 | 1407 | rb_dlink_node *ptr; |
212380e3 | 1408 | int j = 0; |
70b72a07 | 1409 | char buf[128]; |
212380e3 | 1410 | |
d4f7eb4c | 1411 | if(ConfigServerHide.flatten_links && !IsOperGeneral (source_p) && |
212380e3 AC |
1412 | !IsExemptShide(source_p)) |
1413 | { | |
1414 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
1415 | form_str (ERR_NOPRIVILEGES)); | |
1416 | return; | |
1417 | } | |
1418 | ||
1419 | sendK = receiveK = 0; | |
1420 | ||
5b96d9a6 | 1421 | RB_DLINK_FOREACH (ptr, serv_list.head) |
212380e3 AC |
1422 | { |
1423 | target_p = ptr->data; | |
1424 | ||
1425 | j++; | |
1426 | sendK += target_p->localClient->sendK; | |
1427 | receiveK += target_p->localClient->receiveK; | |
1428 | ||
1429 | sendto_one(source_p, Sformat, | |
1430 | get_id(&me, source_p), RPL_STATSLINKINFO, get_id(source_p, source_p), | |
b3ebc7ab | 1431 | target_p->name, |
d269d0b6 | 1432 | (int) rb_linebuf_len (&target_p->localClient->buf_sendq), |
212380e3 AC |
1433 | (int) target_p->localClient->sendM, |
1434 | (int) target_p->localClient->sendK, | |
1435 | (int) target_p->localClient->receiveM, | |
1436 | (int) target_p->localClient->receiveK, | |
e3354945 | 1437 | rb_current_time() - target_p->localClient->firsttime, |
55abcbb2 | 1438 | (rb_current_time() > target_p->localClient->lasttime) ? |
e3354945 | 1439 | (rb_current_time() - target_p->localClient->lasttime) : 0, |
d4f7eb4c | 1440 | IsOperGeneral (source_p) ? show_capabilities (target_p) : "TS"); |
212380e3 AC |
1441 | } |
1442 | ||
1443 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1444 | "? :%u total server(s)", j); | |
1445 | ||
70b72a07 | 1446 | snprintf(buf, sizeof buf, "%7.2f", _GMKv ((sendK))); |
212380e3 | 1447 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
70b72a07 JT |
1448 | "? :Sent total : %s %s", |
1449 | buf, _GMKs (sendK)); | |
1450 | snprintf(buf, sizeof buf, "%7.2f", _GMKv ((receiveK))); | |
212380e3 | 1451 | sendto_one_numeric(source_p, RPL_STATSDEBUG, |
70b72a07 JT |
1452 | "? :Recv total : %s %s", |
1453 | buf, _GMKs (receiveK)); | |
212380e3 | 1454 | |
e3354945 | 1455 | uptime = (rb_current_time() - startup_time); |
70b72a07 | 1456 | snprintf(buf, sizeof buf, "%7.2f %s (%4.1f K/s)", |
55abcbb2 | 1457 | _GMKv (me.localClient->sendK), |
212380e3 AC |
1458 | _GMKs (me.localClient->sendK), |
1459 | (float) ((float) me.localClient->sendK / (float) uptime)); | |
70b72a07 JT |
1460 | sendto_one_numeric(source_p, RPL_STATSDEBUG, "? :Server send: %s", buf); |
1461 | snprintf(buf, sizeof buf, "%7.2f %s (%4.1f K/s)", | |
212380e3 AC |
1462 | _GMKv (me.localClient->receiveK), |
1463 | _GMKs (me.localClient->receiveK), | |
1464 | (float) ((float) me.localClient->receiveK / (float) uptime)); | |
70b72a07 | 1465 | sendto_one_numeric(source_p, RPL_STATSDEBUG, "? :Server recv: %s", buf); |
212380e3 AC |
1466 | } |
1467 | ||
3c7d6fcc | 1468 | static inline bool |
24652f2b | 1469 | stats_l_should_show_oper(struct Client *source_p, struct Client *target_p) |
4727c0f5 | 1470 | { |
24652f2b | 1471 | return SeesOper(target_p, source_p); |
4727c0f5 AC |
1472 | } |
1473 | ||
212380e3 AC |
1474 | static void |
1475 | stats_ltrace(struct Client *source_p, int parc, const char *parv[]) | |
1476 | { | |
3c7d6fcc EM |
1477 | bool doall = false; |
1478 | bool wilds = false; | |
212380e3 AC |
1479 | const char *name; |
1480 | char statchar = parv[1][0]; | |
1481 | ||
63ab1dd6 EK |
1482 | if (ConfigFileEntry.stats_l_oper_only == STATS_L_OPER_ONLY_YES && !IsOperGeneral(source_p)) |
1483 | { | |
1484 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, form_str(ERR_NOPRIVILEGES)); | |
1485 | return; | |
1486 | } | |
1487 | ||
212380e3 | 1488 | /* this is def targeted at us somehow.. */ |
63ab1dd6 | 1489 | if (parc > 2 && !EmptyString(parv[2])) |
212380e3 AC |
1490 | { |
1491 | /* directed at us generically? */ | |
63ab1dd6 | 1492 | if (match(parv[2], me.name) || (!MyClient(source_p) && !irccmp(parv[2], me.id))) |
212380e3 AC |
1493 | { |
1494 | name = me.name; | |
3c7d6fcc | 1495 | doall = true; |
212380e3 AC |
1496 | } |
1497 | else | |
1498 | { | |
1499 | name = parv[2]; | |
1500 | wilds = strchr(name, '*') || strchr(name, '?'); | |
1501 | } | |
1502 | ||
1503 | /* must be directed at a specific person thats not us */ | |
63ab1dd6 | 1504 | if (!doall && !wilds) |
212380e3 AC |
1505 | { |
1506 | struct Client *target_p; | |
1507 | ||
63ab1dd6 | 1508 | if (MyClient(source_p)) |
212380e3 AC |
1509 | target_p = find_named_person(name); |
1510 | else | |
1511 | target_p = find_person(name); | |
1512 | ||
63ab1dd6 EK |
1513 | if (target_p != source_p && ConfigFileEntry.stats_l_oper_only != STATS_L_OPER_ONLY_NO |
1514 | && !IsOperGeneral(source_p)) | |
1515 | { | |
1516 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, form_str(ERR_NOPRIVILEGES)); | |
1517 | } | |
1518 | else if (target_p != NULL) | |
212380e3 AC |
1519 | { |
1520 | stats_spy(source_p, statchar, target_p->name); | |
1521 | stats_l_client(source_p, target_p, statchar); | |
1522 | } | |
1523 | else | |
63ab1dd6 | 1524 | { |
212380e3 AC |
1525 | sendto_one_numeric(source_p, ERR_NOSUCHSERVER, |
1526 | form_str(ERR_NOSUCHSERVER), | |
1527 | name); | |
63ab1dd6 | 1528 | } |
212380e3 AC |
1529 | |
1530 | return; | |
1531 | } | |
1532 | } | |
1533 | else | |
1534 | { | |
1535 | name = me.name; | |
3c7d6fcc | 1536 | doall = true; |
212380e3 AC |
1537 | } |
1538 | ||
1539 | stats_spy(source_p, statchar, name); | |
1540 | ||
63ab1dd6 EK |
1541 | if (ConfigFileEntry.stats_l_oper_only != STATS_L_OPER_ONLY_NO && !IsOperGeneral(source_p)) |
1542 | { | |
1543 | if (doall && MyClient(source_p)) | |
1544 | stats_l_client(source_p, source_p, statchar); | |
1545 | else | |
1546 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, form_str(ERR_NOPRIVILEGES)); | |
1547 | return; | |
1548 | } | |
1549 | ||
1550 | if (doall) | |
212380e3 AC |
1551 | { |
1552 | /* local opers get everyone */ | |
1553 | if(MyOper(source_p)) | |
1554 | { | |
e82bda18 AC |
1555 | stats_l_list(source_p, name, doall, wilds, &unknown_list, statchar, NULL); |
1556 | stats_l_list(source_p, name, doall, wilds, &lclient_list, statchar, NULL); | |
212380e3 AC |
1557 | } |
1558 | else | |
1559 | { | |
1560 | /* they still need themselves if theyre local.. */ | |
1561 | if(MyClient(source_p)) | |
1562 | stats_l_client(source_p, source_p, statchar); | |
1563 | ||
4727c0f5 | 1564 | stats_l_list(source_p, name, doall, wilds, &local_oper_list, statchar, stats_l_should_show_oper); |
212380e3 AC |
1565 | } |
1566 | ||
d4f7eb4c | 1567 | if (!ConfigServerHide.flatten_links || IsOperGeneral(source_p) || |
212380e3 | 1568 | IsExemptShide(source_p)) |
e82bda18 | 1569 | stats_l_list(source_p, name, doall, wilds, &serv_list, statchar, NULL); |
212380e3 AC |
1570 | |
1571 | return; | |
1572 | } | |
1573 | ||
1574 | /* ok, at this point theyre looking for a specific client whos on | |
1575 | * our server.. but it contains a wildcard. --fl | |
1576 | */ | |
e82bda18 | 1577 | stats_l_list(source_p, name, doall, wilds, &lclient_list, statchar, NULL); |
212380e3 AC |
1578 | |
1579 | return; | |
1580 | } | |
1581 | ||
212380e3 | 1582 | static void |
3c7d6fcc | 1583 | stats_l_list(struct Client *source_p, const char *name, bool doall, bool wilds, |
24652f2b | 1584 | rb_dlink_list * list, char statchar, bool (*check_fn)(struct Client *source_p, struct Client *target_p)) |
212380e3 | 1585 | { |
5b96d9a6 | 1586 | rb_dlink_node *ptr; |
212380e3 AC |
1587 | struct Client *target_p; |
1588 | ||
1589 | /* send information about connections which match. note, we | |
1590 | * dont need tests for IsInvisible(), because non-opers will | |
1591 | * never get here for normal clients --fl | |
1592 | */ | |
5b96d9a6 | 1593 | RB_DLINK_FOREACH(ptr, list->head) |
212380e3 AC |
1594 | { |
1595 | target_p = ptr->data; | |
1596 | ||
1597 | if(!doall && wilds && !match(name, target_p->name)) | |
1598 | continue; | |
1599 | ||
24652f2b | 1600 | if (check_fn == NULL || check_fn(source_p, target_p)) |
e82bda18 | 1601 | stats_l_client(source_p, target_p, statchar); |
212380e3 AC |
1602 | } |
1603 | } | |
1604 | ||
1605 | void | |
1606 | stats_l_client(struct Client *source_p, struct Client *target_p, | |
1607 | char statchar) | |
1608 | { | |
1609 | if(IsAnyServer(target_p)) | |
1610 | { | |
1611 | sendto_one_numeric(source_p, RPL_STATSLINKINFO, Lformat, | |
b3ebc7ab | 1612 | target_p->name, |
d269d0b6 | 1613 | (int) rb_linebuf_len(&target_p->localClient->buf_sendq), |
212380e3 AC |
1614 | (int) target_p->localClient->sendM, |
1615 | (int) target_p->localClient->sendK, | |
1616 | (int) target_p->localClient->receiveM, | |
1617 | (int) target_p->localClient->receiveK, | |
e3354945 | 1618 | rb_current_time() - target_p->localClient->firsttime, |
55abcbb2 | 1619 | (rb_current_time() > target_p->localClient->lasttime) ? |
e3354945 | 1620 | (rb_current_time() - target_p->localClient->lasttime) : 0, |
d4f7eb4c | 1621 | IsOperGeneral(source_p) ? show_capabilities(target_p) : "-"); |
212380e3 AC |
1622 | } |
1623 | ||
1624 | else | |
1625 | { | |
01fb744c DS |
1626 | /* fire the doing_stats_show_idle hook to allow modules to tell us whether to show the idle time */ |
1627 | hook_data_client_approval hdata_showidle; | |
1628 | ||
1629 | hdata_showidle.client = source_p; | |
1630 | hdata_showidle.target = target_p; | |
1631 | hdata_showidle.approved = WHOIS_IDLE_SHOW; | |
1632 | ||
1633 | call_hook(doing_stats_show_idle_hook, &hdata_showidle); | |
212380e3 AC |
1634 | sendto_one_numeric(source_p, RPL_STATSLINKINFO, Lformat, |
1635 | show_ip(source_p, target_p) ? | |
1636 | (IsUpper(statchar) ? | |
1637 | get_client_name(target_p, SHOW_IP) : | |
1638 | get_client_name(target_p, HIDE_IP)) : | |
1639 | get_client_name(target_p, MASK_IP), | |
01fb744c DS |
1640 | hdata_showidle.approved ? (int) rb_linebuf_len(&target_p->localClient->buf_sendq) : 0, |
1641 | hdata_showidle.approved ? (int) target_p->localClient->sendM : 0, | |
1642 | hdata_showidle.approved ? (int) target_p->localClient->sendK : 0, | |
1643 | hdata_showidle.approved ? (int) target_p->localClient->receiveM : 0, | |
1644 | hdata_showidle.approved ? (int) target_p->localClient->receiveK : 0, | |
e3354945 | 1645 | rb_current_time() - target_p->localClient->firsttime, |
01fb744c | 1646 | (rb_current_time() > target_p->localClient->lasttime) && hdata_showidle.approved ? |
e3354945 | 1647 | (rb_current_time() - target_p->localClient->lasttime) : 0, |
212380e3 AC |
1648 | "-"); |
1649 | } | |
1650 | } | |
1651 | ||
f237e31a JT |
1652 | static void |
1653 | rb_dump_fd_callback(int fd, const char *desc, void *data) | |
1654 | { | |
1655 | struct Client *source_p = data; | |
1656 | sendto_one_numeric(source_p, RPL_STATSDEBUG, "F :fd %-3d desc '%s'", fd, desc); | |
1657 | } | |
1658 | ||
1659 | static void | |
1660 | stats_comm(struct Client *source_p) | |
1661 | { | |
1662 | rb_dump_fd(rb_dump_fd_callback, source_p); | |
a235e410 VY |
1663 | } |
1664 | ||
212380e3 AC |
1665 | /* |
1666 | * stats_spy | |
1667 | * | |
1668 | * inputs - pointer to client doing the /stats | |
1669 | * - char letter they are doing /stats on | |
1670 | * output - none | |
1671 | * side effects - | |
1672 | * This little helper function reports to opers if configured. | |
212380e3 | 1673 | */ |
ea2d2700 | 1674 | static int |
212380e3 AC |
1675 | stats_spy(struct Client *source_p, char statchar, const char *name) |
1676 | { | |
1677 | hook_data_int data; | |
1678 | ||
1679 | data.client = source_p; | |
1680 | data.arg1 = name; | |
1681 | data.arg2 = (int) statchar; | |
ea2d2700 | 1682 | data.result = 0; |
212380e3 AC |
1683 | |
1684 | call_hook(doing_stats_hook, &data); | |
ea2d2700 AC |
1685 | |
1686 | return data.result; | |
212380e3 AC |
1687 | } |
1688 | ||
1689 | /* stats_p_spy() | |
1690 | * | |
1691 | * input - pointer to client doing stats | |
1692 | * ouput - | |
1693 | * side effects - call hook doing_stats_p | |
1694 | */ | |
1695 | static void | |
1696 | stats_p_spy (struct Client *source_p) | |
1697 | { | |
1698 | hook_data data; | |
1699 | ||
1700 | data.client = source_p; | |
1701 | data.arg1 = data.arg2 = NULL; | |
1702 | ||
1703 | call_hook(doing_stats_p_hook, &data); | |
1704 | } |