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 23 Nov 2004 22:53:18 -0000 @@ -136,6 +136,8 @@ "Example: RemoveQueueMember(techsupport|SIP/3000)\n" ""; +static const char *queue_members_file = "queue_members.db"; + /* 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. */ @@ -242,6 +244,11 @@ static struct ast_call_queue *queues = NULL; AST_MUTEX_DEFINE_STATIC(qlock); +AST_MUTEX_DEFINE_STATIC(qm_db_lock); + +/* [general] variables */ +static int queue_persistent_members = 1; + static char *int2strat(int strategy) { int x; @@ -1542,6 +1549,68 @@ return( cur ) ; } +/* Dump all members in each specific queue to file so that we can readd them + * back after asterisk has restarted. + * + * File format: + * + * ;;;[;;]...\n + * ;;;[;;]...\n + * ... + * + */ +static void dump_queue_members(void) +{ + struct member *cur_member = NULL; + static struct ast_call_queue *cur_queue = NULL; + char filename[256]; + FILE *qm_db = NULL; + int printed = 0; + + snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_VAR_DIR, queue_members_file); + + ast_mutex_lock(&qm_db_lock); + + if (!(qm_db = fopen(filename, "w"))) { + ast_log(LOG_ERROR, "Unable to open queue member DB file for writing: %s: %s\n", filename, strerror(errno)); + goto EXIT; + } + + ast_mutex_lock(&qlock); + cur_queue = queues; + while (cur_queue) { + ast_mutex_lock(&cur_queue->lock); + if ((cur_member = cur_queue->members)) { + printed = 0; + while (cur_member && cur_member->dynamic) { + if (!printed) { + fprintf(qm_db, "%s;", cur_queue->name); + printed = 1; + } + fprintf(qm_db,"%s/%s;%d;", cur_member->tech, cur_member->loc, cur_member->penalty); + cur_member = cur_member->next; + } + if (printed) + fprintf(qm_db, "\n"); + } + ast_mutex_unlock(&cur_queue->lock); + cur_queue = cur_queue->next; + } + ast_mutex_unlock(&qlock); + + if (fclose(qm_db) == EOF) + ast_log(LOG_ERROR, "Unable to close file stream: %s: %s\n", filename, strerror(errno)); + + if (chmod(filename, 0600) == -1) { + ast_log(LOG_ERROR, "Unable to change permissions to '0600' on file: %s: %s\n", filename, strerror(errno)); + goto EXIT; + } + +EXIT: + ast_mutex_unlock(&qm_db_lock); + +} + static int remove_from_queue(char *queuename, char *interface) { struct ast_call_queue *q; @@ -1580,6 +1649,10 @@ ast_mutex_unlock(&q->lock); } ast_mutex_unlock(&qlock); + + if (queue_persistent_members && res == RES_OKAY) + dump_queue_members(); + return res; } @@ -1623,9 +1696,170 @@ ast_mutex_unlock(&q->lock); } ast_mutex_unlock(&qlock); + + if (queue_persistent_members && res == RES_OKAY) + dump_queue_members(); + return res; } +/* Retrieves next token from the queue members DB file + * created by dump_queue_members() */ +static char * qm_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"); + + *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 filename[256]; + int fd = -1; + char *queue_data = NULL; + struct stat qm_stat; + int bytes = -1; + int parse_data = 0; + char *cur_qm_ptr = NULL; + char *qm_queue_name = NULL; + char *qm_interface = NULL; + char *qm_penalty_tok = NULL; + int qm_penalty = 0; + + snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_VAR_DIR, queue_members_file); + + ast_mutex_lock(&qm_db_lock); + + if (access(filename, R_OK) == -1) { + if (errno != ENOENT) + ast_log(LOG_ERROR, "Unable to access file for reading: %s: %s\n", filename, strerror(errno)); + else + ast_log(LOG_NOTICE, "Reload Queue Members: No members to reload."); + + goto EXIT; + } + + if ((fd = open(filename, O_RDONLY)) == -1) { + ast_log(LOG_ERROR, "Unable to open file for reading: %s: %s\n", filename, strerror(errno)); + goto EXIT; + } + + if (fstat(fd, &qm_stat) == -1) { + ast_log(LOG_ERROR, "Unable to stat file: %s: %s\n", filename, strerror(errno)); + goto EXIT; + } + + if (!(queue_data = malloc(sizeof(char) * (qm_stat.st_size+1)))) { + ast_log(LOG_ERROR, "Out of memory!"); + goto EXIT; + } + + if ((bytes = read(fd, queue_data, qm_stat.st_size)) == -1) { + ast_log(LOG_ERROR, "Unable to read from file: %s: %s\n", filename, strerror(errno)); + goto EXIT; + } + + if (bytes != qm_stat.st_size) { + ast_log(LOG_ERROR, "Only read '%d' of '%d' bytes from '%s'.\n", bytes, (int)qm_stat.st_size, filename); + goto EXIT; + } + + parse_data = 1; + +EXIT: + + if (fd != -1) { + if (close(fd) == -1) + ast_log(LOG_ERROR, "Unable to close file descriptor: %s: %s\n", filename, strerror(errno)); + fd = -1; + } + + ast_mutex_unlock(&qm_db_lock); + + /* If parse_data == 0, then an error occured so we ignore parsing */ + if (!parse_data || !queue_data) + goto ERROR; + + cur_qm_ptr = queue_data; + while ((qm_queue_name = qm_get_next_tok(&cur_qm_ptr))) { + + while ((qm_interface = qm_get_next_tok(&cur_qm_ptr))) { + if (!(qm_penalty_tok = qm_get_next_tok(&cur_qm_ptr))) { + ast_log(LOG_ERROR, "Error parsing corrupted Queue Member DB: %s\n", filename); + goto ERROR; + } + qm_penalty = strtol(qm_penalty_tok, NULL, 10); + if (errno == ERANGE) { + ast_log(LOG_ERROR, "Error converting penalty: %s: Out of range.\n", qm_penalty_tok); + goto ERROR; + } + + if (option_debug) + ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Penalty: %d\n", qm_queue_name, qm_interface, qm_penalty); + + if (add_to_queue(qm_queue_name, qm_interface, qm_penalty) == RES_OUTOFMEMORY) { + ast_log(LOG_ERROR, "Out of Memory.\n"); + goto ERROR; + } + + free(qm_interface); + qm_interface = NULL; + free(qm_penalty_tok); + qm_penalty_tok = NULL; + } + free(qm_queue_name); + qm_queue_name = NULL; + } + + ast_log(LOG_NOTICE, "Queue members sucessfully reloaded from database.\n"); + +ERROR: + if (queue_data) { + free(queue_data); + queue_data = NULL; + } + + if (qm_queue_name) { + free(qm_queue_name); + qm_queue_name = NULL; + } + + if (qm_interface) { + free(qm_interface); + qm_interface = NULL; + } + + if (qm_penalty_tok) { + free(qm_penalty_tok); + qm_penalty_tok = NULL; + } + +} + static int rqm_exec(struct ast_channel *chan, void *data) { int res=-1; @@ -1994,6 +2228,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,7 +2414,14 @@ 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); } ast_destroy(cfg); @@ -2728,6 +2971,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; }