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