]>
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"
12 #include "../lib/version.h"
21 time_t chanstats_lastsample
;
27 unsigned int lastdaysamples
[HISTORYDAYS
];
28 unsigned int todaysamples
;
30 void doupdate(void *arg
);
31 void updatechanstats(chanindex
*cip
, time_t now
);
32 void rotatechanstats();
35 int dochanstats(void *source
, int argc
, char **argv
);
36 int doexpirecheck(void *source
, int cargc
, char **cargv
);
37 int douserhistogram(void *source
, int cargc
, char **cargv
);
38 int dochanhistogram(void *source
, int cargc
, char **cargv
);
39 void dohist_now(int *data
, float *bounds
, int cats
, nick
*np
);
40 void dohist_today(int *data
, float *bounds
, int cats
);
41 void dohist_days(int *data
, float *bounds
, int cats
, int days
);
42 void dohist_namelen(int *data
, float *bounds
, int cats
);
47 csext
=registerchanext("chanstats");
49 /* PANIC PANIC PANIC PANIC */
50 Error("chanstats",ERR_ERROR
,"Couldn't register channel extension");
54 lastday
=(getnettime()/(24*3600));
57 memset(lastdaysamples
,0,sizeof(lastdaysamples
));
63 /* Work out when to take the next sample */
65 if (now
< chanstats_lastsample
) {
66 Error("chanstats",ERR_WARNING
,"Last sample time in future (%d > %d)",chanstats_lastsample
,now
);
68 } else if (now
<(chanstats_lastsample
+SAMPLEINTERVAL
)) {
69 lastday
=chanstats_lastsample
/(24*3600);
70 when
=chanstats_lastsample
+SAMPLEINTERVAL
;
72 if ((chanstats_lastsample
/(24*3600))<lastday
) {
79 registercontrolcmd("chanstats",10,1,&dochanstats
);
80 registercontrolcmd("channelhistogram",10,13,&dochanhistogram
);
81 registercontrolcmd("userhistogram",10,1,&douserhistogram
);
82 registercontrolcmd("expirecheck",10,1,&doexpirecheck
);
83 schedulerecurring(when
,0,SAMPLEINTERVAL
,&doupdate
,NULL
);
90 deleteschedule(NULL
,&doupdate
,NULL
);
91 deregistercontrolcmd("chanstats",&dochanstats
);
92 deregistercontrolcmd("channelhistogram",&dochanhistogram
);
93 deregistercontrolcmd("userhistogram",&douserhistogram
);
94 deregistercontrolcmd("expirecheck",&doexpirecheck
);
95 releasechanext(csext
);
102 * This is the core function which is scheduled.
105 void doupdate(void *arg
) {
110 /* Get the current time and move the history pointer along one and count the sample */
113 Error("chanstats",ERR_INFO
,"Running chanstats update.");
115 if (now
/(24*3600) > lastday
) {
117 lastday
=(now
/(24*3600));
122 if (sampleindex
==SAMPLEHISTORY
) {
128 * Loop over all chans doing update
131 for(i
=0;i
<CHANNELHASHSIZE
;i
++) {
132 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
133 if ((cip
->channel
!=NULL
) || (cip
->exts
[csext
]!=NULL
)) {
134 updatechanstats(cip
,now
);
139 chanstats_lastsample
=now
;
140 if (now
-lastsave
> SAVEINTERVAL
) {
146 void updatechanstats(chanindex
*cip
, time_t now
) {
150 csp
=cip
->exts
[csext
];
152 csp
=cip
->exts
[csext
]=getchanstats();
153 memset(csp
,0,sizeof(chanstats
));
156 if (cip
->channel
!=NULL
) {
157 channel
*cp
= cip
->channel
;
161 currentusers
=countuniquehosts(cp
);
163 for (i
=0;i
<cp
->users
->hashsize
;i
++) {
164 if (cp
->users
->content
[i
]==nouser
)
167 if ((np
=getnickbynumeric(cp
->users
->content
[i
]))==NULL
) {
168 Error("channel",ERR_ERROR
,"Found unknown numeric %u on channel %s",cp
->users
->content
[i
],cp
->index
->name
->content
);
172 if (IsXOper(np
) || IsService(np
))
181 csp
->lastsamples
[sampleindex
]=currentusers
;
182 csp
->lastsampled
=now
;
183 csp
->todayusers
+=currentusers
;
185 if (currentusers
> csp
->todaymax
) {
186 csp
->todaymax
=currentusers
;
190 void rotatechanstats() {
192 chanindex
*cip
,*ncip
;
195 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
196 for (cip
=chantable
[i
];cip
;cip
=ncip
) {
199 if ((csp
=cip
->exts
[csext
])==NULL
) {
203 if (csp
->todaysamples
==0) {
204 /* No samples today, might want to delete */
206 for(j
=0;j
<HISTORYDAYS
;j
++) {
211 cip
->exts
[csext
]=NULL
;
212 releasechanindex(cip
);
217 /* Move the samples along one */
218 memmove(&(csp
->lastdays
[1]),&(csp
->lastdays
[0]),(HISTORYDAYS
-1)*sizeof(short));
219 memmove(&(csp
->lastdaysamples
[1]),&(csp
->lastdaysamples
[0]),(HISTORYDAYS
-1)*sizeof(char));
220 memmove(&(csp
->lastmax
[1]),&(csp
->lastmax
[0]),(HISTORYDAYS
-1)*sizeof(unsigned short));
222 csp
->lastdaysamples
[0]=csp
->todaysamples
;
223 csp
->lastdays
[0]=(csp
->todayusers
* 10)/todaysamples
;
224 csp
->lastmax
[0]=csp
->todaymax
;
229 csp
->todaymax
=cip
->channel
->users
->totalusers
;
236 memmove(&lastdaysamples
[1],&lastdaysamples
[0],(HISTORYDAYS
-1)*sizeof(unsigned int));
237 lastdaysamples
[0]=todaysamples
;
241 void savechanstats() {
247 if ((fp
=fopen("chanstats","w"))==NULL
) {
251 /* header: samples for today + last HISTORYDAYS days */
253 fprintf(fp
,"%lu %u",chanstats_lastsample
,todaysamples
);
254 for(i
=0;i
<HISTORYDAYS
;i
++) {
255 fprintf(fp
," %u",lastdaysamples
[i
]);
259 /* body: channel, chanstats_lastsample, samplestoday, sizetoday, <last sizes, last samples> */
260 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
261 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
262 if ((chp
=cip
->exts
[csext
])==NULL
) {
265 fprintf(fp
,"%s %lu %u %u %u",cip
->name
->content
,chp
->lastsampled
,chp
->todaysamples
,chp
->todayusers
,chp
->todaymax
);
267 for (j
=0;j
<HISTORYDAYS
;j
++) {
268 fprintf(fp
," %u %u %u",chp
->lastdays
[j
],chp
->lastdaysamples
[j
],chp
->lastmax
[j
]);
277 void loadchanstats() {
282 unsigned int chancount
=0;
286 if ((fp
=fopen("chanstats","r"))==NULL
) {
287 Error("chanstats",ERR_ERROR
,"Unable to load channel stats file");
291 fgets(inbuf
,2048,fp
);
293 Error("chanstats",ERR_ERROR
,"Corrupt channel stats file");
298 if ((splitline(inbuf
,args
,50,0))!=(HISTORYDAYS
+2)) {
299 Error("chanstats",ERR_ERROR
,"Corrupt channel stats file");
304 chanstats_lastsample
=strtol(args
[0],NULL
,10);
305 todaysamples
=strtol(args
[1],NULL
,10);
306 for(i
=0;i
<HISTORYDAYS
;i
++) {
307 lastdaysamples
[i
]=strtol(args
[i
+2],NULL
,10);
311 fgets(inbuf
,2048,fp
);
316 if ((splitline(inbuf
,args
,100,0))!=5+(HISTORYDAYS
*3)) {
317 Error("chanstats",ERR_WARNING
,"Corrupt channel stats line");
321 cip
=findorcreatechanindex(args
[0]);
322 if (cip
->exts
[csext
]!=NULL
) {
323 Error("chanstats",ERR_ERROR
,"Duplicate stats entry for channel %s",args
[0]);
326 cip
->exts
[csext
]=chp
=getchanstats();
328 chp
->lastsampled
=strtol(args
[1],NULL
,10);
329 chp
->todaysamples
=strtol(args
[2],NULL
,10);
330 chp
->todayusers
=strtol(args
[3],NULL
,10);
331 chp
->todaymax
=strtol(args
[4],NULL
,10);
333 for(i
=0;i
<HISTORYDAYS
;i
++) {
334 chp
->lastdays
[i
]=strtol(args
[5+(i
*3)],NULL
,10);
335 chp
->lastdaysamples
[i
]=strtol(args
[6+(i
*3)],NULL
,10);
336 chp
->lastmax
[i
]=strtol(args
[7+(i
*3)],NULL
,10);
342 Error("chanstats",ERR_INFO
,"Loaded %u channels",chancount
);
345 int dochanstats(void *source
, int cargc
, char **cargv
) {
348 nick
*sender
=(nick
*)source
;
354 controlreply(sender
,"Usage: chanstats #channel");
358 cip
=findchanindex(cargv
[0]);
361 controlreply(sender
,"Can't find any record of %s.",cargv
[0]);
365 csp
=cip
->exts
[csext
];
368 controlreply(sender
,"Can't find any stats for %s.",cip
->name
->content
);
372 controlreply(sender
,"Statistics for channel %s:",cip
->name
->content
);
375 controlreply(sender
," [Service up <1hour, can't report last hour stats]");
378 for(i
=0;i
<SAMPLEHISTORY
;i
++) {
379 tot
+=csp
->lastsamples
[i
];
380 if (csp
->lastsamples
[i
]==0) {
384 controlreply(sender
," Last hour : %6.1f average users, empty %5.1f%% of the time",(float)tot
/SAMPLEHISTORY
,((float)emp
/SAMPLEHISTORY
)*100);
386 controlreply(sender
, " Today so far : %6.1f average users, empty %5.1f%% of the time, %4d max",
387 (float)csp
->todayusers
/todaysamples
, ((float)(todaysamples
-csp
->todaysamples
)/todaysamples
)*100,
390 themax
=csp
->lastmax
[0];
392 controlreply(sender
, " Yesterday : %6.1f average users, empty %5.1f%% of the time, %4d max",
393 (float)csp
->lastdays
[0]/10, ((float)(lastdaysamples
[0]-csp
->lastdaysamples
[0])/lastdaysamples
[0])*100,
400 k
+=csp
->lastdaysamples
[i
];
401 l
+=lastdaysamples
[i
];
402 if (csp
->lastmax
[i
]>themax
) {
403 themax
=csp
->lastmax
[i
];
406 controlreply(sender
, " 7-day average : %6.1f average users, empty %5.1f%% of the time, %4d max",
407 (float)j
/70,(float)((l
-k
)*100)/l
, themax
);
409 /* 14-day average: continuation of last loop */
412 k
+=csp
->lastdaysamples
[i
];
413 l
+=lastdaysamples
[i
];
414 if (csp
->lastmax
[i
]>themax
) {
415 themax
=csp
->lastmax
[i
];
418 controlreply(sender
, " 14-day average: %6.1f average users, empty %5.1f%% of the time, %4d max",
419 (float)j
/140,(float)((l
-k
)*100)/l
, themax
);
426 int doexpirecheck(void *source
, int cargc
, char **cargv
) {
427 nick
*sender
=(nick
*)source
;
433 controlreply(sender
, "Usage: expirecheck #channel");
437 if ((cip
=findchanindex(cargv
[0]))==NULL
) {
438 /* Couldn't find the channel at all: it's in no way big enough! */
440 controlreply(sender
,"delchan %s",cargv
[0]);
444 if ((csp
=(chanstats
*)cip
->exts
[csext
])==NULL
) {
445 /* No stats: similar */
447 controlreply(sender
,"delchan %s",cargv
[0]);
451 /* Did they hit the minimum today? */
452 if (csp
->todaymax
>= EXPIREMIN
) {
457 for (i
=0;i
<HISTORYDAYS
;i
++) {
458 if (csp
->lastmax
[i
] >= EXPIREMIN
) {
463 /* If not, delchan time! */
464 controlreply(sender
,"delchan %s",cargv
[0]);
468 int douserhistogram(void *source
, int cargc
, char **cargv
) {
472 nick
*sender
=(nick
*)source
;
473 int top
=0,tot
=0,servertot
=0,servertop
=0;
476 int sig
=0,serversig
=0;
479 if ((theserver
=findserver(cargv
[0]))<0) {
480 controlreply(sender
,"Can't find server %s",cargv
[0]);
490 for (i
=0;i
<NICKHASHSIZE
;i
++) {
491 for (np
=nicktable
[i
];np
;np
=np
->next
) {
492 if (np
->channels
->cursi
<= 20) {
493 histdata
[np
->channels
->cursi
]++;
494 if (theserver
>=0 && homeserver(np
->numeric
)==theserver
) {
496 serverdata
[np
->channels
->cursi
]++;
506 controlreply(sender
,"Size Count %%ge Count+ %s",(theserver
>=0?serverlist
[theserver
].name
->content
:""));
510 controlreply(sender
,"%4d %6d %4.1f%% %6d %6d %4.1f%% %6d",i
,histdata
[i
],(100.0*histdata
[i
])/tot
,top
,
511 serverdata
[i
],(100.0*serverdata
[i
])/servertot
, servertop
);
512 servertop
-=serverdata
[i
];
513 serversig
+= (i
* serverdata
[i
]);
515 controlreply(sender
,"%4d %6d %4.1f%% %6d",i
,histdata
[i
],(100.0*histdata
[i
])/tot
,top
);
517 sig
+= (i
* histdata
[i
]);
521 controlreply(sender
,"Average channel count (all users): %.2f",(float)sig
/tot
);
523 controlreply(sender
,"Average channel count (%s): %.2f",serverlist
[theserver
].name
->content
,(float)serversig
/servertot
);
531 int dochanhistogram(void *source
, int cargc
, char **cargv
) {
532 int histdata
[6][HISTITEMS
];
533 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,
534 25.0, 50.0, 75.0, 100.0, 150.0, 200.0, 250.0, 300.0, 400.0, 500.0,
538 nick
*sender
=(nick
*)source
;
541 char header
[(6*14)+10];
547 controlreply(sender
,"Usage: channelhistogram <metrics>");
548 controlreply(sender
,"Valid metrics are: now, today, yesterday, days <daycount>, nick <nick>.");
549 controlreply(sender
,"Up to 6 metrics may be specified.");
553 for (i
=0;i
<cargc
;i
++) {
554 /* Look for a valid metric */
555 if (!ircd_strcmp(cargv
[i
],"now")) {
556 dohist_now(histdata
[hists
++],bounds
,histsize
,NULL
);
557 strcat(header
,"Current Chans ");
558 } else if (!ircd_strcmp(cargv
[i
],"today")) {
559 dohist_today(histdata
[hists
++],bounds
,histsize
);
560 strcat(header
,"Today Average ");
561 } else if (!ircd_strcmp(cargv
[i
],"yesterday")) {
562 dohist_days(histdata
[hists
++],bounds
,histsize
,1);
563 strcat(header
,"Yesterday Avg ");
564 } else if (!ircd_strcmp(cargv
[i
],"namelen")) {
565 dohist_namelen(histdata
[hists
++],bounds
,histsize
);
566 strcat(header
,"Name Length ");
567 } else if (!ircd_strcmp(cargv
[i
],"days")) {
569 controlreply(sender
,"'days' metric requires a numeric argument.");
572 j
=strtol(cargv
[i
+1],NULL
,10);
574 controlreply(sender
,"Must specify a number of days between 1 and 14.");
577 dohist_days(histdata
[hists
++],bounds
,histsize
,j
);
578 sprintf(buf
,"%2d Day Avg ",j
);
581 } else if (!ircd_strcmp(cargv
[i
],"nick")) {
583 controlreply(sender
,"'nick' metric requires a nickname argument.");
586 if ((targetnick
=getnickbynick(cargv
[i
+1]))==NULL
) {
587 controlreply(sender
,"Couldn't find user %s.",cargv
[i
+1]);
590 dohist_now (histdata
[hists
++],bounds
,histsize
,targetnick
);
591 sprintf(buf
,"w/%-11.11s ",targetnick
->nick
);
595 controlreply(sender
,"Unknown metric %s.",cargv
[i
]);
600 controlreply(sender
," %s",header
);
603 for (i
=0;i
<hists
;i
++) {
604 strcat(buf
,"InRnge Range+ ");
606 controlreply(sender
," %s",buf
);
608 for(i
=0;i
<(histsize
-1);i
++) {
609 if (i
==(histsize
-2)) {
610 pos
=sprintf(buf
,"%6.1f+ ",bounds
[i
]);
612 pos
=sprintf(buf
,"%6.1f-%6.1f ",bounds
[i
],bounds
[i
+1]);
614 for(j
=0;j
<hists
;j
++) {
615 pos
+=sprintf(&(buf
[pos
]),"%6d %6d ",histdata
[j
][i
]-histdata
[j
][i
+1],histdata
[j
][i
]);
617 controlreply(sender
,"%s",buf
);
623 void dohist_now(int *data
, float *bounds
, int cats
, nick
*np
) {
630 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
631 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
632 if (cip
->channel
==NULL
) {
635 if (np
==NULL
|| NULL
!=getnumerichandlefromchanhash(cip
->channel
->users
, np
->numeric
))
636 for (j
=0;j
<cats
;j
++) {
637 if (cip
->channel
->users
->totalusers
>=bounds
[j
]) {
645 void dohist_today(int *data
, float *bounds
, int cats
) {
654 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
655 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
656 if ((csp
=cip
->exts
[csext
])==NULL
) {
659 f
=(float)csp
->todayusers
/todaysamples
;
660 for(j
=0;j
<cats
;j
++) {
669 void dohist_days(int *data
, float *bounds
, int cats
, int days
) {
678 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
679 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
680 if ((csp
=cip
->exts
[csext
])==NULL
) {
684 for (j
=0;j
<days
;j
++) {
687 f
=(float)k
/(days
*10);
688 for(j
=0;j
<cats
;j
++) {
697 void dohist_namelen(int *data
, float *bounds
, int cats
) {
704 for(i
=0;i
<CHANNELHASHSIZE
;i
++) {
705 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
706 for (j
=0;j
<cats
;j
++) {
707 if (cip
->name
->length
>=bounds
[j
]) {