Lets hope that I did this right :)
[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
21 #include "sys.h"
22 #if HAVE_SYS_FILE_H
23 #include <sys/file.h>
24 #endif
25 #if defined(HPUX) && HAVE_FCNTL_H
26 #include <fcntl.h>
27 #endif
28 #ifdef HPUX
29 #include <sys/syscall.h>
30 #define getrusage(a,b) syscall(SYS_GETRUSAGE, a, b)
31 #endif
32 #if HAVE_GETRUSAGE
33 #include <sys/resource.h>
34 #else
35 #if HAVE_TIMES
36 #include <sys/times.h>
37 #endif
38 #endif
39 #if HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42 #include <stdarg.h>
43 #include "h.h"
44 #include "struct.h"
45 #include "numeric.h"
46 #include "hash.h"
47 #include "send.h"
48 #include "s_conf.h"
49 #include "class.h"
50 #include "ircd.h"
51 #include "s_bsd.h"
52 #include "bsd.h"
53 #include "whowas.h"
54 #include "s_serv.h"
55 #include "res.h"
56 #include "channel.h"
57 #include "numnicks.h"
58
59 RCSTAG_CC("$Id$");
60
61 /* *INDENT-OFF* */
62
63 /*
64  * Option string.  Must be before #ifdef DEBUGMODE.
65  */
66 char serveropts[] = {
67 #if BUFFERPOOL < 1000000
68     'b',
69 #if BUFFERPOOL > 99999
70     (char)('0' + (BUFFERPOOL/100000)),
71 #endif
72 #if BUFFERPOOL > 9999
73     (char)('0' + (BUFFERPOOL/10000) % 10),
74 #endif
75     (char)('0' + (BUFFERPOOL/1000) % 10),
76 #else
77     'B',
78 #if BUFFERPOOL > 99999999
79     (char)('0' + (BUFFERPOOL/100000000)),
80 #endif
81 #if BUFFERPOOL > 9999999
82     (char)('0' + (BUFFERPOOL/10000000) % 10),
83 #endif
84     (char)('0' + (BUFFERPOOL/1000000) % 10),
85 #endif
86 #ifdef  CHROOTDIR
87     'c',
88 #endif
89 #ifdef  CMDLINE_CONFIG
90     'C',
91 #endif
92 #ifdef  DO_ID
93     'd',
94 #endif
95 #ifdef  DEBUGMODE
96     'D',
97 #endif
98 #ifdef  LOCOP_REHASH
99     'e',
100 #endif
101 #ifdef  OPER_REHASH
102     'E',
103 #endif
104 #ifdef  HUB
105     'H',
106 #endif
107 #if defined(SHOW_INVISIBLE_USERS) ||  defined(SHOW_ALL_INVISIBLE_USERS)
108 #ifdef  SHOW_ALL_INVISIBLE_USERS
109     'I',
110 #else
111     'i',
112 #endif
113 #endif
114 #ifdef  OPER_KILL
115 #ifdef  LOCAL_KILL_ONLY
116     'k',
117 #else
118     'K',
119 #endif
120 #endif
121 #ifdef  LEAST_IDLE
122     'L',
123 #endif
124 #ifdef  IDLE_FROM_MSG
125     'M',
126 #endif
127 #ifdef  USEONE
128     'O',
129 #endif
130 #ifdef  CRYPT_OPER_PASSWORD
131     'p',
132 #endif
133 #ifdef  CRYPT_LINK_PASSWORD
134     'P',
135 #endif
136 #ifdef  DEBUGMALLOC
137 #ifdef  MEMLEAKSTATS
138     'Q',
139 #else
140     'q',
141 #endif
142 #endif
143 #ifdef  RELIABLE_CLOCK
144     'R',
145 #endif
146 #ifdef  LOCOP_RESTART
147     's',
148 #endif
149 #ifdef  OPER_RESTART
150     'S',
151 #endif
152 #ifdef  OPER_REMOTE
153     't',
154 #endif
155 #if defined(USE_POLL) && defined(HAVE_POLL_H)
156     'U',
157 #endif
158 #ifdef  VIRTUAL_HOST
159     'v',
160 #endif
161 #ifdef WT_BADCHAN
162    'W',
163 #ifdef WT_LOCAL_BADCHAN
164    't',
165 #endif
166 #endif
167 #ifdef  UNIXPORT
168     'X',
169 #endif
170 #ifdef  USE_SYSLOG
171     'Y',
172 #endif
173     '\0'
174 };
175
176 /* *INDENT-ON* */
177
178 #ifdef DEBUGMODE
179 static char debugbuf[1024];
180
181 void vdebug(int level, const char *form, va_list vl)
182 {
183   int err = errno;
184
185   if ((debuglevel >= 0) && (level <= debuglevel))
186   {
187     vsprintf(debugbuf, form, vl);
188     if (loc_clients[2])
189     {
190       loc_clients[2]->sendM++;
191       loc_clients[2]->sendB += strlen(debugbuf);
192     }
193     fprintf(stderr, "%s", debugbuf);
194     fputc('\n', stderr);
195   }
196   errno = err;
197 }
198
199 void debug(int level, const char *form, ...)
200 {
201   va_list vl;
202   va_start(vl, form);
203   vdebug(level, form, vl);
204   va_end(vl);
205 }
206
207 /*
208  * This is part of the STATS replies. There is no offical numeric for this
209  * since this isnt an official command, in much the same way as HASH isnt.
210  * It is also possible that some systems wont support this call or have
211  * different field names for "struct rusage".
212  * -avalon
213  */
214 void send_usage(aClient *cptr, char *nick)
215 {
216
217 #if HAVE_GETRUSAGE
218   struct rusage rus;
219   time_t secs, rup;
220 #ifdef  hz
221 #define hzz hz
222 #else
223 #ifdef HZ
224 #define hzz HZ
225 #else
226   int hzz = 1;
227 #ifdef HPUX
228   hzz = (int)sysconf(_SC_CLK_TCK);
229 #endif
230 #endif
231 #endif
232
233   if (getrusage(RUSAGE_SELF, &rus) == -1)
234   {
235     if (MyUser(cptr) || Protocol(cptr->from) < 10)
236       sendto_one(cptr, ":%s NOTICE %s :Getruseage error: %s.",
237           me.name, nick, sys_errlist[errno]);
238     else
239       sendto_one(cptr, "%s NOTICE %s%s :Getruseage error: %s.",
240           NumServ(&me), NumNick(cptr), sys_errlist[errno]);
241     return;
242   }
243   secs = rus.ru_utime.tv_sec + rus.ru_stime.tv_sec;
244   rup = now - me.since;
245   if (secs == 0)
246     secs = 1;
247
248 #if defined(__sun__) || defined(__bsdi__) || (__GLIBC__ >= 2) || defined(__NetBSD__)
249   sendto_one(cptr, ":%s %d %s :CPU Secs %ld:%ld User %ld:%ld System %ld:%ld",
250 #else
251   sendto_one(cptr, ":%s %d %s :CPU Secs %ld:%ld User %d:%d System %d:%d",
252 #endif
253       me.name, RPL_STATSDEBUG, nick, secs / 60, secs % 60,
254       rus.ru_utime.tv_sec / 60, rus.ru_utime.tv_sec % 60,
255       rus.ru_stime.tv_sec / 60, rus.ru_stime.tv_sec % 60);
256   sendto_one(cptr, ":%s %d %s :RSS %ld ShMem %ld Data %ld Stack %ld",
257       me.name, RPL_STATSDEBUG, nick, rus.ru_maxrss,
258       rus.ru_ixrss / (rup * hzz), rus.ru_idrss / (rup * hzz),
259       rus.ru_isrss / (rup * hzz));
260   sendto_one(cptr, ":%s %d %s :Swaps %ld Reclaims %ld Faults %ld",
261       me.name, RPL_STATSDEBUG, nick, rus.ru_nswap,
262       rus.ru_minflt, rus.ru_majflt);
263   sendto_one(cptr, ":%s %d %s :Block in %ld out %ld",
264       me.name, RPL_STATSDEBUG, nick, rus.ru_inblock, rus.ru_oublock);
265   sendto_one(cptr, ":%s %d %s :Msg Rcv %ld Send %ld",
266       me.name, RPL_STATSDEBUG, nick, rus.ru_msgrcv, rus.ru_msgsnd);
267   sendto_one(cptr, ":%s %d %s :Signals %ld Context Vol. %ld Invol %ld",
268       me.name, RPL_STATSDEBUG, nick, rus.ru_nsignals,
269       rus.ru_nvcsw, rus.ru_nivcsw);
270 #else /* HAVE_GETRUSAGE */
271 #if HAVE_TIMES
272   struct tms tmsbuf;
273   time_t secs, mins;
274   int hzz = 1, ticpermin;
275   int umin, smin, usec, ssec;
276
277 #ifdef HPUX
278   hzz = sysconf(_SC_CLK_TCK);
279 #endif
280   ticpermin = hzz * 60;
281
282   umin = tmsbuf.tms_utime / ticpermin;
283   usec = (tmsbuf.tms_utime % ticpermin) / (float)hzz;
284   smin = tmsbuf.tms_stime / ticpermin;
285   ssec = (tmsbuf.tms_stime % ticpermin) / (float)hzz;
286   secs = usec + ssec;
287   mins = (secs / 60) + umin + smin;
288   secs %= hzz;
289
290   if (times(&tmsbuf) == -1)
291   {
292     sendto_one(cptr, ":%s %d %s :times(2) error: %s.",
293         me.name, RPL_STATSDEBUG, nick, strerror(errno));
294     return;
295   }
296   secs = tmsbuf.tms_utime + tmsbuf.tms_stime;
297
298   sendto_one(cptr, ":%s %d %s :CPU Secs %d:%d User %d:%d System %d:%d",
299       me.name, RPL_STATSDEBUG, nick, mins, secs, umin, usec, smin, ssec);
300 #endif /* HAVE_TIMES */
301 #endif /* HAVE_GETRUSAGE */
302   sendto_one(cptr, ":%s %d %s :Reads %d Writes %d",
303       me.name, RPL_STATSDEBUG, nick, readcalls, writecalls);
304   sendto_one(cptr, ":%s %d %s :DBUF alloc %d used %d",
305       me.name, RPL_STATSDEBUG, nick, DBufAllocCount, DBufUsedCount);
306   sendto_one(cptr,
307       ":%s %d %s :Writes:  <0 %d 0 %d <16 %d <32 %d <64 %d",
308       me.name, RPL_STATSDEBUG, nick,
309       writeb[0], writeb[1], writeb[2], writeb[3], writeb[4]);
310   sendto_one(cptr,
311       ":%s %d %s :<128 %d <256 %d <512 %d <1024 %d >1024 %d",
312       me.name, RPL_STATSDEBUG, nick,
313       writeb[5], writeb[6], writeb[7], writeb[8], writeb[9]);
314   return;
315 }
316 #endif /* DEBUGMODE */
317
318 void count_memory(aClient *cptr, char *nick)
319 {
320   Reg1 aClient *acptr;
321   Reg2 Link *link;
322   Reg3 aChannel *chptr;
323   Reg4 aConfItem *aconf;
324   Reg5 aConfClass *cltmp;
325
326   int lc = 0,                   /* local clients */
327       ch = 0,                   /* channels */
328       lcc = 0,                  /* local client conf links */
329       rc = 0,                   /* remote clients */
330       us = 0,                   /* user structs */
331       chu = 0,                  /* channel users */
332       chi = 0,                  /* channel invites */
333       chb = 0,                  /* channel bans */
334       wwu = 0,                  /* whowas users */
335       cl = 0,                   /* classes */
336       co = 0;                   /* conf lines */
337
338   int usi = 0,                  /* users invited */
339       usc = 0,                  /* users in channels */
340       aw = 0,                   /* aways set */
341       wwa = 0;                  /* whowas aways */
342
343   size_t chm = 0,               /* memory used by channels */
344       chbm = 0,                 /* memory used by channel bans */
345       lcm = 0,                  /* memory used by local clients */
346       rcm = 0,                  /* memory used by remote clients */
347       awm = 0,                  /* memory used by aways */
348       wwam = 0,                 /* whowas away memory used */
349       wwm = 0,                  /* whowas array memory used */
350       com = 0,                  /* memory used by conf lines */
351       dbufs_allocated = 0,      /* memory used by dbufs */
352       dbufs_used = 0,           /* memory used by dbufs */
353       rm = 0,                   /* res memory used */
354       totcl = 0, totch = 0, totww = 0, tot = 0;
355
356   count_whowas_memory(&wwu, &wwm, &wwa, &wwam);
357   wwm += sizeof(aWhowas) * NICKNAMEHISTORYLENGTH;
358   wwm += sizeof(aWhowas *) * WW_MAX;
359
360   for (acptr = client; acptr; acptr = acptr->next)
361   {
362     if (IsPing(acptr))
363       continue;
364     if (MyConnect(acptr))
365     {
366       lc++;
367       for (link = acptr->confs; link; link = link->next)
368         lcc++;
369     }
370     else
371       rc++;
372     if (acptr->user)
373     {
374       us++;
375       for (link = acptr->user->invited; link; link = link->next)
376         usi++;
377       for (link = acptr->user->channel; link; link = link->next)
378         usc++;
379       if (acptr->user->away)
380       {
381         aw++;
382         awm += (strlen(acptr->user->away) + 1);
383       }
384     }
385   }
386   lcm = lc * CLIENT_LOCAL_SIZE;
387   rcm = rc * CLIENT_REMOTE_SIZE;
388
389   for (chptr = channel; chptr; chptr = chptr->nextch)
390   {
391     ch++;
392     chm += (strlen(chptr->chname) + sizeof(aChannel));
393     for (link = chptr->members; link; link = link->next)
394       chu++;
395     for (link = chptr->invites; link; link = link->next)
396       chi++;
397     for (link = chptr->banlist; link; link = link->next)
398     {
399       chb++;
400       chbm += (strlen(link->value.cp) + 1 + sizeof(Link));
401     }
402   }
403
404   for (aconf = conf; aconf; aconf = aconf->next)
405   {
406     co++;
407     com += aconf->host ? strlen(aconf->host) + 1 : 0;
408     com += aconf->passwd ? strlen(aconf->passwd) + 1 : 0;
409     com += aconf->name ? strlen(aconf->name) + 1 : 0;
410     com += sizeof(aConfItem);
411   }
412
413   for (cltmp = classes; cltmp; cltmp = cltmp->next)
414     cl++;
415
416   sendto_one(cptr, ":%s %d %s :Client Local %d(" SIZE_T_FMT
417       ") Remote %d(" SIZE_T_FMT ")",
418       me.name, RPL_STATSDEBUG, nick, lc, lcm, rc, rcm);
419   sendto_one(cptr, ":%s %d %s :Users %d(" SIZE_T_FMT
420       ") Invites %d(" SIZE_T_FMT ")",
421       me.name, RPL_STATSDEBUG, nick, us, us * sizeof(anUser), usi,
422       usi * sizeof(Link));
423   sendto_one(cptr, ":%s %d %s :User channels %d(" SIZE_T_FMT
424       ") Aways %d(" SIZE_T_FMT ")",
425       me.name, RPL_STATSDEBUG, nick, usc, usc * sizeof(Link), aw, awm);
426   sendto_one(cptr, ":%s %d %s :Attached confs %d(" SIZE_T_FMT ")",
427       me.name, RPL_STATSDEBUG, nick, lcc, lcc * sizeof(Link));
428
429   totcl = lcm + rcm + us * sizeof(anUser) + usc * sizeof(Link) + awm;
430   totcl += lcc * sizeof(Link) + usi * sizeof(Link);
431
432   sendto_one(cptr, ":%s %d %s :Conflines %d(" SIZE_T_FMT ")",
433       me.name, RPL_STATSDEBUG, nick, co, com);
434
435   sendto_one(cptr, ":%s %d %s :Classes %d(" SIZE_T_FMT ")",
436       me.name, RPL_STATSDEBUG, nick, cl, cl * sizeof(aConfClass));
437
438   sendto_one(cptr, ":%s %d %s :Channels %d(" SIZE_T_FMT
439       ") Bans %d(" SIZE_T_FMT ")",
440       me.name, RPL_STATSDEBUG, nick, ch, chm, chb, chbm);
441   sendto_one(cptr, ":%s %d %s :Channel membrs %d(" SIZE_T_FMT
442       ") invite %d(" SIZE_T_FMT ")",
443       me.name, RPL_STATSDEBUG, nick, chu, chu * sizeof(Link),
444       chi, chi * sizeof(Link));
445
446   totch = chm + chbm + chu * sizeof(Link) + chi * sizeof(Link);
447
448   sendto_one(cptr, ":%s %d %s :Whowas users %d(" SIZE_T_FMT
449       ") away %d(" SIZE_T_FMT ")",
450       me.name, RPL_STATSDEBUG, nick, wwu, wwu * sizeof(anUser), wwa, wwam);
451   sendto_one(cptr, ":%s %d %s :Whowas array %d(" SIZE_T_FMT ")",
452       me.name, RPL_STATSDEBUG, nick, NICKNAMEHISTORYLENGTH, wwm);
453
454   totww = wwu * sizeof(anUser) + wwam + wwm;
455
456   sendto_one(cptr, ":%s %d %s :Hash: client %d(" SIZE_T_FMT
457       "), chan is the same",
458       me.name, RPL_STATSDEBUG, nick, HASHSIZE, sizeof(void *) * HASHSIZE);
459
460   /*
461    * NOTE: this count will be accurate only for the exact instant that this
462    * message is being sent, so the count is affected by the dbufs that
463    * are being used to send this message out. If this is not desired, move
464    * the dbuf_count_memory call to a place before we start sending messages
465    * and cache DBufAllocCount and DBufUsedCount in variables until they 
466    * are sent.
467    */
468   dbuf_count_memory(&dbufs_allocated, &dbufs_used);
469   sendto_one(cptr,
470       ":%s %d %s :DBufs allocated %d(" SIZE_T_FMT ") used %d(" SIZE_T_FMT ")",
471       me.name, RPL_STATSDEBUG, nick, DBufAllocCount, dbufs_allocated,
472       DBufUsedCount, dbufs_used);
473
474   rm = cres_mem(cptr);
475
476   tot =
477       totww + totch + totcl + com + cl * sizeof(aConfClass) + dbufs_allocated +
478       rm;
479   tot += sizeof(void *) * HASHSIZE * 3;
480
481   sendto_one(cptr, ":%s %d %s :Total: ww " SIZE_T_FMT " ch " SIZE_T_FMT
482       " cl " SIZE_T_FMT " co " SIZE_T_FMT " db " SIZE_T_FMT,
483       me.name, RPL_STATSDEBUG, nick, totww, totch, totcl, com, dbufs_allocated);
484   return;
485 }
486
487 #ifdef MSGLOG_ENABLED
488
489 /* Define here what level of messages you want to log */
490 #define LOG_MASK_LEVEL LEVEL_MODE       /* This that change some data */
491
492 static struct log_entry log_table[MSGLOG_SIZE];
493
494 static int unused_log_entries = MSGLOG_SIZE;
495 static int last_log_entry = -1; /* Nothing stored yet */
496 static int entry_stored_forlog = 0;     /* Just a flag */
497
498 /*
499  * RollBackMsgLog
500  *
501  * Just a little utility function used to retract
502  * an half stored Message log entry
503  */
504 void RollBackMsgLog(void)
505 {
506   /* We won't log this, abort and free the entry */
507   last_log_entry--;
508   unused_log_entries++;
509   return;
510 }
511
512 /*
513  * Log_Message (macroed as LogMessage)
514  *
515  * Permanently stores a log entry into the recent log memory area
516  * Store_Buffer MUST have been called before calling Log_Message
517  */
518 void Log_Message(aClient *sptr, int msgclass)
519 {
520   register int n = last_log_entry;
521
522   /* Clear our flag, since we are going to
523    * finish the processing of this entry */
524   entry_stored_forlog = 0;
525
526   /* Check  if the level of this message is high enough */
527   if (msgclass < LOG_MASK_LEVEL)
528   {
529     RollBackMsgLog();
530     return;
531   }
532
533   /* Check if we wanna log the type of connection from
534    * where this message did come from */
535   if (!((0x8000 >> (8 + log_table[n].cptr_status)) & LOG_MASK_TYPE))
536   {
537     RollBackMsgLog();
538     return;
539   }
540
541   /* Complete the entry */
542   if (sptr)
543   {
544     log_table[n].sptr_status = sptr->status;
545     strncpy(log_table[n].sptr_name, sptr->name, HOSTLEN);
546     log_table[n].sptr_name[HOSTLEN] = '\0';
547     strncpy(log_table[n].sptr_yxx, sptr->yxx, 4);
548     log_table[n].sptr = sptr;
549
550     if (sptr->from)
551     {
552       strncpy(log_table[n].sptr_from_name, sptr->name, HOSTLEN);
553       log_table[n].sptr_from_name[HOSTLEN] = '\0';
554     }
555     else
556     {
557       memset(log_table[n].sptr_from_name, 0, HOSTLEN);
558     }
559   }
560   else
561   {
562     log_table[n].sptr_status = 0xFF;    /* Dummy value */
563     memset(&log_table[n].sptr_name, 0, HOSTLEN);
564     memset(log_table[n].sptr_yxx, 0, 4);
565
566     log_table[n].sptr = 0;
567     memset(&log_table[n].sptr_from_name, 0, HOSTLEN);
568   }
569 }
570
571 /*
572  * Store_Buffer (macroed as StoreBuffer)
573  *
574  * Saves the buffer and cptr info at the very first stage
575  * of parsing, if Log_Message doesn't get called between
576  * two Store_Buffer calls this function assumes that the parser
577  * has rejected the message and therefore calls Log_Message
578  * as if the message class was 0 and the sptr null
579  */
580 void Store_Buffer(char *buf, aClient *cptr)
581 {
582   register int n;
583
584   /* Check if we have an entry pending, if so
585    * complete it's processing */
586   if (entry_stored_forlog)
587     Log_Message((aClient *)NULL, 0);
588
589   /* Update the "half used entry" flag */
590   entry_stored_forlog = 1;
591
592   /* First update the free entries counter */
593   if (unused_log_entries)
594     unused_log_entries--;
595
596   /* Get an entry */
597   n = (last_log_entry + 1) % MSGLOG_SIZE;
598
599   /* Update the last_log_entry index */
600   last_log_entry = n;
601
602   /* Store what we have by now in it */
603   log_table[n].cptr_status = cptr->status;
604   strncpy(log_table[n].cptr_name, cptr->name, HOSTLEN);
605   log_table[n].cptr_name[HOSTLEN] = '\0';
606   strncpy(log_table[n].cptr_yxx, cptr->yxx, 4);
607   log_table[n].cptr_fd = cptr->fd;
608   log_table[n].cptr = cptr;     /* No checking for this, is lossy */
609   strncpy(log_table[n].buffer, buf, 511);
610 }
611
612 #endif /* MSGLOG_ENABLED */