Forráskód Böngészése

added new module prefix_route

Alfred E. Heggestad 17 éve
szülő
commit
388b3a4094

+ 7 - 0
modules_s/prefix_route/Makefile

@@ -0,0 +1,7 @@
+include ../../Makefile.defs
+
+auto_gen=
+NAME=prefix_route.so
+LIBS=
+
+include ../../Makefile.modules

+ 97 - 0
modules_s/prefix_route/README

@@ -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://ser@localhost/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         | 
+   +--------+-------+---------------+

+ 33 - 0
modules_s/prefix_route/pr.h

@@ -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);

+ 96 - 0
modules_s/prefix_route/pr_rpc.c

@@ -0,0 +1,96 @@
+/*
+ * 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
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.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)
+{
+	char buf[1024];
+	FILE *f;
+
+	f = tmpfile();
+	if (!f) {
+		rpc->fault(c, 500, "failed to open temp file");
+		return;
+	}
+
+	tree_print(f);
+
+	rewind(f);
+
+	while (!feof(f)) {
+
+		if (!fgets(buf, sizeof(buf), f))
+			break;
+
+		buf[strlen(buf)-1] = '\0';
+
+		rpc->printf(c, "%s", buf);
+	}
+
+	fclose(f);
+}
+
+
+/**
+ * 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}
+};

+ 323 - 0
modules_s/prefix_route/prefix_route.c

@@ -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     */
+};

+ 11 - 0
modules_s/prefix_route/prefix_route.sql

@@ -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 ""
+);

+ 401 - 0
modules_s/prefix_route/tree.c

@@ -0,0 +1,401 @@
+/*
+ * 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    */
+	atomic_t 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;
+
+	tree->root    = NULL;
+	atomic_set(&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 (;;) {
+		const int refcnt = atomic_get(&tree->refcnt);
+
+		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(void)
+{
+	struct tree *tree;
+
+	lock_get(&shared_tree_lock);
+	tree = *shared_tree;
+	lock_release(&shared_tree_lock);
+
+	return tree;
+}
+
+
+static struct tree *tree_ref(void)
+{
+	struct tree *tree;
+
+	lock_get(&shared_tree_lock);
+	tree = *shared_tree;
+	atomic_inc(&tree->refcnt);
+	lock_release(&shared_tree_lock);
+
+	return tree;
+}
+
+
+struct tree *tree_deref(struct tree *tree)
+{
+	if (tree)
+		atomic_dec(&tree->refcnt);
+	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());
+}
+
+
+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();
+
+	/* Critical - swap trees */
+	lock_get(&shared_tree_lock);
+	*shared_tree = new_tree;
+	lock_release(&shared_tree_lock);
+
+	/* 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_ref();
+	if (NULL == tree) {
+		return -1;
+	}
+
+	route = tree_item_get(tree->root, user);
+	tree_deref(tree);
+
+	return route;
+}
+
+
+void tree_print(FILE *f)
+{
+	struct tree *tree;
+
+	tree = tree_ref();
+
+	fprintf(f, "Prefix route tree:\n");
+
+	if (tree) {
+		fprintf(f, " reference count: %d\n",
+			atomic_get(&tree->refcnt));
+		tree_item_print(tree->root, f, 0);
+	}
+	else {
+		fprintf(f, " (no tree)\n");
+	}
+
+	tree_deref(tree);
+}

+ 46 - 0
modules_s/prefix_route/tree.h

@@ -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);