Mercurial > hg > freeDiameter
changeset 1020:8110a781116a
New extension rt_busypeers. See doc/rt_busypeers.conf.sample for more information. This code is not tested yet
author | Sebastien Decugis <sdecugis@freediameter.net> |
---|---|
date | Mon, 01 Apr 2013 16:17:13 +0800 |
parents | 6fcd30ce3ce7 |
children | 6f987640a4b2 9beef9a09faa |
files | doc/rt_busypeers.conf.sample extensions/CMakeLists.txt extensions/rt_busypeers/CMakeLists.txt extensions/rt_busypeers/rtbusy.c extensions/rt_busypeers/rtbusy.h extensions/rt_busypeers/rtbusy_conf.l extensions/rt_busypeers/rtbusy_conf.y |
diffstat | 7 files changed, 670 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/rt_busypeers.conf.sample Mon Apr 01 16:17:13 2013 +0800 @@ -0,0 +1,47 @@ +# This file contains information for configuring the rt_busypeers extension. +# To find how to have freeDiameter load this extension, please refer to the freeDiameter documentation. +# +# The rt_busypeers extension has two purposes. +# - when the local peer receives an error DIAMETER_TOO_BUSY from a peer, +# this extension catchs this error and attempts to retransmit the query to another peer if it makes sense, i.e.: +# * the peer issuing the error is not the peer referenced in the Destination-Host AVP of the message, +# * we have a direct link with the peer that issued the error (see parameter RetryDistantPeers below) +# +# - When a request is forwarded by the local peer, start a timer and if the corresponding answer/error has +# not been received within RelayTimeout seconds, either send to another peer or return a DIAMETER_TOO_BUSY +# error, depending on the RetryMaxPeers parameter. +# +# This extension is mainly useful for Diameter agents, for Diameter clients it is recommended to +# implement this logic directly in the client application. + + +# Parameter: SkipTooBusyErrors +# If defined, this parameter disables the handling of Diameter Errors message with a Result-Code set to DIAMETER_TOO_BUSY in this extension. +# When this parameter is defined, the parameter RetryDistantPeer has no effect. +# Default: parameter is not defined. +#SkipTooBusyErrors; + + +# Parameter: RetryDistantPeers +# By default, the extension only retries to send messages if the peer that issued the DIAMETER_TOO_BUSY error is directly connected to +# the local peer (not through a Diameter agent). This avoids the situation where the message is sent to a different relay that will deliver +# to the same busy peer afterwards. If the parameter is defined, then the extension will also retry sending messages for errors generated in +# distant peers. This should increase the chance that the message is delivered, but also can increase the load of the network unnecessarily. +# Default: parameter is not defined. +#RetryDistantPeers; + + +# Parameter: RetryMaxPeers +# This parameter specifies the limit on the number of times a request can be re-sent to a different peer, before the local relay gives up and +# forwards the error to upstream. +# Default: 0, meaning all possible candidates are attempted before give up. +#RetryMaxPeers=0; + + +# Parameter: RelayTimeout +# If the value of this parameter is not 0, it specifies the number of seconds that the local relay waits for an answer to a +# forwarded request before considering the remote peer is busy and taking corrective action (similar as if that relay had returned TOO_BUSY status). +# Note: this parameter does not apply for requests issued locally. In that case, the extension issuing the request should directly specify the timeout. +# Default: 0, meaning that there is no timeout parameter. +#RelayTimeout=0; +
--- a/extensions/CMakeLists.txt Mon Apr 01 16:16:28 2013 +0800 +++ b/extensions/CMakeLists.txt Mon Apr 01 16:17:13 2013 +0800 @@ -69,6 +69,7 @@ FD_EXTENSION_SUBDIR(rt_default "Configurable routing rules for freeDiameter" ON) FD_EXTENSION_SUBDIR(rt_redirect "Handling of Diameter Redirect messages" ON) +FD_EXTENSION_SUBDIR(rt_busypeers "Handling of Diameter TOO_BUSY messages and relay timeouts" ON) FD_EXTENSION_SUBDIR(rt_ereg "Configurable routing based on regexp matching of AVP values" OFF)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/rt_busypeers/CMakeLists.txt Mon Apr 01 16:17:13 2013 +0800 @@ -0,0 +1,30 @@ +# The rt_ereg extension +PROJECT("Handling of TOO_BUSY messages and relay timeout capability routing extension" C) + +# Parser files +BISON_FILE(rtbusy_conf.y) +FLEX_FILE(rtbusy_conf.l) +SET_SOURCE_FILES_PROPERTIES(lex.rtbusy_conf.c rtbusy_conf.tab.c PROPERTIES COMPILE_FLAGS "-I ${CMAKE_CURRENT_SOURCE_DIR}") + +# List of source files +SET( RTBUSY_SRC + rtbusy.c + rtbusy.h + lex.rtbusy_conf.c + rtbusy_conf.tab.c + rtbusy_conf.tab.h +) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) + +# Compile these files as a freeDiameter extension +FD_ADD_EXTENSION(rt_busypeers ${RTBUSY_SRC}) + + +#### +## INSTALL section ## + +# We install with the daemon component because it is a base feature. +INSTALL(TARGETS rt_busypeers + LIBRARY DESTINATION ${INSTALL_EXTENSIONS_SUFFIX} + COMPONENT freeDiameter-daemon)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/rt_busypeers/rtbusy.c Mon Apr 01 16:17:13 2013 +0800 @@ -0,0 +1,263 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@freediameter.net> * +* * +* Copyright (c) 2011, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* See doc/rt_busypeers.conf.sample for more details about the features of this extension */ +#include "rtbusy.h" + +/* The configuration structure */ +struct rtbusy_conf rtbusy_conf; + +static struct fd_rt_fwd_hdl * rt_busy_hdl = NULL; + +static void rtbusy_expirecb(void * data, DiamId_t sentto, size_t senttolen, struct msg ** req); + +/* The function that does the actual work */ +int rt_busy_process_busy(struct msg ** pmsg, int is_req, DiamId_t sentto, size_t senttolen, union avp_value *oh) +{ + struct msg * qry = NULL; + struct rt_data * rtd = NULL; + struct fd_list * candidates = NULL; + int sendingattemtps; + int resend = 1; + + + TRACE_ENTRY("%p(%p) %d %p %p", pmsg, pmsg?*pmsg:NULL, is_req, sentto, oh); + + if (is_req) { + qry = *pmsg; + } else { + CHECK_FCT( fd_msg_answ_getq( *pmsg, &qry ) ); + } + + CHECK_FCT( fd_msg_rt_get ( qry, &rtd ) ); + ASSERT(rtd); + + /* rtd is the routing data associated with the query that was sent */ + + /* Store the error in this routing data, this avoids sending the message to the same peer again */ + CHECK_FCT( fd_rtd_error_add(rtd, + sentto, senttolen, + oh ? (DiamId_t)oh->os.data : fd_g_config->cnf_diamid, oh ? oh->os.len : fd_g_config->cnf_diamid_len , + ER_DIAMETER_TOO_BUSY, + &candidates, + &sendingattemtps) ); + + /* Now we need to decide if we re-send this query to a different peer or return an error to upstream */ + + /* First, are we exceeding the allowed attempts? */ + if (rtbusy_conf.RetryMaxPeers != 0) { + if (sendingattemtps >= rtbusy_conf.RetryMaxPeers) { + TRACE_DEBUG(FULL, "Maximum number of sending attempts reached for message %p, returning an error upstream", qry); + resend = 0; + } + } + + if (resend) { + /* Check if we have any valid candidate left for sending the message. This may not be 100% accurate but there should not be + any situation where this is actually an issue. */ + if (FD_IS_LIST_EMPTY(candidates)) { + resend = 0; + } else { + struct rtd_candidate * first = candidates->next->o; + if (first->score < 0) /* No more candidate available */ + resend = 0; + } + } + + /* Ok, now act on the message, we know what to do */ + if (resend) { + if (!is_req) { + /* We must free the answer we received, and send the query again */ + CHECK_FCT( fd_msg_answ_detach(*pmsg) ); + CHECK_FCT( fd_msg_free(*pmsg) ); + *pmsg = qry; + } + /* Send the query again. We need to re-associate the expirecb which was cleaned, if it is used */ + if (rtbusy_conf.RelayTimeout) { + struct timespec tm = { .tv_sec = rtbusy_conf.RelayTimeout, .tv_nsec=0 }; + CHECK_FCT( fd_msg_send_timeout( pmsg, NULL, NULL, rtbusy_expirecb, &tm ) ); + } else { + CHECK_FCT( fd_msg_send(pmsg, NULL, NULL) ); + } + + } else { + if (is_req) { + /* We must create an answer */ + CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, pmsg, MSGFL_ANSW_ERROR ) ); + + CHECK_FCT( fd_msg_rescode_set(*pmsg, "DIAMETER_TOO_BUSY", "[rt_busypeers] Timeout reached while waiting for an anwer", NULL, 1 ) ); + + CHECK_FCT( fd_msg_send(pmsg, NULL, NULL) ); + } + /* Otherwise, we have nothing to do at all, the answer will be forwarded upstream as part of the normal processing */ + + } + + return 0; +} + +/* Callback called on expiry of the timeout timer */ +static void rtbusy_expirecb(void * data, DiamId_t sentto, size_t senttolen, struct msg ** preq) +{ + CHECK_FCT_DO( rt_busy_process_busy(preq, 1, sentto, senttolen, NULL), /* continue */ ); +} + +/* the routing callback that handles all the tasks of this extension */ +static int rtbusy_fwd_cb(void * cbdata, struct msg ** pmsg) +{ + struct msg_hdr * hdr; + struct avp * avp; + union avp_value *a_rc = NULL, *a_oh = NULL; + DiamId_t sentto = NULL; + size_t senttolen; + + /* Get the header of the message */ + CHECK_FCT( fd_msg_hdr(*pmsg, &hdr) ); + + /* If the message is a request, we only associate the timeout */ + if (hdr->msg_flags & CMD_FLAG_REQUEST) { + struct timespec tm = { .tv_sec = rtbusy_conf.RelayTimeout, .tv_nsec=0 }; + CHECK_FCT( fd_msg_anscb_associate( *pmsg, NULL, NULL, rtbusy_expirecb, &tm ) ); + return 0; + } + + /* From this point, the message is an answer; we need to check if the E flag is set and if the Result-Code is DIAMETER_TOO_BUSY */ + + if (!(hdr->msg_flags & CMD_FLAG_ERROR)) { + /* This answer does not have the E flag, no need to process further */ + return 0; + } + + CHECK_FCT( fd_msg_source_get( *pmsg, &sentto, &senttolen ) ); + + /* Now get the AVPs we are interested in */ + CHECK_FCT( fd_msg_browse(*pmsg, MSG_BRW_FIRST_CHILD, &avp, NULL) ); + while (avp) { + struct avp_hdr * ahdr; + + CHECK_FCT( fd_msg_avp_hdr( avp, &ahdr ) ); + if (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) { + switch (ahdr->avp_code) { + case AC_ORIGIN_HOST: + /* Parse this AVP */ + CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) ); + ASSERT( ahdr->avp_value ); + a_oh = ahdr->avp_value; + break; + + case AC_RESULT_CODE: + /* Parse this AVP */ + CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) ); + ASSERT( ahdr->avp_value ); + a_rc = ahdr->avp_value; + + if (a_rc->u32 != ER_DIAMETER_TOO_BUSY) { + /* It is not a TOO_BUSY error, we don't do anything */ + goto out; + } + break; + } + + if (a_rc && a_oh) + break; + } + + /* Go to next AVP */ + CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) ); + } + + /* Check we have received the necessary information */ + if (!a_rc) { + TRACE_ERROR( "Invalid Diameter answer without a Result-Code AVP, rt_busypeer module gave up processing"); + goto out; + } + + if (!a_oh) { + TRACE_ERROR( "Invalid Diameter answer without an Origin-Host AVP, rt_busypeer module gave up processing"); + goto out; + } + + /* Pass this error to the function that processes BUSY status */ + CHECK_FCT( rt_busy_process_busy(pmsg, 0, sentto, senttolen, a_oh) ); + +out: + return 0; +} + + +/* entry point */ +static int rtbusy_entry(char * conffile) +{ + enum fd_rt_fwd_dir dir = RT_FWD_ANS; + TRACE_ENTRY("%p", conffile); + + /* Initialize the configuration */ + memset(&rtbusy_conf, 0, sizeof(rtbusy_conf)); + + /* Parse the configuration file */ + CHECK_FCT( rtbusy_conf_handle(conffile) ); + + if (rtbusy_conf.SkipTooBusyErrors && !rtbusy_conf.RelayTimeout) { + TRACE_NOTICE("[rt_busypeers] Configuration file does not specify any behavior (no effect)!"); + return 0; + } + + if (rtbusy_conf.SkipTooBusyErrors) + dir = RT_FWD_REQ; /* in this case, RelayTimeout is not 0 */ + else if (rtbusy_conf.RelayTimeout) + dir = RT_FWD_ALL; + + /* Register the callback */ + CHECK_FCT( fd_rt_fwd_register ( rtbusy_fwd_cb, NULL, dir, &rt_busy_hdl ) ); + + /* We're done */ + return 0; +} + +/* Unload */ +void fd_ext_fini(void) +{ + int i; + TRACE_ENTRY(); + + /* Unregister the cb */ + if (rt_busy_hdl) + CHECK_FCT_DO( fd_rt_fwd_unregister ( rt_busy_hdl, NULL), /* continue */); + + /* Done */ + return ; +} + +EXTENSION_ENTRY("rt_busypeers", rtbusy_entry);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/rt_busypeers/rtbusy.h Mon Apr 01 16:17:13 2013 +0800 @@ -0,0 +1,54 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@freediameter.net> * +* * +* Copyright (c) 2013, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* + * See the rt_busypeers.conf.sample file for the format of the configuration file. + */ + +/* FreeDiameter's common include file */ +#include <freeDiameter/extension.h> + + +/* Parse the configuration file */ +int rtbusy_conf_handle(char * conffile); + +/* The configuration structure */ +extern struct rtbusy_conf { + int SkipTooBusyErrors; + int RetryDistantPeers; + int RetryMaxPeers; + int RelayTimeout; +} rtbusy_conf; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/rt_busypeers/rtbusy_conf.l Mon Apr 01 16:17:13 2013 +0800 @@ -0,0 +1,107 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@freediameter.net> * +* * +* Copyright (c) 2013, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* Tokenizer + * + */ + +%{ +#include "rtbusy.h" +#include "rtbusy_conf.tab.h" + +/* Update the column information */ +#define YY_USER_ACTION { \ + yylloc->first_column = yylloc->last_column + 1; \ + yylloc->last_column = yylloc->first_column + yyleng - 1; \ +} + +/* Avoid warning with newer flex */ +#define YY_NO_INPUT + +%} + +qstring \"[^\"\n]*\" + + +%option bison-bridge bison-locations +%option noyywrap +%option nounput + +%% + + /* Update the line count */ +\n { + yylloc->first_line++; + yylloc->last_line++; + yylloc->last_column=0; + } + + /* Eat all spaces but not new lines */ +([[:space:]]{-}[\n])+ ; + /* Eat all comments */ +#.*$ ; + + /* Recognize any integer */ +[-]?[[:digit:]]+ { + /* Convert this to an integer value */ + int ret=0; + ret = sscanf(yytext, "%i", &yylval->integer); + if (ret != 1) { + /* No matching: an error occurred */ + TRACE_ERROR("Unable to convert the value '%s' to a valid number: %s", yytext, strerror(errno)); + return LEX_ERROR; /* trig an error in yacc parser */ + /* Maybe we could REJECT instead of failing here? */ + } + return INTEGER; + } + + + + /* The key words */ +(?i:"SkipTooBusyErrors") { return SKIPTOOBUSYERRORS; } +(?i:"RetryDistantPeers") { return RETRYDISTANTPEERS; } +(?i:"RetryMaxPeers") { return RETRYMAXPEERS; } +(?i:"RelayTimeout") { return RELAYTIMEOUT; } + + /* Valid single characters for yyparse */ +[=;] { return yytext[0]; } + + /* Unrecognized sequence, if it did not match any previous pattern */ +[^[:space:]=;\n]+ { + TRACE_ERROR("Unrecognized text on line %d col %d: '%s'.", yylloc->first_line, yylloc->first_column, yytext); + return LEX_ERROR; + } + +%%
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/rt_busypeers/rtbusy_conf.y Mon Apr 01 16:17:13 2013 +0800 @@ -0,0 +1,168 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis <sdecugis@freediameter.net> * +* * +* Copyright (c) 2013, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* Yacc extension's configuration parser. + */ + +/* For development only : */ +%debug +%error-verbose + +/* The parser receives the configuration file filename as parameter */ +%parse-param {char * conffile} + +/* Keep track of location */ +%locations +%pure-parser + +%{ +#include "rtbusy.h" +#include "rtbusy_conf.tab.h" + +/* Forward declaration */ +int yyparse(char * conffile); + +/* Parse the configuration file */ +int rtbusy_conf_handle(char * conffile) +{ + extern FILE * rtbusy_confin; + int ret; + + TRACE_ENTRY("%p", conffile); + + TRACE_DEBUG (FULL, "Parsing configuration file: %s...", conffile); + + rtbusy_confin = fopen(conffile, "r"); + if (rtbusy_confin == NULL) { + ret = errno; + TRACE_ERROR("Unable to open extension configuration file %s for reading: %s", conffile, strerror(ret)); + return ret; + } + + ret = yyparse(conffile); + + fclose(rtbusy_confin); + + if (ret != 0) { + TRACE_ERROR( "Unable to parse the configuration file."); + return EINVAL; + } else { + TRACE_DEBUG(FULL, "[rt_busypeers] Configuration: %d-%d-%d-%d.", rtbusy_conf.SkipTooBusyErrors, rtbusy_conf.RetryDistantPeers, rtbusy_conf.RetryMaxPeers, rtbusy_conf.RelayTimeout); + } + + return 0; +} + +/* The Lex parser prototype */ +int rtbusy_conflex(YYSTYPE *lvalp, YYLTYPE *llocp); + +/* Function to report the errors */ +void yyerror (YYLTYPE *ploc, char * conffile, char const *s) +{ + TRACE_DEBUG(INFO, "Error in configuration parsing"); + + if (ploc->first_line != ploc->last_line) + fd_log_error("%s:%d.%d-%d.%d : %s", conffile, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s); + else if (ploc->first_column != ploc->last_column) + fd_log_error("%s:%d.%d-%d : %s", conffile, ploc->first_line, ploc->first_column, ploc->last_column, s); + else + fd_log_error("%s:%d.%d : %s", conffile, ploc->first_line, ploc->first_column, s); +} + +%} + +/* Values returned by lex for token */ +%union { + int integer; +} + +/* In case of error in the lexical analysis */ +%token LEX_ERROR + +/* A (de)quoted string (malloc'd in lex parser; it must be freed after use) */ +%token <integer> INTEGER + +/* Tokens */ +%token SKIPTOOBUSYERRORS +%token RETRYDISTANTPEERS +%token RETRYMAXPEERS +%token RELAYTIMEOUT + + +/* -------------------------------------- */ +%% + + /* The grammar definition */ +conffile: /* empty is OK */ + | conffile toobusy + | conffile distant + | conffile maxretry + | conffile timeout + | conffile errors + { + yyerror(&yylloc, conffile, "An error occurred while parsing the configuration file"); + return EINVAL; + } + ; + + /* Lexical or syntax error */ +errors: LEX_ERROR + | error + ; + +toobusy: SKIPTOOBUSYERRORS ';' + { + rtbusy_conf.SkipTooBusyErrors=1; + } + ; + +distant: RETRYDISTANTPEERS ';' + { + rtbusy_conf.RetryDistantPeers=1; + } + ; + +maxretry: RETRYMAXPEERS '=' INTEGER ';' + { + rtbusy_conf.RetryMaxPeers=$3; + } + ; + +timeout: RELAYTIMEOUT '=' INTEGER ';' + { + rtbusy_conf.RelayTimeout=$3; + } + ; +