Hi

please find attached a patch for a new module called 'prefix_route'


we want to merge this into the main SER v2.1 CVS, but would prefer
some review comments from the community before it is merged.
the module description can be found in the README file (in the patch).


This work was sponsored by Telio Telecom



/alfred


---
diff -Naur -X /home/alfredh/svn/.priv/exclude modules/a/Makefile 
modules/prefix_route/Makefile
--- modules/a/Makefile  1970-01-01 01:00:00.000000000 +0100
+++ modules/prefix_route/Makefile       2008-06-06 13:46:47.000000000 +0200
@@ -0,0 +1,7 @@
+include ../../Makefile.defs
+
+auto_gen=
+NAME=prefix_route.so
+LIBS=
+
+include ../../Makefile.modules
diff -Naur -X /home/alfredh/svn/.priv/exclude modules/a/prefix_route.c 
modules/prefix_route/prefix_route.c
--- modules/a/prefix_route.c    1970-01-01 01:00:00.000000000 +0100
+++ modules/prefix_route/prefix_route.c 2008-12-04 12:22:50.000000000 +0100
@@ -0,0 +1,323 @@
+/*
+ * Prefix Route Module
+ *
+ * Copyright (C) 2007 Alfred E. Heggestad
+ * Copyright (C) 2008 Telio Telecom AS
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [EMAIL PROTECTED]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "../../db/db.h"
+#include "../../rpc.h"
+#include "../../sr_module.h"
+#include "../../mem/mem.h"
+#include "../../data_lump_rpl.h"
+#include "../../parser/parse_uri.h"
+#include "../../parser/msg_parser.h"
+#include "../../locking.h"
+#include "../../action.h"
+#include "../../route.h"
+#include "tree.h"
+#include "pr.h"
+
+
+MODULE_VERSION
+
+
+/* Modules parameters */
+static char *db_url   = DEFAULT_DB_URL;
+static char *db_table = "prefix_route";
+
+
+static int add_route(struct tree_item *root, const char *prefix,
+                    const char *route)
+{
+       int ix, err;
+
+       /* We cache the route index here so we can avoid route_lookup()
+        * in the prefix_route() routing function.
+        */
+       ix = route_lookup(&main_rt, (char *)route);
+       if (ix < 0) {
+               LOG(L_CRIT, "prefix_route: db_load(): "
+                   "route name '%s' is not defined\n", route);
+               return -1;
+       }
+
+       if (ix >= main_rt.entries) {
+               LOG(L_CRIT, "prefix_route: route %d > n_entries (%d)\n",
+                   ix, main_rt.entries);
+               return -1;
+       }
+
+       err = tree_item_add(root, prefix, route, ix);
+       if (0 != err) {
+               LOG(L_CRIT, "prefix_route: db_load(): "
+                   "tree_item_add() failed (%d)\n", err);
+               return err;
+       }
+
+       return 0;
+}
+
+
+/**
+ * Load prefix rules from database and build tree in memory
+ */
+int pr_db_load(void)
+{
+       db_ctx_t *ctx;
+       db_cmd_t *cmd = NULL;
+       db_res_t* res = NULL;
+       db_rec_t* rec;
+       struct tree_item *root;
+       int err = -1;
+       int count = 0;
+       db_fld_t match[] = {
+               { .name = "prefix",  .type = DB_CSTR },
+               { .name = "route",   .type = DB_CSTR },
+               { .name = "comment", .type = DB_CSTR },
+               { .name = NULL,      .type = DB_NONE }
+       };
+
+       ctx = db_ctx("prefix_route");
+       if (!ctx) {
+               LOG(L_ERR, "prefix_route: db_load(): db_ctx() failed\n");
+               return -1;
+       }
+       if (db_add_db(ctx, db_url) < 0) {
+               LOG(L_ERR, "prefix_route: db_load(): could not add db\n");
+               goto out;
+       }
+       if (db_connect(ctx) < 0) {
+               LOG(L_ERR, "prefix_route: db_load(): could not connect\n");
+               goto out;
+       }
+
+       cmd = db_cmd(DB_GET, ctx, db_table, match, NULL, NULL);
+       if (!cmd) {
+               LOG(L_ERR, "prefix_route: db_load(): db_cmd() failed\n");
+               goto out;
+       }
+
+       if (db_exec(&res, cmd) < 0) {
+               LOG(L_ERR, "prefix_route: db_load(): db_exec() failed\n");
+               goto out;
+       }
+
+       root = tree_item_alloc();
+       if (NULL == root) {
+               LOG(L_ERR, "prefix_route: db_load() tree alloc failed\n");
+               err = -1;
+               goto out;
+       }
+
+       /* Assume Success */
+       err = 0;
+
+       /* Read from DB and build tree */
+       for (rec = db_first(res); rec != NULL && !err; rec = db_next(res)) {
+               const char *prefix, *route, *comment;
+
+               ++count;
+
+               if (rec->fld[0].flags & DB_NULL) {
+                       LOG(L_CRIT, "prefix_route: ERROR: bad 'prefix' "
+                           "record in table %s, skipping...\n", db_table);
+                       continue;
+               }
+               if (rec->fld[1].flags & DB_NULL) {
+                       LOG(L_CRIT, "prefix_route: ERROR: bad 'route' "
+                           "record in table %s, skipping...\n", db_table);
+                       continue;
+               }
+
+               prefix  = rec->fld[0].v.cstr;
+               route   = rec->fld[1].v.cstr;
+               comment = rec->fld[2].v.cstr;
+
+               LOG(L_INFO, "  %d: prefix=%-10s  route=%-15s  comment=%s\n",
+                   count, prefix, route, comment);
+
+               err = add_route(root, prefix, route);
+       }
+
+       LOG(L_NOTICE, "prefix_route: Total prefix routes loaded: %d\n", count);
+
+       /* Error */
+       if (0 != err) {
+               LOG(L_ERR, "prefix_route: db_load(): error, flushing tree\n");
+               tree_item_free(root);
+               goto out;
+       }
+
+       /* Swap trees */
+       err = tree_swap(root);
+       if (0 != err)
+               goto out;
+
+ out:
+       /* Free database results */
+       if (res)
+               db_res_free(res);
+       if (cmd)
+               db_cmd_free(cmd);
+
+       /* Close database connection */
+       if (ctx)
+               db_ctx_free(ctx);
+
+       return err;
+}
+
+
+/**
+ * Initialize module
+ */
+static int mod_init(void)
+{
+       /* Initialise tree */
+       if (0 != tree_init()) {
+               LOG(L_CRIT, "prefix_route: tree_init() failed\n\n");
+               return -1;
+       }
+
+       /* Populate database */
+       if (0 != pr_db_load()) {
+               LOG(L_CRIT, "prefix_route: db_load() failed\n\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * Destroy module and free all resources
+ */
+static void mod_destroy(void)
+{
+       tree_close();
+}
+
+
+/**
+ * Extract username from the Request URI
+ * First try to look at the original Request URI and if there
+ * is no username use the new Request URI
+ */
+static int get_username(struct sip_msg* msg, str *user)
+{
+       if (!msg || !user)
+               return -1;
+
+       if (parse_sip_msg_uri(msg) < 0){
+               LOG(L_ERR, "get_username(): bad uri\n");
+               return -1; /* error, bad uri */
+       }
+
+       if (msg->parsed_uri.user.s == 0){
+               /* no user in uri */
+               LOG(L_ERR, "get_username(): no user in uri\n");
+               return -2;
+       }
+
+       *user = msg->parsed_uri.user;
+
+       return 0;
+}
+
+
+/**
+ * SER Command "prefix_route"
+ */
+static int prefix_route(struct sip_msg *msg, char *p1, char *p2)
+{
+       struct run_act_ctx ra_ctx;
+       str user;
+       int err;
+       int route;
+
+       /* Unused */
+       (void)p1;
+       (void)p2;
+
+       /* Get request URI */
+       err = get_username(msg, &user);
+       if (0 != err) {
+               LOG(L_ERR, "prefix_route: could not get username in"
+                   " Request URI (%d)\n", err);
+               return err;
+       }
+
+       route = tree_route_get(&user);
+       if (route <= 0)
+               return -1;
+
+       /* If match send to route[x] */
+       init_run_actions_ctx(&ra_ctx);
+
+       err = run_actions(&ra_ctx, main_rt.rlist[route], msg);
+       if (err < 0) {
+               LOG(L_ERR, "prefix_route: run_actions failed (%d)\n", err);
+               return -1;
+       }
+
+       /* Success */
+       return 0;
+}
+
+
+/*
+ * Exported functions
+ */
+static cmd_export_t cmds[] = {
+       {"prefix_route", prefix_route, 0, 0, REQUEST_ROUTE},
+       {0,              0,            0, 0, 0            }
+};
+
+/*
+ * Exported parameters
+ */
+static param_export_t params[] = {
+       {"db_url",       STR_PARAM, &db_url  },
+       {"db_table",     STR_PARAM, &db_table},
+       {0,              0,         0        }
+};
+
+/*
+ * Module description
+ */
+struct module_exports exports = {
+       "prefix_route",  /* Module name             */
+       cmds,            /* Exported functions      */
+       pr_rpc,          /* RPC methods             */
+       params,          /* Exported parameters     */
+       mod_init,        /* Initialization function */
+       0,               /* Response function       */
+       mod_destroy,     /* Destroy function        */
+       0,               /* OnCancel function       */
+       0                /* Child init function     */
+};
diff -Naur -X /home/alfredh/svn/.priv/exclude modules/a/prefix_route.sql 
modules/prefix_route/prefix_route.sql
--- modules/a/prefix_route.sql  1970-01-01 01:00:00.000000000 +0100
+++ modules/prefix_route/prefix_route.sql       2008-06-06 13:46:47.000000000 
+0200
@@ -0,0 +1,11 @@
+
+-- SQL script for prefix route module
+
+USE ser;
+
+-- create the table
+CREATE TABLE prefix_route (
+    prefix  VARCHAR(64) NOT NULL DEFAULT "",
+    route   VARCHAR(64) NOT NULL DEFAULT "",
+    comment VARCHAR(64) NOT NULL DEFAULT ""
+);
diff -Naur -X /home/alfredh/svn/.priv/exclude modules/a/pr.h 
modules/prefix_route/pr.h
--- modules/a/pr.h      1970-01-01 01:00:00.000000000 +0100
+++ modules/prefix_route/pr.h   2008-12-04 12:20:22.000000000 +0100
@@ -0,0 +1,33 @@
+/*
+ * Prefix Route Module - Internal module interface
+ *
+ * Copyright (C) 2007 Alfred E. Heggestad
+ * Copyright (C) 2008 Telio Telecom AS
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [EMAIL PROTECTED]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+extern rpc_export_t pr_rpc[];
+
+
+int pr_db_load(void);
diff -Naur -X /home/alfredh/svn/.priv/exclude modules/a/pr_rpc.c 
modules/prefix_route/pr_rpc.c
--- modules/a/pr_rpc.c  1970-01-01 01:00:00.000000000 +0100
+++ modules/prefix_route/pr_rpc.c       2008-12-04 12:23:05.000000000 +0100
@@ -0,0 +1,89 @@
+/*
+ * Prefix Route Module - RPC Commands
+ *
+ * Copyright (C) 2007 Alfred E. Heggestad
+ * Copyright (C) 2008 Telio Telecom AS
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [EMAIL PROTECTED]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define _GNU_SOURCE 1 /* needed for open_memstream() */
+#include <stdio.h>
+#include <stdlib.h>
+#include "../../str.h"
+#include "../../dprint.h"
+#include "../../rpc.h"
+#include "tree.h"
+#include "pr.h"
+
+
+static const char *rpc_dump_doc[2]   = {"Dump the prefix route tree",   NULL};
+static const char *rpc_reload_doc[2] = {"Reload prefix routes from DB", NULL};
+
+
+/**
+ * RPC command - dump prefix route tree
+ */
+static void rpc_dump(rpc_t *rpc, void *c)
+{
+       FILE *f;
+       size_t size;
+       char *ptr;
+
+       f = open_memstream(&ptr, &size);
+       if (!f) {
+               rpc->fault(c, 500, "failed to open memstream");
+               return;
+       }
+
+       tree_print(f);
+
+       fclose(f);
+
+       rpc->printf(c, "%.*s", size, ptr);
+
+       free(ptr);
+}
+
+
+/**
+ * RPC command - reload prefix tree from database
+ */
+static void rpc_reload(rpc_t *rpc, void *c)
+{
+       LOG(L_NOTICE, "prefix_route: Reloading prefix route tree from DB\n");
+
+       if (0 != pr_db_load()) {
+               LOG(L_ERR, "prefix_route: rpc_reload(): db_load() failed\n");
+               rpc->fault(c, 400, "failed to reload prefix routes");
+       }
+       else {
+               rpc->printf(c, "Prefix routes reloaded successfully");
+       }
+}
+
+
+rpc_export_t pr_rpc[] = {
+       {"prefix_route.reload", rpc_reload, rpc_reload_doc, 0},
+       {"prefix_route.dump",   rpc_dump,   rpc_dump_doc,   0},
+       {0, 0, 0, 0}
+};
diff -Naur -X /home/alfredh/svn/.priv/exclude modules/a/README 
modules/prefix_route/README
--- modules/a/README    1970-01-01 01:00:00.000000000 +0100
+++ modules/prefix_route/README 2008-12-04 12:11:22.000000000 +0100
@@ -0,0 +1,97 @@
+Prefix route module
+Alfred E. Heggestad
+
+Sponsored by Telio Telecom
+
+
+
+
+Overview
+
+   The prefix_route module does routing based on a set of prefixes from the
+   database. The prefix rule-set is loaded from the database into a binary
+   tree in memory, either on startup or when issuing the "prefix_route.reload" 
RPC
+   command. When calling the "prefix_route()" function from the ser.cfg
+   configuration script, it will try to match the user part of the request URI
+   with the best matching route. If a route is found, it will be used for
+   further processing. If not found normal processing will continue.
+
+
+
+
+Exported Parameters
+
+   db_url (string)
+
+   Database URL.
+
+   Default value is "mysql://[EMAIL PROTECTED]/ser".
+
+   Example. Set the "db_url" parameter
+   ...
+   modparam("prefix_route", "db_url", "mysql://user:[EMAIL PROTECTED]/dbname")
+   ...
+
+
+   db_table (string)
+
+   The name of table where to read prefix route set
+
+   Default value is "prefix_route".
+
+   Example. Set the "db_table" parameter
+   ...
+   modparam("prefix_route", "db_table", "prefix_route")
+   ...
+
+
+
+
+Exported Functions
+
+   prefix_route()
+
+   This function tries to find a route from the user part of the request URI.
+   If a route is found, it will be used for further processing. Otherwise the
+   function will return false.
+
+   Example:
+
+        if (!prefix_route()) {
+                xlog("L_ERR", "prefix_route(): no matching routes\n");
+        }
+
+
+
+
+RPC commands
+
+   "prefix_route.reload" - Reload prefix route tree from the database.
+                           Validation is done and the prefix route tree will
+                           only be reloaded if there are no errors.
+
+   "prefix_route.dump"   - Dump the current prefix route tree.
+
+
+
+
+Database schema
+
+   A prefix route set consists of three fields:
+
+     - prefix   varchar(64)  Prefix rule
+     - route    varchar(64)  Route name
+     - comment  varchar(64)  Free-form comment
+
+
+   Example:
+
+   +--------+-------+---------------+
+   | prefix | route | comment       |
+   +--------+-------+---------------+
+   | 46     | SE    | Sweden        |
+   | 47     | NO    | Norway        |
+   | 479    | NOMOB | Norway mobile |
+   | 49     | DE    | Deutschland   |
+   | 39     | IT    | Italy         |
+   +--------+-------+---------------+
diff -Naur -X /home/alfredh/svn/.priv/exclude modules/a/tree.c 
modules/prefix_route/tree.c
--- modules/a/tree.c    1970-01-01 01:00:00.000000000 +0100
+++ modules/prefix_route/tree.c 2008-12-04 12:30:10.000000000 +0100
@@ -0,0 +1,391 @@
+/*
+ * Prefix Route Module - tree search implementation
+ *
+ * Copyright (C) 2007 Alfred E. Heggestad
+ * Copyright (C) 2008 Telio Telecom AS
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [EMAIL PROTECTED]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include "../../mem/shm_mem.h"
+#include "../../str.h"
+#include "tree.h"
+
+
+enum {
+       DIGITS = 10
+};
+
+
+/** Defines a route item in the prefix tree */
+struct tree_item {
+       struct tree_item *digits[DIGITS];  /**< Child items for each digit */
+       char name[16];                     /**< Route name (for dump)      */
+       int route;                         /**< Valid route number if >0   */
+};
+
+
+/** Defines a locked prefix tree */
+struct tree {
+       struct tree_item *root;  /**< Root item of tree    */
+       gen_lock_t lock;         /**< Reference count lock */
+       int refcnt;              /**< Reference counting   */
+};
+
+
+/* Local variables */
+static struct tree **shared_tree = NULL;
+static gen_lock_t shared_tree_lock;
+
+
+/**
+ * Allocate and initialize a new tree item
+ */
+struct tree_item *tree_item_alloc(void)
+{
+       struct tree_item *root;
+       int i;
+
+       root = (struct tree_item *)shm_malloc(sizeof(*root));
+       if (NULL == root) {
+               LOG(L_CRIT, "tree_item_alloc: shared memory alloc failed\n");
+               return NULL;
+       }
+
+       for (i=0; i<DIGITS; i++)
+               root->digits[i] = NULL;
+
+       root->route = 0;
+
+       return root;
+}
+
+
+/**
+ * Flush tree item
+ */
+void tree_item_free(struct tree_item *item)
+{
+       int i;
+
+       if (NULL == item)
+               return;
+
+       for (i=0; i<DIGITS; i++) {
+               tree_item_free(item->digits[i]);
+       }
+
+       shm_free(item);
+}
+
+
+/**
+ * Add a route prefix rule to the tree
+ */
+int tree_item_add(struct tree_item *root, const char *prefix,
+                 const char *route, int route_ix)
+{
+       struct tree_item *item;
+       const char *p;
+       int err;
+
+       if (NULL == root || NULL == prefix || route_ix <= 0)
+               return -1;
+
+       item = root;
+       for (p = prefix; '\0' != *p; p++) {
+               int digit;
+
+               if (!isdigit(*p))
+                       continue;
+
+               digit = *p - '0';
+
+               /* exist? */
+               if (!item->digits[digit]) {
+                       item->digits[digit] = tree_item_alloc();
+                       if (!item->digits[digit]) {
+                               LOG(L_CRIT, "tree_item_add: alloc failed\n");
+                               err = -1;
+                               goto out;
+                       }
+               }
+
+               item = item->digits[digit];
+       }
+
+       if (NULL == item) {
+               LOG(L_CRIT, "tree_item_add: internal error (no item)\n");
+               err = -1;
+               goto out;
+       }
+
+       if (item->route > 0) {
+               LOG(L_ERR, "tree_item_add: prefix %s already set to %s\n",
+                   prefix, item->name);
+       }
+
+       /* Set route number for the tree item */
+       item->route = route_ix;
+
+       /* Copy the route name (used in tree dump) */
+       strncpy(item->name, route, sizeof(item->name)-1);
+       item->name[sizeof(item->name)-1] = '\0';
+
+       err = 0;
+
+ out:
+       return err;
+}
+
+
+/**
+ * Get route number from username
+ */
+int tree_item_get(const struct tree_item *root, const str *user)
+{
+       const struct tree_item *item;
+       const char *p, *pmax;
+       int route = 0;
+
+       if (NULL == root || NULL == user || NULL == user->s || !user->len)
+               return -1;
+
+       pmax = user->s + user->len;
+       item = root;
+       for (p = user->s; p < pmax ; p++) {
+               int digit;
+
+               if (!isdigit(*p)) {
+                       continue;
+               }
+
+               digit = *p - '0';
+
+               /* Update route with best match so far */
+               if (item->route > 0) {
+                       route = item->route;
+               }
+
+               /* exist? */
+               if (NULL == item->digits[digit]) {
+                       break;
+               }
+
+               item = item->digits[digit];
+       }
+
+       return route;
+}
+
+
+/**
+ * Print one tree item to a file handle
+ */
+void tree_item_print(const struct tree_item *item, FILE *f, int level)
+{
+       int i;
+
+       if (NULL == item || NULL == f)
+               return;
+
+       if (item->route > 0) {
+               fprintf(f, " \t--> route[%s] ", item->name);
+       }
+
+       for (i=0; i<DIGITS; i++) {
+               int j;
+
+               if (!item->digits[i]) {
+                       continue;
+               }
+
+               fputc('\n', f);
+               for (j=0; j<level; j++)
+                       fputc(' ', f);
+
+               fprintf(f, "%d ", i);
+               tree_item_print(item->digits[i], f, level+1);
+       }
+}
+
+
+/**
+ * Allocate a new tree structure
+ */
+static struct tree *tree_alloc(void)
+{
+       struct tree *tree;
+
+       tree = (struct tree *)shm_malloc(sizeof(*tree));
+       if (NULL == tree)
+               return NULL;
+
+       lock_init(&tree->lock);
+       tree->root    = NULL;
+       tree->refcnt  = 0;
+
+       return tree;
+}
+
+
+/**
+ * Flush the tree
+ */
+static void tree_flush(struct tree *tree)
+{
+       if (NULL == tree)
+               return;
+
+       /* Wait for old tree to be released */
+       for (;;) {
+               int refcnt;
+
+               lock_get(&tree->lock);
+               refcnt = tree->refcnt;
+               lock_release(&tree->lock);
+
+               if (refcnt <= 0)
+                       break;
+
+               LOG(L_NOTICE, "prefix_route: tree_flush: waiting refcnt=%d\n",
+                   refcnt);
+
+               usleep(100000);
+       };
+
+       tree_item_free(tree->root);
+       shm_free(tree);
+}
+
+
+/**
+ * Access the shared tree and optionally increment/decrement the
+ * reference count.
+ */
+static struct tree *tree_get(struct tree **src, int ref)
+{
+       struct tree *tree;
+
+       if (NULL == src)
+               return NULL;
+
+       lock_get(&shared_tree_lock);
+       tree = *src;
+
+       if (NULL != tree && 0 != ref) {
+               lock_get(&tree->lock);
+               tree->refcnt += ref;
+
+               lock_release(&tree->lock);
+       }
+
+       lock_release(&shared_tree_lock);
+
+       return tree;
+}
+
+
+int tree_init(void)
+{
+       /* Initialize lock */
+       lock_init(&shared_tree_lock);
+
+       /* Pointer to global tree must be in shared memory */
+       shared_tree = (struct tree **)shm_malloc(sizeof(*shared_tree));
+       if (NULL == shared_tree) {
+               return -1;
+       }
+
+       *shared_tree = NULL;
+
+       return 0;
+}
+
+
+void tree_close(void)
+{
+       tree_flush(tree_get(shared_tree, 0));
+}
+
+
+int tree_swap(struct tree_item *root)
+{
+       struct tree *new_tree, *old_tree;
+
+       new_tree = tree_alloc();
+       if (NULL == new_tree)
+               return -1;
+
+       new_tree->root = root;
+
+       /* Save old tree */
+       old_tree = tree_get(shared_tree, 0);
+
+       /* Critical - swap trees */
+       *shared_tree = tree_get(&new_tree, 0);
+
+       /* Flush old tree */
+       tree_flush(old_tree);
+
+       return 0;
+}
+
+
+int tree_route_get(const str *user)
+{
+       struct tree *tree;
+       int route;
+
+       /* Find match in tree */
+       tree = tree_get(shared_tree, 1);
+       if (NULL == tree) {
+               return -1;
+       }
+
+       route = tree_item_get(tree->root, user);
+       (void)tree_get(&tree, -1);
+
+       return route;
+}
+
+
+void tree_print(FILE *f)
+{
+       struct tree *tree;
+
+       tree = tree_get(shared_tree, 1);
+
+       fprintf(f, "Prefix route tree:\n");
+       fprintf(f, " reference count: %d\n", tree ? tree->refcnt-1 : -1);
+
+       if (NULL != tree) {
+               tree_item_print(tree->root, f, 0);
+       }
+
+       (void)tree_get(&tree, -1);
+}
diff -Naur -X /home/alfredh/svn/.priv/exclude modules/a/tree.h 
modules/prefix_route/tree.h
--- modules/a/tree.h    1970-01-01 01:00:00.000000000 +0100
+++ modules/prefix_route/tree.h 2008-12-03 16:49:15.000000000 +0100
@@ -0,0 +1,46 @@
+/*
+ * Prefix Route Module - tree search interface
+ *
+ * Copyright (C) 2007 Alfred E. Heggestad
+ * Copyright (C) 2008 Telio Telecom AS
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [EMAIL PROTECTED]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+struct tree_item;
+
+struct tree_item *tree_item_alloc(void);
+void tree_item_free(struct tree_item *item);
+int  tree_item_add(struct tree_item *root, const char *prefix,
+                  const char *route, int route_ix);
+int  tree_item_get(const struct tree_item *root, const str *user);
+void tree_item_print(const struct tree_item *item, FILE *f, int level);
+
+
+struct tree;
+
+int  tree_init(void);
+void tree_close(void);
+int  tree_swap(struct tree_item *root);
+int  tree_route_get(const str *user);
+void tree_print(FILE *f);
_______________________________________________
Serdev mailing list
[email protected]
http://lists.iptel.org/mailman/listinfo/serdev

Reply via email to