📄 event.c
字号:
/* * event.c Server event handling * * Version: $Id: event.c,v 1.100 2008/04/20 15:00:06 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: event.c,v 1.100 2008/04/20 15:00:06 aland Exp $")#include <freeradius-devel/radiusd.h>#include <freeradius-devel/modules.h>#include <freeradius-devel/event.h>#include <freeradius-devel/detail.h>#include <freeradius-devel/radius_snmp.h>#include <freeradius-devel/rad_assert.h>#include <signal.h>#include <fcntl.h>#ifdef HAVE_SYS_WAIT_H# include <sys/wait.h>#endif#define USEC (1000000)extern pid_t radius_pid;extern int dont_fork;extern int check_config;extern void force_log_reopen(void);/* * Ridiculous amounts of local state. */static fr_event_list_t *el = NULL;static fr_packet_list_t *pl = NULL;static int request_num_counter = 0;static struct timeval now;static time_t start_time;static int have_children;static int has_detail_listener = FALSE;static int just_started = FALSE;#ifndef __MINGW32__static int self_pipe[2];#endif#ifdef HAVE_PTHREAD_Hstatic pthread_mutex_t proxy_mutex;#define PTHREAD_MUTEX_LOCK if (have_children) pthread_mutex_lock#define PTHREAD_MUTEX_UNLOCK if (have_children) pthread_mutex_unlock#else/* * This is easier than ifdef's throughout the code. */#define PTHREAD_MUTEX_LOCK(_x)#define PTHREAD_MUTEX_UNLOCK(_x)#endif#define INSERT_EVENT(_function, _ctx) if (!fr_event_insert(el, _function, _ctx, &((_ctx)->when), &((_ctx)->ev))) { _rad_panic(__FILE__, __LINE__, "Failed to insert event"); }static fr_packet_list_t *proxy_list = NULL;/* * We keep the proxy FD's here. The RADIUS Id's are marked * "allocated" per Id, via a bit per proxy FD. */static int proxy_fds[32];static rad_listen_t *proxy_listeners[32];static void request_post_handler(REQUEST *request);static void wait_a_bit(void *ctx);static void event_socket_handler(fr_event_list_t *xel, UNUSED int fd, void *ctx);static void NEVER_RETURNS _rad_panic(const char *file, unsigned int line, const char *msg){ radlog(L_ERR, "[%s:%d] %s", file, line, msg); _exit(1);}#define rad_panic(x) _rad_panic(__FILE__, __LINE__, x)static void tv_add(struct timeval *tv, int usec_delay){ if (usec_delay > USEC) { tv->tv_sec += usec_delay / USEC; usec_delay %= USEC; } tv->tv_usec += usec_delay; if (tv->tv_usec > USEC) { tv->tv_usec -= USEC; tv->tv_sec++; }}#ifdef WITH_SNMPstatic void snmp_inc_counters(REQUEST *request){ if (!request->root->do_snmp) return; if (request->master_state == REQUEST_COUNTED) return; if ((request->listener->type != RAD_LISTEN_AUTH) && (request->listener->type != RAD_LISTEN_ACCT)) return; /* * Update the SNMP statistics. * * Note that we do NOT do this in a child thread. * Instead, we update the stats when a request is * deleted, because only the main server thread calls * this function, which makes it thread-safe. */ switch (request->reply->code) { case PW_AUTHENTICATION_ACK: rad_snmp.auth.total_responses++; rad_snmp.auth.total_access_accepts++; if (request->client && request->client->auth) { request->client->auth->accepts++; } break; case PW_AUTHENTICATION_REJECT: rad_snmp.auth.total_responses++; rad_snmp.auth.total_access_rejects++; if (request->client && request->client->auth) { request->client->auth->rejects++; } break; case PW_ACCESS_CHALLENGE: rad_snmp.auth.total_responses++; rad_snmp.auth.total_access_challenges++; if (request->client && request->client->auth) { request->client->auth->challenges++; } break; case PW_ACCOUNTING_RESPONSE: rad_snmp.acct.total_responses++; if (request->client && request->client->acct) { request->client->acct->responses++; } break; /* * No response, it must have been a bad * authenticator. */ case 0: if (request->packet->code == PW_AUTHENTICATION_REQUEST) { rad_snmp.auth.total_bad_authenticators++; if (request->client && request->client->auth) { request->client->auth->bad_authenticators++; } } break; default: break; } request->master_state = REQUEST_COUNTED;}#else#define snmp_inc_counters(_x)#endifstatic void remove_from_request_hash(REQUEST *request){ if (!request->in_request_hash) return; fr_packet_list_yank(pl, request->packet); request->in_request_hash = FALSE; snmp_inc_counters(request);}static REQUEST *lookup_in_proxy_hash(RADIUS_PACKET *reply){ RADIUS_PACKET **proxy_p; REQUEST *request; PTHREAD_MUTEX_LOCK(&proxy_mutex); proxy_p = fr_packet_list_find_byreply(proxy_list, reply); if (!proxy_p) { PTHREAD_MUTEX_UNLOCK(&proxy_mutex); return NULL; } request = fr_packet2myptr(REQUEST, proxy, proxy_p); if (!request) { PTHREAD_MUTEX_UNLOCK(&proxy_mutex); return NULL; } request->num_proxied_responses++; /* * Catch the most common case of everything working * correctly. */ if (request->num_proxied_requests == request->num_proxied_responses) { fr_packet_list_yank(proxy_list, request->proxy); fr_packet_list_id_free(proxy_list, request->proxy); request->in_proxy_hash = FALSE; } /* * On the FIRST reply, decrement the count of outstanding * requests. Note that this is NOT the count of sent * packets, but whether or not the home server has * responded at all. */ if (!request->proxy_reply && request->home_server->currently_outstanding) { request->home_server->currently_outstanding--; } PTHREAD_MUTEX_UNLOCK(&proxy_mutex); return request;}static void remove_from_proxy_hash(REQUEST *request){ if (!request->in_proxy_hash) return; PTHREAD_MUTEX_LOCK(&proxy_mutex); fr_packet_list_yank(proxy_list, request->proxy); fr_packet_list_id_free(proxy_list, request->proxy); /* * The home server hasn't replied, but we've given up on * this request. Don't count this request against the * home server. */ if (!request->proxy_reply && request->home_server->currently_outstanding) { request->home_server->currently_outstanding--; } PTHREAD_MUTEX_UNLOCK(&proxy_mutex); request->in_proxy_hash = FALSE;}static int insert_into_proxy_hash(REQUEST *request){ int i, proxy; char buf[128]; rad_assert(request->proxy != NULL); rad_assert(proxy_list != NULL); request->proxy->sockfd = -1; PTHREAD_MUTEX_LOCK(&proxy_mutex); request->home_server->currently_outstanding++; request->home_server->total_requests_sent++; /* * On overflow, back up to ~0. */ if (!request->home_server->total_requests_sent) { request->home_server->total_requests_sent--; } if (!fr_packet_list_id_alloc(proxy_list, request->proxy)) { int found; rad_listen_t *proxy_listener; /* * Allocate a new proxy fd. This function adds * it to the tail of the list of listeners. With * some care, this can be thread-safe. */ proxy_listener = proxy_new_listener(); if (!proxy_listener) { PTHREAD_MUTEX_UNLOCK(&proxy_mutex); DEBUG2("ERROR: Failed to create a new socket for proxying requests."); return 0; } /* * Cache it locally. */ found = -1; proxy = proxy_listener->fd; for (i = 0; i < 32; i++) { /* * Found a free entry. Save the socket, * and remember where we saved it. */ if (proxy_fds[(proxy + i) & 0x1f] == -1) { found = (proxy + i) & 0x1f; proxy_fds[found] = proxy; proxy_listeners[found] = proxy_listener; break; } } rad_assert(found >= 0); if (!fr_packet_list_socket_add(proxy_list, proxy_listener->fd)) { PTHREAD_MUTEX_UNLOCK(&proxy_mutex); DEBUG2("ERROR: Failed to create a new socket for proxying requests."); return 0; } if (!fr_packet_list_id_alloc(proxy_list, request->proxy)) { PTHREAD_MUTEX_UNLOCK(&proxy_mutex); DEBUG2("ERROR: Failed to create a new socket for proxying requests."); return 0; } /* * Signal the main thread to add the new FD to the list * of listening FD's. */ radius_signal_self(RADIUS_SIGNAL_SELF_NEW_FD); } rad_assert(request->proxy->sockfd >= 0); /* * FIXME: Hack until we get rid of rad_listen_t, and put * the information into the packet_list. */ proxy = -1; for (i = 0; i < 32; i++) { if (proxy_fds[i] == request->proxy->sockfd) { proxy = i; break; } } if (proxy < 0) { PTHREAD_MUTEX_UNLOCK(&proxy_mutex); DEBUG2("ERROR: All sockets are full."); return 0; } rad_assert(proxy_fds[proxy] != -1); rad_assert(proxy_listeners[proxy] != NULL); request->proxy_listener = proxy_listeners[proxy]; if (!fr_packet_list_insert(proxy_list, &request->proxy)) { fr_packet_list_id_free(proxy_list, request->proxy); PTHREAD_MUTEX_UNLOCK(&proxy_mutex); DEBUG2("ERROR: Failed to insert entry into proxy list"); return 0; } PTHREAD_MUTEX_UNLOCK(&proxy_mutex); DEBUG3(" proxy: allocating destination %s port %d - Id %d", inet_ntop(request->proxy->dst_ipaddr.af, &request->proxy->dst_ipaddr.ipaddr, buf, sizeof(buf)), request->proxy->dst_port, request->proxy->id); request->in_proxy_hash = TRUE; return 1;}/* * Called as BOTH an event, and in-line from other functions. */static void wait_for_proxy_id_to_expire(void *ctx){ REQUEST *request = ctx; home_server *home = request->home_server; rad_assert(request->magic == REQUEST_MAGIC); rad_assert(request->proxy != NULL); if (!fr_event_now(el, &now)) gettimeofday(&now, NULL); request->when = request->proxy_when; request->when.tv_sec += home->response_window; if ((request->num_proxied_requests == request->num_proxied_responses) || timercmp(&now, &request->when, >)) { if (request->packet) { DEBUG2("Cleaning up request %d ID %d with timestamp +%d", request->number, request->packet->id, (unsigned int) (request->timestamp - start_time)); } else { DEBUG2("Cleaning up request %d with timestamp +%d", request->number, (unsigned int) (request->timestamp - start_time)); } fr_event_delete(el, &request->ev); remove_from_proxy_hash(request); remove_from_request_hash(request); request_free(&request); return; } INSERT_EVENT(wait_for_proxy_id_to_expire, request);}static void wait_for_child_to_die(void *ctx){ REQUEST *request = ctx; rad_assert(request->magic == REQUEST_MAGIC); if ((request->child_state == REQUEST_QUEUED) | (request->child_state == REQUEST_RUNNING)) { request->delay += (request->delay >> 1); tv_add(&request->when, request->delay); DEBUG2("Child is still stuck for request %d", request->number); INSERT_EVENT(wait_for_child_to_die, request); return; } DEBUG2("Child is finally responsive for request %d", request->number); remove_from_request_hash(request); if (request->proxy) { wait_for_proxy_id_to_expire(request); return; } request_free(&request);}static void cleanup_delay(void *ctx){ REQUEST *request = ctx; rad_assert(request->magic == REQUEST_MAGIC); rad_assert((request->child_state == REQUEST_CLEANUP_DELAY) || (request->child_state == REQUEST_DONE)); remove_from_request_hash(request); if (request->proxy && request->in_proxy_hash) { wait_for_proxy_id_to_expire(request); return; } DEBUG2("Cleaning up request %d ID %d with timestamp +%d", request->number, request->packet->id, (unsigned int) (request->timestamp - start_time)); fr_event_delete(el, &request->ev); request_free(&request);}static void reject_delay(void *ctx){ REQUEST *request = ctx; rad_assert(request->magic == REQUEST_MAGIC); rad_assert(request->child_state == REQUEST_REJECT_DELAY); DEBUG2("Sending delayed reject for request %d", request->number); request->listener->send(request->listener, request); request->when.tv_sec += request->root->cleanup_delay; request->child_state = REQUEST_CLEANUP_DELAY; INSERT_EVENT(cleanup_delay, request);}static void revive_home_server(void *ctx){ home_server *home = ctx; home->state = HOME_STATE_ALIVE; DEBUG2("Marking home server alive again... we have no idea if it really is alive or not."); home->currently_outstanding = 0;}static void no_response_to_ping(void *ctx){ REQUEST *request = ctx; home_server *home = request->home_server; char buffer[128]; home->num_received_pings = 0; DEBUG2("No response to status check %d from home server %s port %d", request->number,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -