📄 modcall.c
字号:
/* * modcall.c * * Version: $Id: modcall.c,v 1.22.2.1.2.4 2007/06/12 09:31:45 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright 2000 The FreeRADIUS server project */#include <stdlib.h>#include <string.h>#include <stdarg.h>#include "radiusd.h"#include "rad_assert.h"#include "conffile.h"#include "modpriv.h"#include "modules.h"#include "modcall.h"/* mutually-recursive static functions need a prototype up front */static modcallable *do_compile_modgroup(int, CONF_SECTION *, const char *, int, int);/* Actions may be a positive integer (the highest one returned in the group * will be returned), or the keyword "return", represented here by * MOD_ACTION_RETURN, to cause an immediate return. * There's also the keyword "reject", represented here by MOD_ACTION_REJECT * to cause an immediate reject. */#define MOD_ACTION_RETURN (-1)#define MOD_ACTION_REJECT (-2)/* Here are our basic types: modcallable, modgroup, and modsingle. For an * explanation of what they are all about, see ../../doc/README.failover */struct modcallable { struct modcallable *next; const char *name; int actions[RLM_MODULE_NUMCODES]; enum { MOD_SINGLE, MOD_GROUP, MOD_LOAD_BALANCE, MOD_REDUNDANT_LOAD_BALANCE } type;};#define GROUPTYPE_SIMPLE 0#define GROUPTYPE_REDUNDANT 1#define GROUPTYPE_APPEND 2#define GROUPTYPE_COUNT 3typedef struct { modcallable mc; int grouptype; modcallable *children;} modgroup;typedef struct { modcallable mc; module_instance_t *modinst;} modsingle;static const LRAD_NAME_NUMBER grouptype_table[] = { { "", GROUPTYPE_SIMPLE }, { "redundant ", GROUPTYPE_REDUNDANT }, { "append ", GROUPTYPE_APPEND }, { NULL, -1 }};/* Simple conversions: modsingle and modgroup are subclasses of modcallable, * so we often want to go back and forth between them. */static modsingle *mod_callabletosingle(modcallable *p){ rad_assert(p->type==MOD_SINGLE); return (modsingle *)p;}static modgroup *mod_callabletogroup(modcallable *p){ rad_assert((p->type==MOD_GROUP) || (p->type==MOD_LOAD_BALANCE) || (p->type==MOD_REDUNDANT_LOAD_BALANCE)); return (modgroup *)p;}static modcallable *mod_singletocallable(modsingle *p){ return (modcallable *)p;}static modcallable *mod_grouptocallable(modgroup *p){ return (modcallable *)p;}/* modgroups are grown by adding a modcallable to the end */static void add_child(modgroup *g, modcallable *c){ modcallable **head = &g->children; modcallable *node = *head; modcallable **last = head; if (!c) return; while (node) { last = &node->next; node = node->next; } rad_assert(c->next == NULL); *last = c;}/* Here's where we recognize all of our keywords: first the rcodes, then the * actions */static const LRAD_NAME_NUMBER rcode_table[] = { { "reject", RLM_MODULE_REJECT }, { "fail", RLM_MODULE_FAIL }, { "ok", RLM_MODULE_OK }, { "handled", RLM_MODULE_HANDLED }, { "invalid", RLM_MODULE_INVALID }, { "userlock", RLM_MODULE_USERLOCK }, { "notfound", RLM_MODULE_NOTFOUND }, { "noop", RLM_MODULE_NOOP }, { "updated", RLM_MODULE_UPDATED }, { NULL, 0 }};/* * Compile action && rcode for later use. */static int compile_action(modcallable *c, const char *attr, const char *value, const char *filename, int lineno){ int rcode, action; rcode = lrad_str2int(rcode_table, attr, -1); if (rcode < 0) { radlog(L_ERR|L_CONS, "%s[%d] Unknown module rcode '%s'.\n", filename, lineno, attr); return 0; } if (!strcasecmp(value, "return")) action = MOD_ACTION_RETURN; else if (!strcasecmp(value, "reject")) action = MOD_ACTION_REJECT; else if (strspn(value, "0123456789")==strlen(value)) { action = atoi(value); /* * Don't allow priority zero, for future use. */ if (action == 0) return 0; } else { radlog(L_ERR|L_CONS, "%s[%d] Unknown action '%s'.\n", filename, lineno, value); return 0; } c->actions[rcode] = action; return 1;}#if 0static const char *action2str(int action){ static char buf[32]; if(action==MOD_ACTION_RETURN) return "return"; if(action==MOD_ACTION_REJECT) return "reject"; snprintf(buf, sizeof buf, "%d", action); return buf;}#endif/* Some short names for debugging output */static const char *comp2str[] = { "authenticate", "authorize", "preacct", "accounting", "session", "pre-proxy", "post-proxy", "post-auth"};#ifdef HAVE_PTHREAD_H/* * Lock the mutex for the module */static void safe_lock(module_instance_t *instance){ if (instance->mutex) pthread_mutex_lock(instance->mutex);}/* * Unlock the mutex for the module */static void safe_unlock(module_instance_t *instance){ if (instance->mutex) pthread_mutex_unlock(instance->mutex);}#else/* * No threads: these functions become NULL's. */#define safe_lock(foo)#define safe_unlock(foo)#endifstatic int call_modsingle(int component, modsingle *sp, REQUEST *request, int default_result){ int myresult = default_result; DEBUG3(" modsingle[%s]: calling %s (%s) for request %d", comp2str[component], sp->modinst->name, sp->modinst->entry->name, request->number); request->component = comp2str[component]; request->module = sp->modinst->entry->name; safe_lock(sp->modinst); myresult = sp->modinst->entry->module->methods[component]( sp->modinst->insthandle, request); safe_unlock(sp->modinst); request->module = "<server core>"; DEBUG3(" modsingle[%s]: returned from %s (%s) for request %d", comp2str[component], sp->modinst->name, sp->modinst->entry->name, request->number); return myresult;}/* * Helper function for call_modgroup, and call_modredundantloadbalance * * Returns 0 for "stop", and "1" for continue. */static int call_one(int component, modcallable *p, REQUEST *request, int *priority, int *result){ int r;#ifdef RAD_REQUEST_OPTION_STOP_NOW /* * A module has taken too long to process the request, * and we've been told to stop processing it. */ if (request->options & RAD_REQUEST_OPTION_STOP_NOW) { *result = RLM_MODULE_FAIL; return 0; }#endif /* Call this child by recursing into modcall */ r = modcall(component, p, request); #if 0 DEBUG2("%s: action for %s is %s", comp2str[component], lrad_int2str(rcode_table, r, "??"), action2str(p->actions[r]));#endif /* * Find an action to go with the child's result. If it is * "return", break out of the loop so the rest of the * children in the list will be skipped. */ if (p->actions[r] == MOD_ACTION_RETURN) { *result = r; return 0; } /* If "reject" break out of the loop and return reject */ if (p->actions[r] == MOD_ACTION_REJECT) { *result = RLM_MODULE_REJECT; return 0; } /* * Otherwise, the action is a number, the preference * level of this return code. If no higher preference has * been seen yet, remember this one . */ if (p->actions[r] >= *priority) { *result = r; *priority = p->actions[r]; } return 1;}static int call_modgroup(int component, modgroup *g, REQUEST *request, int default_result){ int myresult = default_result; int priority = 0; /* default result has lowest priority */ modcallable *p; /* * Catch people who have issues. */ if (!g->children) { DEBUG2(" WARNING! Asked to process empty group. Returning %s.", lrad_int2str(rcode_table, myresult, "??")); return default_result; } /* Loop over the children */ for (p = g->children; p; p = p->next) { if (!call_one(component, p, request, &priority, &myresult)) { break; } } return myresult;}static int call_modloadbalance(int component, modgroup *g, REQUEST *request, int default_result){ int count = 1; modcallable *p, *child = NULL; /* * Catch people who have issues. */ if (!g->children) { DEBUG2(" WARNING! Asked to process empty load-balance group. Returning %s.", lrad_int2str(rcode_table, default_result, "??")); return default_result; } /* * Pick a random child. */ /* Loop over the children */ for(p = g->children; p; p = p->next) { if (!child) { child = p; count = 1; continue; } /* * Keep track of how many load balancing servers * we've gone through. */ count++; /* * See the "camel book" for why this works. * * If (rand(0..n) < 1), pick the current realm. * We add a scale factor of 65536, to avoid * floating point. */ if ((count * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) { child = p; } } rad_assert(child != NULL); /* Call the chosen child by recursing into modcall */ return modcall(component, child, request);}/* * For more than 2 modules with redundancy + load balancing * across all of them, layering the "redundant" and * "load-balance" groups gets too complicated. As a result, we * implement a special function to do this. */static int call_modredundantloadbalance(int component, modgroup *g, REQUEST *request, int default_result){ int count = 1; int myresult = default_result; int priority = 0; /* default result has lowest priority */ modcallable *p, *child = NULL; /* * Catch people who have issues. */ if (!g->children) { DEBUG2(" WARNING! Asked to process empty redundant-load-balance group. Returning %s.", lrad_int2str(rcode_table, default_result, "??")); return default_result; } /* * Pick a random child. */ /* Loop over the children */ for(p = g->children; p; p = p->next) { if (!child) { child = p; count = 1; continue;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -