From 0455688ea1e614a2414e5fcbac05d0efada9f2d0 Mon Sep 17 00:00:00 2001 From: Mark Spencer Date: Mon, 6 Dec 2004 05:54:16 +0000 Subject: [PATCH] Add persistent dynamic queue member support (bug #2929) git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@4390 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- apps/app_queue.c | 159 ++++++++++++++++++++++++++++++++++++- configs/queues.conf.sample | 8 +- 2 files changed, 165 insertions(+), 2 deletions(-) diff --git a/apps/app_queue.c b/apps/app_queue.c index 118c9a508f..4b7bae7115 100755 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -7,6 +7,16 @@ * * Mark Spencer * + * 2004-11-25: Persistent Dynamic Members added by: + * NetNation Communications (www.netnation.com) + * Kevin Lindsay + * + * Each dynamic agent in each queue is now stored in the astdb. + * When asterisk is restarted, each agent will be automatically + * readded into their recorded queues. This feature can be + * configured with the 'peristent_members=<1|0>' KVP under the + * '[general]' group in queues.conf. The default is on. + * * 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr). * * These features added by David C. Troy : @@ -22,7 +32,7 @@ * Added servicelevel statistic by Michiel Betel * Added Priority jumping code for adding and removing queue members by Jonathan Stanton * - * Fixed ot work with CVS as of 2004-02-25 and released as 1.07a + * Fixed to work with CVS as of 2004-02-25 and released as 1.07a * by Matthew Enger * * This program is free software, distributed under the terms of @@ -46,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -136,6 +147,13 @@ static char *app_rqm_descrip = "Example: RemoveQueueMember(techsupport|SIP/3000)\n" ""; +/* Persistent Members astdb family */ +static const char *pm_family = "/Queue/PersistentMembers"; +/* The maximum lengh of each persistent member queue database entry */ +#define PM_MAX_LEN 2048 +/* queues.conf [general] option */ +static int queue_persistent_members = 0; + /* We define a customer "local user" structure because we use it not only for keeping track of what is in use but also for keeping track of who we're dialing. */ @@ -1542,6 +1560,46 @@ static struct member * create_queue_node( char * interface, int penalty ) return( cur ) ; } +/* Dump all members in a specific queue to the databse + * + * / = ;;... + * + */ +static void dump_queue_members(struct ast_call_queue *pm_queue) +{ + struct member *cur_member = NULL; + char value[PM_MAX_LEN]; + int value_len = 0; + int res; + + memset(value, 0, sizeof(value)); + + if (pm_queue) { + cur_member = pm_queue->members; + while (cur_member) { + if (cur_member->dynamic) { + value_len = strlen(value); + res = snprintf(value+value_len, sizeof(value)-value_len, "%s/%s;%d;", cur_member->tech, cur_member->loc, cur_member->penalty); + if (res != strlen(value + value_len)) { + ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n"); + break; + } + } + cur_member = cur_member->next; + } + + if (!ast_strlen_zero(value) && !cur_member) { + if (ast_db_put(pm_family, pm_queue->name, value)) + ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n"); + } else { + /* Delete the entry if the queue is empty or there is an error */ + ast_db_del(pm_family, pm_queue->name); + } + + } + +} + static int remove_from_queue(char *queuename, char *interface) { struct ast_call_queue *q; @@ -1570,6 +1628,10 @@ static int remove_from_queue(char *queuename, char *interface) "Location: %s/%s\r\n", q->name, last_member->tech, last_member->loc); free(last_member); + + if (queue_persistent_members) + dump_queue_members(q); + res = RES_OKAY; } else { res = RES_EXISTS; @@ -1610,6 +1672,10 @@ static int add_to_queue(char *queuename, char *interface, int penalty) "Status: %d\r\n", q->name, new_member->tech, new_member->loc, new_member->dynamic ? "dynamic" : "static", new_member->penalty, new_member->calls, new_member->lastcall, new_member->status); + + if (queue_persistent_members) + dump_queue_members(q); + res = RES_OKAY; } else { res = RES_OUTOFMEMORY; @@ -1626,6 +1692,86 @@ static int add_to_queue(char *queuename, char *interface, int penalty) return res; } +/* Add members saved in the queue members DB file saves + * created by dump_queue_members(), back into the queues */ +static void reload_queue_members(void) +{ + char *cur_pm_ptr; + char *pm_queue_name; + char *pm_interface; + char *pm_penalty_tok; + int pm_penalty = 0; + struct ast_db_entry *pm_db_tree = NULL; + int pm_family_len = 0; + struct ast_call_queue *cur_queue = NULL; + char queue_data[PM_MAX_LEN]; + + pm_db_tree = ast_db_gettree(pm_family, NULL); + + pm_family_len = strlen(pm_family); + ast_mutex_lock(&qlock); + /* Each key in 'pm_family' is the name of a specific queue in which + * we will reload members into. */ + while (pm_db_tree) { + pm_queue_name = pm_db_tree->key+pm_family_len+2; + + cur_queue = queues; + while (cur_queue) { + ast_mutex_lock(&cur_queue->lock); + + if (strcmp(pm_queue_name, cur_queue->name) == 0) + break; + + ast_mutex_unlock(&cur_queue->lock); + + cur_queue = cur_queue->next; + } + + if (!cur_queue) { + /* If the queue no longer exists, remove it from the + * database */ + ast_db_del(pm_family, pm_queue_name); + pm_db_tree = pm_db_tree->next; + continue; + } else + ast_mutex_unlock(&cur_queue->lock); + + if (!ast_db_get(pm_family, pm_queue_name, queue_data, PM_MAX_LEN)) { + /* Parse each ;; from the value of the + * queuename key and add it to the respective queue */ + cur_pm_ptr = queue_data; + while ((pm_interface = strsep(&cur_pm_ptr, ";"))) { + if (!(pm_penalty_tok = strsep(&cur_pm_ptr, ";"))) { + ast_log(LOG_WARNING, "Error parsing corrupted Queue DB string for '%s'\n", pm_queue_name); + break; + } + pm_penalty = strtol(pm_penalty_tok, NULL, 10); + if (errno == ERANGE) { + ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", pm_penalty_tok); + break; + } + + if (option_debug) + ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Penalty: %d\n", pm_queue_name, pm_interface, pm_penalty); + + if (add_to_queue(pm_queue_name, pm_interface, pm_penalty) == RES_OUTOFMEMORY) { + ast_log(LOG_ERROR, "Out of Memory\n"); + break; + } + } + } + + pm_db_tree = pm_db_tree->next; + } + + ast_log(LOG_NOTICE, "Queue members sucessfully reloaded from database.\n"); + ast_mutex_unlock(&qlock); + if (pm_db_tree) { + ast_db_freetree(pm_db_tree); + pm_db_tree = NULL; + } +} + static int rqm_exec(struct ast_channel *chan, void *data) { int res=-1; @@ -1994,6 +2140,8 @@ static void reload_queues(void) struct ast_variable *var; struct member *prev, *cur; int new; + char *general_val = NULL; + cfg = ast_load("queues.conf"); if (!cfg) { ast_log(LOG_NOTICE, "No call queueing config file, so no call queues\n"); @@ -2178,6 +2326,11 @@ static void reload_queues(void) queues = q; } } + } else { + /* Initialize global settings */ + queue_persistent_members = 0; + if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) + queue_persistent_members = ast_true(general_val); } cat = ast_category_browse(cfg, cat); } @@ -2728,6 +2881,10 @@ int load_module(void) ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ; } reload_queues(); + + if (queue_persistent_members) + reload_queue_members(); + return res; } diff --git a/configs/queues.conf.sample b/configs/queues.conf.sample index 5b570a0c54..f6975b6e9a 100755 --- a/configs/queues.conf.sample +++ b/configs/queues.conf.sample @@ -1,7 +1,13 @@ [general] ; ; Global settings for call queues -; (none exist currently) +; +; Persistent Members +; Store each dynamic agent in each queue in the astdb so that +; when asterisk is restarted, each agent will be automatically +; readded into their recorded queues. Default is 'yes'. +; +persistentmembers = yes ; ; Note that a timeout to fail out of a queue may be passed as part of application call ; from extensions.conf: