]>
jfr.im git - irc/quakenet/newserv.git/blob - chanstats/chanstats.c
3 #include "../channel/channel.h"
4 #include "../lib/sstring.h"
5 #include "../lib/splitline.h"
6 #include "../irc/irc.h"
7 #include "../core/schedule.h"
8 #include "../core/error.h"
9 #include "../control/control.h"
10 #include "../nick/nick.h"
11 #include "../lib/irc_string.h"
24 unsigned int lastdaysamples
[HISTORYDAYS
];
25 unsigned int todaysamples
;
27 void doupdate(void *arg
);
28 void updatechanstats(chanindex
*cip
, time_t now
);
29 void rotatechanstats();
32 int dochanstats(void *source
, int argc
, char **argv
);
33 int doexpirecheck(void *source
, int cargc
, char **cargv
);
34 int douserhistogram(void *source
, int cargc
, char **cargv
);
35 int dochanhistogram(void *source
, int cargc
, char **cargv
);
36 void dohist_now(int *data
, float *bounds
, int cats
, nick
*np
);
37 void dohist_today(int *data
, float *bounds
, int cats
);
38 void dohist_days(int *data
, float *bounds
, int cats
, int days
);
39 void dohist_namelen(int *data
, float *bounds
, int cats
);
44 csext
=registerchanext("chanstats");
46 /* PANIC PANIC PANIC PANIC */
47 Error("chanstats",ERR_ERROR
,"Couldn't register channel extension");
51 lastday
=(getnettime()/(24*3600));
54 memset(lastdaysamples
,0,sizeof(lastdaysamples
));
60 /* Work out when to take the next sample */
62 if (now
< lastsample
) {
63 Error("chanstats",ERR_WARNING
,"Last sample time in future (%d > %d)",lastsample
,now
);
65 } else if (now
<(lastsample
+SAMPLEINTERVAL
)) {
66 lastday
=lastsample
/(24*3600);
67 when
=lastsample
+SAMPLEINTERVAL
;
69 if ((lastsample
/(24*3600))<lastday
) {
76 registercontrolcmd("chanstats",10,1,&dochanstats
);
77 registercontrolcmd("channelhistogram",10,13,&dochanhistogram
);
78 registercontrolcmd("userhistogram",10,1,&douserhistogram
);
79 registercontrolcmd("expirecheck",10,1,&doexpirecheck
);
80 schedulerecurring(when
,0,SAMPLEINTERVAL
,&doupdate
,NULL
);
87 deleteschedule(NULL
,&doupdate
,NULL
);
88 deregistercontrolcmd("chanstats",&dochanstats
);
89 deregistercontrolcmd("channelhistogram",&dochanhistogram
);
90 deregistercontrolcmd("userhistogram",&douserhistogram
);
91 deregistercontrolcmd("expirecheck",&doexpirecheck
);
92 releasechanext(csext
);
99 * This is the core function which is scheduled.
102 void doupdate(void *arg
) {
107 /* Get the current time and move the history pointer along one and count the sample */
110 Error("chanstats",ERR_INFO
,"Running chanstats update.");
112 if (now
/(24*3600) > lastday
) {
114 lastday
=(now
/(24*3600));
119 if (sampleindex
==SAMPLEHISTORY
) {
125 * Loop over all chans doing update
128 for(i
=0;i
<CHANNELHASHSIZE
;i
++) {
129 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
130 if ((cip
->channel
!=NULL
) || (cip
->exts
[csext
]!=NULL
)) {
131 updatechanstats(cip
,now
);
137 if (now
-lastsave
> SAVEINTERVAL
) {
143 void updatechanstats(chanindex
*cip
, time_t now
) {
147 csp
=cip
->exts
[csext
];
149 csp
=cip
->exts
[csext
]=getchanstats();
150 memset(csp
,0,sizeof(chanstats
));
153 if (cip
->channel
!=NULL
) {
154 currentusers
=countuniquehosts(cip
->channel
);
160 csp
->lastsamples
[sampleindex
]=currentusers
;
161 csp
->lastsampled
=now
;
162 csp
->todayusers
+=currentusers
;
164 if (currentusers
> csp
->todaymax
) {
165 csp
->todaymax
=currentusers
;
169 void rotatechanstats() {
171 chanindex
*cip
,*ncip
;
174 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
175 for (cip
=chantable
[i
];cip
;cip
=ncip
) {
178 if ((csp
=cip
->exts
[csext
])==NULL
) {
182 if (csp
->todaysamples
==0) {
183 /* No samples today, might want to delete */
185 for(j
=0;j
<HISTORYDAYS
;j
++) {
190 cip
->exts
[csext
]=NULL
;
191 releasechanindex(cip
);
196 /* Move the samples along one */
197 memmove(&(csp
->lastdays
[1]),&(csp
->lastdays
[0]),(HISTORYDAYS
-1)*sizeof(short));
198 memmove(&(csp
->lastdaysamples
[1]),&(csp
->lastdaysamples
[0]),(HISTORYDAYS
-1)*sizeof(char));
199 memmove(&(csp
->lastmax
[1]),&(csp
->lastmax
[0]),(HISTORYDAYS
-1)*sizeof(unsigned short));
201 csp
->lastdaysamples
[0]=csp
->todaysamples
;
202 csp
->lastdays
[0]=(csp
->todayusers
* 10)/todaysamples
;
203 csp
->lastmax
[0]=csp
->todaymax
;
208 csp
->todaymax
=cip
->channel
->users
->totalusers
;
215 memmove(&lastdaysamples
[1],&lastdaysamples
[0],(HISTORYDAYS
-1)*sizeof(unsigned int));
216 lastdaysamples
[0]=todaysamples
;
220 void savechanstats() {
226 if ((fp
=fopen("chanstats","w"))==NULL
) {
230 /* header: samples for today + last HISTORYDAYS days */
232 fprintf(fp
,"%lu %u",lastsample
,todaysamples
);
233 for(i
=0;i
<HISTORYDAYS
;i
++) {
234 fprintf(fp
," %u",lastdaysamples
[i
]);
238 /* body: channel, lastsample, samplestoday, sizetoday, <last sizes, last samples> */
239 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
240 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
241 if ((chp
=cip
->exts
[csext
])==NULL
) {
244 fprintf(fp
,"%s %lu %u %u %u",cip
->name
->content
,chp
->lastsampled
,chp
->todaysamples
,chp
->todayusers
,chp
->todaymax
);
246 for (j
=0;j
<HISTORYDAYS
;j
++) {
247 fprintf(fp
," %u %u %u",chp
->lastdays
[j
],chp
->lastdaysamples
[j
],chp
->lastmax
[j
]);
256 void loadchanstats() {
261 unsigned int chancount
=0;
265 if ((fp
=fopen("chanstats","r"))==NULL
) {
266 Error("chanstats",ERR_ERROR
,"Unable to load channel stats file");
270 fgets(inbuf
,2048,fp
);
272 Error("chanstats",ERR_ERROR
,"Corrupt channel stats file");
277 if ((splitline(inbuf
,args
,50,0))!=(HISTORYDAYS
+2)) {
278 Error("chanstats",ERR_ERROR
,"Corrupt channel stats file");
283 lastsample
=strtol(args
[0],NULL
,10);
284 todaysamples
=strtol(args
[1],NULL
,10);
285 for(i
=0;i
<HISTORYDAYS
;i
++) {
286 lastdaysamples
[i
]=strtol(args
[i
+2],NULL
,10);
290 fgets(inbuf
,2048,fp
);
295 if ((splitline(inbuf
,args
,100,0))!=5+(HISTORYDAYS
*3)) {
296 Error("chanstats",ERR_WARNING
,"Corrupt channel stats line");
300 cip
=findorcreatechanindex(args
[0]);
301 if (cip
->exts
[csext
]!=NULL
) {
302 Error("chanstats",ERR_ERROR
,"Duplicate stats entry for channel %s",args
[0]);
305 cip
->exts
[csext
]=chp
=getchanstats();
307 chp
->lastsampled
=strtol(args
[1],NULL
,10);
308 chp
->todaysamples
=strtol(args
[2],NULL
,10);
309 chp
->todayusers
=strtol(args
[3],NULL
,10);
310 chp
->todaymax
=strtol(args
[4],NULL
,10);
312 for(i
=0;i
<HISTORYDAYS
;i
++) {
313 chp
->lastdays
[i
]=strtol(args
[5+(i
*3)],NULL
,10);
314 chp
->lastdaysamples
[i
]=strtol(args
[6+(i
*3)],NULL
,10);
315 chp
->lastmax
[i
]=strtol(args
[7+(i
*3)],NULL
,10);
321 Error("chanstats",ERR_INFO
,"Loaded %u channels",chancount
);
324 int dochanstats(void *source
, int cargc
, char **cargv
) {
327 nick
*sender
=(nick
*)source
;
333 controlreply(sender
,"Usage: chanstats #channel");
337 cip
=findchanindex(cargv
[0]);
340 controlreply(sender
,"Can't find any record of %s.",cargv
[0]);
344 csp
=cip
->exts
[csext
];
347 controlreply(sender
,"Can't find any stats for %s.",cip
->name
->content
);
351 controlreply(sender
,"Statistics for channel %s:",cip
->name
->content
);
354 controlreply(sender
," [Service up <1hour, can't report last hour stats]");
357 for(i
=0;i
<SAMPLEHISTORY
;i
++) {
358 tot
+=csp
->lastsamples
[i
];
359 if (csp
->lastsamples
[i
]==0) {
363 controlreply(sender
," Last hour : %6.1f average users, empty %5.1f%% of the time",(float)tot
/SAMPLEHISTORY
,((float)emp
/SAMPLEHISTORY
)*100);
365 controlreply(sender
, " Today so far : %6.1f average users, empty %5.1f%% of the time, %4d max",
366 (float)csp
->todayusers
/todaysamples
, ((float)(todaysamples
-csp
->todaysamples
)/todaysamples
)*100,
369 themax
=csp
->lastmax
[0];
371 controlreply(sender
, " Yesterday : %6.1f average users, empty %5.1f%% of the time, %4d max",
372 (float)csp
->lastdays
[0]/10, ((float)(lastdaysamples
[0]-csp
->lastdaysamples
[0])/lastdaysamples
[0])*100,
379 k
+=csp
->lastdaysamples
[i
];
380 l
+=lastdaysamples
[i
];
381 if (csp
->lastmax
[i
]>themax
) {
382 themax
=csp
->lastmax
[i
];
385 controlreply(sender
, " 7-day average : %6.1f average users, empty %5.1f%% of the time, %4d max",
386 (float)j
/70,(float)((l
-k
)*100)/l
, themax
);
388 /* 14-day average: continuation of last loop */
391 k
+=csp
->lastdaysamples
[i
];
392 l
+=lastdaysamples
[i
];
393 if (csp
->lastmax
[i
]>themax
) {
394 themax
=csp
->lastmax
[i
];
397 controlreply(sender
, " 14-day average: %6.1f average users, empty %5.1f%% of the time, %4d max",
398 (float)j
/140,(float)((l
-k
)*100)/l
, themax
);
405 int doexpirecheck(void *source
, int cargc
, char **cargv
) {
406 nick
*sender
=(nick
*)source
;
412 controlreply(sender
, "Usage: expirecheck #channel");
416 if ((cip
=findchanindex(cargv
[0]))==NULL
) {
417 /* Couldn't find the channel at all: it's in no way big enough! */
419 controlreply(sender
,"delchan %s",cargv
[0]);
423 if ((csp
=(chanstats
*)cip
->exts
[csext
])==NULL
) {
424 /* No stats: similar */
426 controlreply(sender
,"delchan %s",cargv
[0]);
430 /* Did they hit the minimum today? */
431 if (csp
->todaymax
>= EXPIREMIN
) {
436 for (i
=0;i
<HISTORYDAYS
;i
++) {
437 if (csp
->lastmax
[i
] >= EXPIREMIN
) {
442 /* If not, delchan time! */
443 controlreply(sender
,"delchan %s",cargv
[0]);
447 int douserhistogram(void *source
, int cargc
, char **cargv
) {
451 nick
*sender
=(nick
*)source
;
452 int top
=0,tot
=0,servertot
=0,servertop
=0;
455 int sig
=0,serversig
=0;
458 if ((theserver
=findserver(cargv
[0]))<0) {
459 controlreply(sender
,"Can't find server %s",cargv
[0]);
469 for (i
=0;i
<NICKHASHSIZE
;i
++) {
470 for (np
=nicktable
[i
];np
;np
=np
->next
) {
471 if (np
->channels
->cursi
<= 20) {
472 histdata
[np
->channels
->cursi
]++;
473 if (theserver
>=0 && homeserver(np
->numeric
)==theserver
) {
475 serverdata
[np
->channels
->cursi
]++;
485 controlreply(sender
,"Size Count %%ge Count+ %s",(theserver
>=0?serverlist
[theserver
].name
->content
:""));
489 controlreply(sender
,"%4d %6d %4.1f%% %6d %6d %4.1f%% %6d",i
,histdata
[i
],(100.0*histdata
[i
])/tot
,top
,
490 serverdata
[i
],(100.0*serverdata
[i
])/servertot
, servertop
);
491 servertop
-=serverdata
[i
];
492 serversig
+= (i
* serverdata
[i
]);
494 controlreply(sender
,"%4d %6d %4.1f%% %6d",i
,histdata
[i
],(100.0*histdata
[i
])/tot
,top
);
496 sig
+= (i
* histdata
[i
]);
500 controlreply(sender
,"Average channel count (all users): %.2f",(float)sig
/tot
);
502 controlreply(sender
,"Average channel count (%s): %.2f",serverlist
[theserver
].name
->content
,(float)serversig
/servertot
);
508 int dochanhistogram(void *source
, int cargc
, char **cargv
) {
509 int histdata
[6][HISTITEMS
];
510 float bounds
[HISTITEMS
] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 15.0, 20.0,
511 25.0, 50.0, 75.0, 100.0, 150.0, 200.0, 250.0, 300.0, 400.0, 500.0,
515 nick
*sender
=(nick
*)source
;
518 char header
[(6*14)+10];
524 controlreply(sender
,"Usage: channelhistogram <metrics>");
525 controlreply(sender
,"Valid metrics are: now, today, yesterday, days <daycount>, nick <nick>.");
526 controlreply(sender
,"Up to 6 metrics may be specified.");
530 for (i
=0;i
<cargc
;i
++) {
531 /* Look for a valid metric */
532 if (!ircd_strcmp(cargv
[i
],"now")) {
533 dohist_now(histdata
[hists
++],bounds
,histsize
,NULL
);
534 strcat(header
,"Current Chans ");
535 } else if (!ircd_strcmp(cargv
[i
],"today")) {
536 dohist_today(histdata
[hists
++],bounds
,histsize
);
537 strcat(header
,"Today Average ");
538 } else if (!ircd_strcmp(cargv
[i
],"yesterday")) {
539 dohist_days(histdata
[hists
++],bounds
,histsize
,1);
540 strcat(header
,"Yesterday Avg ");
541 } else if (!ircd_strcmp(cargv
[i
],"namelen")) {
542 dohist_namelen(histdata
[hists
++],bounds
,histsize
);
543 strcat(header
,"Name Length ");
544 } else if (!ircd_strcmp(cargv
[i
],"days")) {
546 controlreply(sender
,"'days' metric requires a numeric argument.");
549 j
=strtol(cargv
[i
+1],NULL
,10);
551 controlreply(sender
,"Must specify a number of days between 1 and 14.");
554 dohist_days(histdata
[hists
++],bounds
,histsize
,j
);
555 sprintf(buf
,"%2d Day Avg ",j
);
558 } else if (!ircd_strcmp(cargv
[i
],"nick")) {
560 controlreply(sender
,"'nick' metric requires a nickname argument.");
563 if ((targetnick
=getnickbynick(cargv
[i
+1]))==NULL
) {
564 controlreply(sender
,"Couldn't find user %s.",cargv
[i
+1]);
567 dohist_now (histdata
[hists
++],bounds
,histsize
,targetnick
);
568 sprintf(buf
,"w/%-11.11s ",targetnick
->nick
);
572 controlreply(sender
,"Unknown metric %s.",cargv
[i
]);
577 controlreply(sender
," %s",header
);
580 for (i
=0;i
<hists
;i
++) {
581 strcat(buf
,"InRnge Range+ ");
583 controlreply(sender
," %s",buf
);
585 for(i
=0;i
<(histsize
-1);i
++) {
586 if (i
==(histsize
-2)) {
587 pos
=sprintf(buf
,"%6.1f+ ",bounds
[i
]);
589 pos
=sprintf(buf
,"%6.1f-%6.1f ",bounds
[i
],bounds
[i
+1]);
591 for(j
=0;j
<hists
;j
++) {
592 pos
+=sprintf(&(buf
[pos
]),"%6d %6d ",histdata
[j
][i
]-histdata
[j
][i
+1],histdata
[j
][i
]);
594 controlreply(sender
,"%s",buf
);
600 void dohist_now(int *data
, float *bounds
, int cats
, nick
*np
) {
607 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
608 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
609 if (cip
->channel
==NULL
) {
612 if (np
==NULL
|| NULL
!=getnumerichandlefromchanhash(cip
->channel
->users
, np
->numeric
))
613 for (j
=0;j
<cats
;j
++) {
614 if (cip
->channel
->users
->totalusers
>=bounds
[j
]) {
622 void dohist_today(int *data
, float *bounds
, int cats
) {
631 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
632 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
633 if ((csp
=cip
->exts
[csext
])==NULL
) {
636 f
=(float)csp
->todayusers
/todaysamples
;
637 for(j
=0;j
<cats
;j
++) {
646 void dohist_days(int *data
, float *bounds
, int cats
, int days
) {
655 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
656 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
657 if ((csp
=cip
->exts
[csext
])==NULL
) {
661 for (j
=0;j
<days
;j
++) {
664 f
=(float)k
/(days
*10);
665 for(j
=0;j
<cats
;j
++) {
674 void dohist_namelen(int *data
, float *bounds
, int cats
) {
681 for(i
=0;i
<CHANNELHASHSIZE
;i
++) {
682 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
683 for (j
=0;j
<cats
;j
++) {
684 if (cip
->name
->length
>=bounds
[j
]) {