Author: Bleep <tomh@inxpress.net>
[ircu2.10.12-pk.git] / ircd / s_debug.c
1 /*
2  * IRC - Internet Relay Chat, ircd/s_debug.c
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Computing Center
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 1, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  * $Id$
21  *
22  */
23 #include "s_debug.h"
24 #include "channel.h"
25 #include "class.h"
26 #include "client.h"
27 #include "hash.h"
28 #include "ircd_alloc.h"
29 #include "ircd_osdep.h"
30 #include "ircd.h"
31 #include "list.h"
32 #include "numeric.h"
33 #include "numnicks.h"
34 #include "res.h"
35 #include "s_bsd.h"
36 #include "s_conf.h"
37 #include "send.h"
38 #include "struct.h"
39 #include "sys.h"
40 #include "whowas.h"
41
42 #include <assert.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <stdarg.h>
46 #include <stddef.h>     /* offsetof */
47 #include <stdio.h>
48 #include <string.h>
49 #include <unistd.h>
50
51 /* *INDENT-OFF* */
52
53 /*
54  * Option string.  Must be before #ifdef DEBUGMODE.
55  */
56 char serveropts[] = {
57 #if BUFFERPOOL < 1000000
58     'b',
59 #if BUFFERPOOL > 99999
60     (char)('0' + (BUFFERPOOL/100000)),
61 #endif
62 #if BUFFERPOOL > 9999
63     (char)('0' + (BUFFERPOOL/10000) % 10),
64 #endif
65     (char)('0' + (BUFFERPOOL/1000) % 10),
66 #else
67     'B',
68 #if BUFFERPOOL > 99999999
69     (char)('0' + (BUFFERPOOL/100000000)),
70 #endif
71 #if BUFFERPOOL > 9999999
72     (char)('0' + (BUFFERPOOL/10000000) % 10),
73 #endif
74     (char)('0' + (BUFFERPOOL/1000000) % 10),
75 #endif
76 #ifdef  CHROOTDIR
77     'c',
78 #endif
79 #ifdef  CMDLINE_CONFIG
80     'C',
81 #endif
82 #ifdef  DO_ID
83     'd',
84 #endif
85 #ifdef  DEBUGMODE
86     'D',
87 #endif
88 #ifdef  LOCOP_REHASH
89     'e',
90 #endif
91 #ifdef  OPER_REHASH
92     'E',
93 #endif
94 #ifdef OPER_NO_CHAN_LIMIT
95     'F',
96 #endif
97 #ifdef OPER_MODE_LCHAN
98     'f',
99 #endif
100 #ifdef  HUB
101     'H',
102 #endif
103 #if defined(SHOW_INVISIBLE_USERS) ||  defined(SHOW_ALL_INVISIBLE_USERS)
104 #ifdef  SHOW_ALL_INVISIBLE_USERS
105     'I',
106 #else
107     'i',
108 #endif
109 #endif
110 #ifdef  OPER_KILL
111 #ifdef  LOCAL_KILL_ONLY
112     'k',
113 #else
114     'K',
115 #endif
116 #endif
117 #ifdef  LEAST_IDLE
118     'L',
119 #endif
120 #ifdef OPER_WALK_THROUGH_LMODES
121     'l',
122 #endif
123 #ifdef  IDLE_FROM_MSG
124     'M',
125 #endif
126 #ifdef  USEONE
127     'O',
128 #endif
129 #ifdef NO_OPER_DEOP_LCHAN
130     'o',
131 #endif
132 #ifdef  CRYPT_OPER_PASSWORD
133     'p',
134 #endif
135 #ifdef  CRYPT_LINK_PASSWORD
136     'P',
137 #endif
138 #ifdef  DEBUGMALLOC
139 #ifdef  MEMLEAKSTATS
140     'Q',
141 #else
142     'q',
143 #endif
144 #endif
145 #ifdef  RELIABLE_CLOCK
146     'R',
147 #endif
148 #ifdef  LOCOP_RESTART
149     's',
150 #endif
151 #ifdef  OPER_RESTART
152     'S',
153 #endif
154 #ifdef  OPER_REMOTE
155     't',
156 #endif
157 #if defined(USE_POLL) && defined(HAVE_POLL_H)
158     'U',
159 #endif
160 #ifdef  VIRTUAL_HOST
161     'v',
162 #endif
163 #ifdef BADCHAN
164     'W',
165 #ifdef LOCAL_BADCHAN
166     'x',
167 #endif
168 #endif
169 #ifdef  USE_SYSLOG
170     'Y',
171 #endif
172     '\0'
173 };
174
175 /* *INDENT-ON* */
176
177
178 /*
179  * open_debugfile
180  *
181  * If the -t option is not given on the command line when the server is
182  * started, all debugging output is sent to the file set by LPATH in config.h
183  * Here we just open that file and make sure it is opened to fd 2 so that
184  * any fprintf's to stderr also goto the logfile.  If the debuglevel is not
185  * set from the command line by -x, use /dev/null as the dummy logfile as long
186  * as DEBUGMODE has been defined, else dont waste the fd.
187  */
188 void open_debugfile(void)
189 {
190 #ifdef  DEBUGMODE
191   if (debuglevel >= 0) {
192     printf("isatty = %d ttyname = %#x\n", isatty(2), (unsigned int)ttyname(2));
193     if (!(bootopt & BOOT_TTY)) {
194       int fd;
195       /* 
196        * leave debugging output on fd 2
197        */
198       if ((fd = open(LOGFILE, O_CREAT | O_WRONLY | O_APPEND, 0600)) < 0) {
199         if ((fd = open("/dev/null", O_WRONLY)) < 0)
200           exit(-1);
201       }
202       if (fd != 2) {
203         dup2(fd, 2);
204         close(fd);
205       }
206     }
207   }
208 #endif
209 }
210
211 #ifdef DEBUGMODE
212 static char debugbuf[1024];
213
214 void vdebug(int level, const char *form, va_list vl)
215 {
216   int err = errno;
217
218   if ((debuglevel >= 0) && (level <= debuglevel))
219   {
220     vsprintf(debugbuf, form, vl);
221     fprintf(stderr, "%s\n", debugbuf);
222   }
223   errno = err;
224 }
225
226 void debug(int level, const char *form, ...)
227 {
228   va_list vl;
229   va_start(vl, form);
230   vdebug(level, form, vl);
231   va_end(vl);
232 }
233
234 static void debug_enumerator(struct Client* cptr, const char* msg)
235 {
236   assert(0 != cptr);
237   sendto_one(cptr, ":%s %d %s :%s", me.name, RPL_STATSDEBUG, cptr->name, msg);
238 }
239
240 /*
241  * This is part of the STATS replies. There is no offical numeric for this
242  * since this isnt an official command, in much the same way as HASH isnt.
243  * It is also possible that some systems wont support this call or have
244  * different field names for "struct rusage".
245  * -avalon
246  */
247 void send_usage(struct Client *cptr, char *nick)
248 {
249   os_get_rusage(cptr, CurrentTime - me.since, debug_enumerator);
250
251   sendto_one(cptr, ":%s %d %s :DBUF alloc %d used %d",
252       me.name, RPL_STATSDEBUG, nick, DBufAllocCount, DBufUsedCount);
253 }
254 #endif /* DEBUGMODE */
255
256 void count_memory(struct Client *cptr, char *nick)
257 {
258   struct Client *acptr;
259   struct SLink *link;
260   struct Channel *chptr;
261   struct ConfItem *aconf;
262   struct ConfClass *cltmp;
263   struct Membership* member;
264
265   int lc = 0,                   /* local clients */
266       ch = 0,                   /* channels */
267       lcc = 0,                  /* local client conf links */
268       rc = 0,                   /* remote clients */
269       us = 0,                   /* user structs */
270       chi = 0,                  /* channel invites */
271       chb = 0,                  /* channel bans */
272       wwu = 0,                  /* whowas users */
273       cl = 0,                   /* classes */
274       co = 0,                   /* conf lines */
275       memberships = 0;          /* channel memberships */
276
277   int usi = 0,                  /* users invited */
278       aw = 0,                   /* aways set */
279       wwa = 0;                  /* whowas aways */
280
281   size_t chm = 0,               /* memory used by channels */
282       chbm = 0,                 /* memory used by channel bans */
283       lcm = 0,                  /* memory used by local clients */
284       rcm = 0,                  /* memory used by remote clients */
285       awm = 0,                  /* memory used by aways */
286       wwam = 0,                 /* whowas away memory used */
287       wwm = 0,                  /* whowas array memory used */
288       com = 0,                  /* memory used by conf lines */
289       dbufs_allocated = 0,      /* memory used by dbufs */
290       dbufs_used = 0,           /* memory used by dbufs */
291       rm = 0,                   /* res memory used */
292       totcl = 0, totch = 0, totww = 0, tot = 0;
293
294   count_whowas_memory(&wwu, &wwm, &wwa, &wwam);
295   wwm += sizeof(struct Whowas) * NICKNAMEHISTORYLENGTH;
296   wwm += sizeof(struct Whowas *) * WW_MAX;
297
298   for (acptr = GlobalClientList; acptr; acptr = acptr->next)
299   {
300     if (MyConnect(acptr))
301     {
302       lc++;
303       for (link = acptr->confs; link; link = link->next)
304         lcc++;
305     }
306     else
307       rc++;
308     if (acptr->user)
309     {
310       us++;
311       for (link = acptr->user->invited; link; link = link->next)
312         usi++;
313       for (member = acptr->user->channel; member; member = member->next_channel)
314         ++memberships;
315       if (acptr->user->away)
316       {
317         aw++;
318         awm += (strlen(acptr->user->away) + 1);
319       }
320     }
321   }
322   lcm = lc * CLIENT_LOCAL_SIZE;
323   rcm = rc * CLIENT_REMOTE_SIZE;
324
325   for (chptr = GlobalChannelList; chptr; chptr = chptr->next)
326   {
327     ch++;
328     chm += (strlen(chptr->chname) + sizeof(struct Channel));
329 #if 0
330     /*
331      * XXX - Members already counted in clients, don't count twice
332      */
333     for (member = chptr->members; member; member = member->next_member)
334       chu++;
335 #endif
336     for (link = chptr->invites; link; link = link->next)
337       chi++;
338     for (link = chptr->banlist; link; link = link->next)
339     {
340       chb++;
341       chbm += (strlen(link->value.cp) + 1 + sizeof(struct SLink));
342     }
343   }
344
345   for (aconf = GlobalConfList; aconf; aconf = aconf->next)
346   {
347     co++;
348     com += aconf->host ? strlen(aconf->host) + 1 : 0;
349     com += aconf->passwd ? strlen(aconf->passwd) + 1 : 0;
350     com += aconf->name ? strlen(aconf->name) + 1 : 0;
351     com += sizeof(struct ConfItem);
352   }
353
354   for (cltmp = classes; cltmp; cltmp = cltmp->next)
355     cl++;
356
357   sendto_one(cptr, ":%s %d %s :Client Local %d(" SIZE_T_FMT
358       ") Remote %d(" SIZE_T_FMT ")",
359       me.name, RPL_STATSDEBUG, nick, lc, lcm, rc, rcm);
360   sendto_one(cptr, ":%s %d %s :Users %d(" SIZE_T_FMT
361       ") Invites %d(" SIZE_T_FMT ")",
362       me.name, RPL_STATSDEBUG, nick, us, us * sizeof(struct User), usi,
363       usi * sizeof(struct SLink));
364   sendto_one(cptr, 
365              ":%s %d %s :User channels %d(" SIZE_T_FMT ") Aways %d(" SIZE_T_FMT ")",
366              me.name, RPL_STATSDEBUG, nick, memberships,
367              memberships * sizeof(struct Membership), aw, awm);
368   sendto_one(cptr, ":%s %d %s :Attached confs %d(" SIZE_T_FMT ")",
369       me.name, RPL_STATSDEBUG, nick, lcc, lcc * sizeof(struct SLink));
370
371   totcl = lcm + rcm + us * sizeof(struct User) + memberships * sizeof(struct Membership) + awm;
372   totcl += lcc * sizeof(struct SLink) + usi * sizeof(struct SLink);
373
374   sendto_one(cptr, ":%s %d %s :Conflines %d(" SIZE_T_FMT ")",
375       me.name, RPL_STATSDEBUG, nick, co, com);
376
377   sendto_one(cptr, ":%s %d %s :Classes %d(" SIZE_T_FMT ")",
378       me.name, RPL_STATSDEBUG, nick, cl, cl * sizeof(struct ConfClass));
379
380   sendto_one(cptr, ":%s %d %s :Channels %d(" SIZE_T_FMT
381       ") Bans %d(" SIZE_T_FMT ")",
382       me.name, RPL_STATSDEBUG, nick, ch, chm, chb, chbm);
383   sendto_one(cptr, ":%s %d %s :Channel membrs %d(" SIZE_T_FMT ") invite %d(" SIZE_T_FMT ")",
384       me.name, RPL_STATSDEBUG, nick, memberships, memberships * sizeof(struct Membership),
385       chi, chi * sizeof(struct SLink));
386
387   totch = chm + chbm + chi * sizeof(struct SLink);
388
389   sendto_one(cptr, ":%s %d %s :Whowas users %d(" SIZE_T_FMT
390       ") away %d(" SIZE_T_FMT ")",
391       me.name, RPL_STATSDEBUG, nick, wwu, wwu * sizeof(struct User), wwa, wwam);
392   sendto_one(cptr, ":%s %d %s :Whowas array %d(" SIZE_T_FMT ")",
393       me.name, RPL_STATSDEBUG, nick, NICKNAMEHISTORYLENGTH, wwm);
394
395   totww = wwu * sizeof(struct User) + wwam + wwm;
396
397   sendto_one(cptr, ":%s %d %s :Hash: client %d(" SIZE_T_FMT
398       "), chan is the same",
399       me.name, RPL_STATSDEBUG, nick, HASHSIZE, sizeof(void *) * HASHSIZE);
400
401   /*
402    * NOTE: this count will be accurate only for the exact instant that this
403    * message is being sent, so the count is affected by the dbufs that
404    * are being used to send this message out. If this is not desired, move
405    * the dbuf_count_memory call to a place before we start sending messages
406    * and cache DBufAllocCount and DBufUsedCount in variables until they 
407    * are sent.
408    */
409   dbuf_count_memory(&dbufs_allocated, &dbufs_used);
410   sendto_one(cptr,
411       ":%s %d %s :DBufs allocated %d(" SIZE_T_FMT ") used %d(" SIZE_T_FMT ")",
412       me.name, RPL_STATSDEBUG, nick, DBufAllocCount, dbufs_allocated,
413       DBufUsedCount, dbufs_used);
414
415   rm = cres_mem(cptr);
416
417   tot =
418       totww + totch + totcl + com + cl * sizeof(struct ConfClass) + dbufs_allocated +
419       rm;
420   tot += sizeof(void *) * HASHSIZE * 3;
421
422 #if !defined(NDEBUG)
423   sendto_one(cptr, ":%s %d %s :Allocations: " SIZE_T_FMT "(" SIZE_T_FMT ")",
424              me.name, RPL_STATSDEBUG, nick, fda_get_block_count(),
425              fda_get_byte_count());
426 #endif
427
428   sendto_one(cptr, ":%s %d %s :Total: ww " SIZE_T_FMT " ch " SIZE_T_FMT
429       " cl " SIZE_T_FMT " co " SIZE_T_FMT " db " SIZE_T_FMT,
430       me.name, RPL_STATSDEBUG, nick, totww, totch, totcl, com, dbufs_allocated);
431 }
432