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