f582817aad20550ebda7e7b523eb86937d6ecbe0
[freeradius.git] / src / main / detail.c
1 /*
2  * detail.c     Process the detail file
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 2007  The FreeRADIUS server project
21  * Copyright 2007  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/modules.h>
29 #include <freeradius-devel/detail.h>
30 #include <freeradius-devel/rad_assert.h>
31
32 #ifdef HAVE_SYS_STAT_H
33 #include <sys/stat.h>
34 #endif
35
36 #ifdef HAVE_GLOB_H
37 #include <glob.h>
38 #endif
39
40 #include <fcntl.h>
41
42 #ifdef WITH_DETAIL
43
44 #define USEC (1000000)
45
46 typedef struct listen_detail_t {
47         int             delay_time; /* should be first entry */
48         char            *filename;
49         char            *filename_work;
50         VALUE_PAIR      *vps;
51         FILE            *fp;
52         int             state;
53         time_t          timestamp;
54         fr_ipaddr_t     client_ip;
55         int             load_factor; /* 1..100 */
56         int             signal;
57         int             poll_interval;
58
59         int             has_rtt;
60         int             srtt;
61         int             rttvar;
62         struct timeval  last_packet;
63         RADCLIENT       detail_client;
64 } listen_detail_t;
65
66
67 #define STATE_UNOPENED  (0)
68 #define STATE_UNLOCKED  (1)
69 #define STATE_HEADER    (2)
70 #define STATE_READING   (3)
71 #define STATE_QUEUED    (4)
72 #define STATE_RUNNING   (5)
73 #define STATE_NO_REPLY  (6)
74 #define STATE_REPLIED   (7)
75
76 /*
77  *      If we're limiting outstanding packets, then mark the response
78  *      as being sent.
79  */
80 int detail_send(rad_listen_t *listener, REQUEST *request)
81 {
82         int rtt;
83         struct timeval now;
84         listen_detail_t *data = listener->data;
85
86         rad_assert(request->listener == listener);
87         rad_assert(listener->send == detail_send);
88
89         /*
90          *      This request timed out.  Remember that, and tell the
91          *      caller it's OK to read more "detail" file stuff.
92          */
93         if (request->reply->code == 0) {
94                 data->delay_time = USEC;
95                 data->signal = 1;
96                 data->state = STATE_NO_REPLY;
97                 radius_signal_self(RADIUS_SIGNAL_SELF_DETAIL);
98                 return 0;
99         }
100
101         /*
102          *      We call gettimeofday a lot.  But here it should be OK,
103          *      because there's nothing else to do.
104          */
105         gettimeofday(&now, NULL);
106
107         /*
108          *      If we haven't sent a packet in the last second, reset
109          *      the RTT.
110          */
111         now.tv_sec -= 1;
112         if (timercmp(&data->last_packet, &now, <)) {
113                 data->has_rtt = FALSE;
114         }
115         now.tv_sec += 1;
116
117         /*
118          *      Only one detail packet may be outstanding at a time,
119          *      so it's safe to update some entries in the detail
120          *      structure.
121          *
122          *      We keep smoothed round trip time (SRTT), but not round
123          *      trip timeout (RTO).  We use SRTT to calculate a rough
124          *      load factor.
125          */
126         rtt = now.tv_sec - request->received.tv_sec;
127         rtt *= USEC;
128         rtt += now.tv_usec;
129         rtt -= request->received.tv_usec;
130
131         /*
132          *      If we're proxying, the RTT is our processing time,
133          *      plus the network delay there and back, plus the time
134          *      on the other end to process the packet.  Ideally, we
135          *      should remove the network delays from the RTT, but we
136          *      don't know what they are.
137          *
138          *      So, to be safe, we over-estimate the total cost of
139          *      processing the packet.
140          */
141         if (!data->has_rtt) {
142                 data->has_rtt = TRUE;
143                 data->srtt = rtt;
144                 data->rttvar = rtt / 2;
145
146         } else {
147                 data->rttvar -= data->rttvar >> 2;
148                 data->rttvar += (data->srtt - rtt);
149                 data->srtt -= data->srtt >> 3;
150                 data->srtt += rtt >> 3;
151         }
152
153         /*
154          *      Calculate the time we wait before sending the next
155          *      packet.
156          *
157          *      rtt / (rtt + delay) = load_factor / 100
158          */
159         data->delay_time = (data->srtt * (100 - data->load_factor)) / (data->load_factor);
160         if (data->delay_time == 0) data->delay_time = USEC / 10;
161
162         /*
163          *      Cap delay at 4 packets/s.  If the end system can't
164          *      handle this, then it's very broken.
165          */
166         if (data->delay_time > (USEC / 4)) data->delay_time= USEC / 4;
167
168 #if 0
169         DEBUG2("RTT %d\tdelay %d", data->srtt, data->delay_time);
170 #endif
171
172         data->last_packet = now;
173         data->signal = 1;
174         data->state = STATE_REPLIED;
175         radius_signal_self(RADIUS_SIGNAL_SELF_DETAIL);
176
177         return 0;
178 }
179
180 int detail_delay(rad_listen_t *listener)
181 {
182         listen_detail_t *data = listener->data;
183
184         if (!data->signal) return 0;
185
186         data->signal = 0;
187
188         return data->delay_time;
189 }
190
191
192 /*
193  *      Open the detail file, if we can.
194  *
195  *      FIXME: create it, if it's not already there, so that the main
196  *      server select() will wake us up if there's anything to read.
197  */
198 static int detail_open(rad_listen_t *this)
199 {
200         struct stat st;
201         listen_detail_t *data = this->data;
202         char *filename = data->filename;
203
204         rad_assert(data->state == STATE_UNOPENED);
205         data->delay_time = USEC;
206
207         /*
208          *      Open detail.work first, so we don't lose
209          *      accounting packets.  It's probably better to
210          *      duplicate them than to lose them.
211          *
212          *      Note that we're not writing to the file, but
213          *      we've got to open it for writing in order to
214          *      establish the lock, to prevent rlm_detail from
215          *      writing to it.
216          *
217          *      This also means that if we're doing globbing,
218          *      this file will be read && processed before the
219          *      file globbing is done.
220          */
221         this->fd = open(data->filename_work, O_RDWR);
222         if (this->fd < 0) {
223                 DEBUG2("Polling for detail file %s", filename);
224
225                 /*
226                  *      Try reading the detail file.  If it
227                  *      doesn't exist, we can't do anything.
228                  *
229                  *      Doing the stat will tell us if the file
230                  *      exists, even if we don't have permissions
231                  *      to read it.
232                  */
233                 if (stat(filename, &st) < 0) {
234 #ifdef HAVE_GLOB_H
235                         int i, found;
236                         time_t chtime;
237                         glob_t files;
238
239                         memset(&files, 0, sizeof(files));
240                         if (glob(filename, 0, NULL, &files) != 0) {
241                                 return 0;
242                         }
243
244                         chtime = 0;
245                         found = -1;
246                         for (i = 0; i < files.gl_pathc; i++) {
247                                 if (stat(files.gl_pathv[i], &st) < 0) continue;
248
249                                 if ((i == 0) ||
250                                     (st.st_ctime < chtime)) {
251                                         chtime = st.st_ctime;
252                                         found = i;
253                                 }
254                         }
255
256                         if (found < 0) {
257                                 globfree(&files);
258                                 return 0;
259                         }
260
261                         filename = strdup(files.gl_pathv[found]);
262                         globfree(&files);
263 #else
264                         return 0;
265 #endif
266                 }
267
268                 /*
269                  *      Open it BEFORE we rename it, just to
270                  *      be safe...
271                  */
272                 this->fd = open(filename, O_RDWR);
273                 if (this->fd < 0) {
274                         radlog(L_ERR, "Failed to open %s: %s",
275                                filename, strerror(errno));
276                         if (filename != data->filename) free(filename);
277                         return 0;
278                 }
279
280                 /*
281                  *      Rename detail to detail.work
282                  */
283                 DEBUG("detail_recv: Renaming %s -> %s", filename, data->filename_work);
284                 if (rename(filename, data->filename_work) < 0) {
285                         if (filename != data->filename) free(filename);
286                         close(this->fd);
287                         this->fd = -1;
288                         return 0;
289                 }
290                 if (filename != data->filename) free(filename);
291         } /* else detail.work existed, and we opened it */
292
293         rad_assert(data->vps == NULL);
294         rad_assert(data->fp == NULL);
295
296         data->state = STATE_UNLOCKED;
297
298         data->client_ip.af = AF_UNSPEC;
299         data->timestamp = 0;
300
301         return 1;
302 }
303
304
305 /*
306  *      FIXME: add a configuration "exit when done" so that the detail
307  *      file reader can be used as a one-off tool to update stuff.
308  *
309  *      The time sequence for reading from the detail file is:
310  *
311  *      t_0             signalled that the server is idle, and we
312  *                      can read from the detail file.
313  *
314  *      t_rtt           the packet has been processed successfully,
315  *                      wait for t_delay to enforce load factor.
316  *                      
317  *      t_rtt + t_delay wait for signal that the server is idle.
318  *      
319  */
320 int detail_recv(rad_listen_t *listener,
321                 RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
322 {
323         char            key[256], value[1024];
324         VALUE_PAIR      *vp, **tail;
325         RADIUS_PACKET   *packet;
326         char            buffer[2048];
327         listen_detail_t *data = listener->data;
328
329         switch (data->state) {
330                 case STATE_UNOPENED:
331         open_file:
332                         rad_assert(listener->fd < 0);
333                         
334                         if (!detail_open(listener)) return 0;
335
336                         rad_assert(data->state == STATE_UNLOCKED);
337                         rad_assert(listener->fd >= 0);
338
339                         /* FALL-THROUGH */
340
341                         /*
342                          *      Try to lock fd.  If we can't, return.
343                          *      If we can, continue.  This means that
344                          *      the server doesn't block while waiting
345                          *      for the lock to open...
346                          */
347                 case STATE_UNLOCKED:
348                         /*
349                          *      Note that we do NOT block waiting for
350                          *      the lock.  We've re-named the file
351                          *      above, so we've already guaranteed
352                          *      that any *new* detail writer will not
353                          *      be opening this file.  The only
354                          *      purpose of the lock is to catch a race
355                          *      condition where the execution
356                          *      "ping-pongs" between radiusd &
357                          *      radrelay.
358                          */
359                         if (rad_lockfd_nonblock(listener->fd, 0) < 0) {
360                                 /*
361                                  *      Close the FD.  The main loop
362                                  *      will wake up in a second and
363                                  *      try again.
364                                  */
365                                 close(listener->fd);
366                                 listener->fd = -1;
367                                 data->state = STATE_UNOPENED;
368                                 return 0;
369                         }
370
371                         data->fp = fdopen(listener->fd, "r");
372                         if (!data->fp) {
373                                 radlog(L_ERR, "FATAL: Failed to re-open detail file %s: %s",
374                                        data->filename, strerror(errno));
375                                 exit(1);
376                         }
377
378                         /*
379                          *      Look for the header
380                          */
381                         data->state = STATE_HEADER;
382                         data->vps = NULL;
383
384                         /* FALL-THROUGH */
385
386                 case STATE_HEADER:
387                 do_header:
388                         if (!data->fp) {
389                                 data->state = STATE_UNOPENED;
390                                 goto open_file;
391                         }
392
393                         {
394                                 struct stat buf;
395                                 
396                                 fstat(listener->fd, &buf);
397                                 if (((off_t) ftell(data->fp)) == buf.st_size) {
398                                         goto cleanup;
399                                 }
400                         }
401
402                         /*
403                          *      End of file.  Delete it, and re-set
404                          *      everything.
405                          */
406                         if (feof(data->fp)) {
407                         cleanup:
408                                 unlink(data->filename_work);
409                                 if (data->fp) fclose(data->fp);
410                                 data->fp = NULL;
411                                 listener->fd = -1;
412                                 data->state = STATE_UNOPENED;
413                                 rad_assert(data->vps == NULL);
414                                 return 0;
415                         }
416
417                         /*
418                          *      Else go read something.
419                          */
420                         break;
421
422                         /*
423                          *      Read more value-pair's, unless we're
424                          *      at EOF.  In that case, queue whatever
425                          *      we have.
426                          */
427                 case STATE_READING:
428                         if (data->fp && !feof(data->fp)) break;
429                         data->state = STATE_QUEUED;
430
431                         /* FALL-THROUGH */
432
433                 case STATE_QUEUED:
434                         goto alloc_packet;
435
436                         /*
437                          *      We still have an outstanding packet.
438                          *      Don't read any more.
439                          */
440                 case STATE_RUNNING:
441                         return 0;
442
443                         /*
444                          *      If there's no reply, keep
445                          *      retransmitting the current packet
446                          *      forever.
447                          */
448                 case STATE_NO_REPLY:
449                         data->state = STATE_QUEUED;
450                         goto alloc_packet;
451                                 
452                         /*
453                          *      We have a reply.  Clean up the old
454                          *      request, and go read another one.
455                          */
456                 case STATE_REPLIED:
457                         pairfree(&data->vps);
458                         data->state = STATE_HEADER;
459                         goto do_header;
460         }
461         
462         tail = &data->vps;
463         while (*tail) tail = &(*tail)->next;
464
465         /*
466          *      Read a header, OR a value-pair.
467          */
468         while (fgets(buffer, sizeof(buffer), data->fp)) {
469                 /*
470                  *      Badly formatted file: delete it.
471                  *
472                  *      FIXME: Maybe flag an error?
473                  */
474                 if (!strchr(buffer, '\n')) {
475                         pairfree(&data->vps);
476                         goto cleanup;
477                 }
478
479                 /*
480                  *      We're reading VP's, and got a blank line.
481                  *      Queue the packet.
482                  */
483                 if ((data->state == STATE_READING) &&
484                     (buffer[0] == '\n')) {
485                         data->state = STATE_QUEUED;
486                         break;
487                 }
488
489                 /*
490                  *      Look for date/time header, and read VP's if
491                  *      found.  If not, keep reading lines until we
492                  *      find one.
493                  */
494                 if (data->state == STATE_HEADER) {
495                         int y;
496
497                         if (sscanf(buffer, "%*s %*s %*d %*d:%*d:%*d %d", &y)) {
498                                 data->state = STATE_READING;
499                         }
500                         continue;
501                 }
502
503                 /*
504                  *      We have a full "attribute = value" line.
505                  *      If it doesn't look reasonable, skip it.
506                  *
507                  *      FIXME: print an error for badly formatted attributes?
508                  */
509                 if (sscanf(buffer, "%255s = %1023s", key, value) != 2) {
510                         continue;
511                 }
512
513                 /*
514                  *      Skip non-protocol attributes.
515                  */
516                 if (!strcasecmp(key, "Request-Authenticator")) continue;
517
518                 /*
519                  *      Set the original client IP address, based on
520                  *      what's in the detail file.
521                  *
522                  *      Hmm... we don't set the server IP address.
523                  *      or port.  Oh well.
524                  */
525                 if (!strcasecmp(key, "Client-IP-Address")) {
526                         data->client_ip.af = AF_INET;
527                         ip_hton(value, AF_INET, &data->client_ip);
528                         continue;
529                 }
530
531                 /*
532                  *      The original time at which we received the
533                  *      packet.  We need this to properly calculate
534                  *      Acct-Delay-Time.
535                  */
536                 if (!strcasecmp(key, "Timestamp")) {
537                         data->timestamp = atoi(value);
538
539                         vp = paircreate(PW_PACKET_ORIGINAL_TIMESTAMP,
540                                         PW_TYPE_DATE);
541                         if (vp) {
542                                 vp->vp_date = (uint32_t) data->timestamp;
543                                 *tail = vp;
544                                 tail = &(vp->next);
545                         }
546                         continue;
547                 }
548
549                 /*
550                  *      Read one VP.
551                  *
552                  *      FIXME: do we want to check for non-protocol
553                  *      attributes like radsqlrelay does?
554                  */
555                 vp = NULL;
556                 if ((userparse(buffer, &vp) > 0) &&
557                     (vp != NULL)) {
558                         *tail = vp;
559                         tail = &(vp->next);
560                 }
561         }
562
563         /*
564          *      Some kind of error.
565          *
566          *      FIXME: Leave the file in-place, and warn the
567          *      administrator?
568          */
569         if (ferror(data->fp)) goto cleanup;
570
571         /*
572          *      Process the packet.
573          */
574  alloc_packet:
575         rad_assert(data->state == STATE_QUEUED);
576
577         /*
578          *      We're done reading the file, but we didn't read
579          *      anything.  Clean up, and don't return anything.
580          */
581         if (!data->vps) {
582                 data->state = STATE_HEADER;
583                 if (feof(data->fp)) goto cleanup; 
584                 return 0;
585         }
586
587         /*
588          *      Allocate the packet.  If we fail, it's a serious
589          *      problem.
590          */
591         packet = rad_alloc(1);
592         if (!packet) {
593                 radlog(L_ERR, "FATAL: Failed allocating memory for detail");
594                 exit(1);
595         }
596
597         memset(packet, 0, sizeof(*packet));
598         packet->sockfd = -1;
599         packet->src_ipaddr.af = AF_INET;
600         packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
601         packet->code = PW_ACCOUNTING_REQUEST;
602         packet->timestamp = time(NULL);
603
604         /*
605          *      Remember where it came from, so that we don't
606          *      proxy it to the place it came from...
607          */
608         if (data->client_ip.af != AF_UNSPEC) {
609                 packet->src_ipaddr = data->client_ip;
610         }
611
612         vp = pairfind(packet->vps, PW_PACKET_SRC_IP_ADDRESS);
613         if (vp) {
614                 packet->src_ipaddr.af = AF_INET;
615                 packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
616         } else {
617                 vp = pairfind(packet->vps, PW_PACKET_SRC_IPV6_ADDRESS);
618                 if (vp) {
619                         packet->src_ipaddr.af = AF_INET6;
620                         memcpy(&packet->src_ipaddr.ipaddr.ip6addr,
621                                &vp->vp_ipv6addr, sizeof(vp->vp_ipv6addr));
622                 }
623         }
624
625         vp = pairfind(packet->vps, PW_PACKET_DST_IP_ADDRESS);
626         if (vp) {
627                 packet->dst_ipaddr.af = AF_INET;
628                 packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
629         } else {
630                 vp = pairfind(packet->vps, PW_PACKET_DST_IPV6_ADDRESS);
631                 if (vp) {
632                         packet->dst_ipaddr.af = AF_INET6;
633                         memcpy(&packet->dst_ipaddr.ipaddr.ip6addr,
634                                &vp->vp_ipv6addr, sizeof(vp->vp_ipv6addr));
635                 }
636         }
637
638         /*
639          *      We've got to give SOME value for Id & ports, so that
640          *      the packets can be added to the request queue.
641          *      However, we don't want to keep track of used/unused
642          *      id's and ports, as that's a lot of work.  This hack
643          *      ensures that (if we have real random numbers), that
644          *      there will be a collision on every 2^(16+15+15+24 - 1)
645          *      packets, on average.  That means we can read 2^37
646          *      packets before having a collision, which means it's
647          *      effectively impossible.
648          */
649         packet->id = fr_rand() & 0xffff;
650         packet->src_port = 1024 + (fr_rand() & 0x7fff);
651         packet->dst_port = 1024 + (fr_rand() & 0x7fff);
652
653         packet->dst_ipaddr.af = AF_INET;
654         packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl((INADDR_LOOPBACK & ~0xffffff) | (fr_rand() & 0xffffff));
655
656         /*
657          *      If everything's OK, this is a waste of memory.
658          *      Otherwise, it lets us re-send the original packet
659          *      contents, unmolested.
660          */
661         packet->vps = paircopy(data->vps);
662
663         /*
664          *      Look for Acct-Delay-Time, and update
665          *      based on Acct-Delay-Time += (time(NULL) - timestamp)
666          */
667         vp = pairfind(packet->vps, PW_ACCT_DELAY_TIME);
668         if (!vp) {
669                 vp = paircreate(PW_ACCT_DELAY_TIME, PW_TYPE_INTEGER);
670                 rad_assert(vp != NULL);
671                 pairadd(&packet->vps, vp);
672         }
673         if (data->timestamp != 0) {
674                 vp->vp_integer += time(NULL) - data->timestamp;
675         }
676
677         *pfun = rad_accounting;
678
679         if (debug_flag) {
680                 fr_printf_log("detail_recv: Read packet from %s\n", data->filename_work);
681                 for (vp = packet->vps; vp; vp = vp->next) {
682                         debug_pair(vp);
683                 }
684         }
685
686         /*
687          *      FIXME: many of these checks may not be necessary when
688          *      reading from the detail file.
689          *
690          *      Try again later...
691          */
692         if (!received_request(listener, packet, prequest,
693                               &data->detail_client)) {
694                 rad_free(&packet);
695                 data->state = STATE_NO_REPLY;   /* try again later */
696                 return 0;
697         }
698
699         data->state = STATE_RUNNING;
700
701         return 1;
702 }
703
704
705 /*
706  *      Free detail-specific stuff.
707  */
708 void detail_free(rad_listen_t *this)
709 {
710         listen_detail_t *data = this->data;
711
712         free(data->filename);
713         pairfree(&data->vps);
714
715         if (data->fp != NULL) fclose(data->fp);
716 }
717
718
719 int detail_print(rad_listen_t *this, char *buffer, size_t bufsize)
720 {
721         if (!this->server) {
722                 return snprintf(buffer, bufsize, "%s",
723                                 ((listen_detail_t *)(this->data))->filename);
724         }
725
726         return snprintf(buffer, bufsize, "detail file %s as server %s",
727                         ((listen_detail_t *)(this->data))->filename,
728                         this->server);
729 }
730
731 int detail_encode(UNUSED rad_listen_t *this, UNUSED REQUEST *request)
732 {
733         /*
734          *      We never encode responses "sent to" the detail file.
735          */
736         return 0;
737 }
738
739 int detail_decode(UNUSED rad_listen_t *this, UNUSED REQUEST *request)
740 {
741         /*
742          *      We never decode responses read from the detail file.
743          */
744         return 0;
745 }
746
747
748 static const CONF_PARSER detail_config[] = {
749         { "filename",   PW_TYPE_STRING_PTR,
750           offsetof(listen_detail_t, filename), NULL,  NULL },
751         { "load_factor",   PW_TYPE_INTEGER,
752           offsetof(listen_detail_t, load_factor), NULL, Stringify(10)},
753         { "poll_interval",   PW_TYPE_INTEGER,
754           offsetof(listen_detail_t, poll_interval), NULL, Stringify(1)},
755
756         { NULL, -1, 0, NULL, NULL }             /* end the list */
757 };
758
759
760 /*
761  *      Parse a detail section.
762  */
763 int detail_parse(CONF_SECTION *cs, rad_listen_t *this)
764 {
765         int             rcode;
766         listen_detail_t *data;
767         RADCLIENT       *client;
768         char buffer[2048];
769
770         if (!this->data) {
771                 this->data = rad_malloc(sizeof(*data));
772                 memset(this->data, 0, sizeof(*data));
773         }
774
775         data = this->data;
776         data->delay_time = USEC;
777
778         rcode = cf_section_parse(cs, data, detail_config);
779         if (rcode < 0) {
780                 cf_log_err(cf_sectiontoitem(cs), "Failed parsing listen section");
781                 return -1;
782         }
783
784         if (!data->filename) {
785                 cf_log_err(cf_sectiontoitem(cs), "No detail file specified in listen section");
786                 return -1;
787         }
788
789         if ((data->load_factor < 1) || (data->load_factor > 100)) {
790                 cf_log_err(cf_sectiontoitem(cs), "Load factor must be between 1 and 100");
791                 return -1;
792         }
793
794         if ((data->poll_interval < 1) || (data->poll_interval > 3600)) {
795                 cf_log_err(cf_sectiontoitem(cs), "poll_interval must be between 1 and 3600");
796                 return -1;
797         }
798
799         /*
800          *      If the filename is a glob, use "detail.work" as the
801          *      work file name.
802          */
803         if ((strchr(data->filename, '*') != NULL) ||
804             (strchr(data->filename, '[') != NULL)) {
805                 char *p;
806
807 #ifndef HAVE_GLOB_H
808                 radlog(L_INFO, "WARNING: Detail file \"%s\" appears to use file globbing, but it is not supported on this system.", data->filename);
809 #endif
810                 strlcpy(buffer, data->filename, sizeof(buffer));
811                 p = strrchr(buffer, FR_DIR_SEP);
812                 if (p) {
813                         p[1] = '\0';
814                 } else {
815                         buffer[0] = '\0';
816                 }
817                 strlcat(buffer, "detail.work",
818                         sizeof(buffer) - strlen(buffer));
819                         
820         } else {
821                 snprintf(buffer, sizeof(buffer), "%s.work", data->filename);
822         }
823
824         free(data->filename_work);
825         data->filename_work = strdup(buffer); /* FIXME: leaked */
826
827         data->vps = NULL;
828         data->fp = NULL;
829         data->state = STATE_UNOPENED;
830
831         /*
832          *      Initialize the fake client.
833          */
834         client = &data->detail_client;
835         memset(client, 0, sizeof(*client));
836         client->ipaddr.af = AF_INET;
837         client->ipaddr.ipaddr.ip4addr.s_addr = INADDR_NONE;
838         client->prefix = 0;
839         client->longname = client->shortname = data->filename;
840         client->secret = client->shortname;
841         client->nastype = strdup("none");
842
843         return 0;
844 }
845
846 int detail_poll_interval(rad_listen_t *listener)
847 {
848         listen_detail_t *data = listener->data;
849
850         return data->poll_interval;
851 }
852
853 #endif