]>
Commit | Line | Data |
---|---|---|
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 | |
23 | * | |
24 | * $Id: m_stats.c 1608 2006-06-04 02:11:40Z jilles $ | |
25 | */ | |
26 | ||
27 | #include "stdinc.h" | |
28 | #include "tools.h" /* dlink_node/dlink_list */ | |
29 | #include "class.h" /* report_classes */ | |
30 | #include "client.h" /* Client */ | |
31 | #include "common.h" /* TRUE/FALSE */ | |
32 | #include "irc_string.h" | |
33 | #include "ircd.h" /* me */ | |
34 | #include "listener.h" /* show_ports */ | |
35 | #include "s_gline.h" | |
36 | #include "msg.h" /* Message */ | |
37 | #include "hostmask.h" /* report_mtrie_conf_links */ | |
38 | #include "numeric.h" /* ERR_xxx */ | |
39 | #include "scache.h" /* list_scache */ | |
40 | #include "send.h" /* sendto_one */ | |
41 | #include "commio.h" /* highest_fd */ | |
42 | #include "s_conf.h" /* ConfItem */ | |
43 | #include "s_serv.h" /* hunt_server */ | |
44 | #include "s_stats.h" /* tstats */ | |
45 | #include "s_user.h" /* show_opers */ | |
46 | #include "event.h" /* events */ | |
47 | #include "blacklist.h" /* dnsbl stuff */ | |
48 | #include "linebuf.h" | |
49 | #include "parse.h" | |
50 | #include "modules.h" | |
51 | #include "hook.h" | |
52 | #include "s_newconf.h" | |
53 | #include "hash.h" | |
54 | ||
55 | static int m_stats (struct Client *, struct Client *, int, const char **); | |
56 | ||
57 | struct Message stats_msgtab = { | |
58 | "STATS", 0, 0, 0, MFLG_SLOW, | |
59 | {mg_unreg, {m_stats, 2}, {m_stats, 3}, mg_ignore, mg_ignore, {m_stats, 2}} | |
60 | }; | |
61 | ||
62 | int doing_stats_hook; | |
63 | int doing_stats_p_hook; | |
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 }, | |
69 | { NULL, NULL } | |
70 | }; | |
71 | ||
72 | DECLARE_MODULE_AV1(stats, NULL, NULL, stats_clist, stats_hlist, NULL, "$Revision: 1608 $"); | |
73 | ||
74 | const char *Lformat = "%s %u %u %u %u %u :%u %u %s"; | |
75 | ||
76 | static void stats_l_list(struct Client *s, const char *, int, int, dlink_list *, char); | |
77 | static void stats_l_client(struct Client *source_p, struct Client *target_p, | |
78 | char statchar); | |
79 | ||
80 | static void stats_spy(struct Client *, char, const char *); | |
81 | static void stats_p_spy(struct Client *); | |
82 | ||
83 | /* Heres our struct for the stats table */ | |
84 | struct StatsStruct | |
85 | { | |
86 | char letter; | |
87 | void (*handler) (); | |
88 | int need_oper; | |
89 | int need_admin; | |
90 | }; | |
91 | ||
92 | static void stats_dns_servers (struct Client *); | |
93 | static void stats_delay(struct Client *); | |
94 | static void stats_hash(struct Client *); | |
95 | static void stats_connect(struct Client *); | |
96 | static void stats_tdeny(struct Client *); | |
97 | static void stats_deny(struct Client *); | |
98 | static void stats_exempt(struct Client *); | |
99 | static void stats_events(struct Client *); | |
100 | static void stats_glines(struct Client *); | |
101 | static void stats_pending_glines(struct Client *); | |
102 | static void stats_hubleaf(struct Client *); | |
103 | static void stats_auth(struct Client *); | |
104 | static void stats_tklines(struct Client *); | |
105 | static void stats_klines(struct Client *); | |
106 | static void stats_messages(struct Client *); | |
107 | static void stats_dnsbl(struct Client *); | |
108 | static void stats_oper(struct Client *); | |
109 | static void stats_operedup(struct Client *); | |
110 | static void stats_ports(struct Client *); | |
111 | static void stats_tresv(struct Client *); | |
112 | static void stats_resv(struct Client *); | |
113 | static void stats_usage(struct Client *); | |
114 | static void stats_tstats(struct Client *); | |
115 | static void stats_uptime(struct Client *); | |
116 | static void stats_shared(struct Client *); | |
117 | static void stats_servers(struct Client *); | |
118 | static void stats_tgecos(struct Client *); | |
119 | static void stats_gecos(struct Client *); | |
120 | static void stats_class(struct Client *); | |
121 | static void stats_memory(struct Client *); | |
122 | static void stats_servlinks(struct Client *); | |
123 | static void stats_ltrace(struct Client *, int, const char **); | |
124 | static void stats_ziplinks(struct Client *); | |
125 | ||
126 | /* This table contains the possible stats items, in order: | |
127 | * stats letter, function to call, operonly? adminonly? | |
128 | * case only matters in the stats letter column.. -- fl_ | |
129 | */ | |
130 | static struct StatsStruct stats_cmd_table[] = { | |
131 | /* letter function need_oper need_admin */ | |
132 | {'a', stats_dns_servers, 1, 1, }, | |
133 | {'A', stats_dns_servers, 1, 1, }, | |
134 | {'b', stats_delay, 1, 1, }, | |
135 | {'B', stats_hash, 1, 1, }, | |
136 | {'c', stats_connect, 0, 0, }, | |
137 | {'C', stats_connect, 0, 0, }, | |
138 | {'d', stats_tdeny, 1, 0, }, | |
139 | {'D', stats_deny, 1, 0, }, | |
140 | {'e', stats_exempt, 1, 0, }, | |
141 | {'E', stats_events, 1, 1, }, | |
142 | {'f', comm_dump, 1, 1, }, | |
143 | {'F', comm_dump, 1, 1, }, | |
144 | {'g', stats_pending_glines, 1, 0, }, | |
145 | {'G', stats_glines, 1, 0, }, | |
146 | {'h', stats_hubleaf, 0, 0, }, | |
147 | {'H', stats_hubleaf, 0, 0, }, | |
148 | {'i', stats_auth, 0, 0, }, | |
149 | {'I', stats_auth, 0, 0, }, | |
150 | {'k', stats_tklines, 0, 0, }, | |
151 | {'K', stats_klines, 0, 0, }, | |
152 | {'l', stats_ltrace, 0, 0, }, | |
153 | {'L', stats_ltrace, 0, 0, }, | |
154 | {'m', stats_messages, 0, 0, }, | |
155 | {'M', stats_messages, 0, 0, }, | |
156 | {'n', stats_dnsbl, 0, 0, }, | |
157 | {'o', stats_oper, 0, 0, }, | |
158 | {'O', stats_oper, 0, 0, }, | |
159 | {'p', stats_operedup, 0, 0, }, | |
160 | {'P', stats_ports, 0, 0, }, | |
161 | {'q', stats_tresv, 1, 0, }, | |
162 | {'Q', stats_resv, 1, 0, }, | |
163 | {'r', stats_usage, 1, 0, }, | |
164 | {'R', stats_usage, 1, 0, }, | |
165 | {'t', stats_tstats, 1, 0, }, | |
166 | {'T', stats_tstats, 1, 0, }, | |
167 | {'u', stats_uptime, 0, 0, }, | |
168 | {'U', stats_shared, 1, 0, }, | |
169 | {'v', stats_servers, 0, 0, }, | |
170 | {'V', stats_servers, 0, 0, }, | |
171 | {'x', stats_tgecos, 1, 0, }, | |
172 | {'X', stats_gecos, 1, 0, }, | |
173 | {'y', stats_class, 0, 0, }, | |
174 | {'Y', stats_class, 0, 0, }, | |
175 | {'z', stats_memory, 1, 0, }, | |
176 | {'Z', stats_ziplinks, 1, 0, }, | |
177 | {'?', stats_servlinks, 0, 0, }, | |
178 | {(char) 0, (void (*)()) 0, 0, 0, } | |
179 | }; | |
180 | ||
181 | /* | |
182 | * m_stats by fl_ | |
183 | * parv[0] = sender prefix | |
184 | * parv[1] = stat letter/command | |
185 | * parv[2] = (if present) server/mask in stats L, or target | |
186 | * | |
187 | * This will search the tables for the appropriate stats letter, | |
188 | * if found execute it. | |
189 | */ | |
190 | static int | |
191 | m_stats(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) | |
192 | { | |
193 | static time_t last_used = 0; | |
194 | int i; | |
195 | char statchar; | |
196 | ||
197 | statchar = parv[1][0]; | |
198 | ||
199 | if(MyClient(source_p) && !IsOper(source_p)) | |
200 | { | |
201 | /* Check the user is actually allowed to do /stats, and isnt flooding */ | |
202 | if((last_used + ConfigFileEntry.pace_wait) > CurrentTime) | |
203 | { | |
204 | /* safe enough to give this on a local connect only */ | |
205 | sendto_one(source_p, form_str(RPL_LOAD2HI), | |
206 | me.name, source_p->name, "STATS"); | |
207 | sendto_one_numeric(source_p, RPL_ENDOFSTATS, | |
208 | form_str(RPL_ENDOFSTATS), statchar); | |
209 | return 0; | |
210 | } | |
211 | else | |
212 | last_used = CurrentTime; | |
213 | } | |
214 | ||
215 | if(hunt_server (client_p, source_p, ":%s STATS %s :%s", 2, parc, parv) != HUNTED_ISME) | |
216 | return 0; | |
217 | ||
218 | if((statchar != 'L') && (statchar != 'l')) | |
219 | stats_spy(source_p, statchar, NULL); | |
220 | ||
221 | for (i = 0; stats_cmd_table[i].handler; i++) | |
222 | { | |
223 | if(stats_cmd_table[i].letter == statchar) | |
224 | { | |
225 | /* The stats table says what privs are needed, so check --fl_ */ | |
226 | /* Called for remote clients and for local opers, so check need_admin | |
227 | * and need_oper | |
228 | */ | |
229 | if((stats_cmd_table[i].need_admin && !IsOperAdmin (source_p)) || | |
230 | (stats_cmd_table[i].need_oper && !IsOper (source_p))) | |
231 | { | |
232 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
233 | form_str (ERR_NOPRIVILEGES)); | |
234 | break; | |
235 | } | |
236 | ||
237 | /* Blah, stats L needs the parameters, none of the others do.. */ | |
238 | if(statchar == 'L' || statchar == 'l') | |
239 | stats_cmd_table[i].handler (source_p, parc, parv); | |
240 | else | |
241 | stats_cmd_table[i].handler (source_p); | |
242 | } | |
243 | } | |
244 | ||
245 | /* Send the end of stats notice, and the stats_spy */ | |
246 | sendto_one_numeric(source_p, RPL_ENDOFSTATS, | |
247 | form_str(RPL_ENDOFSTATS), statchar); | |
248 | ||
249 | return 0; | |
250 | } | |
251 | ||
252 | static void | |
253 | stats_dns_servers (struct Client *source_p) | |
254 | { | |
255 | report_dns_servers (source_p); | |
256 | } | |
257 | ||
258 | static void | |
259 | stats_delay(struct Client *source_p) | |
260 | { | |
261 | struct nd_entry *nd; | |
262 | dlink_node *ptr; | |
263 | int i; | |
264 | ||
265 | HASH_WALK(i, U_MAX, ptr, ndTable) | |
266 | { | |
267 | nd = ptr->data; | |
268 | sendto_one_notice(source_p, "Delaying: %s for %ld", | |
269 | nd->name, (long) nd->expire); | |
270 | } | |
271 | HASH_WALK_END | |
272 | } | |
273 | ||
274 | static void | |
275 | stats_hash(struct Client *source_p) | |
276 | { | |
277 | hash_stats(source_p); | |
278 | } | |
279 | ||
280 | static void | |
281 | stats_connect(struct Client *source_p) | |
282 | { | |
283 | static char buf[5]; | |
284 | struct server_conf *server_p; | |
285 | char *s; | |
286 | dlink_node *ptr; | |
287 | ||
288 | if((ConfigFileEntry.stats_c_oper_only || | |
289 | (ConfigServerHide.flatten_links && !IsExemptShide(source_p))) && | |
290 | !IsOper(source_p)) | |
291 | { | |
292 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
293 | form_str(ERR_NOPRIVILEGES)); | |
294 | return; | |
295 | } | |
296 | ||
297 | DLINK_FOREACH(ptr, server_conf_list.head) | |
298 | { | |
299 | server_p = ptr->data; | |
300 | ||
301 | if(ServerConfIllegal(server_p)) | |
302 | continue; | |
303 | ||
304 | buf[0] = '\0'; | |
305 | s = buf; | |
306 | ||
307 | if(IsOper(source_p)) | |
308 | { | |
309 | if(ServerConfAutoconn(server_p)) | |
310 | *s++ = 'A'; | |
311 | if(ServerConfTb(server_p)) | |
312 | *s++ = 'T'; | |
313 | if(ServerConfCompressed(server_p)) | |
314 | *s++ = 'Z'; | |
315 | } | |
316 | ||
317 | if(!buf[0]) | |
318 | *s++ = '*'; | |
319 | ||
320 | *s = '\0'; | |
321 | ||
322 | sendto_one_numeric(source_p, RPL_STATSCLINE, | |
323 | form_str(RPL_STATSCLINE), | |
324 | #ifndef HIDE_SERVERS_IPS | |
325 | server_p->host, | |
326 | #else | |
327 | "*@127.0.0.1", | |
328 | #endif | |
329 | buf, server_p->name, | |
330 | server_p->port, server_p->class_name); | |
331 | } | |
332 | } | |
333 | ||
334 | /* stats_tdeny() | |
335 | * | |
336 | * input - client to report to | |
337 | * output - none | |
338 | * side effects - client is given temp dline list. | |
339 | */ | |
340 | static void | |
341 | stats_tdeny (struct Client *source_p) | |
342 | { | |
343 | char *host, *pass, *user, *oper_reason; | |
344 | struct AddressRec *arec; | |
345 | struct ConfItem *aconf; | |
346 | int i; | |
347 | ||
348 | for (i = 0; i < ATABLE_SIZE; i++) | |
349 | { | |
350 | for (arec = atable[i]; arec; arec = arec->next) | |
351 | { | |
352 | if(arec->type == CONF_DLINE) | |
353 | { | |
354 | aconf = arec->aconf; | |
355 | ||
356 | if(!(aconf->flags & CONF_FLAGS_TEMPORARY)) | |
357 | continue; | |
358 | ||
359 | get_printable_kline(source_p, aconf, &host, &pass, &user, &oper_reason); | |
360 | ||
361 | sendto_one_numeric(source_p, RPL_STATSDLINE, | |
362 | form_str (RPL_STATSDLINE), | |
363 | 'd', host, pass, | |
364 | oper_reason ? "|" : "", | |
365 | oper_reason ? oper_reason : ""); | |
366 | } | |
367 | } | |
368 | } | |
369 | } | |
370 | ||
371 | /* stats_deny() | |
372 | * | |
373 | * input - client to report to | |
374 | * output - none | |
375 | * side effects - client is given dline list. | |
376 | */ | |
377 | static void | |
378 | stats_deny (struct Client *source_p) | |
379 | { | |
380 | char *host, *pass, *user, *oper_reason; | |
381 | struct AddressRec *arec; | |
382 | struct ConfItem *aconf; | |
383 | int i; | |
384 | ||
385 | for (i = 0; i < ATABLE_SIZE; i++) | |
386 | { | |
387 | for (arec = atable[i]; arec; arec = arec->next) | |
388 | { | |
389 | if(arec->type == CONF_DLINE) | |
390 | { | |
391 | aconf = arec->aconf; | |
392 | ||
393 | if(aconf->flags & CONF_FLAGS_TEMPORARY) | |
394 | continue; | |
395 | ||
396 | get_printable_kline(source_p, aconf, &host, &pass, &user, &oper_reason); | |
397 | ||
398 | sendto_one_numeric(source_p, RPL_STATSDLINE, | |
399 | form_str (RPL_STATSDLINE), | |
400 | 'D', host, pass, | |
401 | oper_reason ? "|" : "", | |
402 | oper_reason ? oper_reason : ""); | |
403 | } | |
404 | } | |
405 | } | |
406 | } | |
407 | ||
408 | ||
409 | /* stats_exempt() | |
410 | * | |
411 | * input - client to report to | |
412 | * output - none | |
413 | * side effects - client is given list of exempt blocks | |
414 | */ | |
415 | static void | |
416 | stats_exempt(struct Client *source_p) | |
417 | { | |
418 | char *name, *host, *pass, *user, *classname; | |
419 | struct AddressRec *arec; | |
420 | struct ConfItem *aconf; | |
421 | int i, port; | |
422 | ||
423 | if(ConfigFileEntry.stats_e_disabled) | |
424 | { | |
425 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
426 | form_str (ERR_NOPRIVILEGES)); | |
427 | return; | |
428 | } | |
429 | ||
430 | for (i = 0; i < ATABLE_SIZE; i++) | |
431 | { | |
432 | for (arec = atable[i]; arec; arec = arec->next) | |
433 | { | |
434 | if(arec->type == CONF_EXEMPTDLINE) | |
435 | { | |
436 | aconf = arec->aconf; | |
437 | get_printable_conf (aconf, &name, &host, &pass, | |
438 | &user, &port, &classname); | |
439 | ||
440 | sendto_one_numeric(source_p, RPL_STATSDLINE, | |
441 | form_str(RPL_STATSDLINE), | |
442 | 'e', host, pass, "", ""); | |
443 | } | |
444 | } | |
445 | }} | |
446 | ||
447 | ||
448 | static void | |
449 | stats_events (struct Client *source_p) | |
450 | { | |
451 | show_events (source_p); | |
452 | } | |
453 | ||
454 | /* stats_pending_glines() | |
455 | * | |
456 | * input - client pointer | |
457 | * output - none | |
458 | * side effects - client is shown list of pending glines | |
459 | */ | |
460 | static void | |
461 | stats_pending_glines (struct Client *source_p) | |
462 | { | |
463 | if(ConfigFileEntry.glines) | |
464 | { | |
465 | dlink_node *pending_node; | |
466 | struct gline_pending *glp_ptr; | |
467 | char timebuffer[MAX_DATE_STRING]; | |
468 | struct tm *tmptr; | |
469 | ||
470 | DLINK_FOREACH (pending_node, pending_glines.head) | |
471 | { | |
472 | glp_ptr = pending_node->data; | |
473 | ||
474 | tmptr = localtime (&glp_ptr->time_request1); | |
475 | strftime (timebuffer, MAX_DATE_STRING, "%Y/%m/%d %H:%M:%S", tmptr); | |
476 | ||
477 | sendto_one_notice(source_p, | |
478 | ":1) %s!%s@%s on %s requested gline at %s for %s@%s [%s]", | |
479 | glp_ptr->oper_nick1, | |
480 | glp_ptr->oper_user1, glp_ptr->oper_host1, | |
481 | glp_ptr->oper_server1, timebuffer, | |
482 | glp_ptr->user, glp_ptr->host, glp_ptr->reason1); | |
483 | ||
484 | if(glp_ptr->oper_nick2[0]) | |
485 | { | |
486 | tmptr = localtime (&glp_ptr->time_request2); | |
487 | strftime (timebuffer, MAX_DATE_STRING, "%Y/%m/%d %H:%M:%S", tmptr); | |
488 | sendto_one_notice(source_p, | |
489 | ":2) %s!%s@%s on %s requested gline at %s for %s@%s [%s]", | |
490 | glp_ptr->oper_nick2, | |
491 | glp_ptr->oper_user2, glp_ptr->oper_host2, | |
492 | glp_ptr->oper_server2, timebuffer, | |
493 | glp_ptr->user, glp_ptr->host, glp_ptr->reason2); | |
494 | } | |
495 | } | |
496 | ||
497 | if(dlink_list_length (&pending_glines) > 0) | |
498 | sendto_one_notice(source_p, ":End of Pending G-lines"); | |
499 | } | |
500 | else | |
501 | sendto_one_notice(source_p, ":This server does not support G-Lines"); | |
502 | ||
503 | } | |
504 | ||
505 | /* stats_glines() | |
506 | * | |
507 | * input - client pointer | |
508 | * output - none | |
509 | * side effects - client is shown list of glines | |
510 | */ | |
511 | static void | |
512 | stats_glines (struct Client *source_p) | |
513 | { | |
514 | if(ConfigFileEntry.glines) | |
515 | { | |
516 | dlink_node *gline_node; | |
517 | struct ConfItem *kill_ptr; | |
518 | ||
519 | DLINK_FOREACH_PREV (gline_node, glines.tail) | |
520 | { | |
521 | kill_ptr = gline_node->data; | |
522 | ||
523 | sendto_one_numeric(source_p, RPL_STATSKLINE, | |
524 | form_str(RPL_STATSKLINE), 'G', | |
525 | kill_ptr->host ? kill_ptr->host : "*", | |
526 | kill_ptr->user ? kill_ptr->user : "*", | |
527 | kill_ptr->passwd ? kill_ptr->passwd : "No Reason", | |
528 | kill_ptr->spasswd ? "|" : "", | |
529 | kill_ptr->spasswd ? kill_ptr->spasswd : ""); | |
530 | } | |
531 | } | |
532 | else | |
533 | sendto_one_notice(source_p, ":This server does not support G-Lines"); | |
534 | } | |
535 | ||
536 | ||
537 | static void | |
538 | stats_hubleaf(struct Client *source_p) | |
539 | { | |
540 | struct remote_conf *hub_p; | |
541 | dlink_node *ptr; | |
542 | ||
543 | if((ConfigFileEntry.stats_h_oper_only || | |
544 | (ConfigServerHide.flatten_links && !IsExemptShide(source_p))) && | |
545 | !IsOper(source_p)) | |
546 | { | |
547 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
548 | form_str (ERR_NOPRIVILEGES)); | |
549 | return; | |
550 | } | |
551 | ||
552 | DLINK_FOREACH(ptr, hubleaf_conf_list.head) | |
553 | { | |
554 | hub_p = ptr->data; | |
555 | ||
556 | if(hub_p->flags & CONF_HUB) | |
557 | sendto_one_numeric(source_p, RPL_STATSHLINE, | |
558 | form_str(RPL_STATSHLINE), | |
559 | hub_p->host, hub_p->server); | |
560 | else | |
561 | sendto_one_numeric(source_p, RPL_STATSLLINE, | |
562 | form_str(RPL_STATSLLINE), | |
563 | hub_p->host, hub_p->server); | |
564 | } | |
565 | } | |
566 | ||
567 | ||
568 | static void | |
569 | stats_auth (struct Client *source_p) | |
570 | { | |
571 | /* Oper only, if unopered, return ERR_NOPRIVS */ | |
572 | if((ConfigFileEntry.stats_i_oper_only == 2) && !IsOper (source_p)) | |
573 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
574 | form_str (ERR_NOPRIVILEGES)); | |
575 | ||
576 | /* If unopered, Only return matching auth blocks */ | |
577 | else if((ConfigFileEntry.stats_i_oper_only == 1) && !IsOper (source_p)) | |
578 | { | |
579 | struct ConfItem *aconf; | |
580 | char *name, *host, *pass, *user, *classname; | |
581 | int port; | |
582 | ||
583 | if(MyConnect (source_p)) | |
584 | aconf = find_conf_by_address (source_p->host, source_p->sockhost, NULL, | |
585 | (struct sockaddr *)&source_p->localClient->ip, | |
586 | CONF_CLIENT, | |
587 | source_p->localClient->ip.ss_family, | |
588 | source_p->username); | |
589 | else | |
590 | aconf = find_conf_by_address (source_p->host, NULL, NULL, NULL, CONF_CLIENT, | |
591 | 0, source_p->username); | |
592 | ||
593 | if(aconf == NULL) | |
594 | return; | |
595 | ||
596 | get_printable_conf (aconf, &name, &host, &pass, &user, &port, &classname); | |
597 | ||
598 | sendto_one_numeric(source_p, RPL_STATSILINE, form_str(RPL_STATSILINE), | |
599 | name, show_iline_prefix(source_p, aconf, user), | |
600 | host, port, classname); | |
601 | } | |
602 | ||
603 | /* Theyre opered, or allowed to see all auth blocks */ | |
604 | else | |
605 | report_auth (source_p); | |
606 | } | |
607 | ||
608 | ||
609 | static void | |
610 | stats_tklines(struct Client *source_p) | |
611 | { | |
612 | /* Oper only, if unopered, return ERR_NOPRIVS */ | |
613 | if((ConfigFileEntry.stats_k_oper_only == 2) && !IsOper (source_p)) | |
614 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
615 | form_str (ERR_NOPRIVILEGES)); | |
616 | ||
617 | /* If unopered, Only return matching klines */ | |
618 | else if((ConfigFileEntry.stats_k_oper_only == 1) && !IsOper (source_p)) | |
619 | { | |
620 | struct ConfItem *aconf; | |
621 | char *host, *pass, *user, *oper_reason; | |
622 | ||
623 | if(MyConnect (source_p)) | |
624 | aconf = find_conf_by_address (source_p->host, source_p->sockhost, NULL, | |
625 | (struct sockaddr *)&source_p->localClient->ip, | |
626 | CONF_KILL, | |
627 | source_p->localClient->ip.ss_family, | |
628 | source_p->username); | |
629 | else | |
630 | aconf = find_conf_by_address (source_p->host, NULL, NULL, NULL, CONF_KILL, | |
631 | 0, source_p->username); | |
632 | ||
633 | if(aconf == NULL) | |
634 | return; | |
635 | ||
636 | /* dont report a permanent kline as a tkline */ | |
637 | if((aconf->flags & CONF_FLAGS_TEMPORARY) == 0) | |
638 | return; | |
639 | ||
640 | get_printable_kline(source_p, aconf, &host, &pass, &user, &oper_reason); | |
641 | ||
642 | sendto_one_numeric(source_p, RPL_STATSKLINE, | |
643 | form_str(RPL_STATSKLINE), 'k', | |
644 | user, pass, oper_reason ? "|" : "", | |
645 | oper_reason ? oper_reason : ""); | |
646 | } | |
647 | /* Theyre opered, or allowed to see all klines */ | |
648 | else | |
649 | { | |
650 | struct ConfItem *aconf; | |
651 | dlink_node *ptr; | |
652 | int i; | |
653 | char *user, *host, *pass, *oper_reason; | |
654 | ||
655 | for(i = 0; i < LAST_TEMP_TYPE; i++) | |
656 | { | |
657 | DLINK_FOREACH(ptr, temp_klines[i].head) | |
658 | { | |
659 | aconf = ptr->data; | |
660 | ||
661 | get_printable_kline(source_p, aconf, &host, &pass, | |
662 | &user, &oper_reason); | |
663 | ||
664 | sendto_one_numeric(source_p, RPL_STATSKLINE, | |
665 | form_str(RPL_STATSKLINE), | |
666 | 'k', host, user, pass, | |
667 | oper_reason ? "|" : "", | |
668 | oper_reason ? oper_reason : ""); | |
669 | } | |
670 | } | |
671 | } | |
672 | } | |
673 | ||
674 | static void | |
675 | stats_klines(struct Client *source_p) | |
676 | { | |
677 | /* Oper only, if unopered, return ERR_NOPRIVS */ | |
678 | if((ConfigFileEntry.stats_k_oper_only == 2) && !IsOper (source_p)) | |
679 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
680 | form_str (ERR_NOPRIVILEGES)); | |
681 | ||
682 | /* If unopered, Only return matching klines */ | |
683 | else if((ConfigFileEntry.stats_k_oper_only == 1) && !IsOper (source_p)) | |
684 | { | |
685 | struct ConfItem *aconf; | |
686 | char *host, *pass, *user, *oper_reason; | |
687 | ||
688 | /* search for a kline */ | |
689 | if(MyConnect (source_p)) | |
690 | aconf = find_conf_by_address (source_p->host, source_p->sockhost, NULL, | |
691 | (struct sockaddr *)&source_p->localClient->ip, | |
692 | CONF_KILL, | |
693 | source_p->localClient->ip.ss_family, | |
694 | source_p->username); | |
695 | else | |
696 | aconf = find_conf_by_address (source_p->host, NULL, NULL, NULL, CONF_KILL, | |
697 | 0, source_p->username); | |
698 | ||
699 | if(aconf == NULL) | |
700 | return; | |
701 | ||
702 | /* dont report a tkline as a kline */ | |
703 | if(aconf->flags & CONF_FLAGS_TEMPORARY) | |
704 | return; | |
705 | ||
706 | get_printable_kline(source_p, aconf, &host, &pass, &user, &oper_reason); | |
707 | ||
708 | sendto_one_numeric(source_p, RPL_STATSKLINE, form_str(RPL_STATSKLINE), | |
709 | 'K', host, user, pass, oper_reason ? "|" : "", | |
710 | oper_reason ? oper_reason : ""); | |
711 | } | |
712 | /* Theyre opered, or allowed to see all klines */ | |
713 | else | |
714 | report_Klines (source_p); | |
715 | } | |
716 | ||
717 | static void | |
718 | stats_messages(struct Client *source_p) | |
719 | { | |
720 | report_messages(source_p); | |
721 | } | |
722 | ||
723 | static void | |
724 | stats_dnsbl(struct Client *source_p) | |
725 | { | |
726 | dlink_node *ptr; | |
727 | struct Blacklist *blptr; | |
728 | ||
729 | DLINK_FOREACH(ptr, blacklist_list.head) | |
730 | { | |
731 | blptr = ptr->data; | |
732 | ||
733 | /* use RPL_STATSDEBUG for now -- jilles */ | |
734 | sendto_one_numeric(source_p, RPL_STATSDEBUG, "n :%d %s %s (%d)", | |
735 | blptr->hits, | |
736 | blptr->host, | |
737 | blptr->status & CONF_ILLEGAL ? "disabled" : "active", | |
738 | blptr->refcount); | |
739 | } | |
740 | } | |
741 | ||
742 | static void | |
743 | stats_oper(struct Client *source_p) | |
744 | { | |
745 | struct oper_conf *oper_p; | |
746 | dlink_node *ptr; | |
747 | ||
748 | if(!IsOper(source_p) && ConfigFileEntry.stats_o_oper_only) | |
749 | { | |
750 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
751 | form_str (ERR_NOPRIVILEGES)); | |
752 | return; | |
753 | } | |
754 | ||
755 | DLINK_FOREACH(ptr, oper_conf_list.head) | |
756 | { | |
757 | oper_p = ptr->data; | |
758 | ||
759 | sendto_one_numeric(source_p, RPL_STATSOLINE, | |
760 | form_str(RPL_STATSOLINE), | |
761 | oper_p->username, oper_p->host, oper_p->name, | |
762 | IsOper(source_p) ? get_oper_privs(oper_p->flags) : "0", "-1"); | |
763 | } | |
764 | } | |
765 | ||
766 | ||
767 | /* stats_operedup() | |
768 | * | |
769 | * input - client pointer | |
770 | * output - none | |
771 | * side effects - client is shown a list of active opers | |
772 | */ | |
773 | static void | |
774 | stats_operedup (struct Client *source_p) | |
775 | { | |
776 | struct Client *target_p; | |
777 | dlink_node *oper_ptr; | |
778 | unsigned int count = 0; | |
779 | ||
780 | DLINK_FOREACH (oper_ptr, oper_list.head) | |
781 | { | |
782 | target_p = oper_ptr->data; | |
783 | ||
784 | if(IsOperInvis(target_p) && !IsOper(source_p)) | |
785 | continue; | |
786 | ||
787 | if(target_p->user->away) | |
788 | continue; | |
789 | ||
790 | count++; | |
791 | ||
792 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
793 | "p :%s (%s@%s)", | |
794 | target_p->name, target_p->username, | |
795 | target_p->host); | |
796 | } | |
797 | ||
798 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
799 | "p :%u staff members", count); | |
800 | ||
801 | stats_p_spy (source_p); | |
802 | } | |
803 | ||
804 | static void | |
805 | stats_ports (struct Client *source_p) | |
806 | { | |
807 | if(!IsOper (source_p) && ConfigFileEntry.stats_P_oper_only) | |
808 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
809 | form_str (ERR_NOPRIVILEGES)); | |
810 | else | |
811 | show_ports (source_p); | |
812 | } | |
813 | ||
814 | static void | |
815 | stats_tresv(struct Client *source_p) | |
816 | { | |
817 | struct ConfItem *aconf; | |
818 | dlink_node *ptr; | |
819 | int i; | |
820 | ||
821 | DLINK_FOREACH(ptr, resv_conf_list.head) | |
822 | { | |
823 | aconf = ptr->data; | |
824 | if(aconf->hold) | |
825 | sendto_one_numeric(source_p, RPL_STATSQLINE, | |
826 | form_str(RPL_STATSQLINE), | |
827 | 'q', aconf->port, aconf->name, aconf->passwd); | |
828 | } | |
829 | ||
830 | HASH_WALK(i, R_MAX, ptr, resvTable) | |
831 | { | |
832 | aconf = ptr->data; | |
833 | if(aconf->hold) | |
834 | sendto_one_numeric(source_p, RPL_STATSQLINE, | |
835 | form_str(RPL_STATSQLINE), | |
836 | 'q', aconf->port, aconf->name, aconf->passwd); | |
837 | } | |
838 | HASH_WALK_END | |
839 | } | |
840 | ||
841 | ||
842 | static void | |
843 | stats_resv(struct Client *source_p) | |
844 | { | |
845 | struct ConfItem *aconf; | |
846 | dlink_node *ptr; | |
847 | int i; | |
848 | ||
849 | DLINK_FOREACH(ptr, resv_conf_list.head) | |
850 | { | |
851 | aconf = ptr->data; | |
852 | if(!aconf->hold) | |
853 | sendto_one_numeric(source_p, RPL_STATSQLINE, | |
854 | form_str(RPL_STATSQLINE), | |
855 | 'Q', aconf->port, aconf->name, aconf->passwd); | |
856 | } | |
857 | ||
858 | HASH_WALK(i, R_MAX, ptr, resvTable) | |
859 | { | |
860 | aconf = ptr->data; | |
861 | if(!aconf->hold) | |
862 | sendto_one_numeric(source_p, RPL_STATSQLINE, | |
863 | form_str(RPL_STATSQLINE), | |
864 | 'Q', aconf->port, aconf->name, aconf->passwd); | |
865 | } | |
866 | HASH_WALK_END | |
867 | } | |
868 | ||
869 | static void | |
870 | stats_usage (struct Client *source_p) | |
871 | { | |
872 | struct rusage rus; | |
873 | time_t secs; | |
874 | time_t rup; | |
875 | #ifdef hz | |
876 | # define hzz hz | |
877 | #else | |
878 | # ifdef HZ | |
879 | # define hzz HZ | |
880 | # else | |
881 | int hzz = 1; | |
882 | # endif | |
883 | #endif | |
884 | ||
885 | if(getrusage(RUSAGE_SELF, &rus) == -1) | |
886 | { | |
887 | sendto_one_notice(source_p, ":Getruseage error: %s.", | |
888 | strerror(errno)); | |
889 | return; | |
890 | } | |
891 | secs = rus.ru_utime.tv_sec + rus.ru_stime.tv_sec; | |
892 | if(0 == secs) | |
893 | secs = 1; | |
894 | ||
895 | rup = (CurrentTime - startup_time) * hzz; | |
896 | if(0 == rup) | |
897 | rup = 1; | |
898 | ||
899 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
900 | "R :CPU Secs %d:%d User %d:%d System %d:%d", | |
901 | (int) (secs / 60), (int) (secs % 60), | |
902 | (int) (rus.ru_utime.tv_sec / 60), | |
903 | (int) (rus.ru_utime.tv_sec % 60), | |
904 | (int) (rus.ru_stime.tv_sec / 60), | |
905 | (int) (rus.ru_stime.tv_sec % 60)); | |
906 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
907 | "R :RSS %ld ShMem %ld Data %ld Stack %ld", | |
908 | rus.ru_maxrss, (rus.ru_ixrss / rup), | |
909 | (rus.ru_idrss / rup), (rus.ru_isrss / rup)); | |
910 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
911 | "R :Swaps %d Reclaims %d Faults %d", | |
912 | (int) rus.ru_nswap, (int) rus.ru_minflt, (int) rus.ru_majflt); | |
913 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
914 | "R :Block in %d out %d", | |
915 | (int) rus.ru_inblock, (int) rus.ru_oublock); | |
916 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
917 | "R :Msg Rcv %d Send %d", | |
918 | (int) rus.ru_msgrcv, (int) rus.ru_msgsnd); | |
919 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
920 | "R :Signals %d Context Vol. %d Invol %d", | |
921 | (int) rus.ru_nsignals, (int) rus.ru_nvcsw, | |
922 | (int) rus.ru_nivcsw); | |
923 | } | |
924 | ||
925 | static void | |
926 | stats_tstats (struct Client *source_p) | |
927 | { | |
928 | tstats (source_p); | |
929 | } | |
930 | ||
931 | static void | |
932 | stats_uptime (struct Client *source_p) | |
933 | { | |
934 | time_t now; | |
935 | ||
936 | now = CurrentTime - startup_time; | |
937 | sendto_one_numeric(source_p, RPL_STATSUPTIME, | |
938 | form_str (RPL_STATSUPTIME), | |
939 | now / 86400, (now / 3600) % 24, | |
940 | (now / 60) % 60, now % 60); | |
941 | sendto_one_numeric(source_p, RPL_STATSCONN, | |
942 | form_str (RPL_STATSCONN), | |
943 | MaxConnectionCount, MaxClientCount, | |
944 | Count.totalrestartcount); | |
945 | } | |
946 | ||
947 | struct shared_flags | |
948 | { | |
949 | int flag; | |
950 | char letter; | |
951 | }; | |
952 | static struct shared_flags shared_flagtable[] = | |
953 | { | |
954 | { SHARED_PKLINE, 'K' }, | |
955 | { SHARED_TKLINE, 'k' }, | |
956 | { SHARED_UNKLINE, 'U' }, | |
957 | { SHARED_PXLINE, 'X' }, | |
958 | { SHARED_TXLINE, 'x' }, | |
959 | { SHARED_UNXLINE, 'Y' }, | |
960 | { SHARED_PRESV, 'Q' }, | |
961 | { SHARED_TRESV, 'q' }, | |
962 | { SHARED_UNRESV, 'R' }, | |
963 | { SHARED_LOCOPS, 'L' }, | |
964 | { SHARED_REHASH, 'H' }, | |
965 | { 0, '\0'} | |
966 | }; | |
967 | ||
968 | ||
969 | static void | |
970 | stats_shared (struct Client *source_p) | |
971 | { | |
972 | struct remote_conf *shared_p; | |
973 | dlink_node *ptr; | |
974 | char buf[15]; | |
975 | char *p; | |
976 | int i; | |
977 | ||
978 | DLINK_FOREACH(ptr, shared_conf_list.head) | |
979 | { | |
980 | shared_p = ptr->data; | |
981 | ||
982 | p = buf; | |
983 | ||
984 | *p++ = 'c'; | |
985 | ||
986 | for(i = 0; shared_flagtable[i].flag != 0; i++) | |
987 | { | |
988 | if(shared_p->flags & shared_flagtable[i].flag) | |
989 | *p++ = shared_flagtable[i].letter; | |
990 | } | |
991 | ||
992 | *p = '\0'; | |
993 | ||
994 | sendto_one_numeric(source_p, RPL_STATSULINE, | |
995 | form_str(RPL_STATSULINE), | |
996 | shared_p->server, shared_p->username, | |
997 | shared_p->host, buf); | |
998 | } | |
999 | ||
1000 | DLINK_FOREACH(ptr, cluster_conf_list.head) | |
1001 | { | |
1002 | shared_p = ptr->data; | |
1003 | ||
1004 | p = buf; | |
1005 | ||
1006 | *p++ = 'C'; | |
1007 | ||
1008 | for(i = 0; shared_flagtable[i].flag != 0; i++) | |
1009 | { | |
1010 | if(shared_p->flags & shared_flagtable[i].flag) | |
1011 | *p++ = shared_flagtable[i].letter; | |
1012 | } | |
1013 | ||
1014 | *p = '\0'; | |
1015 | ||
1016 | sendto_one_numeric(source_p, RPL_STATSULINE, | |
1017 | form_str(RPL_STATSULINE), | |
1018 | shared_p->server, "*", "*", buf); | |
1019 | } | |
1020 | } | |
1021 | ||
1022 | /* stats_servers() | |
1023 | * | |
1024 | * input - client pointer | |
1025 | * output - none | |
1026 | * side effects - client is shown lists of who connected servers | |
1027 | */ | |
1028 | static void | |
1029 | stats_servers (struct Client *source_p) | |
1030 | { | |
1031 | struct Client *target_p; | |
1032 | dlink_node *ptr; | |
1033 | time_t seconds; | |
1034 | int days, hours, minutes; | |
1035 | int j = 0; | |
1036 | ||
1037 | if(ConfigServerHide.flatten_links && !IsOper(source_p) && | |
1038 | !IsExemptShide(source_p)) | |
1039 | { | |
1040 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
1041 | form_str (ERR_NOPRIVILEGES)); | |
1042 | return; | |
1043 | } | |
1044 | ||
1045 | DLINK_FOREACH (ptr, serv_list.head) | |
1046 | { | |
1047 | target_p = ptr->data; | |
1048 | ||
1049 | j++; | |
1050 | seconds = CurrentTime - target_p->localClient->firsttime; | |
1051 | ||
1052 | days = (int) (seconds / 86400); | |
1053 | seconds %= 86400; | |
1054 | hours = (int) (seconds / 3600); | |
1055 | seconds %= 3600; | |
1056 | minutes = (int) (seconds / 60); | |
1057 | seconds %= 60; | |
1058 | ||
1059 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1060 | "V :%s (%s!*@*) Idle: %d SendQ: %d " | |
1061 | "Connected: %d day%s, %d:%02d:%02d", | |
1062 | target_p->name, | |
1063 | (target_p->serv->by[0] ? target_p->serv->by : "Remote."), | |
1064 | (int) (CurrentTime - target_p->localClient->lasttime), | |
1065 | (int) linebuf_len (&target_p->localClient->buf_sendq), | |
1066 | days, (days == 1) ? "" : "s", hours, minutes, | |
1067 | (int) seconds); | |
1068 | } | |
1069 | ||
1070 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1071 | "V :%d Server(s)", j); | |
1072 | } | |
1073 | ||
1074 | static void | |
1075 | stats_tgecos(struct Client *source_p) | |
1076 | { | |
1077 | struct ConfItem *aconf; | |
1078 | dlink_node *ptr; | |
1079 | ||
1080 | DLINK_FOREACH(ptr, xline_conf_list.head) | |
1081 | { | |
1082 | aconf = ptr->data; | |
1083 | ||
1084 | if(aconf->hold) | |
1085 | sendto_one_numeric(source_p, RPL_STATSXLINE, | |
1086 | form_str(RPL_STATSXLINE), | |
1087 | 'x', aconf->port, aconf->name, | |
1088 | aconf->passwd); | |
1089 | } | |
1090 | } | |
1091 | ||
1092 | static void | |
1093 | stats_gecos(struct Client *source_p) | |
1094 | { | |
1095 | struct ConfItem *aconf; | |
1096 | dlink_node *ptr; | |
1097 | ||
1098 | DLINK_FOREACH(ptr, xline_conf_list.head) | |
1099 | { | |
1100 | aconf = ptr->data; | |
1101 | ||
1102 | if(!aconf->hold) | |
1103 | sendto_one_numeric(source_p, RPL_STATSXLINE, | |
1104 | form_str(RPL_STATSXLINE), | |
1105 | 'X', aconf->port, aconf->name, | |
1106 | aconf->passwd); | |
1107 | } | |
1108 | } | |
1109 | ||
1110 | static void | |
1111 | stats_class(struct Client *source_p) | |
1112 | { | |
1113 | if(ConfigFileEntry.stats_y_oper_only && !IsOper(source_p)) | |
1114 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
1115 | form_str (ERR_NOPRIVILEGES)); | |
1116 | else | |
1117 | report_classes(source_p); | |
1118 | } | |
1119 | ||
1120 | static void | |
1121 | stats_memory (struct Client *source_p) | |
1122 | { | |
1123 | count_memory (source_p); | |
1124 | } | |
1125 | ||
1126 | static void | |
1127 | stats_ziplinks (struct Client *source_p) | |
1128 | { | |
1129 | dlink_node *ptr; | |
1130 | struct Client *target_p; | |
1131 | int sent_data = 0; | |
1132 | ||
1133 | DLINK_FOREACH (ptr, serv_list.head) | |
1134 | { | |
1135 | target_p = ptr->data; | |
1136 | if(IsCapable (target_p, CAP_ZIP)) | |
1137 | { | |
1138 | /* we use memcpy(3) and a local copy of the structure to | |
1139 | * work around a register use bug on GCC on the SPARC. | |
1140 | * -jmallett, 04/27/2002 | |
1141 | */ | |
1142 | struct ZipStats zipstats; | |
1143 | memcpy (&zipstats, &target_p->localClient->zipstats, | |
1144 | sizeof (struct ZipStats)); | |
1145 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1146 | "Z :ZipLinks stats for %s send[%.2f%% compression " | |
1147 | "(%lu kB data/%lu kB wire)] recv[%.2f%% compression " | |
1148 | "(%lu kB data/%lu kB wire)]", | |
1149 | target_p->name, | |
1150 | zipstats.out_ratio, zipstats.outK, zipstats.outK_wire, | |
1151 | zipstats.in_ratio, zipstats.inK, zipstats.inK_wire); | |
1152 | sent_data++; | |
1153 | } | |
1154 | } | |
1155 | ||
1156 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1157 | "Z :%u ziplink(s)", sent_data); | |
1158 | } | |
1159 | ||
1160 | static void | |
1161 | stats_servlinks (struct Client *source_p) | |
1162 | { | |
1163 | static char Sformat[] = ":%s %d %s %s %u %u %u %u %u :%u %u %s"; | |
1164 | long uptime, sendK, receiveK; | |
1165 | struct Client *target_p; | |
1166 | dlink_node *ptr; | |
1167 | int j = 0; | |
1168 | ||
1169 | if(ConfigServerHide.flatten_links && !IsOper (source_p) && | |
1170 | !IsExemptShide(source_p)) | |
1171 | { | |
1172 | sendto_one_numeric(source_p, ERR_NOPRIVILEGES, | |
1173 | form_str (ERR_NOPRIVILEGES)); | |
1174 | return; | |
1175 | } | |
1176 | ||
1177 | sendK = receiveK = 0; | |
1178 | ||
1179 | DLINK_FOREACH (ptr, serv_list.head) | |
1180 | { | |
1181 | target_p = ptr->data; | |
1182 | ||
1183 | j++; | |
1184 | sendK += target_p->localClient->sendK; | |
1185 | receiveK += target_p->localClient->receiveK; | |
1186 | ||
1187 | sendto_one(source_p, Sformat, | |
1188 | get_id(&me, source_p), RPL_STATSLINKINFO, get_id(source_p, source_p), | |
1189 | get_server_name(target_p, SHOW_IP), | |
1190 | (int) linebuf_len (&target_p->localClient->buf_sendq), | |
1191 | (int) target_p->localClient->sendM, | |
1192 | (int) target_p->localClient->sendK, | |
1193 | (int) target_p->localClient->receiveM, | |
1194 | (int) target_p->localClient->receiveK, | |
1195 | CurrentTime - target_p->localClient->firsttime, | |
1196 | (CurrentTime > target_p->localClient->lasttime) ? | |
1197 | (CurrentTime - target_p->localClient->lasttime) : 0, | |
1198 | IsOper (source_p) ? show_capabilities (target_p) : "TS"); | |
1199 | } | |
1200 | ||
1201 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1202 | "? :%u total server(s)", j); | |
1203 | ||
1204 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1205 | "? :Sent total : %7.2f %s", | |
1206 | _GMKv (sendK), _GMKs (sendK)); | |
1207 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1208 | "? :Recv total : %7.2f %s", | |
1209 | _GMKv (receiveK), _GMKs (receiveK)); | |
1210 | ||
1211 | uptime = (CurrentTime - startup_time); | |
1212 | ||
1213 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1214 | "? :Server send: %7.2f %s (%4.1f K/s)", | |
1215 | _GMKv (me.localClient->sendK), | |
1216 | _GMKs (me.localClient->sendK), | |
1217 | (float) ((float) me.localClient->sendK / (float) uptime)); | |
1218 | sendto_one_numeric(source_p, RPL_STATSDEBUG, | |
1219 | "? :Server recv: %7.2f %s (%4.1f K/s)", | |
1220 | _GMKv (me.localClient->receiveK), | |
1221 | _GMKs (me.localClient->receiveK), | |
1222 | (float) ((float) me.localClient->receiveK / (float) uptime)); | |
1223 | } | |
1224 | ||
1225 | static void | |
1226 | stats_ltrace(struct Client *source_p, int parc, const char *parv[]) | |
1227 | { | |
1228 | int doall = 0; | |
1229 | int wilds = 0; | |
1230 | const char *name; | |
1231 | char statchar = parv[1][0]; | |
1232 | ||
1233 | /* this is def targeted at us somehow.. */ | |
1234 | if(parc > 2 && !EmptyString(parv[2])) | |
1235 | { | |
1236 | /* directed at us generically? */ | |
1237 | if(match(parv[2], me.name) || | |
1238 | (!MyClient(source_p) && !irccmp(parv[2], me.id))) | |
1239 | { | |
1240 | name = me.name; | |
1241 | doall = 1; | |
1242 | } | |
1243 | else | |
1244 | { | |
1245 | name = parv[2]; | |
1246 | wilds = strchr(name, '*') || strchr(name, '?'); | |
1247 | } | |
1248 | ||
1249 | /* must be directed at a specific person thats not us */ | |
1250 | if(!doall && !wilds) | |
1251 | { | |
1252 | struct Client *target_p; | |
1253 | ||
1254 | if(MyClient(source_p)) | |
1255 | target_p = find_named_person(name); | |
1256 | else | |
1257 | target_p = find_person(name); | |
1258 | ||
1259 | if(target_p != NULL) | |
1260 | { | |
1261 | stats_spy(source_p, statchar, target_p->name); | |
1262 | stats_l_client(source_p, target_p, statchar); | |
1263 | } | |
1264 | else | |
1265 | sendto_one_numeric(source_p, ERR_NOSUCHSERVER, | |
1266 | form_str(ERR_NOSUCHSERVER), | |
1267 | name); | |
1268 | ||
1269 | return; | |
1270 | } | |
1271 | } | |
1272 | else | |
1273 | { | |
1274 | name = me.name; | |
1275 | doall = 1; | |
1276 | } | |
1277 | ||
1278 | stats_spy(source_p, statchar, name); | |
1279 | ||
1280 | if(doall) | |
1281 | { | |
1282 | /* local opers get everyone */ | |
1283 | if(MyOper(source_p)) | |
1284 | { | |
1285 | stats_l_list(source_p, name, doall, wilds, &unknown_list, statchar); | |
1286 | stats_l_list(source_p, name, doall, wilds, &lclient_list, statchar); | |
1287 | } | |
1288 | else | |
1289 | { | |
1290 | /* they still need themselves if theyre local.. */ | |
1291 | if(MyClient(source_p)) | |
1292 | stats_l_client(source_p, source_p, statchar); | |
1293 | ||
1294 | stats_l_list(source_p, name, doall, wilds, &local_oper_list, statchar); | |
1295 | } | |
1296 | ||
1297 | if (!ConfigServerHide.flatten_links || IsOper(source_p) || | |
1298 | IsExemptShide(source_p)) | |
1299 | stats_l_list(source_p, name, doall, wilds, &serv_list, statchar); | |
1300 | ||
1301 | return; | |
1302 | } | |
1303 | ||
1304 | /* ok, at this point theyre looking for a specific client whos on | |
1305 | * our server.. but it contains a wildcard. --fl | |
1306 | */ | |
1307 | stats_l_list(source_p, name, doall, wilds, &lclient_list, statchar); | |
1308 | ||
1309 | return; | |
1310 | } | |
1311 | ||
1312 | ||
1313 | static void | |
1314 | stats_l_list(struct Client *source_p, const char *name, int doall, int wilds, | |
1315 | dlink_list * list, char statchar) | |
1316 | { | |
1317 | dlink_node *ptr; | |
1318 | struct Client *target_p; | |
1319 | ||
1320 | /* send information about connections which match. note, we | |
1321 | * dont need tests for IsInvisible(), because non-opers will | |
1322 | * never get here for normal clients --fl | |
1323 | */ | |
1324 | DLINK_FOREACH(ptr, list->head) | |
1325 | { | |
1326 | target_p = ptr->data; | |
1327 | ||
1328 | if(!doall && wilds && !match(name, target_p->name)) | |
1329 | continue; | |
1330 | ||
1331 | stats_l_client(source_p, target_p, statchar); | |
1332 | } | |
1333 | } | |
1334 | ||
1335 | void | |
1336 | stats_l_client(struct Client *source_p, struct Client *target_p, | |
1337 | char statchar) | |
1338 | { | |
1339 | if(IsAnyServer(target_p)) | |
1340 | { | |
1341 | sendto_one_numeric(source_p, RPL_STATSLINKINFO, Lformat, | |
1342 | get_server_name(target_p, SHOW_IP), | |
1343 | (int) linebuf_len(&target_p->localClient->buf_sendq), | |
1344 | (int) target_p->localClient->sendM, | |
1345 | (int) target_p->localClient->sendK, | |
1346 | (int) target_p->localClient->receiveM, | |
1347 | (int) target_p->localClient->receiveK, | |
1348 | CurrentTime - target_p->localClient->firsttime, | |
1349 | (CurrentTime > target_p->localClient->lasttime) ? | |
1350 | (CurrentTime - target_p->localClient->lasttime) : 0, | |
1351 | IsOper(source_p) ? show_capabilities(target_p) : "-"); | |
1352 | } | |
1353 | ||
1354 | else | |
1355 | { | |
1356 | sendto_one_numeric(source_p, RPL_STATSLINKINFO, Lformat, | |
1357 | show_ip(source_p, target_p) ? | |
1358 | (IsUpper(statchar) ? | |
1359 | get_client_name(target_p, SHOW_IP) : | |
1360 | get_client_name(target_p, HIDE_IP)) : | |
1361 | get_client_name(target_p, MASK_IP), | |
1362 | (int) linebuf_len(&target_p->localClient->buf_sendq), | |
1363 | (int) target_p->localClient->sendM, | |
1364 | (int) target_p->localClient->sendK, | |
1365 | (int) target_p->localClient->receiveM, | |
1366 | (int) target_p->localClient->receiveK, | |
1367 | CurrentTime - target_p->localClient->firsttime, | |
1368 | (CurrentTime > target_p->localClient->lasttime) ? | |
1369 | (CurrentTime - target_p->localClient->lasttime) : 0, | |
1370 | "-"); | |
1371 | } | |
1372 | } | |
1373 | ||
1374 | /* | |
1375 | * stats_spy | |
1376 | * | |
1377 | * inputs - pointer to client doing the /stats | |
1378 | * - char letter they are doing /stats on | |
1379 | * output - none | |
1380 | * side effects - | |
1381 | * This little helper function reports to opers if configured. | |
1382 | * personally, I don't see why opers need to see stats requests | |
1383 | * at all. They are just "noise" to an oper, and users can't do | |
1384 | * any damage with stats requests now anyway. So, why show them? | |
1385 | * -Dianora | |
1386 | */ | |
1387 | static void | |
1388 | stats_spy(struct Client *source_p, char statchar, const char *name) | |
1389 | { | |
1390 | hook_data_int data; | |
1391 | ||
1392 | data.client = source_p; | |
1393 | data.arg1 = name; | |
1394 | data.arg2 = (int) statchar; | |
1395 | ||
1396 | call_hook(doing_stats_hook, &data); | |
1397 | } | |
1398 | ||
1399 | /* stats_p_spy() | |
1400 | * | |
1401 | * input - pointer to client doing stats | |
1402 | * ouput - | |
1403 | * side effects - call hook doing_stats_p | |
1404 | */ | |
1405 | static void | |
1406 | stats_p_spy (struct Client *source_p) | |
1407 | { | |
1408 | hook_data data; | |
1409 | ||
1410 | data.client = source_p; | |
1411 | data.arg1 = data.arg2 = NULL; | |
1412 | ||
1413 | call_hook(doing_stats_p_hook, &data); | |
1414 | } | |
1415 |