Enable building WITHOUT_ACCOUNTING
[freeradius.git] / src / main / stats.c
1 /*
2  * stats.c      Internal statistics handling.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2008  The FreeRADIUS server project
21  * Copyright 2008  Alan DeKok <aland@deployingradius.com>
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/rad_assert.h>
29
30 #ifdef WITH_STATS
31
32 #define USEC (1000000)
33 #define EMA_SCALE (100)
34 #define PREC (USEC * EMA_SCALE)
35
36 #define F_EMA_SCALE (1000000)
37
38 static struct timeval   start_time;
39 static struct timeval   hup_time;
40
41 fr_stats_t radius_auth_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
42 #ifdef WITH_ACCOUNTING
43 fr_stats_t radius_acct_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
44 #endif
45
46 #ifdef WITH_PROXY
47 fr_stats_t proxy_auth_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
48 #ifdef WITH_ACCOUNTING
49 fr_stats_t proxy_acct_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
50 #endif
51 #endif
52
53 void request_stats_final(REQUEST *request)
54 {
55         if (request->master_state == REQUEST_COUNTED) return;
56
57         if ((request->listener->type != RAD_LISTEN_NONE) &&
58 #ifdef WITH_ACCOUNTING
59             (request->listener->type != RAD_LISTEN_ACCT) &&
60 #endif
61             (request->listener->type != RAD_LISTEN_AUTH)) return;
62
63 #undef INC_AUTH
64 #define INC_AUTH(_x) radius_auth_stats._x++;request->listener->stats._x++;if (request->client && request->client->auth) request->client->auth->_x++;
65
66
67 #undef INC_ACCT
68 #ifdef WITH_ACCOUNTING
69 #define INC_ACCT(_x) radius_acct_stats._x++;request->listener->stats._x++;if (request->client && request->client->acct) request->client->acct->_x++
70 #else
71 #define INC_ACCT(_x)
72 #endif
73
74         /*
75          *      Update the statistics.
76          *
77          *      Note that we do NOT do this in a child thread.
78          *      Instead, we update the stats when a request is
79          *      deleted, because only the main server thread calls
80          *      this function, which makes it thread-safe.
81          */
82         if (request->reply) switch (request->reply->code) {
83         case PW_AUTHENTICATION_ACK:
84                 INC_AUTH(total_responses);
85                 INC_AUTH(total_access_accepts);
86                 break;
87
88         case PW_AUTHENTICATION_REJECT:
89                 INC_AUTH(total_responses);
90                 INC_AUTH(total_access_rejects);
91                 break;
92
93         case PW_ACCESS_CHALLENGE:
94                 INC_AUTH(total_responses);
95                 INC_AUTH(total_access_challenges);
96                 break;
97
98 #ifdef WITH_ACCOUNTING
99         case PW_ACCOUNTING_RESPONSE:
100                 INC_ACCT(total_responses);
101                 break;
102 #endif
103
104                 /*
105                  *      No response, it must have been a bad
106                  *      authenticator.
107                  */
108         case 0:
109                 if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
110                         if (request->reply->offset == -2) {
111                                 INC_AUTH(total_bad_authenticators);
112                         } else {
113                                 INC_AUTH(total_packets_dropped);
114                         }
115                 } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
116                         if (request->reply->offset == -2) {
117                                 INC_ACCT(total_bad_authenticators);
118                         } else {
119                                 INC_ACCT(total_packets_dropped);
120                         }
121                 }
122                 break;
123
124         default:
125                 break;
126         }
127
128 #ifdef WITH_PROXY
129         if (!request->proxy || !request->proxy_listener) goto done;     /* simplifies formatting */
130
131         switch (request->proxy->code) {
132         case PW_AUTHENTICATION_REQUEST:
133                 proxy_auth_stats.total_requests += request->num_proxied_requests;
134                 request->proxy_listener->stats.total_requests += request->num_proxied_requests;
135                 request->home_server->stats.total_requests += request->num_proxied_requests;
136                 break;
137
138 #ifdef WITH_ACCOUNTING
139         case PW_ACCOUNTING_REQUEST:
140                 proxy_acct_stats.total_requests++;
141                 request->proxy_listener->stats.total_requests += request->num_proxied_requests;
142                 request->home_server->stats.total_requests += request->num_proxied_requests;
143                 break;
144 #endif
145
146         default:
147                 break;
148         }
149
150         if (!request->proxy_reply) goto done;   /* simplifies formatting */
151
152 #undef INC
153 #define INC(_x) proxy_auth_stats._x += request->num_proxied_responses; request->proxy_listener->stats._x += request->num_proxied_responses; request->home_server->stats._x += request->num_proxied_responses;
154
155         switch (request->proxy_reply->code) {
156         case PW_AUTHENTICATION_ACK:
157                 INC(total_responses);
158                 INC(total_access_accepts);
159                 break;
160
161         case PW_AUTHENTICATION_REJECT:
162                 INC(total_responses);
163                 INC(total_access_rejects);
164                 break;
165
166         case PW_ACCESS_CHALLENGE:
167                 INC(total_responses);
168                 INC(total_access_challenges);
169                 break;
170
171 #ifdef WITH_ACCOUNTING
172         case PW_ACCOUNTING_RESPONSE:
173                 proxy_acct_stats.total_responses++;
174                 request->proxy_listener->stats.total_responses++;
175                 request->home_server->stats.total_responses++;
176                 break;
177 #endif
178
179         default:
180                 proxy_auth_stats.total_unknown_types++;
181                 request->proxy_listener->stats.total_unknown_types++;
182                 request->home_server->stats.total_unknown_types++;
183                 break;
184         }
185
186  done:
187 #endif /* WITH_PROXY */
188
189         request->master_state = REQUEST_COUNTED;
190 }
191
192 typedef struct fr_stats2vp {
193         int     attribute;
194         size_t  offset;
195 } fr_stats2vp;
196
197 /*
198  *      Authentication
199  */
200 static fr_stats2vp authvp[] = {
201         { 128, offsetof(fr_stats_t, total_requests) },
202         { 129, offsetof(fr_stats_t, total_access_accepts) },
203         { 130, offsetof(fr_stats_t, total_access_rejects) },
204         { 131, offsetof(fr_stats_t, total_access_challenges) },
205         { 132, offsetof(fr_stats_t, total_responses) },
206         { 133, offsetof(fr_stats_t, total_dup_requests) },
207         { 134, offsetof(fr_stats_t, total_malformed_requests) },
208         { 135, offsetof(fr_stats_t, total_bad_authenticators) },
209         { 136, offsetof(fr_stats_t, total_packets_dropped) },
210         { 137, offsetof(fr_stats_t, total_unknown_types) },
211         { 0, 0 }
212 };
213
214
215 #ifdef WITH_PROXY
216 /*
217  *      Proxied authentication requests.
218  */
219 static fr_stats2vp proxy_authvp[] = {
220         { 138, offsetof(fr_stats_t, total_requests) },
221         { 139, offsetof(fr_stats_t, total_access_accepts) },
222         { 140, offsetof(fr_stats_t, total_access_rejects) },
223         { 141, offsetof(fr_stats_t, total_access_challenges) },
224         { 142, offsetof(fr_stats_t, total_responses) },
225         { 143, offsetof(fr_stats_t, total_dup_requests) },
226         { 144, offsetof(fr_stats_t, total_malformed_requests) },
227         { 145, offsetof(fr_stats_t, total_bad_authenticators) },
228         { 146, offsetof(fr_stats_t, total_packets_dropped) },
229         { 147, offsetof(fr_stats_t, total_unknown_types) },
230         { 0, 0 }
231 };
232 #endif
233
234
235 #ifdef WITH_ACCOUNTING
236 /*
237  *      Accounting
238  */
239 static fr_stats2vp acctvp[] = {
240         { 148, offsetof(fr_stats_t, total_requests) },
241         { 149, offsetof(fr_stats_t, total_responses) },
242         { 150, offsetof(fr_stats_t, total_dup_requests) },
243         { 151, offsetof(fr_stats_t, total_malformed_requests) },
244         { 152, offsetof(fr_stats_t, total_bad_authenticators) },
245         { 153, offsetof(fr_stats_t, total_packets_dropped) },
246         { 154, offsetof(fr_stats_t, total_unknown_types) },
247         { 0, 0 }
248 };
249
250 #ifdef WITH_PROXY
251 static fr_stats2vp proxy_acctvp[] = {
252         { 155, offsetof(fr_stats_t, total_requests) },
253         { 156, offsetof(fr_stats_t, total_responses) },
254         { 157, offsetof(fr_stats_t, total_dup_requests) },
255         { 158, offsetof(fr_stats_t, total_malformed_requests) },
256         { 159, offsetof(fr_stats_t, total_bad_authenticators) },
257         { 160, offsetof(fr_stats_t, total_packets_dropped) },
258         { 161, offsetof(fr_stats_t, total_unknown_types) },
259         { 0, 0 }
260 };
261 #endif
262 #endif
263
264 static fr_stats2vp client_authvp[] = {
265         { 128, offsetof(fr_stats_t, total_requests) },
266         { 129, offsetof(fr_stats_t, total_access_accepts) },
267         { 130, offsetof(fr_stats_t, total_access_rejects) },
268         { 131, offsetof(fr_stats_t, total_access_challenges) },
269         { 132, offsetof(fr_stats_t, total_responses) },
270         { 133, offsetof(fr_stats_t, total_dup_requests) },
271         { 134, offsetof(fr_stats_t, total_malformed_requests) },
272         { 135, offsetof(fr_stats_t, total_bad_authenticators) },
273         { 136, offsetof(fr_stats_t, total_packets_dropped) },
274         { 137, offsetof(fr_stats_t, total_unknown_types) },
275         { 0, 0 }
276 };
277
278 #ifdef WITH_ACCOUNTING
279 static fr_stats2vp client_acctvp[] = {
280         { 148, offsetof(fr_stats_t, total_requests) },
281         { 149, offsetof(fr_stats_t, total_responses) },
282         { 150, offsetof(fr_stats_t, total_dup_requests) },
283         { 151, offsetof(fr_stats_t, total_malformed_requests) },
284         { 152, offsetof(fr_stats_t, total_bad_authenticators) },
285         { 153, offsetof(fr_stats_t, total_packets_dropped) },
286         { 154, offsetof(fr_stats_t, total_unknown_types) },
287         { 0, 0 }
288 };
289 #endif
290
291 static void request_stats_addvp(REQUEST *request,
292                                 fr_stats2vp *table, fr_stats_t *stats)
293 {
294         int i;
295         VALUE_PAIR *vp;
296
297         for (i = 0; table[i].attribute != 0; i++) {
298                 vp = radius_paircreate(request, &request->reply->vps,
299                                        table[i].attribute, VENDORPEC_FREERADIUS,
300                                        PW_TYPE_INTEGER);
301                 if (!vp) continue;
302
303                 vp->vp_integer = *(int *)(((char *) stats) + table[i].offset);
304         }
305 }
306
307
308 void request_stats_reply(REQUEST *request)
309 {
310         VALUE_PAIR *flag, *vp;
311
312         /*
313          *      Statistics are available ONLY on a "status" port.
314          */
315         rad_assert(request->packet->code == PW_STATUS_SERVER);
316         rad_assert(request->listener->type == RAD_LISTEN_NONE);
317                 
318         flag = pairfind(request->packet->vps, 127, VENDORPEC_FREERADIUS);
319         if (!flag || (flag->vp_integer == 0)) return;
320
321         /*
322          *      Authentication.
323          */
324         if (((flag->vp_integer & 0x01) != 0) &&
325             ((flag->vp_integer & 0xc0) == 0)) {
326                 request_stats_addvp(request, authvp, &radius_auth_stats);
327         }
328                 
329 #ifdef WITH_ACCOUNTING
330         /*
331          *      Accounting
332          */
333         if (((flag->vp_integer & 0x02) != 0) &&
334             ((flag->vp_integer & 0xc0) == 0)) {
335                 request_stats_addvp(request, acctvp, &radius_acct_stats);
336         }
337 #endif
338
339 #ifdef WITH_PROXY
340         /*
341          *      Proxied authentication requests.
342          */
343         if (((flag->vp_integer & 0x04) != 0) &&
344             ((flag->vp_integer & 0x20) == 0)) {
345                 request_stats_addvp(request, proxy_authvp, &proxy_auth_stats);
346         }
347
348 #ifdef WITH_ACCOUNTING
349         /*
350          *      Proxied accounting requests.
351          */
352         if (((flag->vp_integer & 0x08) != 0) &&
353             ((flag->vp_integer & 0x20) == 0)) {
354                 request_stats_addvp(request, proxy_acctvp, &proxy_acct_stats);
355         }
356 #endif
357 #endif
358
359         /*
360          *      Internal server statistics
361          */
362         if ((flag->vp_integer & 0x10) != 0) {
363                 vp = radius_paircreate(request, &request->reply->vps,
364                                        176, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
365                 if (vp) vp->vp_date = start_time.tv_sec;
366                 vp = radius_paircreate(request, &request->reply->vps,
367                                        177, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
368                 if (vp) vp->vp_date = hup_time.tv_sec;
369                 
370 #ifdef HAVE_PTHREAD_H
371                 int i, array[RAD_LISTEN_MAX];
372
373                 thread_pool_queue_stats(array);
374
375 #ifdef WITH_DETAIL
376                 for (i = 0; i <= RAD_LISTEN_DETAIL; i++) {
377                         vp = radius_paircreate(request, &request->reply->vps,
378                                                162 + i, VENDORPEC_FREERADIUS,
379                                                PW_TYPE_INTEGER);
380                         
381                         if (!vp) continue;
382                         vp->vp_integer = array[i];
383                 }
384 #endif
385 #endif
386         }
387
388         /*
389          *      For a particular client.
390          */
391         if ((flag->vp_integer & 0x20) != 0) {
392                 fr_ipaddr_t ipaddr;
393                 VALUE_PAIR *server_ip, *server_port = NULL;
394                 RADCLIENT *client = NULL;
395                 RADCLIENT_LIST *cl = NULL;
396
397                 /*
398                  *      See if we need to look up the client by server
399                  *      socket.
400                  */
401                 server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS);
402                 if (server_ip) {
403                         server_port = pairfind(request->packet->vps,
404                                                171, VENDORPEC_FREERADIUS);
405
406                         if (server_port) {
407                                 ipaddr.af = AF_INET;
408                                 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
409                                 cl = listener_find_client_list(&ipaddr, server_port->vp_integer);
410                                                                
411                                 /*
412                                  *      Not found: don't do anything
413                                  */
414                                 if (!cl) return;
415                         }
416                 }
417
418
419                 vp = pairfind(request->packet->vps, 167, VENDORPEC_FREERADIUS);
420                 if (vp) {
421                         ipaddr.af = AF_INET;
422                         ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
423                         client = client_find(cl, &ipaddr, IPPROTO_UDP);
424 #ifdef WITH_TCP
425                         if (!client) {
426                                 client = client_find(cl, &ipaddr, IPPROTO_TCP);
427                         }
428 #endif
429
430                         /*
431                          *      Else look it up by number.
432                          */
433                 } else if ((vp = pairfind(request->packet->vps,
434                                            168, VENDORPEC_FREERADIUS)) != NULL) {
435                         client = client_findbynumber(cl, vp->vp_integer);
436                 }
437
438                 if (client) {
439                         /*
440                          *      If found, echo it back, along with
441                          *      the requested statistics.
442                          */
443                         pairadd(&request->reply->vps, paircopyvp(vp));
444
445                         /*
446                          *      When retrieving client by number, also
447                          *      echo back it's IP address.
448                          */
449                         if ((vp->type == PW_TYPE_INTEGER) &&
450                             (client->ipaddr.af == AF_INET)) {
451                                 vp = radius_paircreate(request,
452                                                        &request->reply->vps,
453                                                        167, VENDORPEC_FREERADIUS,
454                                                        PW_TYPE_IPADDR);
455                                 if (vp) {
456                                         vp->vp_ipaddr = client->ipaddr.ipaddr.ip4addr.s_addr;
457                                 }
458
459                                 if (client->prefix != 32) {
460                                         vp = radius_paircreate(request,
461                                                                &request->reply->vps,
462                                                                169, VENDORPEC_FREERADIUS,
463                                                                PW_TYPE_INTEGER);
464                                         if (vp) {
465                                                 vp->vp_integer = client->prefix;
466                                         }
467                                 }
468                         }
469                         
470                         if (server_ip) {
471                                 pairadd(&request->reply->vps,
472                                         paircopyvp(server_ip));
473                                 pairadd(&request->reply->vps,
474                                         paircopyvp(server_port));
475                         }
476
477                         if (client->auth &&
478                             ((flag->vp_integer & 0x01) != 0)) {
479                                 request_stats_addvp(request, client_authvp,
480                                                     client->auth);
481                         }
482 #ifdef WITH_ACCOUNTING
483                         if (client->acct &&
484                             ((flag->vp_integer & 0x01) != 0)) {
485                                 request_stats_addvp(request, client_acctvp,
486                                                     client->acct);
487                         }
488 #endif
489                 } /* else client wasn't found, don't echo it back */
490         }
491
492         /*
493          *      For a particular "listen" socket.
494          */
495         if (((flag->vp_integer & 0x40) != 0) &&
496             ((flag->vp_integer & 0x03) != 0)) {
497                 rad_listen_t *this;
498                 VALUE_PAIR *server_ip, *server_port;
499                 fr_ipaddr_t ipaddr;
500
501                 /*
502                  *      See if we need to look up the server by socket
503                  *      socket.
504                  */
505                 server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS);
506                 if (!server_ip) return;
507
508                 server_port = pairfind(request->packet->vps,
509                                        171, VENDORPEC_FREERADIUS);
510                 if (!server_port) return;
511                 
512                 ipaddr.af = AF_INET;
513                 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
514                 this = listener_find_byipaddr(&ipaddr,
515                                               server_port->vp_integer);
516                 
517                 /*
518                  *      Not found: don't do anything
519                  */
520                 if (!this) return;
521                 
522                 pairadd(&request->reply->vps,
523                         paircopyvp(server_ip));
524                 pairadd(&request->reply->vps,
525                         paircopyvp(server_port));
526
527                 if (((flag->vp_integer & 0x01) != 0) &&
528                     ((request->listener->type == RAD_LISTEN_AUTH) ||
529                      (request->listener->type == RAD_LISTEN_NONE))) {
530                         request_stats_addvp(request, authvp, &this->stats);
531                 }
532                 
533 #ifdef WITH_ACCOUNTING
534                 if (((flag->vp_integer & 0x02) != 0) &&
535                     ((request->listener->type == RAD_LISTEN_ACCT) ||
536                      (request->listener->type == RAD_LISTEN_NONE))) {
537                         request_stats_addvp(request, acctvp, &this->stats);
538                 }
539 #endif
540         }
541
542 #ifdef WITH_PROXY
543         /*
544          *      Home servers.
545          */
546         if (((flag->vp_integer & 0x80) != 0) &&
547             ((flag->vp_integer & 0x03) != 0)) {
548                 home_server *home;
549                 VALUE_PAIR *server_ip, *server_port;
550                 fr_ipaddr_t ipaddr;
551
552                 /*
553                  *      See if we need to look up the server by socket
554                  *      socket.
555                  */
556                 server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS);
557                 if (!server_ip) return;
558
559                 server_port = pairfind(request->packet->vps,
560                                        171, VENDORPEC_FREERADIUS);
561                 if (!server_port) return;
562                 
563                 ipaddr.af = AF_INET;
564                 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
565                 home = home_server_find(&ipaddr, server_port->vp_integer,
566                                         IPPROTO_UDP);
567
568                 /*
569                  *      Not found: don't do anything
570                  */
571                 if (!home) return;
572                 
573                 pairadd(&request->reply->vps,
574                         paircopyvp(server_ip));
575                 pairadd(&request->reply->vps,
576                         paircopyvp(server_port));
577
578                 vp = radius_paircreate(request, &request->reply->vps,
579                                        172, VENDORPEC_FREERADIUS, PW_TYPE_INTEGER);
580                 if (vp) vp->vp_integer = home->currently_outstanding;
581
582                 vp = radius_paircreate(request, &request->reply->vps,
583                                        173, VENDORPEC_FREERADIUS, PW_TYPE_INTEGER);
584                 if (vp) vp->vp_integer = home->state;
585
586                 if ((home->state == HOME_STATE_ALIVE) &&
587                     (home->revive_time.tv_sec != 0)) {
588                         vp = radius_paircreate(request, &request->reply->vps,
589                                                175, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
590                         if (vp) vp->vp_date = home->revive_time.tv_sec;
591                 }
592
593                 if ((home->state == HOME_STATE_ALIVE) &&
594                     (home->ema.window > 0)) {
595                                 vp = radius_paircreate(request,
596                                                        &request->reply->vps,
597                                                        178, VENDORPEC_FREERADIUS,
598                                                        PW_TYPE_INTEGER);
599                                 if (vp) vp->vp_integer = home->ema.window;
600                                 vp = radius_paircreate(request,
601                                                        &request->reply->vps,
602                                                        179, VENDORPEC_FREERADIUS,
603                                                        PW_TYPE_INTEGER);
604                                 if (vp) vp->vp_integer = home->ema.ema1 / EMA_SCALE;
605                                 vp = radius_paircreate(request,
606                                                        &request->reply->vps,
607                                                        180, VENDORPEC_FREERADIUS,
608                                                        PW_TYPE_INTEGER);
609                                 if (vp) vp->vp_integer = home->ema.ema10 / EMA_SCALE;
610
611                 }
612
613                 if (home->state == HOME_STATE_IS_DEAD) {
614                         vp = radius_paircreate(request, &request->reply->vps,
615                                                174, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
616                         if (vp) vp->vp_date = home->zombie_period_start.tv_sec + home->zombie_period;
617                 }
618
619                 if (((flag->vp_integer & 0x01) != 0) &&
620                     (home->type == HOME_TYPE_AUTH)) {
621                         request_stats_addvp(request, proxy_authvp,
622                                             &home->stats);
623                 }
624
625 #ifdef WITH_ACCOUNTING
626                 if (((flag->vp_integer & 0x02) != 0) &&
627                     (home->type == HOME_TYPE_ACCT)) {
628                         request_stats_addvp(request, proxy_acctvp,
629                                             &home->stats);
630                 }
631 #endif
632         }
633 #endif  /* WITH_PROXY */
634 }
635
636 void radius_stats_init(int flag)
637 {
638         if (!flag) {
639                 gettimeofday(&start_time, NULL);
640                 hup_time = start_time; /* it's just nicer this way */
641         } else {
642                 gettimeofday(&hup_time, NULL);
643         }
644 }
645
646 void radius_stats_ema(fr_stats_ema_t *ema,
647                       struct timeval *start, struct timeval *end)
648 {
649         int micro;
650         time_t tdiff;
651 #ifdef WITH_STATS_DEBUG
652         static int n = 0;
653 #endif
654         if (ema->window == 0) return;
655
656         rad_assert(start->tv_sec >= end->tv_sec);
657
658         /*
659          *      Initialize it.
660          */
661         if (ema->f1 == 0) {
662                 if (ema->window > 10000) ema->window = 10000;
663                 
664                 ema->f1 =  (2 * F_EMA_SCALE) / (ema->window + 1);
665                 ema->f10 = (2 * F_EMA_SCALE) / ((10 * ema->window) + 1);
666         }
667
668
669         tdiff = start->tv_sec;
670         tdiff -= end->tv_sec;
671         
672         micro = (int) tdiff;
673         if (micro > 40) micro = 40; /* don't overflow 32-bit ints */
674         micro *= USEC;
675         micro += start->tv_usec;
676         micro -= end->tv_usec;
677         
678         micro *= EMA_SCALE;
679
680         if (ema->ema1 == 0) {
681                 ema->ema1 = micro;
682                 ema->ema10 = micro;
683         } else {
684                 int diff;
685                 
686                 diff = ema->f1 * (micro - ema->ema1);
687                 ema->ema1 += (diff / 1000000);
688                 
689                 diff = ema->f10 * (micro - ema->ema10);
690                 ema->ema10 += (diff / 1000000);
691         }
692         
693         
694 #ifdef WITH_STATS_DEBUG
695         DEBUG("time %d %d.%06d\t%d.%06d\t%d.%06d\n",
696               n, micro / PREC, (micro / EMA_SCALE) % USEC,
697               ema->ema1 / PREC, (ema->ema1 / EMA_SCALE) % USEC,
698               ema->ema10 / PREC, (ema->ema10 / EMA_SCALE) % USEC);
699         n++;
700 #endif  
701 }
702
703 #endif /* WITH_STATS */