Index: include/asterisk/cdr.h =================================================================== --- include/asterisk/cdr.h (revision 82392) +++ include/asterisk/cdr.h (working copy) @@ -114,6 +114,7 @@ int ast_cdr_copy_vars(struct ast_cdr *to_cdr, struct ast_cdr *from_cdr); typedef int (*ast_cdrbe)(struct ast_cdr *cdr, void *data); +typedef void (*ast_cdrbe_cleanup)(void *data); /*! \brief Return TRUE if CDR subsystem is enabled */ int check_cdr_enabled(void); @@ -169,19 +170,28 @@ * \param name name associated with the particular CDR handler * \param desc description of the CDR handler * \param be function pointer to a CDR handler + * \param cleanup function pointer to a CDR sink cleanup handler * Used to register a Call Detail Record handler. * \retval 0 on success. * \retval -1 on error */ -int ast_cdr_register(const char *name, const char *name_detail, const char *desc, ast_cdrbe be, void *be_data); +int ast_cdr_register(const char *name, const char *name_detail, const char *desc, ast_cdrbe be, ast_cdrbe_cleanup cleanup, void *be_data); /*! * \brief Unregister a CDR handling engine * \param name name of CDR handler to unregister + * \param name_detail name of CDR handler sink to unregister * Unregisters a CDR by it's name */ void ast_cdr_unregister(const char *name, const char *name_detail); +/*! + * \brief Unregister all sinks for a specific CDR handling engine + * \param name name of CDR handler to unregister + * Unregisters all CDR engine sinks by handler name + */ +void ast_cdr_unregister_all(const char *name); + /*! * \brief Start a call * \param cdr the cdr you wish to associate with the call Index: main/cdr.c =================================================================== --- main/cdr.c (revision 82392) +++ main/cdr.c (working copy) @@ -67,6 +67,7 @@ char name_detail[20]; char desc[80]; ast_cdrbe be; + ast_cdrbe_cleanup cleanup; void *be_data; int cancel_thread; int waiting_cdrs; @@ -96,7 +97,7 @@ /*! Register a CDR driver. Each registered CDR driver generates a CDR \return 0 on success, -1 on failure */ -int ast_cdr_register(const char *name, const char *name_detail, const char *desc, ast_cdrbe be, void *be_data) +int ast_cdr_register(const char *name, const char *name_detail, const char *desc, ast_cdrbe be, ast_cdrbe_cleanup cleanup, void *be_data) { struct ast_cdr_beitem *i = NULL; @@ -121,6 +122,7 @@ return -1; i->be = be; + i->cleanup = cleanup; i->cancel_thread = 0; i->waiting_cdrs = 0; ast_copy_string(i->name, name, sizeof(i->name)); @@ -159,9 +161,39 @@ ast_cond_destroy(&i->cdr_retry_cond); AST_RWLIST_REMOVE_CURRENT(&be_list, list); if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Unregistered '%s - %s' CDR backend\n", name, name_detail); + ast_verbose(VERBOSE_PREFIX_2 "Unregistered '%s - %s' CDR backend\n", i->name, i->name_detail); + if (i->cleanup) + i->cleanup(i->be_data); + ast_free(i); + break; + } + } + AST_RWLIST_TRAVERSE_SAFE_END; + AST_RWLIST_UNLOCK(&be_list); +} + +/*! unregister all CDR drivers using the specifed name */ +void ast_cdr_unregister_all(const char *name) +{ + struct ast_cdr_beitem *i = NULL; + + AST_RWLIST_WRLOCK(&be_list); + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&be_list, i, list) { + if (!strcasecmp(name, i->name)) { + i->cancel_thread = 1; + /* signal the thread so it can exit */ + ast_cond_signal(&i->cdr_pending_cond); + /* wait for thread to exit so we can clean up */ + pthread_join(i->cdr_thread, NULL); + i->cdr_thread = AST_PTHREADT_NULL; + ast_cond_destroy(&i->cdr_pending_cond); + ast_cond_destroy(&i->cdr_retry_cond); + AST_RWLIST_REMOVE_CURRENT(&be_list, list); + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "Unregistered '%s - %s' CDR backend\n", i->name, i->name_detail); + if (i->cleanup) + i->cleanup(i->be_data); ast_free(i); - break; } } AST_RWLIST_TRAVERSE_SAFE_END; @@ -870,7 +902,11 @@ ast_cli(fd, "CDR safe shut down: %s\n", safeshutdown ? "enabled" : "disabled"); AST_RWLIST_RDLOCK(&be_list); AST_RWLIST_TRAVERSE(&be_list, beitem, list) { - ast_cli(fd, "CDR registered backend: %s\n", beitem->name); + if (ast_strlen_zero(beitem->name_detail)) { + ast_cli(fd, "CDR registered backend: %s\n", beitem->name); + } else { + ast_cli(fd, "CDR registered backend: %s (%s)\n", beitem->name, beitem->name_detail); + } } AST_RWLIST_UNLOCK(&be_list); } Index: cdr/cdr_csv.c =================================================================== --- cdr/cdr_csv.c (revision 82392) +++ cdr/cdr_csv.c (working copy) @@ -314,7 +314,7 @@ { if (mf) fclose(mf); - ast_cdr_unregister(name, NULL); + ast_cdr_unregister_all(name); return 0; } @@ -325,7 +325,7 @@ if(!load_config(0)) return AST_MODULE_LOAD_DECLINE; - res = ast_cdr_register(name, NULL, ast_module_info->description, csv_log, NULL); + res = ast_cdr_register(name, NULL, ast_module_info->description, csv_log, NULL, NULL); if (res) { ast_log(LOG_ERROR, "Unable to register CSV CDR handling\n"); if (mf) Index: cdr/cdr_adaptive_odbc.c =================================================================== --- cdr/cdr_adaptive_odbc.c (revision 82492) +++ cdr/cdr_adaptive_odbc.c (working copy) @@ -53,7 +53,7 @@ #include "asterisk/module.h" #include "asterisk/logger.h" -#define CONFIG "cdr_adaptive_odbc.conf" +#define CONFIG "cdr_adaptive_odbc.conf" static char *name = "Adaptive ODBC"; /* Optimization to reduce number of memory allocations */ @@ -71,21 +71,30 @@ AST_LIST_ENTRY(columns) list; }; -struct tables { +struct table { char *connection; char *table; AST_LIST_HEAD_NOLOCK(odbc_columns, columns) columns; - AST_RWLIST_ENTRY(tables) list; }; -static AST_RWLIST_HEAD_STATIC(odbc_tables, tables); +static int odbc_log(struct ast_cdr *, void *); +static void free_table(void *data) +{ + struct columns *entry; + struct table *table = (struct table *) data; + while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) { + ast_free(entry); + } + ast_free(table); +} + static int load_config(void) { struct ast_config *cfg; struct ast_variable *var; const char *tmp, *catg; - struct tables *tableptr; + struct table *tableptr; struct columns *entry; struct odbc_obj *obj; char columnname[80]; @@ -214,27 +223,19 @@ SQLFreeHandle(SQL_HANDLE_STMT, stmt); ast_odbc_release_obj(obj); - if (AST_LIST_FIRST(&(tableptr->columns))) - AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list); - else + if (AST_LIST_FIRST(&(tableptr->columns))) { + res = ast_cdr_register(name, catg, ast_module_info->description, odbc_log, free_table, tableptr); + if (res) { + ast_log(LOG_ERROR, "Unable to register Adaptive ODBC handler sink \"%s\"\n", catg); + if (tableptr) + free_table(tableptr); + } + } else ast_free(tableptr); } return res; } -static int free_config(void) -{ - struct tables *table; - struct columns *entry; - while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) { - while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) { - ast_free(entry); - } - ast_free(table); - } - return 0; -} - static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data) { int res, i; @@ -280,8 +281,7 @@ ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR '%s:%s' failed.\n", tableptr->connection, tableptr->table); \ ast_free(sql); \ ast_free(sql2); \ - AST_RWLIST_UNLOCK(&odbc_tables); \ - return -1; \ + return AST_CDR_POST_FATAL; \ } \ } \ } while (0) @@ -296,15 +296,14 @@ ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR '%s:%s' failed.\n", tableptr->connection, tableptr->table); \ ast_free(sql); \ ast_free(sql2); \ - AST_RWLIST_UNLOCK(&odbc_tables); \ - return -1; \ + return AST_CDR_POST_FATAL; \ } \ } \ } while (0) static int odbc_log(struct ast_cdr *cdr, void *data) { - struct tables *tableptr; + struct table *tableptr; struct columns *entry; struct odbc_obj *obj; int lensql, lensql2, sizesql = maxsize, sizesql2 = maxsize2, newsize; @@ -313,13 +312,10 @@ char colbuf[1024], *colptr; SQLHSTMT stmt = NULL; SQLLEN rows = 0; + int res = AST_CDR_POST_OK; - if (AST_RWLIST_RDLOCK(&odbc_tables)) { - ast_log(LOG_ERROR, "Unable to lock table list. Insert CDR(s) failed.\n"); - return -1; - } + tableptr = (struct table *) data; - AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) { lensql = snprintf(sql, sizesql, "INSERT INTO %s (", tableptr->table); lensql2 = snprintf(sql2, sizesql2, " VALUES ("); @@ -558,13 +554,14 @@ } if (rows == 0) { ast_log(LOG_WARNING, "cdr_adaptive_odbc: Insert failed on '%s:%s'. CDR failed: %s\n", tableptr->connection, tableptr->table, sql); + /* We have to assume SQL error here I think, so don't retry. */ + res = AST_CDR_POST_FATAL; } ast_odbc_release_obj(obj); } else { ast_log(LOG_WARNING, "cdr_adaptive_odbc: Unable to retrieve database handle for '%s:%s'. CDR failed: %s\n", tableptr->connection, tableptr->table, sql); + res = AST_CDR_POST_RETRY; } - } - AST_RWLIST_UNLOCK(&odbc_tables); /* Next time, just allocate buffers that are that big to start with. */ if (sizesql > maxsize) @@ -574,47 +571,30 @@ ast_free(sql); ast_free(sql2); - return 0; + + return res; } static int unload_module(void) { - ast_cdr_unregister(name, NULL); - usleep(1); - if (AST_RWLIST_WRLOCK(&odbc_tables)) { - ast_cdr_register(name, NULL, ast_module_info->description, odbc_log, NULL); - ast_log(LOG_ERROR, "Unable to lock column list. Unload failed.\n"); - return -1; - } + ast_cdr_unregister_all(name); - free_config(); - AST_RWLIST_UNLOCK(&odbc_tables); return 0; } static int load_module(void) { - if (AST_RWLIST_WRLOCK(&odbc_tables)) { - ast_log(LOG_ERROR, "Unable to lock column list. Load failed.\n"); - return 0; - } - load_config(); - AST_RWLIST_UNLOCK(&odbc_tables); - ast_cdr_register(name, NULL, ast_module_info->description, odbc_log, NULL); + return 0; } static int reload(void) { - if (AST_RWLIST_WRLOCK(&odbc_tables)) { - ast_log(LOG_ERROR, "Unable to lock column list. Reload failed.\n"); - return -1; - } + ast_cdr_unregister_all(name); - free_config(); load_config(); - AST_RWLIST_UNLOCK(&odbc_tables); + return 0; }