gflags.c

00001 /*$Id$
00002  *
00003  * gflags module: global flags; it keeps a bitmap of flags
00004  * in shared memory and may be used to change behaviour
00005  * of server based on value of the flags. E.g.,
00006  *    if (is_gflag("1")) { t_relay_to_udp("10.0.0.1","5060"); }
00007  *    else { t_relay_to_udp("10.0.0.2","5060"); }
00008  * The benefit of this module is the value of the switch flags
00009  * can be manipulated by external applications such as web interface
00010  * or command line tools.
00011  *
00012  *
00013  * Copyright (C) 2004 FhG FOKUS
00014  *
00015  * This file is part of ser, a free SIP server.
00016  *
00017  * ser is free software; you can redistribute it and/or modify
00018  * it under the terms of the GNU General Public License as published by
00019  * the Free Software Foundation; either version 2 of the License, or
00020  * (at your option) any later version
00021  *
00022  * For a license to use the ser software under conditions
00023  * other than those described here, or to purchase support for this
00024  * software, please contact iptel.org by e-mail at the following addresses:
00025  *    info@iptel.org
00026  *
00027  * ser is distributed in the hope that it will be useful,
00028  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00029  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00030  * GNU General Public License for more details.
00031  *
00032  * You should have received a copy of the GNU General Public License
00033  * along with this program; if not, write to the Free Software
00034  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00035  */
00036 /*
00037  * History:
00038  * --------
00039  *  2004-09-09  initial module created (jiri)
00040  *  2006-12-04  added xmlrpc command global.reload for reloading of global_attrs
00041  *              db table (vlada)
00042  *
00043  * TODO
00044  * - flag range checking
00045  * - named flags (takes a protected name list)
00046  */
00047 
00048 
00049 #include <stdio.h>
00050 #include "../../sr_module.h"
00051 #include "../../error.h"
00052 #include "../../ut.h"
00053 #include "../../lib/srdb2/db.h"
00054 #include "../../mem/mem.h"
00055 #include "../../mem/shm_mem.h"
00056 #include "../../usr_avp.h"
00057 #include "../../rpc.h"
00058 #include "../../config.h"
00059 
00060 
00061 MODULE_VERSION
00062 
00063 static int set_gflag(struct sip_msg*, char *, char *);
00064 static int reset_gflag(struct sip_msg*, char *, char *);
00065 static int is_gflag(struct sip_msg*, char *, char *);
00066 static int flush_gflags(struct sip_msg*, char*, char*);
00067 
00068 static int mod_init(void);
00069 static void mod_destroy(void);
00070 static int child_init(int rank);
00071 static int reload_global_attributes(void);
00072 
00073 static int initial = 0;
00074 static unsigned int *gflags;
00075 
00076 static char* db_url = DEFAULT_DB_URL;
00077 static int   load_global_attrs = 0;
00078 static char* attr_table = "global_attrs";
00079 static char* attr_name = "name";
00080 static char* attr_type = "type";
00081 static char* attr_value = "value";
00082 static char* attr_flags = "flags";
00083 
00084 static db_ctx_t* db = NULL;
00085 static db_cmd_t* load_attrs_cmd = NULL, *save_gflags_cmd = NULL;
00086 
00087 static avp_list_t** active_global_avps;
00088 static avp_list_t *avps_1;
00089 static avp_list_t *avps_2;
00090 static rpc_export_t rpc_methods[];
00091 
00092 static cmd_export_t cmds[]={
00093         {"set_gflag",   set_gflag,   1, fixup_int_1, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE},
00094         {"reset_gflag", reset_gflag, 1, fixup_int_1, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE},
00095         {"is_gflag",    is_gflag,    1, fixup_int_1, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE},
00096         {"flush_gflags", flush_gflags, 0, 0,         REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE},
00097         {0, 0, 0, 0, 0}
00098 };
00099 
00100 static param_export_t params[]={
00101         {"initial",            PARAM_INT,    &initial          },
00102         {"db_url",             PARAM_STRING, &db_url           },
00103         {"load_global_attrs",  PARAM_INT,    &load_global_attrs},
00104         {"global_attrs_table", PARAM_STRING, &attr_table       },
00105         {"global_attrs_name",  PARAM_STRING, &attr_name        },
00106         {"global_attrs_type",  PARAM_STRING, &attr_type        },
00107         {"global_attrs_value", PARAM_STRING, &attr_value       },
00108         {"global_attrs_flags", PARAM_STRING, &attr_flags       },
00109         {0, 0, 0}
00110 };
00111 
00112 struct module_exports exports = {
00113         "gflags",
00114         cmds,
00115         rpc_methods, /* RPC methods */
00116         params,
00117         mod_init,    /* module initialization function */
00118         0,           /* response function*/
00119         mod_destroy, /* destroy function */
00120         0,           /* oncancel function */
00121         child_init   /* per-child init function */
00122 };
00123 
00124 
00125 static int init_db(void)
00126 {
00127         db_fld_t attr_res[] = {
00128                 {.name = attr_name,  DB_STR},
00129                 {.name = attr_type,  DB_INT},
00130                 {.name = attr_value, DB_STR},
00131                 {.name = attr_flags, DB_BITMAP},
00132                 {.name = NULL}
00133         };
00134 
00135         db_fld_t values[] = {
00136                 {.name = attr_name,  DB_CSTR},
00137                 {.name = attr_type,  DB_INT},
00138                 {.name = attr_value, DB_STR},
00139                 {.name = attr_flags, DB_BITMAP},
00140                 {.name = NULL}
00141         };
00142 
00143         db = db_ctx("gflags");
00144         if (db == NULL) {
00145                 ERR("Error while initializing database layer\n");
00146                 return -1;
00147         }
00148         if (db_add_db(db, db_url) < 0) return -1;
00149         if (db_connect(db) < 0) return -1;
00150         
00151         /* SELECT name, type, value, flags FROM global_attrs */
00152         load_attrs_cmd = db_cmd(DB_GET, db, attr_table, attr_res, NULL, NULL);
00153         if (load_attrs_cmd == NULL) {
00154                 ERR("Error while building db query to load global attributes\n");
00155                 return -1;
00156         }
00157 
00158         save_gflags_cmd = db_cmd(DB_PUT, db, attr_table, NULL, NULL, values);
00159         if (save_gflags_cmd == NULL) {
00160                 ERR("Error while building db query to save global flags\n");
00161                 return -1;
00162         }
00163 
00164         return 0;
00165 }
00166 
00167 
00168 static int set_gflag(struct sip_msg *bar, char *flag_par, char *foo)
00169 {
00170         unsigned long int flag;
00171 
00172         if (!flag_par || ((fparam_t*)flag_par)->type != FPARAM_INT) {
00173           LOG(L_ERR, "gflags:set_gflag: Invalid parameter\n");
00174           return -1;
00175         }
00176         
00177         flag=((fparam_t*)flag_par)->v.i; 
00178 
00179         (*gflags) |= 1 << flag;
00180         return 1;
00181 }
00182 
00183 static int reset_gflag(struct sip_msg *bar, char *flag_par, char *foo)
00184 {
00185         unsigned long int flag;
00186 
00187         if (!flag_par || ((fparam_t*)flag_par)->type != FPARAM_INT) {
00188           LOG(L_ERR, "gflags:reset_gflag: Invalid parameter\n");
00189           return -1;
00190         }
00191         
00192         flag=((fparam_t*)flag_par)->v.i; 
00193         (*gflags) &= ~ (1 << flag);
00194         return 1;
00195 }
00196 
00197 static int is_gflag(struct sip_msg *bar, char *flag_par, char *foo)
00198 {
00199         unsigned long int flag;
00200 
00201         if (!flag_par || ((fparam_t*)flag_par)->type != FPARAM_INT) {
00202           LOG(L_ERR, "gflags:is_gflag: Invalid parameter\n");
00203           return -1;
00204         }
00205         
00206         flag=((fparam_t*)flag_par)->v.i; 
00207         return ( (*gflags) & (1<<flag)) ? 1 : -1;
00208 }
00209 
00210 
00211 /*
00212  * Load attributes from global_attrs table
00213  */
00214 static int load_attrs(avp_list_t* global_avps)
00215 {
00216         int_str name, v;
00217         db_res_t* res;
00218         db_rec_t* rec;
00219         str avp_val;
00220         unsigned short flags;
00221 
00222         if (db_exec(&res, load_attrs_cmd) < 0) return -1;
00223 
00224         rec = db_first(res);
00225 
00226         while(rec) {
00227                 if (rec->fld[0].flags & DB_NULL ||
00228                         rec->fld[1].flags & DB_NULL ||
00229                         rec->fld[3].flags & DB_NULL) {
00230                         LOG(L_ERR, "gflags:load_attrs: Skipping row containing NULL entries\n");
00231                         goto skip;
00232                 }
00233 
00234                 if ((rec->fld[3].v.int4 & SRDB_LOAD_SER) == 0) goto skip;
00235 
00236                 name.s = rec->fld[0].v.lstr;
00237 
00238                      /* Test for NULL value */
00239                 if (rec->fld[2].flags & DB_NULL) {
00240                         avp_val.s = 0;
00241                         avp_val.len = 0;
00242                 } else {
00243                         avp_val = rec->fld[2].v.lstr;
00244                 }
00245 
00246                 flags = AVP_CLASS_GLOBAL | AVP_NAME_STR;
00247                 if (rec->fld[1].v.int4 == AVP_VAL_STR) {
00248                         /* String AVP */
00249                         v.s = avp_val;
00250                         flags |= AVP_VAL_STR;
00251                 } else {
00252                         /* Integer AVP */
00253                         str2int(&avp_val, (unsigned*)&v.n);
00254                         if (rec->fld[0].v.lstr.len == (sizeof(AVP_GFLAGS) - 1) &&
00255                                 !strncmp(rec->fld[0].v.lstr.s, AVP_GFLAGS, sizeof(AVP_GFLAGS) - 1)) {
00256                                 /* Restore gflags */
00257                                 *gflags = v.n;
00258                         }
00259                 }
00260                 
00261                 if (add_avp_list(global_avps, flags, name, v) < 0) {
00262                         LOG(L_ERR, "gflags:load_attrs: Error while adding global attribute %.*s, skipping\n",
00263                             rec->fld[0].v.lstr.len, ZSW(rec->fld[0].v.lstr.s));
00264                         goto skip;
00265                 }
00266 
00267         skip:
00268                 rec = db_next(res);
00269         }
00270 
00271         db_res_free(res);
00272         return 0;
00273 }
00274 
00275 
00276 static int mod_init(void)
00277 {
00278         gflags=(unsigned int *) shm_malloc(sizeof(unsigned int));
00279         if (!gflags) {
00280                 LOG(L_ERR, "Error: gflags/mod_init: no shmem\n");
00281                 return -1;
00282         }
00283         *gflags=initial;
00284 
00285         avps_1 = shm_malloc(sizeof(*avps_1));
00286         if (!avps_1) {
00287                 ERR("can't allocate memory\n");
00288                 return -1;
00289         }
00290         *avps_1 = NULL;
00291         avps_2 = shm_malloc(sizeof(*avps_2));
00292         if (!avps_2) {
00293                 ERR("can't allocate memory\n");
00294                 return -1;
00295         }
00296         *avps_2 = NULL;
00297         active_global_avps = &avps_1;
00298 
00299         if (load_global_attrs) {
00300                 if (init_db() < 0) {
00301                         shm_free(gflags);
00302                         return -1;
00303                 }
00304                 
00305                 if (load_attrs(*active_global_avps) < 0) {
00306                         db_cmd_free(load_attrs_cmd);
00307                         db_cmd_free(save_gflags_cmd);
00308                         db_ctx_free(db);
00309                         return -1;
00310                 }
00311                 
00312                 set_avp_list(AVP_CLASS_GLOBAL, *active_global_avps);
00313                 
00314                 db_cmd_free(load_attrs_cmd);
00315                 db_cmd_free(save_gflags_cmd);
00316                 db_ctx_free(db);
00317 
00318                 load_attrs_cmd = NULL;
00319                 save_gflags_cmd = NULL;
00320                 db = NULL;
00321         }
00322 
00323         return 0;
00324 }
00325 
00326 static int child_init(int rank)
00327 {
00328         if (load_global_attrs) {
00329                 if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN)
00330                         return 0; /* do nothing for the main or tcp_main processes */
00331 
00332                 if (init_db() < 0) return -1;
00333         }
00334         return 0;
00335 }
00336 
00337 
00338 static void mod_destroy(void)
00339 {
00340         if (avps_1 != 0) {
00341                 destroy_avp_list(avps_1);
00342         }
00343         if (avps_2 != 0) {
00344                 destroy_avp_list(avps_2);
00345         }
00346         active_global_avps = 0;
00347 
00348         if (load_attrs_cmd) db_cmd_free(load_attrs_cmd);
00349         if (save_gflags_cmd) db_cmd_free(save_gflags_cmd);
00350         if (db) db_ctx_free(db);
00351 }
00352 
00353 
00354 int save_gflags(unsigned int flags)
00355 {
00356         str fl;
00357 
00358         if (!load_global_attrs) {
00359                 LOG(L_ERR, "gflags:save_gflags: You must enable load_global_attrs to make flush_gflag work\n");
00360                 return -1;
00361         }
00362 
00363         fl.s = int2str(flags, &fl.len);
00364 
00365         save_gflags_cmd->vals[0].v.cstr = AVP_GFLAGS;
00366         save_gflags_cmd->vals[1].v.int4 = 0;
00367         save_gflags_cmd->vals[2].v.lstr = fl;
00368         save_gflags_cmd->vals[3].v.bitmap = SRDB_LOAD_SER;
00369 
00370         if (db_exec(NULL, save_gflags_cmd) < 0) {
00371                 LOG(L_ERR, "gflags:save_gflag: Unable to store new value\n");
00372                 return -1;
00373         }
00374 
00375         DBG("gflags:save_gflags: Successfuly stored in database\n");
00376         return 0;
00377 }
00378 
00379 static int reload_global_attributes(void)
00380 {
00381         avp_list_t**  new_global_avps;
00382   
00383   /* Choose new global AVP list and free its old contents */
00384   if (active_global_avps == &avps_1) {
00385         destroy_avp_list(avps_2);
00386                 new_global_avps = &avps_2;
00387         } 
00388         else {
00389                 destroy_avp_list(avps_1);
00390                 new_global_avps = &avps_1;
00391         }
00392     
00393   if (load_attrs(*new_global_avps) < 0) {
00394         goto error;
00395   }
00396   
00397   active_global_avps = new_global_avps;
00398   set_avp_list(AVP_CLASS_GLOBAL, *active_global_avps);
00399 
00400   return 0;
00401     
00402 error:
00403         destroy_avp_list(*new_global_avps);
00404   return -1;
00405 }
00406 
00407 /*
00408  * Flush the state of global flags into database
00409  */
00410 static int flush_gflags(struct sip_msg* msg, char* s1, char* s2)
00411 {
00412         if (save_gflags(*gflags) < 0)  return -1;
00413         else return 1;
00414 }
00415 
00416 
00417 static const char* rpc_set_doc[] = {
00418         "Load a CPL script to the server.", /* Documentation string */
00419         0                                   /* Method signature(s) */
00420 };
00421 
00422 static void rpc_set(rpc_t* rpc, void* c)
00423 {
00424         int flag;
00425 
00426         if (rpc->scan(c, "d", &flag) < 1) {
00427                 rpc->fault(c, 400, "Flag number expected");
00428                 return;
00429         }
00430         if (flag < 0 || flag > 31) {
00431                 rpc->fault(c, 400, "Flag number %d out of range", &flag);
00432         }
00433         (*gflags) |= 1 << flag;
00434 }
00435 
00436 
00437 static const char* rpc_is_set_doc[] = {
00438         "Load a CPL script to the server.", /* Documentation string */
00439         0                                   /* Method signature(s) */
00440 };
00441 
00442 static void rpc_is_set(rpc_t* rpc, void* c)
00443 {
00444         int flag;
00445 
00446         if (rpc->scan(c, "d", &flag) < 1) {
00447                 rpc->fault(c, 400, "Flag number expected");
00448                 return;
00449         }
00450         if (flag < 0 || flag > 31) {
00451                 rpc->fault(c, 400, "Flag number %d out of range", &flag);
00452         }
00453         rpc->add(c, "b", (*gflags) & (1 << flag));
00454 }
00455 
00456 
00457 static const char* rpc_reset_doc[] = {
00458         "Load a CPL script to the server.", /* Documentation string */
00459         0                                   /* Method signature(s) */
00460 };
00461 
00462 static void rpc_reset(rpc_t* rpc, void* c)
00463 {
00464         int flag;
00465 
00466         if (rpc->scan(c, "d", &flag) < 1) {
00467                 rpc->fault(c, 400, "Flag number expected");
00468                 return;
00469         }
00470         if (flag < 0 || flag > 31) {
00471                 rpc->fault(c, 400, "Flag number %d out of range", &flag);
00472         }
00473         (*gflags) &= ~ (1 << flag);
00474 }
00475 
00476 
00477 static const char* rpc_flush_doc[] = {
00478         "Load a CPL script to the server.", /* Documentation string */
00479         0                                   /* Method signature(s) */
00480 };
00481 
00482 static void rpc_flush(rpc_t* rpc, void* c)
00483 {
00484         if (flush_gflags(0, 0, 0) < 0) {
00485                 rpc->fault(c, 400, "Error while saving flags to database");
00486         }
00487 }
00488 
00489 
00490 static const char* rpc_dump_doc[] = {
00491         "Load a CPL script to the server.", /* Documentation string */
00492         0                                   /* Method signature(s) */
00493 };
00494 
00495 static void rpc_dump(rpc_t* rpc, void* c)
00496 {
00497         int i;
00498         for(i = 0; i < 32; i++) {
00499                 rpc->add(c, "b", (*gflags >> i) & 1);
00500         }
00501 }
00502 
00503 static const char* rpc_reload_doc[2] = {
00504         "Reload global attributes from database",
00505         0
00506 };
00507 
00508 /*
00509  * Fifo function to reload domain table
00510  */
00511 static void rpc_reload(rpc_t* rpc, void* ctx)
00512 {
00513         if (reload_global_attributes() < 0) {
00514                 LOG(L_ERR, "ERROR: Reloading of global_attrs table has failed\n");
00515                 rpc->fault(ctx, 400, "Reloading of global attributes failed");
00516         }
00517         else {
00518                 /* reload is successful */
00519                 LOG(L_INFO, "INFO: global_attrs table reloaded\n");
00520         }
00521 }
00522 
00523 /*
00524  * RPC Methods exported by this module
00525  */
00526 static rpc_export_t rpc_methods[] = {
00527         {"gflags.set",    rpc_set,    rpc_set_doc,    0},
00528         {"gflags.is_set", rpc_is_set, rpc_is_set_doc, 0},
00529         {"gflags.reset",  rpc_reset,  rpc_reset_doc,  0},
00530         {"gflags.flush",  rpc_flush,  rpc_flush_doc,  0},
00531         {"gflags.dump",   rpc_dump,   rpc_dump_doc,   0},
00532         {"global.reload", rpc_reload, rpc_reload_doc, 0},
00533         {0, 0, 0, 0}
00534 };