mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
419 lines
7.3 KiB
419 lines
7.3 KiB
/*
|
|
* 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:
|
|
* info@iptel.org
|
|
*
|
|
* 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 "../../atomic_ops.h"
|
|
#include "../../mem/shm_mem.h"
|
|
#include "../../str.h"
|
|
#include "../../lock_alloc.h"
|
|
#include "../../lock_ops.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 */
|
|
shared_tree_lock = lock_alloc();
|
|
if (NULL == shared_tree_lock) {
|
|
return -1;
|
|
}
|
|
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) {
|
|
lock_destroy(shared_tree_lock);
|
|
lock_dealloc(shared_tree_lock);
|
|
shared_tree_lock=0;
|
|
return -1;
|
|
}
|
|
|
|
*shared_tree = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void tree_close(void)
|
|
{
|
|
if (shared_tree)
|
|
tree_flush(tree_get());
|
|
shared_tree = NULL;
|
|
if (shared_tree_lock) {
|
|
lock_destroy(shared_tree_lock);
|
|
lock_dealloc(shared_tree_lock);
|
|
shared_tree_lock=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();
|
|
|
|
/* 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);
|
|
}
|