ratelimit.c

Go to the documentation of this file.
00001 /*
00002  * $Id: ratelimit.c 5769 2009-03-26 21:47:59Z osas $
00003  *
00004  * ratelimit module
00005  *
00006  * Copyright (C) 2006 Hendrik Scholz <hscholz@raisdorf.net>
00007  * Copyright (C) 2008 Ovidiu Sas <osas@voipembedded.com>
00008  *
00009  * This file is part of Kamailio, a free SIP server.
00010  *
00011  * Kamailio is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License as published by
00013  * the Free Software Foundation; either version 2 of the License, or
00014  * (at your option) any later version
00015  *
00016  * Kamailio is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License 
00022  * along with this program; if not, write to the Free Software 
00023  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00024  *
00025  * History:
00026  * ---------
00027  *
00028  * 2008-01-10 ported from SER project (osas)
00029  * 2008-01-16 ported enhancements from openims project (osas) 
00030  */
00031 
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034 #include <sys/types.h>
00035 #include <regex.h>
00036 #include <math.h>
00037 
00038 #include "../../mem/mem.h"
00039 #include "../../mem/shm_mem.h"
00040 #include "../../sr_module.h"
00041 #include "../../dprint.h"
00042 #include "../../timer.h"
00043 #include "../../ut.h"
00044 #include "../../locking.h"
00045 #include "../../mod_fix.h"
00046 #include "../../data_lump.h"
00047 #include "../../data_lump_rpl.h"
00048 #include "../../statistics.h"
00049 #include "../sl/sl_api.h"
00050 
00051 MODULE_VERSION
00052 
00053 #define MAX_PIPES       16
00054 #define MAX_QUEUES      10
00055 
00056 /*
00057  * timer interval length in seconds, tunable via modparam
00058  */
00059 #define RL_TIMER_INTERVAL 10
00060 
00061 #define RXLS(m, str, i) (m)[i].rm_eo - (m)[i].rm_so, (str) + (m)[i].rm_so
00062 #define RXL(m, str, i) (m)[i].rm_eo - (m)[i].rm_so
00063 #define RXS(m, str, i) (str) + (m)[i].rm_so
00064 
00065 /** SL binds */
00066 struct sl_binds slb;
00067 
00068 static inline int str_cmp(const str * a, const str * b);
00069 static inline int str_i_cmp(const str * a, const str * b);
00070 
00071 typedef struct str_map {
00072    str     str;
00073    int     id;
00074 } str_map_t;
00075 
00076 static int str_map_str(const str_map_t * map, const str * key, int * ret);
00077 static int str_map_int(const str_map_t * map, int key, str * ret);
00078 
00079 /* PIPE_ALGO_FEEDBACK holds cpu usage to a fixed value using 
00080  * negative feedback according to the PID controller model
00081  *
00082  * <http://en.wikipedia.org/wiki/PID_controller>
00083  */
00084 enum {
00085    PIPE_ALGO_NOP = 0,
00086    PIPE_ALGO_RED,
00087    PIPE_ALGO_TAILDROP,
00088    PIPE_ALGO_FEEDBACK,
00089    PIPE_ALGO_NETWORK
00090 };
00091 
00092 str_map_t algo_names[] = {
00093    {str_init("NOP"), PIPE_ALGO_NOP},
00094    {str_init("RED"), PIPE_ALGO_RED},
00095    {str_init("TAILDROP"),  PIPE_ALGO_TAILDROP},
00096    {str_init("FEEDBACK"),  PIPE_ALGO_FEEDBACK},
00097    {str_init("NETWORK"),   PIPE_ALGO_NETWORK},
00098    {{0, 0},    0},
00099 };
00100 
00101 /* at jiri@iptel.org's suggestion:
00102  *
00103  * set this to 'cpu' to have openser look at /proc/stat every time_interval
00104  * or set it to 'external' and you can push data in from an external source
00105  * via the fifo interface
00106  */
00107 enum {
00108    LOAD_SOURCE_CPU,
00109    LOAD_SOURCE_EXTERNAL
00110 };
00111 
00112 str_map_t source_names[] = {
00113    {str_init("cpu"), LOAD_SOURCE_CPU},
00114    {str_init("external"),  LOAD_SOURCE_EXTERNAL},
00115    {{0, 0},    0},
00116 };
00117 
00118 static int rl_drop_code = 503;
00119 static str rl_drop_reason = str_init("Server Unavailable");
00120 
00121 typedef struct pipe {
00122    /* stuff that gets read as a modparam or set via fifo */
00123    int *   algo;
00124    int             algo_mp;
00125    int *   limit;
00126    int             limit_mp;
00127 
00128    /* updated values */
00129    int *   counter;
00130    int *   last_counter;
00131    int *   load;
00132 } pipe_t;
00133 
00134 typedef struct rl_queue {
00135    int     *       pipe;
00136    int             pipe_mp;
00137    str     *       method;
00138    str             method_mp;
00139 } rl_queue_t;
00140 
00141 /* === these change after startup */
00142 gen_lock_t * rl_lock;
00143 
00144 static double * load_value;     /* actual load, used by PIPE_ALGO_FEEDBACK */
00145 static double * pid_kp, * pid_ki, * pid_kd, * pid_setpoint; /* PID tuning params */
00146 static int * drop_rate;         /* updated by PIPE_ALGO_FEEDBACK */
00147 
00148 static int * network_load_value;      /* network load */
00149 
00150 /* where to get the load for feedback. values: cpu, external */
00151 static int load_source_mp = LOAD_SOURCE_CPU;
00152 static int * load_source;
00153 
00154 typedef struct pipe_params {
00155    int no;
00156    int algo;
00157    int limit;
00158 } pipe_params_t;
00159 
00160 typedef struct rl_queue_params {
00161    int pipe;
00162    str method;
00163 } rl_queue_params_t;
00164 
00165 static pipe_t pipes[MAX_PIPES];
00166 static rl_queue_t queues[MAX_QUEUES];
00167 
00168 static int nqueues_mp = 0;
00169 static int * nqueues;
00170 
00171 static  str * rl_dbg_str = NULL;
00172 
00173 /* these only change in the mod_init() process -- no locking needed */
00174 static int timer_interval = RL_TIMER_INTERVAL;
00175 static int cfg_setpoint;        /* desired load, used when reading modparams */
00176 /* === */
00177 
00178 #ifndef RL_DEBUG_LOCKS
00179 # define LOCK_GET lock_get
00180 # define LOCK_RELEASE lock_release
00181 #else
00182 # define LOCK_GET(l) do { \
00183    LM_INFO("%d: + get\n", __LINE__); \
00184    lock_get(l); \
00185    LM_INFO("%d: - get\n", __LINE__); \
00186 } while (0)
00187 
00188 # define LOCK_RELEASE(l) do { \
00189    LM_INFO("%d: + release\n", __LINE__); \
00190    lock_release(l); \
00191    LM_INFO("%d: - release\n", __LINE__); \
00192 } while (0)
00193 #endif
00194 
00195 static int params_inited = 0;
00196 static regex_t pipe_params_regex;
00197 static regex_t queue_params_regex;
00198 
00199 /** module functions */
00200 static int mod_init(void);
00201 static void rl_timer(unsigned int, void *);
00202 static int w_rl_check_default(struct sip_msg*, char *, char *);
00203 static int w_rl_check_forced(struct sip_msg*, char *, char *);
00204 static int w_rl_check_forced_pipe(struct sip_msg*, char *, char *);
00205 static int w_rl_drop_default(struct sip_msg*, char *, char *);
00206 static int w_rl_drop_forced(struct sip_msg*, char *, char *);
00207 static int w_rl_drop(struct sip_msg*, char *, char *);
00208 static int add_queue_params(modparam_t, void *);
00209 static int add_pipe_params(modparam_t, void *);
00210 /* RESERVED for future use
00211 static int set_load_source(modparam_t, void *);
00212 */
00213 void destroy(void);
00214 
00215 static cmd_export_t cmds[]={
00216    {"rl_check",      (cmd_function)w_rl_check_default,     0, 0,               0,               REQUEST_ROUTE|LOCAL_ROUTE},
00217    {"rl_check",      (cmd_function)w_rl_check_forced,      1, fixup_pvar_null,
00218       fixup_free_pvar_null, REQUEST_ROUTE|LOCAL_ROUTE},
00219    {"rl_check_pipe", (cmd_function)w_rl_check_forced_pipe, 1, fixup_uint_null, 0,               REQUEST_ROUTE|LOCAL_ROUTE},
00220    {"rl_drop",       (cmd_function)w_rl_drop_default,      0, 0,               0,               REQUEST_ROUTE|LOCAL_ROUTE},
00221    {"rl_drop",       (cmd_function)w_rl_drop_forced,       1, fixup_uint_null, 0,               REQUEST_ROUTE|LOCAL_ROUTE},
00222    {"rl_drop",       (cmd_function)w_rl_drop,              2, fixup_uint_uint, 0,               REQUEST_ROUTE|LOCAL_ROUTE},
00223    {0,0,0,0,0,0}
00224 };
00225 static param_export_t params[]={
00226    {"timer_interval", INT_PARAM,                &timer_interval},
00227    {"queue",          STR_PARAM|USE_FUNC_PARAM, (void *)add_queue_params},
00228    {"pipe",           STR_PARAM|USE_FUNC_PARAM, (void *)add_pipe_params},
00229    {"reply_code",     INT_PARAM,                &rl_drop_code},
00230    {"reply_reason",   STR_PARAM,                &rl_drop_reason.s},
00231    /* RESERVED for future use
00232    {"load_source",    STR_PARAM|USE_FUNC_PARAM, (void *)set_load_source},
00233    */
00234    {0,0,0}
00235 };
00236 
00237 struct mi_root* mi_stats(struct mi_root* cmd_tree, void* param);
00238 struct mi_root* mi_set_pipe(struct mi_root* cmd_tree, void* param);
00239 struct mi_root* mi_get_pipes(struct mi_root* cmd_tree, void* param);
00240 struct mi_root* mi_set_queue(struct mi_root* cmd_tree, void* param);
00241 struct mi_root* mi_get_queues(struct mi_root* cmd_tree, void* param);
00242 struct mi_root* mi_set_pid(struct mi_root* cmd_tree, void* param);
00243 struct mi_root* mi_get_pid(struct mi_root* cmd_tree, void* param);
00244 struct mi_root* mi_push_load(struct mi_root* cmd_tree, void* param);
00245 struct mi_root* mi_set_dbg(struct mi_root* cmd_tree, void* param);
00246 
00247 static mi_export_t mi_cmds [] = {
00248    {"rl_stats",      mi_stats,      MI_NO_INPUT_FLAG, 0, 0},
00249    {"rl_set_pipe",   mi_set_pipe,   0,                0, 0},
00250    {"rl_get_pipes",  mi_get_pipes,  MI_NO_INPUT_FLAG, 0, 0},
00251    {"rl_set_queue",  mi_set_queue,  0,                0, 0},
00252    {"rl_get_queues", mi_get_queues, MI_NO_INPUT_FLAG, 0, 0},
00253    {"rl_set_pid",    mi_set_pid,    0,                0, 0},
00254    {"rl_get_pid",    mi_get_pid,    MI_NO_INPUT_FLAG, 0, 0},
00255    {"rl_push_load",  mi_push_load,  0,                0, 0},
00256    {"rl_set_dbg",    mi_set_dbg,    0,                0, 0},
00257    {0,0,0,0,0}
00258 };
00259 
00260 /** module exports */
00261 struct module_exports exports= {
00262    "ratelimit",
00263    DEFAULT_DLFLAGS,     /* dlopen flags */
00264    cmds,
00265    params,
00266    0,          /* exported statistics */
00267    mi_cmds,       /* exported MI functions */
00268    0,          /* exported pseudo-variables */
00269    0,          /* extra processes */
00270    mod_init,         /* module initialization function */
00271    0,
00272    (destroy_function) destroy,   /* module exit function */
00273    0           /* per-child init function */
00274 };
00275 
00276 
00277 /**
00278  * converts a mapped str to an int
00279  * \return  0 if found, -1 otherwise
00280  */
00281 static int str_map_str(const str_map_t * map, const str * key, int * ret)
00282 {
00283    for (; map->str.s; map++) 
00284       if (! str_cmp(&map->str, key)) {
00285          *ret = map->id;
00286          return 0;
00287       }
00288    LM_DBG("str_map_str() failed map=%p key=%.*s\n", map, key->len, key->s);
00289    return -1;
00290 }
00291 
00292 /**
00293  * converts a mapped int to a str
00294  * \return  0 if found, -1 otherwise
00295  */
00296 static int str_map_int(const str_map_t * map, int key, str * ret)
00297 {
00298    for (; map->str.s; map++) 
00299       if (map->id == key) {
00300          *ret = map->str;
00301          return 0;
00302       }
00303    LM_DBG("str_map_str() failed map=%p key=%d\n", map, key);
00304    return -1;
00305 }
00306 
00307 /**
00308  * strcpy for str's (does not allocate the str structure but only the .s member)
00309  * \return  0 if succeeded, -1 otherwise
00310  */
00311 static int str_cpy(str * dest, str * src)
00312 {
00313    dest->len = src->len;
00314    dest->s = shm_malloc(src->len);
00315    if (! dest->s) {
00316       LM_ERR("oom: '%.*s'\n", src->len, src->s);
00317       return -1;
00318    }
00319    memcpy(dest->s, src->s, src->len);
00320    return 0;
00321 }
00322 
00323 /* not using /proc/loadavg because it only works when our_timer_interval == theirs */
00324 static int get_cpuload(double * load)
00325 {
00326    static 
00327    long long o_user, o_nice, o_sys, o_idle, o_iow, o_irq, o_sirq, o_stl;
00328    long long n_user, n_nice, n_sys, n_idle, n_iow, n_irq, n_sirq, n_stl;
00329    static int first_time = 1;
00330    FILE * f = fopen("/proc/stat", "r");
00331 
00332    if (! f) {
00333       LM_ERR("could not open /proc/stat\n");
00334       return -1;
00335    }
00336    if (fscanf(f, "cpu  %lld%lld%lld%lld%lld%lld%lld%lld",
00337          &n_user, &n_nice, &n_sys, &n_idle, &n_iow, &n_irq, &n_sirq, &n_stl) < 0) {
00338         LM_ERR("could not parse load informations\n");
00339         return -1;
00340    }
00341    fclose(f);
00342 
00343    if (first_time) {
00344       first_time = 0;
00345       *load = 0;
00346    } else {    
00347       long long d_total =  (n_user - o_user) + 
00348                (n_nice  - o_nice)   + 
00349                (n_sys   - o_sys) + 
00350                (n_idle  - o_idle)   + 
00351                (n_iow   - o_iow) + 
00352                (n_irq   - o_irq) + 
00353                (n_sirq  - o_sirq)   + 
00354                (n_stl   - o_stl);
00355       long long d_idle =   (n_idle - o_idle);
00356 
00357       *load = 1.0 - ((double)d_idle) / (double)d_total;
00358    }
00359 
00360    o_user   = n_user; 
00361    o_nice   = n_nice; 
00362    o_sys = n_sys; 
00363    o_idle   = n_idle; 
00364    o_iow = n_iow; 
00365    o_irq = n_irq; 
00366    o_sirq   = n_sirq; 
00367    o_stl = n_stl;
00368    
00369    return 0;
00370 }
00371 
00372 static double int_err = 0.0;
00373 static double last_err = 0.0;
00374 
00375 /* (*load_value) is expected to be in the 0.0 - 1.0 range
00376  * (expects rl_lock to be taken)
00377  */
00378 static void do_update_load(void)
00379 {
00380    static char spcs[51];
00381    int load;
00382    double err, dif_err, output;
00383 
00384    /* PID update */
00385    err = *pid_setpoint - *load_value;
00386 
00387    dif_err = err - last_err;
00388 
00389    /*
00390     * TODO?: the 'if' is needed so low cpu loads for 
00391     * long periods (which can't be compensated by 
00392     * negative drop rates) don't confuse the controller
00393     *
00394     * NB: - "err < 0" means "desired_cpuload < actual_cpuload"
00395     *     - int_err is integral(err) over time
00396     */
00397    if (int_err < 0 || err < 0)
00398       int_err += err;
00399 
00400    output = (*pid_kp) * err + 
00401             (*pid_ki) * int_err + 
00402             (*pid_kd) * dif_err;
00403    last_err = err;
00404 
00405    *drop_rate = (output > 0) ? output  : 0;
00406 
00407    load = 0.5 + 100.0 * *load_value; /* round instead of floor */
00408 
00409    memset(spcs, '-', load / 4);
00410    spcs[load / 4] = 0;
00411 
00412    /*
00413    LM_DBG("p=% 6.2lf i=% 6.2lf d=% 6.2lf o=% 6.2lf %s|%d%%\n",
00414       err, int_err, dif_err, output, spcs, load);
00415    */
00416 }
00417 
00418 static void update_cpu_load(void)
00419 {
00420    if (get_cpuload(load_value)) 
00421       return;
00422 
00423    do_update_load();
00424 }
00425 
00426 /* initialize ratelimit module */
00427 static int mod_init(void)
00428 {
00429    int i;
00430 
00431    rl_lock = lock_alloc();
00432    if (! rl_lock) {
00433       LM_ERR("oom in lock_alloc()\n");
00434       return -1;
00435    }
00436 
00437    if (lock_init(rl_lock)==0) {
00438       LM_ERR("failed to init lock\n");
00439       return -1;
00440    }
00441 
00442    /* register timer to reset counters */
00443    if (register_timer_process(rl_timer, NULL, timer_interval, TIMER_PROC_INIT_FLAG) < 0) {
00444       LM_ERR("could not register timer function\n");
00445       return -1;
00446    }
00447 
00448    /* load the SL API */
00449    if (load_sl_api(&slb)!=0) {
00450       LM_ERR("failed to load SL API\n");
00451       return -1;
00452    }
00453 
00454    network_load_value = shm_malloc(sizeof(int));
00455    if (network_load_value==NULL) {
00456       LM_ERR("oom for network_load_value\n");
00457       return -1;
00458    }
00459 
00460    load_value = shm_malloc(sizeof(double));
00461    if (load_value==NULL) {
00462       LM_ERR("oom for load_value\n");
00463       return -1;
00464    }
00465    load_source = shm_malloc(sizeof(int));
00466    if (load_source==NULL) {
00467       LM_ERR("oom for load_source\n");
00468       return -1;
00469    }
00470    pid_kp = shm_malloc(sizeof(double));
00471    if (pid_kp==NULL) {
00472       LM_ERR("oom for pid_kp\n");
00473       return -1;
00474    }
00475    pid_ki = shm_malloc(sizeof(double));
00476    if (pid_ki==NULL) {
00477       LM_ERR("oom for pid_ki\n");
00478       return -1;
00479    }
00480    pid_kd = shm_malloc(sizeof(double));
00481    if (pid_kd==NULL) {
00482       LM_ERR("oom for pid_kd\n");
00483       return -1;
00484    }
00485    pid_setpoint = shm_malloc(sizeof(double));
00486    if (pid_setpoint==NULL) {
00487       LM_ERR("oom for pid_setpoint\n");
00488       return -1;
00489    }
00490    drop_rate = shm_malloc(sizeof(int));
00491    if (drop_rate==NULL) {
00492       LM_ERR("oom for drop_rate\n");
00493       return -1;
00494    }
00495    nqueues = shm_malloc(sizeof(int));
00496    if (nqueues==NULL) {
00497       LM_ERR("oom for nqueues\n");
00498       return -1;
00499    }
00500    rl_dbg_str = shm_malloc(sizeof(str));
00501    if (rl_dbg_str==NULL) {
00502       LM_ERR("oom for rl_dbg_str\n");
00503       return -1;
00504    }
00505 
00506    *network_load_value = 0;
00507    *load_value = 0.0;
00508    *load_source = load_source_mp;
00509    *pid_kp = 0.0;
00510    *pid_ki = -25.0;
00511    *pid_kd = 0.0;
00512    *pid_setpoint = 0.01 * (double)cfg_setpoint;
00513    *drop_rate      = 0;
00514    *nqueues = nqueues_mp;
00515    rl_dbg_str->s = NULL;
00516    rl_dbg_str->len = 0;
00517 
00518    for (i=0; i<MAX_PIPES; i++) {
00519       pipes[i].algo    = shm_malloc(sizeof(int));
00520       if (pipes[i].algo==NULL) {
00521          LM_ERR("oom for pipes[%d].algo\n", i);
00522          return -1;
00523       }
00524       pipes[i].limit   = shm_malloc(sizeof(int));
00525       if (pipes[i].limit==NULL) {
00526          LM_ERR("oom for pipes[%d].limit\n", i);
00527          return -1;
00528       }
00529       pipes[i].load    = shm_malloc(sizeof(int));
00530       if (pipes[i].load==NULL) {
00531          LM_ERR("oom for pipes[%d].load\n", i);
00532          return -1;
00533       }
00534       pipes[i].counter = shm_malloc(sizeof(int));
00535       if (pipes[i].counter==NULL) {
00536          LM_ERR("oom for pipes[%d].counter\n", i);
00537          return -1;
00538       }
00539       pipes[i].last_counter = shm_malloc(sizeof(int));
00540       if (pipes[i].last_counter==NULL) {
00541          LM_ERR("oom for pipes[%d].last_counter\n", i);
00542          return -1;
00543       }
00544       *pipes[i].algo    = pipes[i].algo_mp;
00545       *pipes[i].limit   = pipes[i].limit_mp;
00546       *pipes[i].load    = 0;
00547       *pipes[i].counter = 0;
00548       *pipes[i].last_counter = 0;
00549    }
00550 
00551    for (i=0; i<*nqueues; i++) {
00552       queues[i].pipe   = shm_malloc(sizeof(int));
00553       if (queues[i].pipe==NULL) {
00554          LM_ERR("oom for queues[%d].pipe\n", i);
00555          return -1;
00556       }
00557       queues[i].method = shm_malloc(sizeof(str));
00558       if (queues[i].method==NULL) {
00559          LM_ERR("oom for queues[%d].method\n", i);
00560          return -1;
00561       }
00562 
00563       *queues[i].pipe   = queues[i].pipe_mp;
00564       if (queues[i].method_mp.s == NULL) {
00565          LM_ERR("unexpected NULL method for queues[%d].method_mp\n", i);
00566          return -1;
00567       }
00568       if(str_cpy(queues[i].method, &queues[i].method_mp)) {
00569          LM_ERR("oom str_cpy(queues[%d].method\n", i);
00570          return -1;
00571       }
00572       pkg_free(queues[i].method_mp.s);
00573       queues[i].method_mp.s = NULL;
00574       queues[i].method_mp.len = 0;
00575    }
00576 
00577    rl_drop_reason.len = strlen(rl_drop_reason.s);
00578 
00579    return 0;
00580 }
00581 
00582 
00583 void destroy(void)
00584 {
00585    int i;
00586 
00587    regfree(&pipe_params_regex);
00588    regfree(&queue_params_regex);
00589 
00590    for (i=0;  i<MAX_PIPES; i++) {
00591       if (pipes[i].algo) {
00592          shm_free(pipes[i].algo);
00593          pipes[i].algo = NULL;
00594       }
00595       if (pipes[i].load) {
00596          shm_free(pipes[i].load);
00597          pipes[i].load = NULL;
00598       }
00599       if (pipes[i].counter) {
00600          shm_free(pipes[i].counter);
00601          pipes[i].counter = NULL;
00602       }
00603       if (pipes[i].last_counter) {
00604          shm_free(pipes[i].last_counter);
00605          pipes[i].last_counter = NULL;
00606       }
00607       if (pipes[i].limit) {
00608          shm_free(pipes[i].limit);
00609          pipes[i].limit = NULL;
00610       }
00611    }
00612 
00613    if (nqueues) {
00614       for (i=0; i<*nqueues; i++) {
00615          if (queues[i].pipe) {
00616             shm_free(queues[i].pipe);
00617             queues[i].pipe = NULL;
00618          }
00619          if (queues[i].method) {
00620             if (queues[i].method->s) {
00621                shm_free(queues[i].method->s);
00622                queues[i].method->s = NULL;
00623                queues[i].method->len = 0;
00624             }
00625             shm_free(queues[i].method);
00626             queues[i].method = NULL;
00627          }
00628       }
00629    }
00630 
00631    if (network_load_value) {
00632       shm_free(network_load_value);
00633       network_load_value = NULL;
00634    }
00635    if (load_value) {
00636       shm_free(load_value);
00637       load_value = NULL;
00638    }
00639    if (load_source) {
00640       shm_free(load_source);
00641       load_source = NULL;
00642    }
00643    if (pid_kp) {
00644       shm_free(pid_kp);
00645       pid_kp= NULL;
00646    }
00647    if (pid_ki) {
00648       shm_free(pid_ki);
00649       pid_ki = NULL;
00650    }
00651    if (pid_kd) {
00652       shm_free(pid_kd);
00653       pid_kd = NULL;
00654    }
00655    if (pid_setpoint) {
00656       shm_free(pid_setpoint);
00657       pid_setpoint = NULL;
00658    }
00659    if (drop_rate) {
00660       shm_free(drop_rate);
00661       drop_rate = NULL;
00662    }
00663    if (nqueues) {
00664       shm_free(nqueues);
00665       nqueues = NULL;
00666    }
00667    if (rl_dbg_str) {
00668       if (rl_dbg_str->s) {
00669          shm_free(rl_dbg_str->s);
00670          rl_dbg_str->s = NULL;
00671          rl_dbg_str->len = 0;
00672       }
00673       shm_free(rl_dbg_str);
00674       rl_dbg_str = NULL;
00675    }
00676 
00677    if (rl_lock) {
00678       lock_destroy(rl_lock);
00679       lock_dealloc((void *)rl_lock);
00680    }
00681 }
00682 
00683 
00684 static int rl_drop(struct sip_msg * msg, unsigned int low, unsigned int high)
00685 {
00686    str hdr;
00687    int ret;
00688 
00689    LM_DBG("(%d, %d)\n", low, high);
00690 
00691    if (slb.send_reply != 0) {
00692       if (low != 0 && high != 0) {
00693          hdr.s = (char *)pkg_malloc(64);
00694          if (hdr.s == 0) {
00695             LM_ERR("Can't allocate memory for Retry-After header\n");
00696             return 0;
00697          }
00698          hdr.len = 0;
00699          if (! hdr.s) {
00700             LM_ERR("no memory for hdr\n");
00701             return 0;
00702          }
00703 
00704          if (high == low) {
00705             hdr.len = snprintf(hdr.s, 63, "Retry-After: %d\r\n", low);
00706          } else {
00707             hdr.len = snprintf(hdr.s, 63, "Retry-After: %d\r\n", 
00708                low + rand() % (high - low + 1));
00709          }
00710 
00711          if (add_lump_rpl(msg, hdr.s, hdr.len, LUMP_RPL_HDR)==0) {
00712             LM_ERR("Can't add header\n");
00713             pkg_free(hdr.s);
00714             return 0;
00715          }
00716 
00717          ret = slb.send_reply(msg, rl_drop_code, &rl_drop_reason);
00718 
00719          pkg_free(hdr.s);
00720       } else {
00721          ret = slb.send_reply(msg, rl_drop_code, &rl_drop_reason);
00722       }
00723    } else {
00724       LM_ERR("Can't send reply\n");
00725       return 0;
00726    }
00727    return ret;
00728 }
00729 
00730 static int w_rl_drop(struct sip_msg* msg, char *p1, char *p2) 
00731 {
00732    unsigned int low, high;
00733 
00734    low = (unsigned int)(unsigned long)p1;
00735    high = (unsigned int)(unsigned long)p2;
00736 
00737    if (high < low) {
00738       return rl_drop(msg, low, low);
00739    } else {
00740       return rl_drop(msg, low, high);
00741    }
00742 }
00743 
00744 static int w_rl_drop_forced(struct sip_msg* msg, char *p1, char *p2)
00745 {
00746    unsigned int i;
00747 
00748    if (p1) {
00749       i = (unsigned int)(unsigned long)p1;
00750       LM_DBG("send retry in %d s\n", i);
00751    } else {
00752       i = 5;
00753       LM_DBG("send default retry in %d s\n", i);
00754    }
00755    return rl_drop(msg, i, i);
00756 }
00757 
00758 static int w_rl_drop_default(struct sip_msg* msg, char *p1, char *p2)
00759 {
00760    return rl_drop(msg, 0, 0);
00761 }
00762 
00763 static inline int str_cmp(const str * a , const str * b)
00764 {
00765    return ! (a->len == b->len && ! strncmp(a->s, b->s, a->len));
00766 }
00767 
00768 static inline int str_i_cmp(const str * a, const str * b)
00769 {
00770    return ! (a->len == b->len && ! strncasecmp(a->s, b->s, a->len));
00771 }
00772 
00773 str queue_other = str_init("*");
00774 
00775 /**
00776  * finds the queue associated with the message's method
00777  * (expects rl_lock to be taken)
00778  * \return  0 if a nueue was found, -1 otherwise
00779  */
00780 static int find_queue(struct sip_msg * msg, int * queue)
00781 {
00782    str method = msg->first_line.u.request.method;
00783    int i;
00784 
00785    *queue = -1;
00786    for (i=0; i<*nqueues; i++)
00787       if (! str_i_cmp(queues[i].method, &method)) {
00788          *queue = i;
00789          return 0;
00790       } else if (! str_i_cmp(queues[i].method, &queue_other)) {
00791          *queue = i;
00792       }
00793    
00794    if (*queue >= 0)
00795       return 0;
00796    
00797    LM_INFO("no queue matches\n");
00798    return -1;
00799 }
00800 
00801 /* this is here to avoid using rand() ... which doesn't _always_ return
00802  * exactly what we want (see NOTES section in 'man 3 rand')
00803  */
00804 int hash[100] = {18, 50, 51, 39, 49, 68, 8, 78, 61, 75, 53, 32, 45, 77, 31, 
00805    12, 26, 10, 37, 99, 29, 0, 52, 82, 91, 22, 7, 42, 87, 43, 73, 86, 70, 
00806    69, 13, 60, 24, 25, 6, 93, 96, 97, 84, 47, 79, 64, 90, 81, 4, 15, 63, 
00807    44, 57, 40, 21, 28, 46, 94, 35, 58, 11, 30, 3, 20, 41, 74, 34, 88, 62, 
00808    54, 33, 92, 76, 85, 5, 72, 9, 83, 56, 17, 95, 55, 80, 98, 66, 14, 16, 
00809    38, 71, 23, 2, 67, 36, 65, 27, 1, 19, 59, 89, 48};
00810 
00811 
00812 /**
00813  * runs the pipe's algorithm
00814  * (expects rl_lock to be taken), TODO revert to "return" instead of "ret ="
00815  * \return  -1 if drop needed, 1 if allowed
00816  */
00817 static int pipe_push(struct sip_msg * msg, int id)
00818 {
00819    int ret;
00820 
00821    (*pipes[id].counter)++;
00822 
00823    switch (*pipes[id].algo) {
00824       case PIPE_ALGO_NOP:
00825          LM_ERR("no algorithm defined for pipe %d\n", id);
00826          ret = 1;
00827          break;
00828       case PIPE_ALGO_TAILDROP:
00829          ret = (*pipes[id].counter <= *pipes[id].limit * timer_interval) ? 1 : -1;
00830          break;
00831       case PIPE_ALGO_RED:
00832          if (*pipes[id].load == 0)
00833             ret = 1;
00834          else
00835             ret = (! (*pipes[id].counter % *pipes[id].load)) ? 1 : -1;
00836          break;
00837       case PIPE_ALGO_FEEDBACK:
00838          ret = (hash[*pipes[id].counter % 100] < *drop_rate) ? -1 : 1;
00839          break;
00840       case PIPE_ALGO_NETWORK:
00841          ret = -1 * *pipes[id].load;
00842          break;
00843       default:
00844          LM_ERR("unknown ratelimit algorithm: %d\n", *pipes[id].algo);
00845          ret = 1;
00846    }
00847 
00848    return ret;     
00849 }
00850 
00851 /**     
00852  * runs the current request through the queues
00853  * \param       msg
00854  * \param       forced_pipe     is >= 0 if a specific pipe should be used, < 0 otherwise
00855  * \return  -1 if drop needed, 1 if allowed
00856  */
00857 static int rl_check(struct sip_msg * msg, int forced_pipe)
00858 {
00859    int que_id, pipe_id, ret;
00860    str method = msg->first_line.u.request.method;
00861 
00862    LOCK_GET(rl_lock);
00863    if (forced_pipe < 0) { 
00864       if (find_queue(msg, &que_id)) {
00865          pipe_id = que_id = 0;
00866          ret = 1;
00867          goto out_release;
00868       }
00869       pipe_id = *queues[que_id].pipe;
00870    } else {
00871       que_id = 0;
00872       pipe_id = forced_pipe;
00873    }
00874 
00875    ret = pipe_push(msg, pipe_id);
00876 out_release:
00877    LOCK_RELEASE(rl_lock);
00878 
00879    /* no locks here because it's only read and pipes[pipe_id] is always alloc'ed */
00880    LM_DBG("meth=%.*s queue=%d pipe=%d algo=%d limit=%d pkg_load=%d counter=%d "
00881       "load=%2.1lf network_load=%d => %s\n",
00882       method.len, method.s, que_id, pipe_id,
00883       *pipes[pipe_id].algo, *pipes[pipe_id].limit,
00884       *pipes[pipe_id].load, *pipes[pipe_id].counter,
00885       *load_value, *network_load_value, (ret == 1) ? "ACCEPT" : "DROP");
00886 
00887    return ret;
00888 }
00889 
00890 static int w_rl_check_forced(struct sip_msg* msg, char *p1, char *p2)
00891 {
00892    int pipe = -1;
00893    pv_value_t pv_val;
00894 
00895    if (p1 && (pv_get_spec_value(msg, (pv_spec_t *)p1, &pv_val) == 0)) {
00896       if (pv_val.flags & PV_VAL_INT) {
00897          pipe = pv_val.ri;
00898          LM_DBG("pipe=%d\n", pipe);
00899       } else if (pv_val.flags & PV_VAL_STR) {
00900          if(str2int(&(pv_val.rs), (unsigned int*)&pipe) != 0) {
00901             LM_ERR("Unable to get pipe from pv '%.*s'"
00902                "=> defaulting to method type checking\n",
00903                pv_val.rs.len, pv_val.rs.s);
00904             pipe = -1;
00905          }
00906       } else {
00907          LM_ERR("pv not a str or int => defaulting to method type checking\n");
00908          pipe = -1;
00909       }
00910    } else {
00911       LM_ERR("Unable to get pipe from pv:%p"
00912          " => defaulting to method type checking\n", p1);
00913       pipe = -1;
00914    }
00915    return rl_check(msg, pipe);
00916 }
00917 static int w_rl_check_forced_pipe(struct sip_msg* msg, char *p1, char *p2)
00918 {
00919    int pipe;
00920 
00921    if (p1) {
00922       pipe = (int)(unsigned int)(unsigned long)p1;
00923       LM_DBG("trying pipe %d\n", pipe);
00924    } else {
00925       pipe = -1;
00926    }
00927 
00928    return rl_check(msg, pipe);
00929 }
00930 
00931 static int w_rl_check_default(struct sip_msg* msg, char *p1, char *p2)
00932 {
00933    return rl_check(msg, -1);
00934 }
00935 
00936 /* RESERVED for future use
00937 static int set_load_source(modparam_t type, void * val)
00938 {
00939    str src_name = { .s = val, .len = strlen(val) };
00940    int src_id;
00941 
00942    if (str_map_str(source_names, &src_name, &src_id)) {
00943       LM_ERR("unknown load source: %.*s\n", src_name.len, src_name.s);
00944       return -1;
00945    }
00946 
00947    load_source_mp = src_id;
00948    LM_INFO("switched to load source: %.*s\n", src_name.len, src_name.s);
00949 
00950    return 0;
00951 }
00952 */
00953 
00954 /**
00955  * compiles regexes for parsing modparams and clears the pipes and queues
00956  * \return      0 on success
00957  */
00958 static int init_params(void)
00959 {
00960    if (regcomp(&pipe_params_regex, "^([0-9]+):([^: ]+):([0-9]+)$", REG_EXTENDED|REG_ICASE) ||
00961       regcomp(&queue_params_regex, "^([0-9]+):([^: ]+)$", REG_EXTENDED|REG_ICASE)) {
00962       LM_ERR("can't compile modparam regexes\n");
00963       return -1;
00964    }
00965 
00966    memset(pipes, 0, sizeof(pipes));
00967    memset(queues, 0, sizeof(queues));
00968 
00969    params_inited = 1;
00970    return 0;
00971 }
00972 
00973 
00974 /**
00975  * parses a "pipe_no:algorithm:bandwidth" line
00976  * \return      0 on success
00977  */
00978 static int parse_pipe_params(char * line, pipe_params_t * params)
00979 {
00980    regmatch_t m[4];
00981    str algo_str;
00982 
00983    if (! params_inited && init_params())
00984       return -1;
00985    if (regexec(&pipe_params_regex, line, 4, m, 0)) {
00986       LM_ERR("invalid param tuple: %s\n", line);
00987       return -1;
00988    }
00989    LM_DBG("pipe: [%.*s|%.*s|%.*s]\n",
00990       RXLS(m, line, 1), RXLS(m, line, 2), RXLS(m, line, 3));
00991    
00992    params->no = atoi(RXS(m, line, 1));
00993    params->limit = atoi(RXS(m, line, 3));
00994 
00995    algo_str.s   = RXS(m, line, 2);
00996    algo_str.len = RXL(m, line, 2);
00997    if (str_map_str(algo_names, &algo_str, &params->algo))
00998       return -1;
00999 
01000    return 0;
01001 }
01002 
01003 /**
01004  * parses a "pipe_no:method" line
01005  * \return      0 on success
01006  */
01007 static int parse_queue_params(char * line, rl_queue_params_t * params)
01008 {
01009    regmatch_t m[3];
01010    int len;
01011 
01012    if (! params_inited && init_params())
01013       return -1;
01014    if (regexec(&queue_params_regex, line, 3, m, 0)) {
01015       LM_ERR("invalid param tuple: %s\n", line);
01016       return -1;
01017    }
01018    LM_DBG("queue: [%.*s|%.*s]\n",
01019       RXLS(m, line, 1), RXLS(m, line, 2));
01020    
01021    params->pipe = atoi(RXS(m, line, 1));
01022 
01023    len = RXL(m, line, 2);
01024    params->method.s = (char *)pkg_malloc(len+1);
01025    if (params->method.s == 0) {
01026       LM_ERR("no memory left for method in params\n");
01027       return -1;
01028    }
01029    params->method.len = len;
01030    memcpy(params->method.s, RXS(m, line, 2), len+1);
01031 
01032    return 0;
01033 }
01034 
01035 /**
01036  * checks that all FEEDBACK pipes use the same setpoint 
01037  * cpu load. also sets (common) cfg_setpoint value
01038  * \param   modparam 1 to check modparam (static) fields, 0 to use shm ones
01039  *
01040  * \return  0 if ok, -1 on error
01041  */
01042 static int check_feedback_setpoints(int modparam)
01043 {
01044         int i, sp;
01045 
01046    cfg_setpoint = -1;
01047 
01048    for (i=0; i<MAX_PIPES; i++)
01049       if (pipes[i].algo_mp == PIPE_ALGO_FEEDBACK) {
01050          sp = modparam ? pipes[i].limit_mp : *pipes[i].limit;
01051 
01052          if (sp < 0 || sp > 100) {
01053             LM_ERR("FEEDBACK cpu load must be >=0 and <= 100\n");
01054             return -1;
01055          } else if (cfg_setpoint == -1) {
01056             cfg_setpoint = sp;
01057          } else if (sp != cfg_setpoint) {
01058             LM_ERR("pipe %d: FEEDBACK cpu load values must "
01059                "be equal for all pipes\n", i);
01060             return -1;
01061          }
01062       }
01063 
01064    return 0;
01065 }
01066 
01067 
01068 static int add_pipe_params(modparam_t type, void * val)
01069 {
01070    char * param_line = val;
01071    pipe_params_t params;
01072 
01073    if (parse_pipe_params(param_line, &params))
01074       return -1;
01075    
01076    if (params.no < 0 || params.no >= MAX_PIPES) {
01077       LM_ERR("pipe number %d not allowed (MAX_PIPES=%d, 0-based)\n",
01078          params.no, MAX_PIPES);
01079       return -1;
01080    }
01081 
01082    pipes[params.no].algo_mp = params.algo;
01083    pipes[params.no].limit_mp = params.limit;
01084 
01085    return check_feedback_setpoints(1);
01086 }
01087 
01088 static int add_queue_params(modparam_t type, void * val)
01089 {
01090    char * param_line = val;
01091    rl_queue_params_t params;
01092 
01093    if (nqueues_mp >= MAX_QUEUES) {
01094       LM_ERR("MAX_QUEUES reached (%d)\n", MAX_QUEUES);
01095       return -1;
01096    }
01097 
01098    if (parse_queue_params(param_line, &params))
01099       return -1;
01100 
01101    if (params.pipe >= MAX_PIPES) {
01102       LM_ERR("pipe number %d not allowed (MAX_PIPES=%d, 0-based)\n",
01103          params.pipe, MAX_PIPES);
01104       return -1;
01105    }
01106 
01107    queues[nqueues_mp].pipe_mp = params.pipe;
01108    queues[nqueues_mp].method_mp = params.method;
01109    nqueues_mp++;
01110 
01111    return 0;
01112 }
01113 
01114 
01115 /* timer housekeeping, invoked each timer interval to reset counters */
01116 static void rl_timer(unsigned int ticks, void *param)
01117 {
01118    int i, len;
01119    char *c, *p;
01120 
01121    LOCK_GET(rl_lock);
01122    switch (*load_source) {
01123       case LOAD_SOURCE_CPU:
01124          update_cpu_load();
01125          break;
01126    }
01127 
01128    *network_load_value = get_total_bytes_waiting();
01129 
01130    if (rl_dbg_str->s) {
01131       c = p = rl_dbg_str->s;
01132       memset(c, ' ', rl_dbg_str->len);
01133       for (i=0; i<MAX_PIPES; i++) {
01134          c = int2str(*pipes[i].counter, &len);
01135          if (len < 4) {
01136             memcpy( p + (5-len), c, len );
01137          } else {
01138             memset(p, '*', 5);
01139             LM_WARN("Counter pipes[%d] to big: %d\n",
01140                i, *pipes[i].counter);
01141          }
01142          p = p + 5;
01143       }
01144       LM_WARN("%.*s\n", rl_dbg_str->len, rl_dbg_str->s);
01145    }
01146 
01147    for (i=0; i<MAX_PIPES; i++) {
01148       if( *pipes[i].algo == PIPE_ALGO_NETWORK ) {
01149          *pipes[i].load = ( *network_load_value > *pipes[i].limit ) ? 1 : -1;
01150       } else if (*pipes[i].limit && timer_interval) {
01151          *pipes[i].load = *pipes[i].counter / (*pipes[i].limit * timer_interval);
01152       }
01153       *pipes[i].last_counter = *pipes[i].counter;
01154       *pipes[i].counter = 0;
01155    }
01156    LOCK_RELEASE(rl_lock);
01157 }
01158 
01159 
01160 /*
01161  * MI functions
01162  *
01163  * mi_stats() dumps the current config/statistics
01164  * mi_{invite|register|subscribe}() set the limits
01165  */
01166 
01167 /* mi function implementations */
01168 struct mi_root* mi_stats(struct mi_root* cmd_tree, void* param)
01169 {
01170    struct mi_root *rpl_tree;
01171    struct mi_node *node=NULL, *rpl=NULL;
01172    struct mi_attr* attr;
01173    char* p;
01174    int i, len;
01175 
01176    rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
01177    if (rpl_tree==0)
01178       return 0;
01179    rpl = &rpl_tree->node;
01180 
01181    LOCK_GET(rl_lock);
01182    for (i=0; i<MAX_PIPES; i++) {
01183       if (*pipes[i].algo != PIPE_ALGO_NOP) {
01184          node = add_mi_node_child(rpl, 0, "PIPE", 4, 0, 0);
01185          if(node == NULL)
01186             goto error;
01187 
01188          p = int2str((unsigned long)(i), &len);
01189          attr = add_mi_attr(node, MI_DUP_VALUE, "id", 2, p, len);
01190          if(attr == NULL)
01191             goto error;
01192 
01193          p = int2str((unsigned long)(*pipes[i].load), &len);
01194          attr = add_mi_attr(node, MI_DUP_VALUE, "load", 4, p, len);
01195          if(attr == NULL)
01196             goto error;
01197 
01198          p = int2str((unsigned long)(*pipes[i].last_counter), &len);
01199          attr = add_mi_attr(node, MI_DUP_VALUE, "counter", 7, p, len);
01200          if(attr == NULL)
01201             goto error;
01202       }
01203    }
01204 
01205    p = int2str((unsigned long)(*drop_rate), &len);
01206    node = add_mi_node_child(rpl, MI_DUP_VALUE, "DROP_RATE", 9, p, len);
01207 
01208    LOCK_RELEASE(rl_lock);
01209    return rpl_tree;
01210 error:
01211    LOCK_RELEASE(rl_lock);
01212    LM_ERR("Unable to create reply\n");
01213    free_mi_tree(rpl_tree); 
01214    return 0;
01215 }
01216 
01217 struct mi_root* mi_get_pipes(struct mi_root* cmd_tree, void* param)
01218 {
01219    struct mi_root *rpl_tree;
01220    struct mi_node *node=NULL, *rpl=NULL;
01221    struct mi_attr* attr;
01222    str algo;
01223    char* p;
01224    int i, len;
01225 
01226    rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
01227    if (rpl_tree==0)
01228       return 0;
01229    rpl = &rpl_tree->node;
01230 
01231    LOCK_GET(rl_lock);
01232    for (i=0; i<MAX_PIPES; i++) {
01233       if (*pipes[i].algo != PIPE_ALGO_NOP) {
01234          node = add_mi_node_child(rpl, 0, "PIPE", 4, 0, 0);
01235          if(node == NULL)
01236             goto error;
01237 
01238          p = int2str((unsigned long)(i), &len);
01239          attr = add_mi_attr(node, MI_DUP_VALUE, "id" , 2, p, len);
01240          if(attr == NULL)
01241             goto error;
01242 
01243          p = int2str((unsigned long)(*pipes[i].algo), &len);
01244          if (str_map_int(algo_names, *pipes[i].algo, &algo))
01245             goto error;
01246          attr = add_mi_attr(node, 0, "algorithm", 9, algo.s, algo.len);
01247          if(attr == NULL)
01248             goto error;
01249 
01250          p = int2str((unsigned long)(*pipes[i].limit), &len);
01251          attr = add_mi_attr(node, MI_DUP_VALUE, "limit", 5, p, len);
01252          if(attr == NULL)
01253             goto error;
01254 
01255          p = int2str((unsigned long)(*pipes[i].counter), &len);
01256          attr = add_mi_attr(node, MI_DUP_VALUE, "counter", 7, p, len);
01257          if(attr == NULL)
01258             goto error;
01259       }
01260    }
01261    LOCK_RELEASE(rl_lock);
01262    return rpl_tree;
01263 error:
01264    LOCK_RELEASE(rl_lock);
01265    LM_ERR("Unable to create reply\n");
01266    free_mi_tree(rpl_tree); 
01267    return 0;
01268 }
01269 
01270 struct mi_root* mi_set_pipe(struct mi_root* cmd_tree, void* param)
01271 {
01272    struct mi_node *node;
01273    unsigned int pipe_no = MAX_PIPES, algo_id, limit = 0;
01274    //str algo;
01275 
01276    node = cmd_tree->node.kids;
01277    if (node == NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
01278    if ( !node->value.s || !node->value.len || strno2int(&node->value,&pipe_no)<0)
01279       goto bad_syntax;
01280    
01281    node = node->next;
01282    if ( !node->value.s || !node->value.len)
01283       goto bad_syntax;
01284    if (str_map_str(algo_names, &(node->value), (int*)&algo_id)) {
01285       LM_ERR("unknown algorithm: '%.*s'\n", node->value.len, node->value.s);
01286       goto bad_syntax;
01287    }
01288    
01289    node = node->next;
01290    if ( !node->value.s || !node->value.len || strno2int(&node->value,&limit)<0)
01291       goto bad_syntax;
01292 
01293    LM_DBG("set_pipe: %d:%d:%d\n", pipe_no, algo_id, limit);
01294 
01295    if (pipe_no >= MAX_PIPES) {
01296       LM_ERR("wrong pipe_no: %d\n", pipe_no);
01297       goto bad_syntax;
01298    }
01299 
01300    LOCK_GET(rl_lock);
01301    *pipes[pipe_no].algo = algo_id;
01302    *pipes[pipe_no].limit = limit;
01303 
01304    if (check_feedback_setpoints(0)) {
01305       LM_ERR("feedback limits don't match\n");
01306       goto error;
01307    } else {
01308       *pid_setpoint = 0.01 * (double)cfg_setpoint;
01309    }
01310 
01311    LOCK_RELEASE(rl_lock);
01312 
01313    return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
01314 error:
01315    LOCK_RELEASE(rl_lock);
01316 bad_syntax:
01317    return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
01318 }
01319 
01320 struct mi_root* mi_get_queues(struct mi_root* cmd_tree, void* param)
01321 {
01322    struct mi_root *rpl_tree;
01323    struct mi_node *node=NULL, *rpl=NULL;
01324    struct mi_attr* attr;
01325    char* p;
01326    int i, len;
01327 
01328    rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
01329    if (rpl_tree==0)
01330       return 0;
01331    rpl = &rpl_tree->node;
01332 
01333    LOCK_GET(rl_lock);
01334    for (i=0; i<MAX_QUEUES; i++) {
01335       if (queues[i].pipe) {
01336          node = add_mi_node_child(rpl, 0, "QUEUE", 5, 0, 0);
01337          if(node == NULL)
01338             goto error;
01339 
01340          p = int2str((unsigned long)(i), &len);
01341          attr = add_mi_attr(node, MI_DUP_VALUE, "id" , 2, p, len);
01342          if(attr == NULL)
01343             goto error;
01344 
01345          p = int2str((unsigned long)(*queues[i].pipe), &len);
01346          attr = add_mi_attr(node, MI_DUP_VALUE, "pipe" , 4, p, len);
01347          if(attr == NULL)
01348             goto error;
01349 
01350          attr = add_mi_attr(node, 0, "method", 6,
01351             (*queues[i].method).s, (*queues[i].method).len);
01352          if(attr == NULL)
01353             goto error;
01354       }
01355    }
01356    LOCK_RELEASE(rl_lock);
01357 
01358    return rpl_tree;
01359 error:
01360    LOCK_RELEASE(rl_lock);
01361    LM_ERR("Unable to create reply\n");
01362    free_mi_tree(rpl_tree); 
01363    return 0;
01364 }
01365 
01366 struct mi_root* mi_set_queue(struct mi_root* cmd_tree, void* param)
01367 {
01368    struct mi_node *node;
01369    unsigned int queue_no = MAX_QUEUES, pipe_no = MAX_PIPES;
01370    str method;
01371 
01372    node = cmd_tree->node.kids;
01373    if (node == NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
01374    if ( !node->value.s || !node->value.len || strno2int(&node->value,&queue_no)<0)
01375       goto bad_syntax;
01376 
01377    node = node->next;
01378    if ( !node->value.s || !node->value.len )
01379       goto bad_syntax;
01380    if (str_cpy(&method, &(node->value))) {
01381       LM_ERR("out of memory\n");
01382       goto early_error;
01383    }
01384 
01385    node = node->next;
01386    if ( !node->value.s || !node->value.len || strno2int(&node->value,&pipe_no)<0)
01387       goto early_error;
01388    if (pipe_no >= MAX_PIPES) {
01389       LM_ERR("invalid pipe number: %d\n", pipe_no);
01390       goto early_error;
01391    }
01392 
01393    LOCK_GET(rl_lock);
01394    if (queue_no >= *nqueues) {
01395       LM_ERR("MAX_QUEUES reached for queue: %d\n", queue_no);
01396       goto error;
01397    }
01398    
01399    *queues[queue_no].pipe = pipe_no;
01400    if (!queues[queue_no].method->s)
01401       shm_free(queues[queue_no].method->s);
01402    queues[queue_no].method->s = method.s;
01403    queues[queue_no].method->len = method.len;
01404    LOCK_RELEASE(rl_lock);
01405 
01406    return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
01407 error:
01408    LOCK_RELEASE(rl_lock);
01409 early_error:
01410    shm_free(method.s);
01411 bad_syntax:
01412    return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
01413 }
01414 
01415 struct mi_root* mi_get_pid(struct mi_root* cmd_tree, void* param)
01416 {
01417    struct mi_root *rpl_tree;
01418    struct mi_node *node=NULL, *rpl=NULL;
01419    struct mi_attr* attr;
01420 
01421    rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
01422    if (rpl_tree==0)
01423       return 0;
01424    rpl = &rpl_tree->node;
01425    node = add_mi_node_child(rpl, 0, "PID", 3, 0, 0);
01426    if(node == NULL)
01427       goto error;
01428    LOCK_GET(rl_lock);
01429    attr= addf_mi_attr(node, 0, "ki", 2, "%0.3f", *pid_ki);
01430    if(attr == NULL)
01431       goto error;
01432    attr= addf_mi_attr(node, 0, "kp", 2, "%0.3f", *pid_kp);
01433    if(attr == NULL)
01434       goto error;
01435    attr= addf_mi_attr(node, 0, "kd", 2, "%0.3f", *pid_kd);
01436    LOCK_RELEASE(rl_lock);
01437    if(attr == NULL)
01438       goto error;
01439 
01440    return rpl_tree;
01441 
01442 error:
01443    LOCK_RELEASE(rl_lock);
01444    LM_ERR("Unable to create reply\n");
01445    free_mi_tree(rpl_tree);
01446    return 0;
01447 }
01448 
01449 struct mi_root* mi_set_pid(struct mi_root* cmd_tree, void* param)
01450 {
01451    struct mi_node *node;
01452    char i[5], p[5], d[5];
01453 
01454    node = cmd_tree->node.kids;
01455    if (node == NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
01456    if ( !node->value.s || !node->value.len || node->value.len >= 5)
01457       goto bad_syntax;
01458    memcpy(i, node->value.s, node->value.len);
01459    i[node->value.len] = '\0';
01460 
01461    node = node->next;
01462    if ( !node->value.s || !node->value.len || node->value.len >= 5)
01463       goto bad_syntax;
01464    memcpy(p, node->value.s, node->value.len);
01465    p[node->value.len] = '\0';
01466 
01467    node = node->next;
01468    if ( !node->value.s || !node->value.len || node->value.len >= 5)
01469       goto bad_syntax;
01470    memcpy(d, node->value.s, node->value.len);
01471    d[node->value.len] = '\0';
01472 
01473    LOCK_GET(rl_lock);
01474    *pid_ki = strtod(i, NULL);
01475    *pid_kp = strtod(p, NULL);
01476    *pid_kd = strtod(d, NULL);
01477    LOCK_RELEASE(rl_lock);
01478 
01479    return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
01480 bad_syntax:
01481    return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
01482 }
01483 
01484 struct mi_root* mi_push_load(struct mi_root* cmd_tree, void* param)
01485 {
01486    struct mi_node *node;
01487    double value;
01488    char c[5];
01489 
01490    node = cmd_tree->node.kids;
01491    if (node == NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
01492    if ( !node->value.s || !node->value.len || node->value.len >= 5)
01493       goto bad_syntax;
01494    memcpy(c, node->value.s, node->value.len);
01495    c[node->value.len] = '\0';
01496    value = strtod(c, NULL);
01497    if (value < 0.0 || value > 1.0) {
01498       LM_ERR("value out of range: %0.3f in not in [0.0,1.0]\n", value);
01499       goto bad_syntax;
01500    }
01501    LOCK_GET(rl_lock);
01502    *load_value = value;
01503    LOCK_RELEASE(rl_lock);
01504 
01505    do_update_load();
01506 
01507    return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
01508 bad_syntax:
01509    return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
01510 }
01511 
01512 struct mi_root* mi_set_dbg(struct mi_root* cmd_tree, void* param)
01513 {
01514    struct mi_node *node;
01515    unsigned int dbg_mode = 0;
01516 
01517    node = cmd_tree->node.kids; 
01518    if (node == NULL) return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
01519    if ( !node->value.s || !node->value.len || strno2int(&node->value,&dbg_mode)<0)
01520       goto bad_syntax;
01521 
01522    LOCK_GET(rl_lock);
01523    if (dbg_mode) {
01524       if (!rl_dbg_str->s) {
01525          rl_dbg_str->len = (MAX_PIPES * 5 * sizeof(char));
01526          rl_dbg_str->s = (char *)shm_malloc(rl_dbg_str->len);
01527          if (!rl_dbg_str->s) {
01528             rl_dbg_str->len = 0;
01529             LM_ERR("oom: %d\n", rl_dbg_str->len);
01530          }
01531       }
01532    } else {
01533       if (rl_dbg_str->s) {
01534          shm_free(rl_dbg_str->s);
01535          rl_dbg_str->s = NULL;
01536          rl_dbg_str->len = 0;
01537       }
01538    }
01539    LOCK_RELEASE(rl_lock);
01540 
01541    return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
01542 bad_syntax:
01543    return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
01544 }
01545 

Generated on Thu May 24 08:00:53 2012 for Kamailio - The Open Source SIP Server by  doxygen 1.5.6