]>
Commit | Line | Data |
---|---|---|
c86edd1d Q |
1 | |
2 | #include "chanstats.h" | |
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" | |
87698d77 | 12 | #include "../lib/version.h" |
c86edd1d Q |
13 | |
14 | #include <stdio.h> | |
15 | #include <string.h> | |
16 | ||
70b0a4e5 | 17 | MODULE_VERSION(""); |
87698d77 | 18 | |
c86edd1d Q |
19 | int csext; |
20 | int sampleindex; | |
e36fed08 | 21 | time_t chanstats_lastsample; |
c86edd1d Q |
22 | time_t lastday; |
23 | int uponehour; | |
24 | int failedinit; | |
25 | time_t lastsave; | |
26 | ||
27 | unsigned int lastdaysamples[HISTORYDAYS]; | |
28 | unsigned int todaysamples; | |
29 | ||
30 | void doupdate(void *arg); | |
31 | void updatechanstats(chanindex *cip, time_t now); | |
32 | void rotatechanstats(); | |
33 | void savechanstats(); | |
34 | void loadchanstats(); | |
ab0c8601 | 35 | int dochanstatssave(void *source, int argc, char **argv); |
c86edd1d Q |
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); | |
44 | ||
45 | void _init() { | |
46 | time_t now,when; | |
47 | ||
48 | csext=registerchanext("chanstats"); | |
49 | if (csext<0) { | |
50 | /* PANIC PANIC PANIC PANIC */ | |
51 | Error("chanstats",ERR_ERROR,"Couldn't register channel extension"); | |
52 | failedinit=1; | |
53 | } else { | |
54 | initchanstatsalloc(); | |
55 | lastday=(getnettime()/(24*3600)); | |
56 | uponehour=0; | |
57 | sampleindex=-1; | |
58 | memset(lastdaysamples,0,sizeof(lastdaysamples)); | |
59 | todaysamples=0; | |
60 | failedinit=0; | |
61 | ||
62 | loadchanstats(); | |
63 | ||
64 | /* Work out when to take the next sample */ | |
65 | now=getnettime(); | |
e36fed08 | 66 | if (now < chanstats_lastsample) { |
67 | Error("chanstats",ERR_WARNING,"Last sample time in future (%d > %d)",chanstats_lastsample,now); | |
c86edd1d | 68 | when=now; |
e36fed08 | 69 | } else if (now<(chanstats_lastsample+SAMPLEINTERVAL)) { |
70 | lastday=chanstats_lastsample/(24*3600); | |
71 | when=chanstats_lastsample+SAMPLEINTERVAL; | |
c86edd1d | 72 | } else { |
e36fed08 | 73 | if ((chanstats_lastsample/(24*3600))<lastday) { |
c86edd1d Q |
74 | rotatechanstats(); |
75 | } | |
76 | when=now; | |
77 | } | |
78 | ||
79 | lastsave=now; | |
80 | registercontrolcmd("chanstats",10,1,&dochanstats); | |
81 | registercontrolcmd("channelhistogram",10,13,&dochanhistogram); | |
82 | registercontrolcmd("userhistogram",10,1,&douserhistogram); | |
83 | registercontrolcmd("expirecheck",10,1,&doexpirecheck); | |
ab0c8601 | 84 | registercontrolhelpcmd("chanstatssave",NO_DEVELOPER,1, &dochanstatssave, "Usage: chanstatssave\nForce a save of chanstats data"); |
c86edd1d Q |
85 | schedulerecurring(when,0,SAMPLEINTERVAL,&doupdate,NULL); |
86 | } | |
87 | } | |
88 | ||
89 | void _fini() { | |
90 | if (failedinit==0) { | |
91 | savechanstats(); | |
92 | deleteschedule(NULL,&doupdate,NULL); | |
93 | deregistercontrolcmd("chanstats",&dochanstats); | |
94 | deregistercontrolcmd("channelhistogram",&dochanhistogram); | |
95 | deregistercontrolcmd("userhistogram",&douserhistogram); | |
96 | deregistercontrolcmd("expirecheck",&doexpirecheck); | |
ab0c8601 | 97 | deregistercontrolcmd("chanstatssave",&dochanstatssave); |
c86edd1d Q |
98 | releasechanext(csext); |
99 | cstsfreeall(); | |
100 | } | |
101 | } | |
102 | ||
103 | /* | |
104 | * doupdate: | |
105 | * This is the core function which is scheduled. | |
106 | */ | |
107 | ||
108 | void doupdate(void *arg) { | |
109 | int i; | |
110 | time_t now; | |
111 | chanindex *cip; | |
112 | ||
113 | /* Get the current time and move the history pointer along one and count the sample */ | |
114 | now=getnettime(); | |
115 | ||
116 | Error("chanstats",ERR_INFO,"Running chanstats update."); | |
117 | ||
118 | if (now/(24*3600) > lastday) { | |
119 | rotatechanstats(); | |
120 | lastday=(now/(24*3600)); | |
121 | } | |
122 | ||
123 | todaysamples++; | |
124 | sampleindex++; | |
125 | if (sampleindex==SAMPLEHISTORY) { | |
126 | sampleindex=0; | |
127 | uponehour=1; | |
128 | } | |
129 | ||
130 | /* | |
131 | * Loop over all chans doing update | |
132 | */ | |
133 | ||
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); | |
138 | } | |
139 | } | |
140 | } | |
141 | ||
e36fed08 | 142 | chanstats_lastsample=now; |
c86edd1d Q |
143 | if (now-lastsave > SAVEINTERVAL) { |
144 | savechanstats(); | |
145 | lastsave=now; | |
146 | } | |
147 | } | |
148 | ||
149 | void updatechanstats(chanindex *cip, time_t now) { | |
150 | chanstats *csp; | |
151 | int currentusers; | |
152 | ||
153 | csp=cip->exts[csext]; | |
154 | if (csp==NULL) { | |
155 | csp=cip->exts[csext]=getchanstats(); | |
156 | memset(csp,0,sizeof(chanstats)); | |
157 | } | |
158 | ||
159 | if (cip->channel!=NULL) { | |
08d6ada8 CP |
160 | channel *cp = cip->channel; |
161 | int i; | |
162 | nick *np; | |
163 | ||
164 | currentusers=countuniquehosts(cp); | |
165 | ||
166 | for (i=0;i<cp->users->hashsize;i++) { | |
167 | if (cp->users->content[i]==nouser) | |
168 | continue; | |
169 | ||
170 | if ((np=getnickbynumeric(cp->users->content[i]))==NULL) { | |
171 | Error("channel",ERR_ERROR,"Found unknown numeric %u on channel %s",cp->users->content[i],cp->index->name->content); | |
172 | continue; | |
173 | } | |
174 | ||
175 | if (IsXOper(np) || IsService(np)) | |
176 | currentusers--; | |
177 | } | |
178 | ||
c86edd1d Q |
179 | csp->todaysamples++; |
180 | } else { | |
181 | currentusers=0; | |
182 | } | |
183 | ||
184 | csp->lastsamples[sampleindex]=currentusers; | |
185 | csp->lastsampled=now; | |
186 | csp->todayusers+=currentusers; | |
187 | ||
188 | if (currentusers > csp->todaymax) { | |
189 | csp->todaymax=currentusers; | |
190 | } | |
191 | } | |
192 | ||
193 | void rotatechanstats() { | |
194 | int i,j,k; | |
195 | chanindex *cip,*ncip; | |
196 | chanstats *csp; | |
197 | ||
198 | for (i=0;i<CHANNELHASHSIZE;i++) { | |
199 | for (cip=chantable[i];cip;cip=ncip) { | |
200 | ncip=cip->next; | |
201 | ||
202 | if ((csp=cip->exts[csext])==NULL) { | |
203 | continue; | |
204 | } | |
205 | ||
206 | if (csp->todaysamples==0) { | |
207 | /* No samples today, might want to delete */ | |
208 | k=0; | |
209 | for(j=0;j<HISTORYDAYS;j++) { | |
210 | k+=csp->lastdays[j]; | |
211 | } | |
212 | if (k<50) { | |
213 | freechanstats(csp); | |
214 | cip->exts[csext]=NULL; | |
215 | releasechanindex(cip); | |
216 | continue; | |
217 | } | |
218 | } | |
219 | ||
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)); | |
224 | ||
225 | csp->lastdaysamples[0]=csp->todaysamples; | |
226 | csp->lastdays[0]=(csp->todayusers * 10)/todaysamples; | |
227 | csp->lastmax[0]=csp->todaymax; | |
228 | ||
229 | csp->todaysamples=0; | |
230 | csp->todayusers=0; | |
231 | if (cip->channel) { | |
232 | csp->todaymax=cip->channel->users->totalusers; | |
233 | } else { | |
234 | csp->todaymax=0; | |
235 | } | |
236 | } | |
237 | } | |
238 | ||
239 | memmove(&lastdaysamples[1],&lastdaysamples[0],(HISTORYDAYS-1)*sizeof(unsigned int)); | |
240 | lastdaysamples[0]=todaysamples; | |
241 | todaysamples=0; | |
242 | } | |
243 | ||
244 | void savechanstats() { | |
245 | FILE *fp; | |
246 | int i,j; | |
247 | chanindex *cip; | |
248 | chanstats *chp; | |
249 | ||
ab0c8601 | 250 | if ((fp=fopen("data/chanstats","w"))==NULL) { |
c86edd1d Q |
251 | return; |
252 | } | |
253 | ||
254 | /* header: samples for today + last HISTORYDAYS days */ | |
255 | ||
e36fed08 | 256 | fprintf(fp,"%lu %u",chanstats_lastsample,todaysamples); |
c86edd1d Q |
257 | for(i=0;i<HISTORYDAYS;i++) { |
258 | fprintf(fp," %u",lastdaysamples[i]); | |
259 | } | |
260 | fprintf(fp,"\n"); | |
261 | ||
e36fed08 | 262 | /* body: channel, chanstats_lastsample, samplestoday, sizetoday, <last sizes, last samples> */ |
c86edd1d Q |
263 | for (i=0;i<CHANNELHASHSIZE;i++) { |
264 | for (cip=chantable[i];cip;cip=cip->next) { | |
265 | if ((chp=cip->exts[csext])==NULL) { | |
266 | continue; | |
267 | } | |
268 | fprintf(fp,"%s %lu %u %u %u",cip->name->content,chp->lastsampled,chp->todaysamples,chp->todayusers,chp->todaymax); | |
269 | ||
270 | for (j=0;j<HISTORYDAYS;j++) { | |
271 | fprintf(fp," %u %u %u",chp->lastdays[j],chp->lastdaysamples[j],chp->lastmax[j]); | |
272 | } | |
273 | fprintf(fp,"\n"); | |
274 | } | |
275 | } | |
276 | ||
277 | fclose(fp); | |
278 | } | |
279 | ||
280 | void loadchanstats() { | |
281 | FILE *fp; | |
282 | int i; | |
283 | char inbuf[2048]; | |
284 | char *args[100]; | |
285 | unsigned int chancount=0; | |
286 | chanstats *chp; | |
287 | chanindex *cip; | |
288 | ||
ab0c8601 | 289 | if ((fp=fopen("data/chanstats","r"))==NULL) { |
c86edd1d Q |
290 | Error("chanstats",ERR_ERROR,"Unable to load channel stats file"); |
291 | return; | |
292 | } | |
293 | ||
294 | fgets(inbuf,2048,fp); | |
295 | if (feof(fp)) { | |
296 | Error("chanstats",ERR_ERROR,"Corrupt channel stats file"); | |
297 | fclose(fp); | |
298 | return; | |
299 | } | |
300 | ||
301 | if ((splitline(inbuf,args,50,0))!=(HISTORYDAYS+2)) { | |
302 | Error("chanstats",ERR_ERROR,"Corrupt channel stats file"); | |
303 | fclose(fp); | |
304 | return; | |
305 | } | |
306 | ||
e36fed08 | 307 | chanstats_lastsample=strtol(args[0],NULL,10); |
c86edd1d Q |
308 | todaysamples=strtol(args[1],NULL,10); |
309 | for(i=0;i<HISTORYDAYS;i++) { | |
310 | lastdaysamples[i]=strtol(args[i+2],NULL,10); | |
311 | } | |
312 | ||
313 | while (!feof(fp)) { | |
314 | fgets(inbuf,2048,fp); | |
315 | if (feof(fp)) { | |
316 | break; | |
317 | } | |
318 | ||
319 | if ((splitline(inbuf,args,100,0))!=5+(HISTORYDAYS*3)) { | |
320 | Error("chanstats",ERR_WARNING,"Corrupt channel stats line"); | |
321 | continue; | |
322 | } | |
323 | ||
324 | cip=findorcreatechanindex(args[0]); | |
325 | if (cip->exts[csext]!=NULL) { | |
326 | Error("chanstats",ERR_ERROR,"Duplicate stats entry for channel %s",args[0]); | |
327 | continue; | |
328 | } | |
329 | cip->exts[csext]=chp=getchanstats(); | |
330 | ||
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); | |
335 | ||
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); | |
340 | } | |
341 | ||
342 | chancount++; | |
343 | } | |
344 | ||
345 | Error("chanstats",ERR_INFO,"Loaded %u channels",chancount); | |
346 | } | |
347 | ||
348 | int dochanstats(void *source, int cargc, char **cargv) { | |
349 | chanstats *csp; | |
350 | chanindex *cip; | |
351 | nick *sender=(nick *)source; | |
352 | int i,j,k,l; | |
353 | int tot,emp; | |
354 | int themax; | |
355 | ||
356 | if (cargc<1) { | |
357 | controlreply(sender,"Usage: chanstats #channel"); | |
358 | return CMD_ERROR; | |
359 | } | |
360 | ||
361 | cip=findchanindex(cargv[0]); | |
362 | ||
363 | if (cip==NULL) { | |
364 | controlreply(sender,"Can't find any record of %s.",cargv[0]); | |
365 | return CMD_ERROR; | |
366 | } | |
367 | ||
368 | csp=cip->exts[csext]; | |
369 | ||
370 | if (csp==NULL) { | |
371 | controlreply(sender,"Can't find any stats for %s.",cip->name->content); | |
372 | return CMD_ERROR; | |
373 | } | |
374 | ||
375 | controlreply(sender,"Statistics for channel %s:",cip->name->content); | |
376 | ||
377 | if (uponehour==0) { | |
378 | controlreply(sender," [Service up <1hour, can't report last hour stats]"); | |
379 | } else { | |
380 | tot=0; emp=0; | |
381 | for(i=0;i<SAMPLEHISTORY;i++) { | |
382 | tot+=csp->lastsamples[i]; | |
383 | if (csp->lastsamples[i]==0) { | |
384 | emp++; | |
385 | } | |
386 | } | |
387 | controlreply(sender," Last hour : %6.1f average users, empty %5.1f%% of the time",(float)tot/SAMPLEHISTORY,((float)emp/SAMPLEHISTORY)*100); | |
388 | } | |
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, | |
391 | csp->todaymax); | |
392 | ||
393 | themax=csp->lastmax[0]; | |
394 | ||
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, | |
397 | themax); | |
398 | ||
399 | /* 7-day average */ | |
400 | j=k=l=0; | |
401 | for (i=0;i<7;i++) { | |
402 | j+=csp->lastdays[i]; | |
403 | k+=csp->lastdaysamples[i]; | |
404 | l+=lastdaysamples[i]; | |
405 | if (csp->lastmax[i]>themax) { | |
406 | themax=csp->lastmax[i]; | |
407 | } | |
408 | } | |
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); | |
411 | ||
412 | /* 14-day average: continuation of last loop */ | |
413 | for (;i<14;i++) { | |
414 | j+=csp->lastdays[i]; | |
415 | k+=csp->lastdaysamples[i]; | |
416 | l+=lastdaysamples[i]; | |
417 | if (csp->lastmax[i]>themax) { | |
418 | themax=csp->lastmax[i]; | |
419 | } | |
420 | } | |
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); | |
423 | ||
424 | return CMD_OK; | |
425 | } | |
426 | ||
427 | #define EXPIREMIN 4 | |
428 | ||
429 | int doexpirecheck(void *source, int cargc, char **cargv) { | |
430 | nick *sender=(nick *)source; | |
431 | chanindex *cip; | |
432 | chanstats *csp; | |
433 | int i; | |
434 | ||
435 | if (cargc<1) { | |
436 | controlreply(sender, "Usage: expirecheck #channel"); | |
437 | return CMD_ERROR; | |
438 | } | |
439 | ||
440 | if ((cip=findchanindex(cargv[0]))==NULL) { | |
441 | /* Couldn't find the channel at all: it's in no way big enough! */ | |
442 | ||
443 | controlreply(sender,"delchan %s",cargv[0]); | |
444 | return CMD_OK; | |
445 | } | |
446 | ||
447 | if ((csp=(chanstats *)cip->exts[csext])==NULL) { | |
448 | /* No stats: similar */ | |
449 | ||
450 | controlreply(sender,"delchan %s",cargv[0]); | |
451 | return CMD_OK; | |
452 | } | |
453 | ||
454 | /* Did they hit the minimum today? */ | |
455 | if (csp->todaymax >= EXPIREMIN) { | |
456 | return CMD_OK; | |
457 | } | |
458 | ||
459 | /* Or recently? */ | |
460 | for (i=0;i<HISTORYDAYS;i++) { | |
461 | if (csp->lastmax[i] >= EXPIREMIN) { | |
462 | return CMD_OK; | |
463 | } | |
464 | } | |
465 | ||
466 | /* If not, delchan time! */ | |
467 | controlreply(sender,"delchan %s",cargv[0]); | |
468 | return CMD_OK; | |
469 | } | |
470 | ||
471 | int douserhistogram(void *source, int cargc, char **cargv) { | |
472 | int histdata[21]; | |
473 | int serverdata[21]; | |
474 | nick *np; | |
475 | nick *sender=(nick *)source; | |
476 | int top=0,tot=0,servertot=0,servertop=0; | |
477 | int i; | |
478 | int theserver=-1; | |
479 | int sig=0,serversig=0; | |
480 | ||
481 | if (cargc>0) { | |
482 | if ((theserver=findserver(cargv[0]))<0) { | |
483 | controlreply(sender,"Can't find server %s",cargv[0]); | |
484 | return CMD_ERROR; | |
485 | } | |
486 | } | |
487 | ||
488 | for (i=0;i<21;i++) { | |
489 | histdata[i]=0; | |
490 | serverdata[i]=0; | |
491 | } | |
492 | ||
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) { | |
498 | servertop++; | |
499 | serverdata[np->channels->cursi]++; | |
500 | } | |
501 | top++; | |
502 | } | |
503 | } | |
504 | } | |
505 | ||
506 | tot=top; | |
507 | servertot=servertop; | |
508 | ||
509 | controlreply(sender,"Size Count %%ge Count+ %s",(theserver>=0?serverlist[theserver].name->content:"")); | |
510 | ||
511 | for (i=0;i<21;i++) { | |
512 | if (theserver>=0) { | |
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]); | |
517 | } else { | |
518 | controlreply(sender,"%4d %6d %4.1f%% %6d",i,histdata[i],(100.0*histdata[i])/tot,top); | |
519 | } | |
520 | sig += (i * histdata[i]); | |
521 | top-=histdata[i]; | |
522 | } | |
523 | ||
524 | controlreply(sender,"Average channel count (all users): %.2f",(float)sig/tot); | |
525 | if (theserver>=0) { | |
526 | controlreply(sender,"Average channel count (%s): %.2f",serverlist[theserver].name->content,(float)serversig/servertot); | |
527 | } | |
4ad1cf7a CP |
528 | |
529 | return CMD_OK; | |
c86edd1d Q |
530 | } |
531 | ||
532 | #define HISTITEMS 30 | |
533 | ||
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, | |
538 | 1000.0, 1000000.0 }; | |
539 | int histsize=23; | |
540 | int hists=0; | |
541 | nick *sender=(nick *)source; | |
542 | nick *targetnick; | |
543 | int i,j,pos; | |
544 | char header[(6*14)+10]; | |
545 | char buf[512]; | |
546 | ||
547 | header[0]='\0'; | |
548 | ||
549 | if (cargc<1) { | |
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."); | |
553 | return CMD_ERROR; | |
554 | } | |
555 | ||
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")) { | |
571 | if ((i+1)>=cargc) { | |
572 | controlreply(sender,"'days' metric requires a numeric argument."); | |
573 | return CMD_ERROR; | |
574 | } | |
575 | j=strtol(cargv[i+1],NULL,10); | |
576 | if (j<1 || j>14) { | |
577 | controlreply(sender,"Must specify a number of days between 1 and 14."); | |
578 | return CMD_ERROR; | |
579 | } | |
580 | dohist_days(histdata[hists++],bounds,histsize,j); | |
581 | sprintf(buf,"%2d Day Avg ",j); | |
582 | strcat(header,buf); | |
583 | i++; | |
584 | } else if (!ircd_strcmp(cargv[i],"nick")) { | |
585 | if ((i+1)>=cargc) { | |
586 | controlreply(sender,"'nick' metric requires a nickname argument."); | |
587 | return CMD_ERROR; | |
588 | } | |
589 | if ((targetnick=getnickbynick(cargv[i+1]))==NULL) { | |
590 | controlreply(sender,"Couldn't find user %s.",cargv[i+1]); | |
591 | return CMD_ERROR; | |
592 | } | |
593 | dohist_now (histdata[hists++],bounds,histsize,targetnick); | |
594 | sprintf(buf,"w/%-11.11s ",targetnick->nick); | |
595 | strcat(header,buf); | |
596 | i++; | |
597 | } else { | |
598 | controlreply(sender,"Unknown metric %s.",cargv[i]); | |
599 | return CMD_ERROR; | |
600 | } | |
601 | } | |
602 | ||
603 | controlreply(sender," %s",header); | |
604 | ||
605 | buf[0]='\0'; | |
606 | for (i=0;i<hists;i++) { | |
607 | strcat(buf,"InRnge Range+ "); | |
608 | } | |
609 | controlreply(sender," %s",buf); | |
610 | ||
611 | for(i=0;i<(histsize-1);i++) { | |
612 | if (i==(histsize-2)) { | |
613 | pos=sprintf(buf,"%6.1f+ ",bounds[i]); | |
614 | } else { | |
615 | pos=sprintf(buf,"%6.1f-%6.1f ",bounds[i],bounds[i+1]); | |
616 | } | |
617 | for(j=0;j<hists;j++) { | |
618 | pos+=sprintf(&(buf[pos]),"%6d %6d ",histdata[j][i]-histdata[j][i+1],histdata[j][i]); | |
619 | } | |
620 | controlreply(sender,"%s",buf); | |
621 | } | |
622 | ||
623 | return CMD_OK; | |
624 | } | |
625 | ||
626 | void dohist_now(int *data, float *bounds, int cats, nick *np) { | |
627 | int i,j; | |
628 | chanindex *cip; | |
629 | ||
630 | for (i=0;i<cats;i++) | |
631 | data[i]=0; | |
632 | ||
633 | for (i=0;i<CHANNELHASHSIZE;i++) { | |
634 | for (cip=chantable[i];cip;cip=cip->next) { | |
635 | if (cip->channel==NULL) { | |
636 | continue; | |
637 | } | |
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]) { | |
641 | data[j]++; | |
642 | } | |
643 | } | |
644 | } | |
645 | } | |
646 | } | |
647 | ||
648 | void dohist_today(int *data, float *bounds, int cats) { | |
649 | int i,j; | |
650 | chanindex *cip; | |
651 | chanstats *csp; | |
652 | float f; | |
653 | ||
654 | for (i=0;i<cats;i++) | |
655 | data[i]=0; | |
656 | ||
657 | for (i=0;i<CHANNELHASHSIZE;i++) { | |
658 | for (cip=chantable[i];cip;cip=cip->next) { | |
659 | if ((csp=cip->exts[csext])==NULL) { | |
660 | continue; | |
661 | } | |
662 | f=(float)csp->todayusers/todaysamples; | |
663 | for(j=0;j<cats;j++) { | |
664 | if (f>=bounds[j]) { | |
665 | data[j]++; | |
666 | } | |
667 | } | |
668 | } | |
669 | } | |
670 | } | |
671 | ||
672 | void dohist_days(int *data, float *bounds, int cats, int days) { | |
673 | int i,j,k; | |
674 | chanindex *cip; | |
675 | chanstats *csp; | |
676 | float f; | |
677 | ||
678 | for (i=0;i<cats;i++) | |
679 | data[i]=0; | |
680 | ||
681 | for (i=0;i<CHANNELHASHSIZE;i++) { | |
682 | for (cip=chantable[i];cip;cip=cip->next) { | |
683 | if ((csp=cip->exts[csext])==NULL) { | |
684 | continue; | |
685 | } | |
686 | k=0; | |
687 | for (j=0;j<days;j++) { | |
688 | k+=csp->lastdays[j]; | |
689 | } | |
690 | f=(float)k/(days*10); | |
691 | for(j=0;j<cats;j++) { | |
692 | if (f>=bounds[j]) { | |
693 | data[j]++; | |
694 | } | |
695 | } | |
696 | } | |
697 | } | |
698 | } | |
699 | ||
700 | void dohist_namelen(int *data, float *bounds, int cats) { | |
701 | int i,j; | |
702 | chanindex *cip; | |
703 | ||
704 | for (i=0;i<cats;i++) | |
705 | data[i]=0; | |
706 | ||
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]) { | |
711 | data[j]++; | |
712 | } | |
713 | } | |
714 | } | |
715 | } | |
716 | } | |
ab0c8601 P |
717 | |
718 | int dochanstatssave(void *source, int cargc, char **cargv) { | |
719 | nick *sender=(nick *)source; | |
720 | ||
721 | controlreply(sender,"Saving Chanstats.."); | |
722 | savechanstats(); | |
723 | controlreply(sender,"Chanstats saved"); | |
724 | return CMD_OK; | |
725 | } |