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