]>
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 dochanstatssave(void *source
, int argc
, char **argv
);
36 int dochanstats(void *source
, int argc
, char **argv
);
37 int doexpirecheck(void *source
, int cargc
, char **cargv
);
38 int douserhistogram(void *source
, int cargc
, char **cargv
);
39 int dochanhistogram(void *source
, int cargc
, char **cargv
);
40 void dohist_now(int *data
, float *bounds
, int cats
, nick
*np
);
41 void dohist_today(int *data
, float *bounds
, int cats
);
42 void dohist_days(int *data
, float *bounds
, int cats
, int days
);
43 void dohist_namelen(int *data
, float *bounds
, int cats
);
48 csext
=registerchanext("chanstats");
50 /* PANIC PANIC PANIC PANIC */
51 Error("chanstats",ERR_ERROR
,"Couldn't register channel extension");
55 lastday
=(getnettime()/(24*3600));
58 memset(lastdaysamples
,0,sizeof(lastdaysamples
));
64 /* Work out when to take the next sample */
66 if (now
< chanstats_lastsample
) {
67 Error("chanstats",ERR_WARNING
,"Last sample time in future (%zu > %zu)",chanstats_lastsample
,now
);
69 } else if (now
<(chanstats_lastsample
+SAMPLEINTERVAL
)) {
70 lastday
=chanstats_lastsample
/(24*3600);
71 when
=chanstats_lastsample
+SAMPLEINTERVAL
;
73 if ((chanstats_lastsample
/(24*3600))<lastday
) {
80 registercontrolcmd("chanstats",10,1,&dochanstats
);
81 registercontrolcmd("channelhistogram",10,13,&dochanhistogram
);
82 registercontrolcmd("userhistogram",10,1,&douserhistogram
);
83 registercontrolcmd("expirecheck",10,1,&doexpirecheck
);
84 registercontrolhelpcmd("chanstatssave",NO_DEVELOPER
,1, &dochanstatssave
, "Usage: chanstatssave\nForce a save of chanstats data");
85 schedulerecurring(when
,0,SAMPLEINTERVAL
,&doupdate
,NULL
);
92 deleteschedule(NULL
,&doupdate
,NULL
);
93 deregistercontrolcmd("chanstats",&dochanstats
);
94 deregistercontrolcmd("channelhistogram",&dochanhistogram
);
95 deregistercontrolcmd("userhistogram",&douserhistogram
);
96 deregistercontrolcmd("expirecheck",&doexpirecheck
);
97 deregistercontrolcmd("chanstatssave",&dochanstatssave
);
98 releasechanext(csext
);
105 * This is the core function which is scheduled.
108 void doupdate(void *arg
) {
113 /* Get the current time and move the history pointer along one and count the sample */
116 Error("chanstats",ERR_INFO
,"Running chanstats update.");
118 if (now
/(24*3600) > lastday
) {
120 lastday
=(now
/(24*3600));
125 if (sampleindex
==SAMPLEHISTORY
) {
131 * Loop over all chans doing update
134 for(i
=0;i
<CHANNELHASHSIZE
;i
++) {
135 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
136 if ((cip
->channel
!=NULL
) || (cip
->exts
[csext
]!=NULL
)) {
137 updatechanstats(cip
,now
);
142 chanstats_lastsample
=now
;
143 if (now
-lastsave
> SAVEINTERVAL
) {
149 void updatechanstats(chanindex
*cip
, time_t now
) {
153 csp
=cip
->exts
[csext
];
155 csp
=cip
->exts
[csext
]=getchanstats();
156 memset(csp
,0,sizeof(chanstats
));
159 if (cip
->channel
!=NULL
) {
160 channel
*cp
= cip
->channel
;
164 currentusers
=countuniquehosts(cp
);
166 for (i
=0;i
<cp
->users
->hashsize
;i
++) {
167 if (cp
->users
->content
[i
]==nouser
)
170 if ((np
=getnickbynumeric(cp
->users
->content
[i
]))==NULL
) {
171 Error("channel",ERR_ERROR
,"Found unknown numeric %lu on channel %s",cp
->users
->content
[i
],cp
->index
->name
->content
);
175 if (IsXOper(np
) || IsService(np
))
184 csp
->lastsamples
[sampleindex
]=currentusers
;
185 csp
->lastsampled
=now
;
186 csp
->todayusers
+=currentusers
;
188 if (currentusers
> csp
->todaymax
) {
189 csp
->todaymax
=currentusers
;
193 void rotatechanstats() {
195 chanindex
*cip
,*ncip
;
198 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
199 for (cip
=chantable
[i
];cip
;cip
=ncip
) {
202 if ((csp
=cip
->exts
[csext
])==NULL
) {
206 if (csp
->todaysamples
==0) {
207 /* No samples today, might want to delete */
209 for(j
=0;j
<HISTORYDAYS
;j
++) {
214 cip
->exts
[csext
]=NULL
;
215 releasechanindex(cip
);
220 /* Move the samples along one */
221 memmove(&(csp
->lastdays
[1]),&(csp
->lastdays
[0]),(HISTORYDAYS
-1)*sizeof(short));
222 memmove(&(csp
->lastdaysamples
[1]),&(csp
->lastdaysamples
[0]),(HISTORYDAYS
-1)*sizeof(char));
223 memmove(&(csp
->lastmax
[1]),&(csp
->lastmax
[0]),(HISTORYDAYS
-1)*sizeof(unsigned short));
225 csp
->lastdaysamples
[0]=csp
->todaysamples
;
226 csp
->lastdays
[0]=(csp
->todayusers
* 10)/todaysamples
;
227 csp
->lastmax
[0]=csp
->todaymax
;
232 csp
->todaymax
=cip
->channel
->users
->totalusers
;
239 memmove(&lastdaysamples
[1],&lastdaysamples
[0],(HISTORYDAYS
-1)*sizeof(unsigned int));
240 lastdaysamples
[0]=todaysamples
;
244 void savechanstats() {
250 if ((fp
=fopen("data/chanstats","w"))==NULL
) {
254 /* header: samples for today + last HISTORYDAYS days */
256 fprintf(fp
,"%lu %u",chanstats_lastsample
,todaysamples
);
257 for(i
=0;i
<HISTORYDAYS
;i
++) {
258 fprintf(fp
," %u",lastdaysamples
[i
]);
262 /* body: channel, chanstats_lastsample, samplestoday, sizetoday, <last sizes, last samples> */
263 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
264 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
265 if ((chp
=cip
->exts
[csext
])==NULL
) {
268 fprintf(fp
,"%s %lu %u %u %u",cip
->name
->content
,chp
->lastsampled
,chp
->todaysamples
,chp
->todayusers
,chp
->todaymax
);
270 for (j
=0;j
<HISTORYDAYS
;j
++) {
271 fprintf(fp
," %u %u %u",chp
->lastdays
[j
],chp
->lastdaysamples
[j
],chp
->lastmax
[j
]);
280 void loadchanstats() {
285 unsigned int chancount
=0;
289 if ((fp
=fopen("data/chanstats","r"))==NULL
) {
290 Error("chanstats",ERR_ERROR
,"Unable to load channel stats file");
294 fgets(inbuf
,2048,fp
);
296 Error("chanstats",ERR_ERROR
,"Corrupt channel stats file");
301 if ((splitline(inbuf
,args
,50,0))!=(HISTORYDAYS
+2)) {
302 Error("chanstats",ERR_ERROR
,"Corrupt channel stats file");
307 chanstats_lastsample
=strtol(args
[0],NULL
,10);
308 todaysamples
=strtol(args
[1],NULL
,10);
309 for(i
=0;i
<HISTORYDAYS
;i
++) {
310 lastdaysamples
[i
]=strtol(args
[i
+2],NULL
,10);
314 fgets(inbuf
,2048,fp
);
319 if ((splitline(inbuf
,args
,100,0))!=5+(HISTORYDAYS
*3)) {
320 Error("chanstats",ERR_WARNING
,"Corrupt channel stats line");
324 cip
=findorcreatechanindex(args
[0]);
325 if (cip
->exts
[csext
]!=NULL
) {
326 Error("chanstats",ERR_ERROR
,"Duplicate stats entry for channel %s",args
[0]);
329 cip
->exts
[csext
]=chp
=getchanstats();
331 chp
->lastsampled
=strtol(args
[1],NULL
,10);
332 chp
->todaysamples
=strtol(args
[2],NULL
,10);
333 chp
->todayusers
=strtol(args
[3],NULL
,10);
334 chp
->todaymax
=strtol(args
[4],NULL
,10);
336 for(i
=0;i
<HISTORYDAYS
;i
++) {
337 chp
->lastdays
[i
]=strtol(args
[5+(i
*3)],NULL
,10);
338 chp
->lastdaysamples
[i
]=strtol(args
[6+(i
*3)],NULL
,10);
339 chp
->lastmax
[i
]=strtol(args
[7+(i
*3)],NULL
,10);
345 Error("chanstats",ERR_INFO
,"Loaded %u channels",chancount
);
348 int dochanstats(void *source
, int cargc
, char **cargv
) {
351 nick
*sender
=(nick
*)source
;
357 controlreply(sender
,"Usage: chanstats #channel");
361 cip
=findchanindex(cargv
[0]);
364 controlreply(sender
,"Can't find any record of %s.",cargv
[0]);
368 csp
=cip
->exts
[csext
];
371 controlreply(sender
,"Can't find any stats for %s.",cip
->name
->content
);
375 controlreply(sender
,"Statistics for channel %s:",cip
->name
->content
);
378 controlreply(sender
," [Service up <1hour, can't report last hour stats]");
381 for(i
=0;i
<SAMPLEHISTORY
;i
++) {
382 tot
+=csp
->lastsamples
[i
];
383 if (csp
->lastsamples
[i
]==0) {
387 controlreply(sender
," Last hour : %6.1f average users, empty %5.1f%% of the time",(float)tot
/SAMPLEHISTORY
,((float)emp
/SAMPLEHISTORY
)*100);
389 controlreply(sender
, " Today so far : %6.1f average users, empty %5.1f%% of the time, %4d max",
390 (float)csp
->todayusers
/todaysamples
, ((float)(todaysamples
-csp
->todaysamples
)/todaysamples
)*100,
393 themax
=csp
->lastmax
[0];
395 controlreply(sender
, " Yesterday : %6.1f average users, empty %5.1f%% of the time, %4d max",
396 (float)csp
->lastdays
[0]/10, ((float)(lastdaysamples
[0]-csp
->lastdaysamples
[0])/lastdaysamples
[0])*100,
403 k
+=csp
->lastdaysamples
[i
];
404 l
+=lastdaysamples
[i
];
405 if (csp
->lastmax
[i
]>themax
) {
406 themax
=csp
->lastmax
[i
];
409 controlreply(sender
, " 7-day average : %6.1f average users, empty %5.1f%% of the time, %4d max",
410 (float)j
/70,(float)((l
-k
)*100)/l
, themax
);
412 /* 14-day average: continuation of last loop */
415 k
+=csp
->lastdaysamples
[i
];
416 l
+=lastdaysamples
[i
];
417 if (csp
->lastmax
[i
]>themax
) {
418 themax
=csp
->lastmax
[i
];
421 controlreply(sender
, " 14-day average: %6.1f average users, empty %5.1f%% of the time, %4d max",
422 (float)j
/140,(float)((l
-k
)*100)/l
, themax
);
429 int doexpirecheck(void *source
, int cargc
, char **cargv
) {
430 nick
*sender
=(nick
*)source
;
436 controlreply(sender
, "Usage: expirecheck #channel");
440 if ((cip
=findchanindex(cargv
[0]))==NULL
) {
441 /* Couldn't find the channel at all: it's in no way big enough! */
443 controlreply(sender
,"delchan %s",cargv
[0]);
447 if ((csp
=(chanstats
*)cip
->exts
[csext
])==NULL
) {
448 /* No stats: similar */
450 controlreply(sender
,"delchan %s",cargv
[0]);
454 /* Did they hit the minimum today? */
455 if (csp
->todaymax
>= EXPIREMIN
) {
460 for (i
=0;i
<HISTORYDAYS
;i
++) {
461 if (csp
->lastmax
[i
] >= EXPIREMIN
) {
466 /* If not, delchan time! */
467 controlreply(sender
,"delchan %s",cargv
[0]);
471 int douserhistogram(void *source
, int cargc
, char **cargv
) {
475 nick
*sender
=(nick
*)source
;
476 int top
=0,tot
=0,servertot
=0,servertop
=0;
479 int sig
=0,serversig
=0;
482 if ((theserver
=findserver(cargv
[0]))<0) {
483 controlreply(sender
,"Can't find server %s",cargv
[0]);
493 for (i
=0;i
<NICKHASHSIZE
;i
++) {
494 for (np
=nicktable
[i
];np
;np
=np
->next
) {
495 if (np
->channels
->cursi
<= 20) {
496 histdata
[np
->channels
->cursi
]++;
497 if (theserver
>=0 && homeserver(np
->numeric
)==theserver
) {
499 serverdata
[np
->channels
->cursi
]++;
509 controlreply(sender
,"Size Count %%ge Count+ %s",(theserver
>=0?serverlist
[theserver
].name
->content
:""));
513 controlreply(sender
,"%4d %6d %4.1f%% %6d %6d %4.1f%% %6d",i
,histdata
[i
],(100.0*histdata
[i
])/tot
,top
,
514 serverdata
[i
],(100.0*serverdata
[i
])/servertot
, servertop
);
515 servertop
-=serverdata
[i
];
516 serversig
+= (i
* serverdata
[i
]);
518 controlreply(sender
,"%4d %6d %4.1f%% %6d",i
,histdata
[i
],(100.0*histdata
[i
])/tot
,top
);
520 sig
+= (i
* histdata
[i
]);
524 controlreply(sender
,"Average channel count (all users): %.2f",(float)sig
/tot
);
526 controlreply(sender
,"Average channel count (%s): %.2f",serverlist
[theserver
].name
->content
,(float)serversig
/servertot
);
534 int dochanhistogram(void *source
, int cargc
, char **cargv
) {
535 int histdata
[6][HISTITEMS
];
536 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,
537 25.0, 50.0, 75.0, 100.0, 150.0, 200.0, 250.0, 300.0, 400.0, 500.0,
541 nick
*sender
=(nick
*)source
;
544 char header
[(6*14)+10];
550 controlreply(sender
,"Usage: channelhistogram <metrics>");
551 controlreply(sender
,"Valid metrics are: now, today, yesterday, days <daycount>, nick <nick>.");
552 controlreply(sender
,"Up to 6 metrics may be specified.");
556 for (i
=0;i
<cargc
;i
++) {
557 /* Look for a valid metric */
558 if (!ircd_strcmp(cargv
[i
],"now")) {
559 dohist_now(histdata
[hists
++],bounds
,histsize
,NULL
);
560 strcat(header
,"Current Chans ");
561 } else if (!ircd_strcmp(cargv
[i
],"today")) {
562 dohist_today(histdata
[hists
++],bounds
,histsize
);
563 strcat(header
,"Today Average ");
564 } else if (!ircd_strcmp(cargv
[i
],"yesterday")) {
565 dohist_days(histdata
[hists
++],bounds
,histsize
,1);
566 strcat(header
,"Yesterday Avg ");
567 } else if (!ircd_strcmp(cargv
[i
],"namelen")) {
568 dohist_namelen(histdata
[hists
++],bounds
,histsize
);
569 strcat(header
,"Name Length ");
570 } else if (!ircd_strcmp(cargv
[i
],"days")) {
572 controlreply(sender
,"'days' metric requires a numeric argument.");
575 j
=strtol(cargv
[i
+1],NULL
,10);
577 controlreply(sender
,"Must specify a number of days between 1 and 14.");
580 dohist_days(histdata
[hists
++],bounds
,histsize
,j
);
581 sprintf(buf
,"%2d Day Avg ",j
);
584 } else if (!ircd_strcmp(cargv
[i
],"nick")) {
586 controlreply(sender
,"'nick' metric requires a nickname argument.");
589 if ((targetnick
=getnickbynick(cargv
[i
+1]))==NULL
) {
590 controlreply(sender
,"Couldn't find user %s.",cargv
[i
+1]);
593 dohist_now (histdata
[hists
++],bounds
,histsize
,targetnick
);
594 sprintf(buf
,"w/%-11.11s ",targetnick
->nick
);
598 controlreply(sender
,"Unknown metric %s.",cargv
[i
]);
603 controlreply(sender
," %s",header
);
606 for (i
=0;i
<hists
;i
++) {
607 strcat(buf
,"InRnge Range+ ");
609 controlreply(sender
," %s",buf
);
611 for(i
=0;i
<(histsize
-1);i
++) {
612 if (i
==(histsize
-2)) {
613 pos
=sprintf(buf
,"%6.1f+ ",bounds
[i
]);
615 pos
=sprintf(buf
,"%6.1f-%6.1f ",bounds
[i
],bounds
[i
+1]);
617 for(j
=0;j
<hists
;j
++) {
618 pos
+=sprintf(&(buf
[pos
]),"%6d %6d ",histdata
[j
][i
]-histdata
[j
][i
+1],histdata
[j
][i
]);
620 controlreply(sender
,"%s",buf
);
626 void dohist_now(int *data
, float *bounds
, int cats
, nick
*np
) {
633 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
634 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
635 if (cip
->channel
==NULL
) {
638 if (np
==NULL
|| NULL
!=getnumerichandlefromchanhash(cip
->channel
->users
, np
->numeric
))
639 for (j
=0;j
<cats
;j
++) {
640 if (cip
->channel
->users
->totalusers
>=bounds
[j
]) {
648 void dohist_today(int *data
, float *bounds
, int cats
) {
657 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
658 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
659 if ((csp
=cip
->exts
[csext
])==NULL
) {
662 f
=(float)csp
->todayusers
/todaysamples
;
663 for(j
=0;j
<cats
;j
++) {
672 void dohist_days(int *data
, float *bounds
, int cats
, int days
) {
681 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
682 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
683 if ((csp
=cip
->exts
[csext
])==NULL
) {
687 for (j
=0;j
<days
;j
++) {
690 f
=(float)k
/(days
*10);
691 for(j
=0;j
<cats
;j
++) {
700 void dohist_namelen(int *data
, float *bounds
, int cats
) {
707 for(i
=0;i
<CHANNELHASHSIZE
;i
++) {
708 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
709 for (j
=0;j
<cats
;j
++) {
710 if (cip
->name
->length
>=bounds
[j
]) {
718 int dochanstatssave(void *source
, int cargc
, char **cargv
) {
719 nick
*sender
=(nick
*)source
;
721 controlreply(sender
,"Saving Chanstats..");
723 controlreply(sender
,"Chanstats saved");