Index: app_queue.c =================================================================== RCS file: /usr/cvsroot/asterisk/apps/app_queue.c,v retrieving revision 1.97 diff -u -r1.97 app_queue.c --- app_queue.c 20 Nov 2004 15:15:51 -0000 1.97 +++ app_queue.c 25 Nov 2004 18:07:32 -0000 @@ -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 : @@ -46,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -136,6 +147,13 @@ "Example: RemoveQueueMember(techsupport|SIP/3000)\n" ""; +/* Persistent Members astdb family */ +static const char *pm_family = "/Queue/PersistentMembers"; +/* The maximum lengh of each database entry */ +static const int pm_value_len = 4096; +/* queues.conf [general] option */ +static int queue_persistent_members = 1; + /* 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,52 @@ 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_value_len]; + char tmp_value[128]; + + value[0] = 0; + + if (!pm_queue) + goto EXIT; + + if (pm_queue && (cur_member = pm_queue->members)) { + + while (cur_member) { + + if (!cur_member->dynamic) + continue; + + if (snprintf(tmp_value, sizeof(tmp_value), "%s/%s;%d;", cur_member->tech, cur_member->loc, cur_member->penalty) < 0) { + ast_log(LOG_ERROR, "Could not create persistent member string: %s\n", strerror(errno)); + goto EXIT; + } + + strncat(value, tmp_value, sizeof(value)); + + cur_member = cur_member->next; + } + + if (ast_db_put(pm_family, pm_queue->name, value)) + goto EXIT; + + } + +EXIT: + /* Delete the entry if the queue is empty or there is an error */ + if (pm_queue && !pm_queue->members) + ast_db_del(pm_family, pm_queue->name); + + +} + static int remove_from_queue(char *queuename, char *interface) { struct ast_call_queue *q; @@ -1570,6 +1634,10 @@ "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 +1678,10 @@ "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 +1698,146 @@ return res; } +/* Retrieves next token from the queue members data + * created by dump_queue_members() */ +static char * pm_get_next_tok(char **buffer) +{ + char *ptr; + char *token = NULL; + + if (!buffer || !*buffer) + return NULL; + + ptr = *buffer; + + while (*ptr != ';' && *ptr != 0 && *ptr != '\n') + ptr++; + + if (*ptr == '\n') { + *buffer = *buffer + 1; + return NULL; + } + + if (*ptr == 0) + return NULL; + + if (!(token = strndup(*buffer, ptr-(*buffer)))) + ast_log(LOG_ERROR, "Out of memory\n"); + + *buffer = ptr+1; + + return token; +} + +/* 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 *queue_data = NULL; + char *cur_pm_ptr = NULL; + char *pm_queue_name = NULL; + char *pm_interface = NULL; + char *pm_penalty_tok = NULL; + int pm_penalty = 0; + struct ast_db_entry *pm_db_tree = NULL; + int pm_family_len = 0; + struct ast_call_queue *cur_queue = NULL; + + 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 (!(queue_data = malloc(sizeof(char) * (pm_value_len)))) { + ast_log(LOG_ERROR, "Out of memory\n"); + goto EXIT; + } + + if (ast_db_get(pm_family, pm_queue_name, queue_data, pm_value_len)) { + goto EXIT; + } + + /* Parse each ;; from the value of the + * queuename key and add it to the respective queue */ + cur_pm_ptr = queue_data; + while ((pm_interface = pm_get_next_tok(&cur_pm_ptr))) { + if (!(pm_penalty_tok = pm_get_next_tok(&cur_pm_ptr))) { + ast_log(LOG_ERROR, "Error parsing corrupted Queue DB string for '%s'\n", pm_queue_name); + goto EXIT; + } + pm_penalty = strtol(pm_penalty_tok, NULL, 10); + if (errno == ERANGE) { + ast_log(LOG_ERROR, "Error converting penalty: %s: Out of range.\n", pm_penalty_tok); + goto EXIT; + } + + 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"); + goto EXIT; + } + + free(pm_interface); + pm_interface = NULL; + free(pm_penalty_tok); + pm_penalty_tok = NULL; + } + + pm_db_tree = pm_db_tree->next; + } + + ast_log(LOG_NOTICE, "Queue members sucessfully reloaded from database.\n"); + +EXIT: + ast_mutex_unlock(&qlock); + if (queue_data) { + free(queue_data); + queue_data = NULL; + } + + if (pm_interface) { + free(pm_interface); + pm_interface = NULL; + } + + if (pm_penalty_tok) { + free(pm_penalty_tok); + pm_penalty_tok = NULL; + } + + 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 +2206,8 @@ 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 +2392,11 @@ queues = q; } } + } else { + /* Initialize global settings */ + queue_persistent_members = 1; + if ((general_val = ast_variable_retrieve(cfg, "general", "persistent_members"))) + queue_persistent_members = ast_true(general_val); } cat = ast_category_browse(cfg, cat); } @@ -2728,6 +2947,10 @@ ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ; } reload_queues(); + + if (queue_persistent_members) + reload_queue_members(); + return res; }