This commit was generated by cvs2svn to compensate for changes in r2,
[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  UNIXPORT
162     'X',
163 #endif
164 #ifdef  USE_SYSLOG
165     'Y',
166 #endif
167     '\0'
168 };
169
170 /* *INDENT-ON* */
171
172 #ifdef DEBUGMODE
173 static char debugbuf[1024];
174
175 void vdebug(int level, const char *form, va_list vl)
176 {
177   int err = errno;
178
179   if ((debuglevel >= 0) && (level <= debuglevel))
180   {
181     vsprintf(debugbuf, form, vl);
182     if (loc_clients[2])
183     {
184       loc_clients[2]->sendM++;
185       loc_clients[2]->sendB += strlen(debugbuf);
186     }
187     fprintf(stderr, "%s", debugbuf);
188     fputc('\n', stderr);
189   }
190   errno = err;
191 }
192
193 void debug(int level, const char *form, ...)
194 {
195   va_list vl;
196   va_start(vl, form);
197   vdebug(level, form, vl);
198   va_end(vl);
199 }
200
201 /*
202  * This is part of the STATS replies. There is no offical numeric for this
203  * since this isnt an official command, in much the same way as HASH isnt.
204  * It is also possible that some systems wont support this call or have
205  * different field names for "struct rusage".
206  * -avalon
207  */
208 void send_usage(aClient *cptr, char *nick)
209 {
210
211 #if HAVE_GETRUSAGE
212   struct rusage rus;
213   time_t secs, rup;
214 #ifdef  hz
215 #define hzz hz
216 #else
217 #ifdef HZ
218 #define hzz HZ
219 #else
220   int hzz = 1;
221 #ifdef HPUX
222   hzz = (int)sysconf(_SC_CLK_TCK);
223 #endif
224 #endif
225 #endif
226
227   if (getrusage(RUSAGE_SELF, &rus) == -1)
228   {
229     if (MyUser(cptr) || Protocol(cptr->from) < 10)
230       sendto_one(cptr, ":%s NOTICE %s :Getruseage error: %s.",
231           me.name, nick, sys_errlist[errno]);
232     else
233       sendto_one(cptr, "%s NOTICE %s%s :Getruseage error: %s.",
234           NumServ(&me), NumNick(cptr), sys_errlist[errno]);
235     return;
236   }
237   secs = rus.ru_utime.tv_sec + rus.ru_stime.tv_sec;
238   rup = now - me.since;
239   if (secs == 0)
240     secs = 1;
241
242 #if defined(__sun__) || defined(__bsdi__) || (__GLIBC__ >= 2) || defined(__NetBSD__)
243   sendto_one(cptr, ":%s %d %s :CPU Secs %ld:%ld User %ld:%ld System %ld:%ld",
244 #else
245   sendto_one(cptr, ":%s %d %s :CPU Secs %ld:%ld User %d:%d System %d:%d",
246 #endif
247       me.name, RPL_STATSDEBUG, nick, secs / 60, secs % 60,
248       rus.ru_utime.tv_sec / 60, rus.ru_utime.tv_sec % 60,
249       rus.ru_stime.tv_sec / 60, rus.ru_stime.tv_sec % 60);
250   sendto_one(cptr, ":%s %d %s :RSS %ld ShMem %ld Data %ld Stack %ld",
251       me.name, RPL_STATSDEBUG, nick, rus.ru_maxrss,
252       rus.ru_ixrss / (rup * hzz), rus.ru_idrss / (rup * hzz),
253       rus.ru_isrss / (rup * hzz));
254   sendto_one(cptr, ":%s %d %s :Swaps %ld Reclaims %ld Faults %ld",
255       me.name, RPL_STATSDEBUG, nick, rus.ru_nswap,
256       rus.ru_minflt, rus.ru_majflt);
257   sendto_one(cptr, ":%s %d %s :Block in %ld out %ld",
258       me.name, RPL_STATSDEBUG, nick, rus.ru_inblock, rus.ru_oublock);
259   sendto_one(cptr, ":%s %d %s :Msg Rcv %ld Send %ld",
260       me.name, RPL_STATSDEBUG, nick, rus.ru_msgrcv, rus.ru_msgsnd);
261   sendto_one(cptr, ":%s %d %s :Signals %ld Context Vol. %ld Invol %ld",
262       me.name, RPL_STATSDEBUG, nick, rus.ru_nsignals,
263       rus.ru_nvcsw, rus.ru_nivcsw);
264 #else /* HAVE_GETRUSAGE */
265 #if HAVE_TIMES
266   struct tms tmsbuf;
267   time_t secs, mins;
268   int hzz = 1, ticpermin;
269   int umin, smin, usec, ssec;
270
271 #ifdef HPUX
272   hzz = sysconf(_SC_CLK_TCK);
273 #endif
274   ticpermin = hzz * 60;
275
276   umin = tmsbuf.tms_utime / ticpermin;
277   usec = (tmsbuf.tms_utime % ticpermin) / (float)hzz;
278   smin = tmsbuf.tms_stime / ticpermin;
279   ssec = (tmsbuf.tms_stime % ticpermin) / (float)hzz;
280   secs = usec + ssec;
281   mins = (secs / 60) + umin + smin;
282   secs %= hzz;
283
284   if (times(&tmsbuf) == -1)
285   {
286     sendto_one(cptr, ":%s %d %s :times(2) error: %s.",
287         me.name, RPL_STATSDEBUG, nick, strerror(errno));
288     return;
289   }
290   secs = tmsbuf.tms_utime + tmsbuf.tms_stime;
291
292   sendto_one(cptr, ":%s %d %s :CPU Secs %d:%d User %d:%d System %d:%d",
293       me.name, RPL_STATSDEBUG, nick, mins, secs, umin, usec, smin, ssec);
294 #endif /* HAVE_TIMES */
295 #endif /* HAVE_GETRUSAGE */
296   sendto_one(cptr, ":%s %d %s :Reads %d Writes %d",
297       me.name, RPL_STATSDEBUG, nick, readcalls, writecalls);
298   sendto_one(cptr, ":%s %d %s :DBUF alloc %d used %d",
299       me.name, RPL_STATSDEBUG, nick, DBufAllocCount, DBufUsedCount);
300   sendto_one(cptr,
301       ":%s %d %s :Writes:  <0 %d 0 %d <16 %d <32 %d <64 %d",
302       me.name, RPL_STATSDEBUG, nick,
303       writeb[0], writeb[1], writeb[2], writeb[3], writeb[4]);
304   sendto_one(cptr,
305       ":%s %d %s :<128 %d <256 %d <512 %d <1024 %d >1024 %d",
306       me.name, RPL_STATSDEBUG, nick,
307       writeb[5], writeb[6], writeb[7], writeb[8], writeb[9]);
308   return;
309 }
310 #endif /* DEBUGMODE */
311
312 void count_memory(aClient *cptr, char *nick)
313 {
314   Reg1 aClient *acptr;
315   Reg2 Link *link;
316   Reg3 aChannel *chptr;
317   Reg4 aConfItem *aconf;
318   Reg5 aConfClass *cltmp;
319
320   int lc = 0,                   /* local clients */
321       ch = 0,                   /* channels */
322       lcc = 0,                  /* local client conf links */
323       rc = 0,                   /* remote clients */
324       us = 0,                   /* user structs */
325       chu = 0,                  /* channel users */
326       chi = 0,                  /* channel invites */
327       chb = 0,                  /* channel bans */
328       wwu = 0,                  /* whowas users */
329       cl = 0,                   /* classes */
330       co = 0;                   /* conf lines */
331
332   int usi = 0,                  /* users invited */
333       usc = 0,                  /* users in channels */
334       aw = 0,                   /* aways set */
335       wwa = 0;                  /* whowas aways */
336
337   size_t chm = 0,               /* memory used by channels */
338       chbm = 0,                 /* memory used by channel bans */
339       lcm = 0,                  /* memory used by local clients */
340       rcm = 0,                  /* memory used by remote clients */
341       awm = 0,                  /* memory used by aways */
342       wwam = 0,                 /* whowas away memory used */
343       wwm = 0,                  /* whowas array memory used */
344       com = 0,                  /* memory used by conf lines */
345       dbufs_allocated = 0,      /* memory used by dbufs */
346       dbufs_used = 0,           /* memory used by dbufs */
347       rm = 0,                   /* res memory used */
348       totcl = 0, totch = 0, totww = 0, tot = 0;
349
350   count_whowas_memory(&wwu, &wwm, &wwa, &wwam);
351   wwm += sizeof(aWhowas) * NICKNAMEHISTORYLENGTH;
352   wwm += sizeof(aWhowas *) * WW_MAX;
353
354   for (acptr = client; acptr; acptr = acptr->next)
355   {
356     if (IsPing(acptr))
357       continue;
358     if (MyConnect(acptr))
359     {
360       lc++;
361       for (link = acptr->confs; link; link = link->next)
362         lcc++;
363     }
364     else
365       rc++;
366     if (acptr->user)
367     {
368       us++;
369       for (link = acptr->user->invited; link; link = link->next)
370         usi++;
371       for (link = acptr->user->channel; link; link = link->next)
372         usc++;
373       if (acptr->user->away)
374       {
375         aw++;
376         awm += (strlen(acptr->user->away) + 1);
377       }
378     }
379   }
380   lcm = lc * CLIENT_LOCAL_SIZE;
381   rcm = rc * CLIENT_REMOTE_SIZE;
382
383   for (chptr = channel; chptr; chptr = chptr->nextch)
384   {
385     ch++;
386     chm += (strlen(chptr->chname) + sizeof(aChannel));
387     for (link = chptr->members; link; link = link->next)
388       chu++;
389     for (link = chptr->invites; link; link = link->next)
390       chi++;
391     for (link = chptr->banlist; link; link = link->next)
392     {
393       chb++;
394       chbm += (strlen(link->value.cp) + 1 + sizeof(Link));
395     }
396   }
397
398   for (aconf = conf; aconf; aconf = aconf->next)
399   {
400     co++;
401     com += aconf->host ? strlen(aconf->host) + 1 : 0;
402     com += aconf->passwd ? strlen(aconf->passwd) + 1 : 0;
403     com += aconf->name ? strlen(aconf->name) + 1 : 0;
404     com += sizeof(aConfItem);
405   }
406
407   for (cltmp = classes; cltmp; cltmp = cltmp->next)
408     cl++;
409
410   sendto_one(cptr, ":%s %d %s :Client Local %d(" SIZE_T_FMT
411       ") Remote %d(" SIZE_T_FMT ")",
412       me.name, RPL_STATSDEBUG, nick, lc, lcm, rc, rcm);
413   sendto_one(cptr, ":%s %d %s :Users %d(" SIZE_T_FMT
414       ") Invites %d(" SIZE_T_FMT ")",
415       me.name, RPL_STATSDEBUG, nick, us, us * sizeof(anUser), usi,
416       usi * sizeof(Link));
417   sendto_one(cptr, ":%s %d %s :User channels %d(" SIZE_T_FMT
418       ") Aways %d(" SIZE_T_FMT ")",
419       me.name, RPL_STATSDEBUG, nick, usc, usc * sizeof(Link), aw, awm);
420   sendto_one(cptr, ":%s %d %s :Attached confs %d(" SIZE_T_FMT ")",
421       me.name, RPL_STATSDEBUG, nick, lcc, lcc * sizeof(Link));
422
423   totcl = lcm + rcm + us * sizeof(anUser) + usc * sizeof(Link) + awm;
424   totcl += lcc * sizeof(Link) + usi * sizeof(Link);
425
426   sendto_one(cptr, ":%s %d %s :Conflines %d(" SIZE_T_FMT ")",
427       me.name, RPL_STATSDEBUG, nick, co, com);
428
429   sendto_one(cptr, ":%s %d %s :Classes %d(" SIZE_T_FMT ")",
430       me.name, RPL_STATSDEBUG, nick, cl, cl * sizeof(aConfClass));
431
432   sendto_one(cptr, ":%s %d %s :Channels %d(" SIZE_T_FMT
433       ") Bans %d(" SIZE_T_FMT ")",
434       me.name, RPL_STATSDEBUG, nick, ch, chm, chb, chbm);
435   sendto_one(cptr, ":%s %d %s :Channel membrs %d(" SIZE_T_FMT
436       ") invite %d(" SIZE_T_FMT ")",
437       me.name, RPL_STATSDEBUG, nick, chu, chu * sizeof(Link),
438       chi, chi * sizeof(Link));
439
440   totch = chm + chbm + chu * sizeof(Link) + chi * sizeof(Link);
441
442   sendto_one(cptr, ":%s %d %s :Whowas users %d(" SIZE_T_FMT
443       ") away %d(" SIZE_T_FMT ")",
444       me.name, RPL_STATSDEBUG, nick, wwu, wwu * sizeof(anUser), wwa, wwam);
445   sendto_one(cptr, ":%s %d %s :Whowas array %d(" SIZE_T_FMT ")",
446       me.name, RPL_STATSDEBUG, nick, NICKNAMEHISTORYLENGTH, wwm);
447
448   totww = wwu * sizeof(anUser) + wwam + wwm;
449
450   sendto_one(cptr, ":%s %d %s :Hash: client %d(" SIZE_T_FMT
451       "), chan is the same",
452       me.name, RPL_STATSDEBUG, nick, HASHSIZE, sizeof(void *) * HASHSIZE);
453
454   /*
455    * NOTE: this count will be accurate only for the exact instant that this
456    * message is being sent, so the count is affected by the dbufs that
457    * are being used to send this message out. If this is not desired, move
458    * the dbuf_count_memory call to a place before we start sending messages
459    * and cache DBufAllocCount and DBufUsedCount in variables until they 
460    * are sent.
461    */
462   dbuf_count_memory(&dbufs_allocated, &dbufs_used);
463   sendto_one(cptr,
464       ":%s %d %s :DBufs allocated %d(" SIZE_T_FMT ") used %d(" SIZE_T_FMT ")",
465       me.name, RPL_STATSDEBUG, nick, DBufAllocCount, dbufs_allocated,
466       DBufUsedCount, dbufs_used);
467
468   rm = cres_mem(cptr);
469
470   tot =
471       totww + totch + totcl + com + cl * sizeof(aConfClass) + dbufs_allocated +
472       rm;
473   tot += sizeof(void *) * HASHSIZE * 3;
474
475   sendto_one(cptr, ":%s %d %s :Total: ww " SIZE_T_FMT " ch " SIZE_T_FMT
476       " cl " SIZE_T_FMT " co " SIZE_T_FMT " db " SIZE_T_FMT,
477       me.name, RPL_STATSDEBUG, nick, totww, totch, totcl, com, dbufs_allocated);
478   return;
479 }
480
481 #ifdef MSGLOG_ENABLED
482
483 /* Define here what level of messages you want to log */
484 #define LOG_MASK_LEVEL LEVEL_MODE       /* This that change some data */
485
486 static struct log_entry log_table[MSGLOG_SIZE];
487
488 static int unused_log_entries = MSGLOG_SIZE;
489 static int last_log_entry = -1; /* Nothing stored yet */
490 static int entry_stored_forlog = 0;     /* Just a flag */
491
492 /*
493  * RollBackMsgLog
494  *
495  * Just a little utility function used to retract
496  * an half stored Message log entry
497  */
498 void RollBackMsgLog(void)
499 {
500   /* We won't log this, abort and free the entry */
501   last_log_entry--;
502   unused_log_entries++;
503   return;
504 }
505
506 /*
507  * Log_Message (macroed as LogMessage)
508  *
509  * Permanently stores a log entry into the recent log memory area
510  * Store_Buffer MUST have been called before calling Log_Message
511  */
512 void Log_Message(aClient *sptr, int msgclass)
513 {
514   register int n = last_log_entry;
515
516   /* Clear our flag, since we are going to
517    * finish the processing of this entry */
518   entry_stored_forlog = 0;
519
520   /* Check  if the level of this message is high enough */
521   if (msgclass < LOG_MASK_LEVEL)
522   {
523     RollBackMsgLog();
524     return;
525   }
526
527   /* Check if we wanna log the type of connection from
528    * where this message did come from */
529   if (!((0x8000 >> (8 + log_table[n].cptr_status)) & LOG_MASK_TYPE))
530   {
531     RollBackMsgLog();
532     return;
533   }
534
535   /* Complete the entry */
536   if (sptr)
537   {
538     log_table[n].sptr_status = sptr->status;
539     strncpy(log_table[n].sptr_name, sptr->name, HOSTLEN);
540     log_table[n].sptr_name[HOSTLEN] = '\0';
541     strncpy(log_table[n].sptr_yxx, sptr->yxx, 4);
542     log_table[n].sptr = sptr;
543
544     if (sptr->from)
545     {
546       strncpy(log_table[n].sptr_from_name, sptr->name, HOSTLEN);
547       log_table[n].sptr_from_name[HOSTLEN] = '\0';
548     }
549     else
550     {
551       memset(log_table[n].sptr_from_name, 0, HOSTLEN);
552     }
553   }
554   else
555   {
556     log_table[n].sptr_status = 0xFF;    /* Dummy value */
557     memset(&log_table[n].sptr_name, 0, HOSTLEN);
558     memset(log_table[n].sptr_yxx, 0, 4);
559
560     log_table[n].sptr = 0;
561     memset(&log_table[n].sptr_from_name, 0, HOSTLEN);
562   }
563 }
564
565 /*
566  * Store_Buffer (macroed as StoreBuffer)
567  *
568  * Saves the buffer and cptr info at the very first stage
569  * of parsing, if Log_Message doesn't get called between
570  * two Store_Buffer calls this function assumes that the parser
571  * has rejected the message and therefore calls Log_Message
572  * as if the message class was 0 and the sptr null
573  */
574 void Store_Buffer(char *buf, aClient *cptr)
575 {
576   register int n;
577
578   /* Check if we have an entry pending, if so
579    * complete it's processing */
580   if (entry_stored_forlog)
581     Log_Message((aClient *)NULL, 0);
582
583   /* Update the "half used entry" flag */
584   entry_stored_forlog = 1;
585
586   /* First update the free entries counter */
587   if (unused_log_entries)
588     unused_log_entries--;
589
590   /* Get an entry */
591   n = (last_log_entry + 1) % MSGLOG_SIZE;
592
593   /* Update the last_log_entry index */
594   last_log_entry = n;
595
596   /* Store what we have by now in it */
597   log_table[n].cptr_status = cptr->status;
598   strncpy(log_table[n].cptr_name, cptr->name, HOSTLEN);
599   log_table[n].cptr_name[HOSTLEN] = '\0';
600   strncpy(log_table[n].cptr_yxx, cptr->yxx, 4);
601   log_table[n].cptr_fd = cptr->fd;
602   log_table[n].cptr = cptr;     /* No checking for this, is lossy */
603   strncpy(log_table[n].buffer, buf, 511);
604 }
605
606 #endif /* MSGLOG_ENABLED */