📄 detail.c
字号:
/* * detail.c Process the detail file * * Version: $Id: detail.c,v 1.14 2008/04/26 15:07:43 aland Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * Copyright 2007 The FreeRADIUS server project * Copyright 2007 Alan DeKok <aland@deployingradius.com> */#include <freeradius-devel/ident.h>RCSID("$Id: detail.c,v 1.14 2008/04/26 15:07:43 aland Exp $")#include <freeradius-devel/radiusd.h>#include <freeradius-devel/modules.h>#include <freeradius-devel/detail.h>#include <freeradius-devel/rad_assert.h>#ifdef HAVE_SYS_STAT_H#include <sys/stat.h>#endif#ifdef HAVE_GLOB_H#include <glob.h>#endif#include <fcntl.h>#define USEC (1000000)typedef struct listen_detail_t { int delay_time; /* should be first entry */ char *filename; char *filename_work; VALUE_PAIR *vps; FILE *fp; int state; time_t timestamp; fr_ipaddr_t client_ip; int load_factor; /* 1..100 */ int signal; int has_rtt; int srtt; int rttvar; struct timeval last_packet; RADCLIENT detail_client;} listen_detail_t;#define STATE_UNOPENED (0)#define STATE_UNLOCKED (1)#define STATE_HEADER (2)#define STATE_READING (3)#define STATE_QUEUED (4)#define STATE_RUNNING (5)#define STATE_NO_REPLY (6)#define STATE_REPLIED (7)/* * If we're limiting outstanding packets, then mark the response * as being sent. */int detail_send(rad_listen_t *listener, REQUEST *request){ int rtt; struct timeval now; listen_detail_t *data = listener->data; rad_assert(request->listener == listener); rad_assert(listener->send == detail_send); /* * This request timed out. Remember that, and tell the * caller it's OK to read more "detail" file stuff. */ if (request->reply->code == 0) { data->delay_time = USEC; data->signal = 1; data->state = STATE_NO_REPLY; radius_signal_self(RADIUS_SIGNAL_SELF_DETAIL); return 0; } /* * We call gettimeofday a lot. But here it should be OK, * because there's nothing else to do. */ gettimeofday(&now, NULL); /* * If we haven't sent a packet in the last second, reset * the RTT. */ now.tv_sec -= 1; if (timercmp(&data->last_packet, &now, <)) { data->has_rtt = FALSE; } now.tv_sec += 1; /* * Only one detail packet may be outstanding at a time, * so it's safe to update some entries in the detail * structure. * * We keep smoothed round trip time (SRTT), but not round * trip timeout (RTO). We use SRTT to calculate a rough * load factor. */ rtt = now.tv_sec - request->received.tv_sec; rtt *= USEC; rtt += now.tv_usec; rtt -= request->received.tv_usec; /* * If we're proxying, the RTT is our processing time, * plus the network delay there and back, plus the time * on the other end to process the packet. Ideally, we * should remove the network delays from the RTT, but we * don't know what they are. * * So, to be safe, we over-estimate the total cost of * processing the packet. */ if (!data->has_rtt) { data->has_rtt = TRUE; data->srtt = rtt; data->rttvar = rtt / 2; } else { data->rttvar -= data->rttvar >> 2; data->rttvar += (data->srtt - rtt); data->srtt -= data->srtt >> 3; data->srtt += rtt >> 3; } /* * Calculate the time we wait before sending the next * packet. * * rtt / (rtt + delay) = load_factor / 100 */ data->delay_time = (data->srtt * (100 - data->load_factor)) / (data->load_factor); if (data->delay_time == 0) data->delay_time = USEC / 10; /* * Cap delay at 4 packets/s. If the end system can't * handle this, then it's very broken. */ if (data->delay_time > (USEC / 4)) data->delay_time= USEC / 4;#if 0 DEBUG2("RTT %d\tdelay %d", data->srtt, data->delay_time);#endif data->last_packet = now; data->signal = 1; data->state = STATE_REPLIED; radius_signal_self(RADIUS_SIGNAL_SELF_DETAIL); return 0;}int detail_delay(rad_listen_t *listener){ listen_detail_t *data = listener->data; if (!data->signal) return 0; data->signal = 0; return data->delay_time;}/* * Open the detail file, if we can. * * FIXME: create it, if it's not already there, so that the main * server select() will wake us up if there's anything to read. */static int detail_open(rad_listen_t *this){ struct stat st; listen_detail_t *data = this->data; char *filename = data->filename; rad_assert(data->state == STATE_UNOPENED); data->delay_time = USEC; /* * Open detail.work first, so we don't lose * accounting packets. It's probably better to * duplicate them than to lose them. * * Note that we're not writing to the file, but * we've got to open it for writing in order to * establish the lock, to prevent rlm_detail from * writing to it. */ this->fd = open(data->filename_work, O_RDWR); if (this->fd < 0) { DEBUG2("Polling for detail file %s", filename); /* * Try reading the detail file. If it * doesn't exist, we can't do anything. * * Doing the stat will tell us if the file * exists, even if we don't have permissions * to read it. */ if (stat(filename, &st) < 0) {#ifdef HAVE_GLOB_H int i, found; time_t ctime; glob_t files; memset(&files, 0, sizeof(files)); if (glob(filename, 0, NULL, &files) != 0) { return 0; } ctime = 0; found = -1; for (i = 0; i < files.gl_pathc; i++) { if (stat(files.gl_pathv[i], &st) < 0) continue; if ((i == 0) || (st.st_ctime < ctime)) { ctime = st.st_ctime; found = i; } } if (found < 0) { globfree(&files); return 0; } filename = strdup(files.gl_pathv[found]); globfree(&files);#else return 0;#endif } /* * Open it BEFORE we rename it, just to * be safe... */ this->fd = open(filename, O_RDWR); if (this->fd < 0) { radlog(L_ERR, "Failed to open %s: %s", filename, strerror(errno)); if (filename != data->filename) free(filename); return 0; } /* * Rename detail to detail.work */ DEBUG("detail_recv: Renaming %s -> %s", filename, data->filename_work); if (rename(filename, data->filename_work) < 0) { if (filename != data->filename) free(filename); close(this->fd); this->fd = -1; return 0; } if (filename != data->filename) free(filename); } /* else detail.work existed, and we opened it */ rad_assert(data->vps == NULL); rad_assert(data->fp == NULL); data->state = STATE_UNLOCKED; data->client_ip.af = AF_UNSPEC; data->timestamp = 0; return 1;}/* * FIXME: add a configuration "exit when done" so that the detail * file reader can be used as a one-off tool to update stuff. * * The time sequence for reading from the detail file is: * * t_0 signalled that the server is idle, and we * can read from the detail file. * * t_rtt the packet has been processed successfully, * wait for t_delay to enforce load factor. * * t_rtt + t_delay wait for signal that the server is idle. * */int detail_recv(rad_listen_t *listener, RAD_REQUEST_FUNP *pfun, REQUEST **prequest){ char key[256], value[1024]; VALUE_PAIR *vp, **tail; RADIUS_PACKET *packet; char buffer[2048]; listen_detail_t *data = listener->data; switch (data->state) { case STATE_UNOPENED: open_file: rad_assert(listener->fd < 0); if (!detail_open(listener)) return 0; rad_assert(data->state == STATE_UNLOCKED); rad_assert(listener->fd >= 0); /* FALL-THROUGH */ /* * Try to lock fd. If we can't, return. * If we can, continue. This means that * the server doesn't block while waiting * for the lock to open... */ case STATE_UNLOCKED: /* * Note that we do NOT block waiting for * the lock. We've re-named the file * above, so we've already guaranteed * that any *new* detail writer will not * be opening this file. The only * purpose of the lock is to catch a race * condition where the execution * "ping-pongs" between radiusd & * radrelay. */ if (rad_lockfd_nonblock(listener->fd, 0) < 0) { /* * Close the FD. The main loop * will wake up in a second and * try again. */ close(listener->fd); listener->fd = -1; data->state = STATE_UNOPENED; return 0; } data->fp = fdopen(listener->fd, "r"); if (!data->fp) { radlog(L_ERR, "FATAL: Failed to re-open detail file %s: %s", data->filename, strerror(errno)); exit(1); } /* * Look for the header */ data->state = STATE_HEADER; data->vps = NULL; /* FALL-THROUGH */ case STATE_HEADER: do_header: if (!data->fp) { data->state = STATE_UNOPENED; goto open_file; } { struct stat buf; fstat(listener->fd, &buf); if (((off_t) ftell(data->fp)) == buf.st_size) { goto cleanup; } } /* * End of file. Delete it, and re-set * everything. */ if (feof(data->fp)) { cleanup: unlink(data->filename_work); if (data->fp) fclose(data->fp);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -