]>
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"
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
< lastsample
) {
66 Error("chanstats",ERR_WARNING
,"Last sample time in future (%d > %d)",lastsample
,now
);
68 } else if (now
<(lastsample
+SAMPLEINTERVAL
)) {
69 lastday
=lastsample
/(24*3600);
70 when
=lastsample
+SAMPLEINTERVAL
;
72 if ((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
);
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 currentusers
=countuniquehosts(cip
->channel
);
163 csp
->lastsamples
[sampleindex
]=currentusers
;
164 csp
->lastsampled
=now
;
165 csp
->todayusers
+=currentusers
;
167 if (currentusers
> csp
->todaymax
) {
168 csp
->todaymax
=currentusers
;
172 void rotatechanstats() {
174 chanindex
*cip
,*ncip
;
177 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
178 for (cip
=chantable
[i
];cip
;cip
=ncip
) {
181 if ((csp
=cip
->exts
[csext
])==NULL
) {
185 if (csp
->todaysamples
==0) {
186 /* No samples today, might want to delete */
188 for(j
=0;j
<HISTORYDAYS
;j
++) {
193 cip
->exts
[csext
]=NULL
;
194 releasechanindex(cip
);
199 /* Move the samples along one */
200 memmove(&(csp
->lastdays
[1]),&(csp
->lastdays
[0]),(HISTORYDAYS
-1)*sizeof(short));
201 memmove(&(csp
->lastdaysamples
[1]),&(csp
->lastdaysamples
[0]),(HISTORYDAYS
-1)*sizeof(char));
202 memmove(&(csp
->lastmax
[1]),&(csp
->lastmax
[0]),(HISTORYDAYS
-1)*sizeof(unsigned short));
204 csp
->lastdaysamples
[0]=csp
->todaysamples
;
205 csp
->lastdays
[0]=(csp
->todayusers
* 10)/todaysamples
;
206 csp
->lastmax
[0]=csp
->todaymax
;
211 csp
->todaymax
=cip
->channel
->users
->totalusers
;
218 memmove(&lastdaysamples
[1],&lastdaysamples
[0],(HISTORYDAYS
-1)*sizeof(unsigned int));
219 lastdaysamples
[0]=todaysamples
;
223 void savechanstats() {
229 if ((fp
=fopen("chanstats","w"))==NULL
) {
233 /* header: samples for today + last HISTORYDAYS days */
235 fprintf(fp
,"%lu %u",lastsample
,todaysamples
);
236 for(i
=0;i
<HISTORYDAYS
;i
++) {
237 fprintf(fp
," %u",lastdaysamples
[i
]);
241 /* body: channel, lastsample, samplestoday, sizetoday, <last sizes, last samples> */
242 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
243 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
244 if ((chp
=cip
->exts
[csext
])==NULL
) {
247 fprintf(fp
,"%s %lu %u %u %u",cip
->name
->content
,chp
->lastsampled
,chp
->todaysamples
,chp
->todayusers
,chp
->todaymax
);
249 for (j
=0;j
<HISTORYDAYS
;j
++) {
250 fprintf(fp
," %u %u %u",chp
->lastdays
[j
],chp
->lastdaysamples
[j
],chp
->lastmax
[j
]);
259 void loadchanstats() {
264 unsigned int chancount
=0;
268 if ((fp
=fopen("chanstats","r"))==NULL
) {
269 Error("chanstats",ERR_ERROR
,"Unable to load channel stats file");
273 fgets(inbuf
,2048,fp
);
275 Error("chanstats",ERR_ERROR
,"Corrupt channel stats file");
280 if ((splitline(inbuf
,args
,50,0))!=(HISTORYDAYS
+2)) {
281 Error("chanstats",ERR_ERROR
,"Corrupt channel stats file");
286 lastsample
=strtol(args
[0],NULL
,10);
287 todaysamples
=strtol(args
[1],NULL
,10);
288 for(i
=0;i
<HISTORYDAYS
;i
++) {
289 lastdaysamples
[i
]=strtol(args
[i
+2],NULL
,10);
293 fgets(inbuf
,2048,fp
);
298 if ((splitline(inbuf
,args
,100,0))!=5+(HISTORYDAYS
*3)) {
299 Error("chanstats",ERR_WARNING
,"Corrupt channel stats line");
303 cip
=findorcreatechanindex(args
[0]);
304 if (cip
->exts
[csext
]!=NULL
) {
305 Error("chanstats",ERR_ERROR
,"Duplicate stats entry for channel %s",args
[0]);
308 cip
->exts
[csext
]=chp
=getchanstats();
310 chp
->lastsampled
=strtol(args
[1],NULL
,10);
311 chp
->todaysamples
=strtol(args
[2],NULL
,10);
312 chp
->todayusers
=strtol(args
[3],NULL
,10);
313 chp
->todaymax
=strtol(args
[4],NULL
,10);
315 for(i
=0;i
<HISTORYDAYS
;i
++) {
316 chp
->lastdays
[i
]=strtol(args
[5+(i
*3)],NULL
,10);
317 chp
->lastdaysamples
[i
]=strtol(args
[6+(i
*3)],NULL
,10);
318 chp
->lastmax
[i
]=strtol(args
[7+(i
*3)],NULL
,10);
324 Error("chanstats",ERR_INFO
,"Loaded %u channels",chancount
);
327 int dochanstats(void *source
, int cargc
, char **cargv
) {
330 nick
*sender
=(nick
*)source
;
336 controlreply(sender
,"Usage: chanstats #channel");
340 cip
=findchanindex(cargv
[0]);
343 controlreply(sender
,"Can't find any record of %s.",cargv
[0]);
347 csp
=cip
->exts
[csext
];
350 controlreply(sender
,"Can't find any stats for %s.",cip
->name
->content
);
354 controlreply(sender
,"Statistics for channel %s:",cip
->name
->content
);
357 controlreply(sender
," [Service up <1hour, can't report last hour stats]");
360 for(i
=0;i
<SAMPLEHISTORY
;i
++) {
361 tot
+=csp
->lastsamples
[i
];
362 if (csp
->lastsamples
[i
]==0) {
366 controlreply(sender
," Last hour : %6.1f average users, empty %5.1f%% of the time",(float)tot
/SAMPLEHISTORY
,((float)emp
/SAMPLEHISTORY
)*100);
368 controlreply(sender
, " Today so far : %6.1f average users, empty %5.1f%% of the time, %4d max",
369 (float)csp
->todayusers
/todaysamples
, ((float)(todaysamples
-csp
->todaysamples
)/todaysamples
)*100,
372 themax
=csp
->lastmax
[0];
374 controlreply(sender
, " Yesterday : %6.1f average users, empty %5.1f%% of the time, %4d max",
375 (float)csp
->lastdays
[0]/10, ((float)(lastdaysamples
[0]-csp
->lastdaysamples
[0])/lastdaysamples
[0])*100,
382 k
+=csp
->lastdaysamples
[i
];
383 l
+=lastdaysamples
[i
];
384 if (csp
->lastmax
[i
]>themax
) {
385 themax
=csp
->lastmax
[i
];
388 controlreply(sender
, " 7-day average : %6.1f average users, empty %5.1f%% of the time, %4d max",
389 (float)j
/70,(float)((l
-k
)*100)/l
, themax
);
391 /* 14-day average: continuation of last loop */
394 k
+=csp
->lastdaysamples
[i
];
395 l
+=lastdaysamples
[i
];
396 if (csp
->lastmax
[i
]>themax
) {
397 themax
=csp
->lastmax
[i
];
400 controlreply(sender
, " 14-day average: %6.1f average users, empty %5.1f%% of the time, %4d max",
401 (float)j
/140,(float)((l
-k
)*100)/l
, themax
);
408 int doexpirecheck(void *source
, int cargc
, char **cargv
) {
409 nick
*sender
=(nick
*)source
;
415 controlreply(sender
, "Usage: expirecheck #channel");
419 if ((cip
=findchanindex(cargv
[0]))==NULL
) {
420 /* Couldn't find the channel at all: it's in no way big enough! */
422 controlreply(sender
,"delchan %s",cargv
[0]);
426 if ((csp
=(chanstats
*)cip
->exts
[csext
])==NULL
) {
427 /* No stats: similar */
429 controlreply(sender
,"delchan %s",cargv
[0]);
433 /* Did they hit the minimum today? */
434 if (csp
->todaymax
>= EXPIREMIN
) {
439 for (i
=0;i
<HISTORYDAYS
;i
++) {
440 if (csp
->lastmax
[i
] >= EXPIREMIN
) {
445 /* If not, delchan time! */
446 controlreply(sender
,"delchan %s",cargv
[0]);
450 int douserhistogram(void *source
, int cargc
, char **cargv
) {
454 nick
*sender
=(nick
*)source
;
455 int top
=0,tot
=0,servertot
=0,servertop
=0;
458 int sig
=0,serversig
=0;
461 if ((theserver
=findserver(cargv
[0]))<0) {
462 controlreply(sender
,"Can't find server %s",cargv
[0]);
472 for (i
=0;i
<NICKHASHSIZE
;i
++) {
473 for (np
=nicktable
[i
];np
;np
=np
->next
) {
474 if (np
->channels
->cursi
<= 20) {
475 histdata
[np
->channels
->cursi
]++;
476 if (theserver
>=0 && homeserver(np
->numeric
)==theserver
) {
478 serverdata
[np
->channels
->cursi
]++;
488 controlreply(sender
,"Size Count %%ge Count+ %s",(theserver
>=0?serverlist
[theserver
].name
->content
:""));
492 controlreply(sender
,"%4d %6d %4.1f%% %6d %6d %4.1f%% %6d",i
,histdata
[i
],(100.0*histdata
[i
])/tot
,top
,
493 serverdata
[i
],(100.0*serverdata
[i
])/servertot
, servertop
);
494 servertop
-=serverdata
[i
];
495 serversig
+= (i
* serverdata
[i
]);
497 controlreply(sender
,"%4d %6d %4.1f%% %6d",i
,histdata
[i
],(100.0*histdata
[i
])/tot
,top
);
499 sig
+= (i
* histdata
[i
]);
503 controlreply(sender
,"Average channel count (all users): %.2f",(float)sig
/tot
);
505 controlreply(sender
,"Average channel count (%s): %.2f",serverlist
[theserver
].name
->content
,(float)serversig
/servertot
);
513 int dochanhistogram(void *source
, int cargc
, char **cargv
) {
514 int histdata
[6][HISTITEMS
];
515 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,
516 25.0, 50.0, 75.0, 100.0, 150.0, 200.0, 250.0, 300.0, 400.0, 500.0,
520 nick
*sender
=(nick
*)source
;
523 char header
[(6*14)+10];
529 controlreply(sender
,"Usage: channelhistogram <metrics>");
530 controlreply(sender
,"Valid metrics are: now, today, yesterday, days <daycount>, nick <nick>.");
531 controlreply(sender
,"Up to 6 metrics may be specified.");
535 for (i
=0;i
<cargc
;i
++) {
536 /* Look for a valid metric */
537 if (!ircd_strcmp(cargv
[i
],"now")) {
538 dohist_now(histdata
[hists
++],bounds
,histsize
,NULL
);
539 strcat(header
,"Current Chans ");
540 } else if (!ircd_strcmp(cargv
[i
],"today")) {
541 dohist_today(histdata
[hists
++],bounds
,histsize
);
542 strcat(header
,"Today Average ");
543 } else if (!ircd_strcmp(cargv
[i
],"yesterday")) {
544 dohist_days(histdata
[hists
++],bounds
,histsize
,1);
545 strcat(header
,"Yesterday Avg ");
546 } else if (!ircd_strcmp(cargv
[i
],"namelen")) {
547 dohist_namelen(histdata
[hists
++],bounds
,histsize
);
548 strcat(header
,"Name Length ");
549 } else if (!ircd_strcmp(cargv
[i
],"days")) {
551 controlreply(sender
,"'days' metric requires a numeric argument.");
554 j
=strtol(cargv
[i
+1],NULL
,10);
556 controlreply(sender
,"Must specify a number of days between 1 and 14.");
559 dohist_days(histdata
[hists
++],bounds
,histsize
,j
);
560 sprintf(buf
,"%2d Day Avg ",j
);
563 } else if (!ircd_strcmp(cargv
[i
],"nick")) {
565 controlreply(sender
,"'nick' metric requires a nickname argument.");
568 if ((targetnick
=getnickbynick(cargv
[i
+1]))==NULL
) {
569 controlreply(sender
,"Couldn't find user %s.",cargv
[i
+1]);
572 dohist_now (histdata
[hists
++],bounds
,histsize
,targetnick
);
573 sprintf(buf
,"w/%-11.11s ",targetnick
->nick
);
577 controlreply(sender
,"Unknown metric %s.",cargv
[i
]);
582 controlreply(sender
," %s",header
);
585 for (i
=0;i
<hists
;i
++) {
586 strcat(buf
,"InRnge Range+ ");
588 controlreply(sender
," %s",buf
);
590 for(i
=0;i
<(histsize
-1);i
++) {
591 if (i
==(histsize
-2)) {
592 pos
=sprintf(buf
,"%6.1f+ ",bounds
[i
]);
594 pos
=sprintf(buf
,"%6.1f-%6.1f ",bounds
[i
],bounds
[i
+1]);
596 for(j
=0;j
<hists
;j
++) {
597 pos
+=sprintf(&(buf
[pos
]),"%6d %6d ",histdata
[j
][i
]-histdata
[j
][i
+1],histdata
[j
][i
]);
599 controlreply(sender
,"%s",buf
);
605 void dohist_now(int *data
, float *bounds
, int cats
, nick
*np
) {
612 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
613 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
614 if (cip
->channel
==NULL
) {
617 if (np
==NULL
|| NULL
!=getnumerichandlefromchanhash(cip
->channel
->users
, np
->numeric
))
618 for (j
=0;j
<cats
;j
++) {
619 if (cip
->channel
->users
->totalusers
>=bounds
[j
]) {
627 void dohist_today(int *data
, float *bounds
, int cats
) {
636 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
637 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
638 if ((csp
=cip
->exts
[csext
])==NULL
) {
641 f
=(float)csp
->todayusers
/todaysamples
;
642 for(j
=0;j
<cats
;j
++) {
651 void dohist_days(int *data
, float *bounds
, int cats
, int days
) {
660 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
661 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
662 if ((csp
=cip
->exts
[csext
])==NULL
) {
666 for (j
=0;j
<days
;j
++) {
669 f
=(float)k
/(days
*10);
670 for(j
=0;j
<cats
;j
++) {
679 void dohist_namelen(int *data
, float *bounds
, int cats
) {
686 for(i
=0;i
<CHANNELHASHSIZE
;i
++) {
687 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
688 for (j
=0;j
<cats
;j
++) {
689 if (cip
->name
->length
>=bounds
[j
]) {