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