diff -ur --unidirectional-new-file asterisk-1.4.24.1/cdr/.cdr_broadcast.moduleinfo asterisk-1.4.24.1-new/cdr/.cdr_broadcast.moduleinfo --- asterisk-1.4.24.1/cdr/.cdr_broadcast.moduleinfo 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.4.24.1-new/cdr/.cdr_broadcast.moduleinfo 2009-05-12 18:12:48.000000000 +0200 @@ -0,0 +1,2 @@ + + diff -ur --unidirectional-new-file asterisk-1.4.24.1/cdr/.moduleinfo asterisk-1.4.24.1-new/cdr/.moduleinfo --- asterisk-1.4.24.1/cdr/.moduleinfo 2009-04-02 19:44:23.000000000 +0200 +++ asterisk-1.4.24.1-new/cdr/.moduleinfo 2009-05-12 18:13:09.000000000 +0200 @@ -1,4 +1,6 @@ + + diff -ur --unidirectional-new-file asterisk-1.4.24.1/cdr/cdr_broadcast.c asterisk-1.4.24.1-new/cdr/cdr_broadcast.c --- asterisk-1.4.24.1/cdr/cdr_broadcast.c 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.4.24.1-new/cdr/cdr_broadcast.c 2009-05-12 18:13:22.000000000 +0200 @@ -0,0 +1,263 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer + * + * Includes code and algorithms from the Zapata library. + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief UDP Event Broadcasting. + * + * \author Philipp Dunkel + * + * Uses cdr_broadcast.conf for configuration + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "--new--") + +#include "asterisk/manager.h" +#include "asterisk/paths.h" +#include "asterisk/module.h" +#include "asterisk/cdr.h" +#include "asterisk/config.h" +#include "asterisk/lock.h" +#include "asterisk/utils.h" +#include "asterisk/threadstorage.h" +#include "asterisk/strings.h" + +#include +#include +#include +#include +#include +#include +#include + + +#define CDR_BROADCAST_FILE "cdr_broadcast.c" +#define CDR_BROADCAST_CONFIG "cdr_broadcast.conf" +#define CDR_BROADCAST_DESC "UDP Broadcasting of CDR and Manager Events" +#define CDR_BROADCAST_LINELEN 1255 + +#define DATE_FORMAT "%Y-%m-%d %T" +#define STRPORT(str) str?htons(strtol(str,NULL,0)):0 +#define STRHOST(str) inet_addr(str?str:"127.0.0.1") +#define SENDSTRING(sock, str) send(sock.socket, str, strlen(str)+1, 0) +#define STRAPPEND(dst,src) strncat(dst, dst[0]?",\"":"\"",CDR_BROADCAST_LINELEN-strlen(dst)-1); strncat(dst,src?src:"",CDR_BROADCAST_LINELEN-strlen(dst)-1); strncat(dst,"\"", CDR_BROADCAST_LINELEN-strlen(dst)-1); +#define INTAPPEND(dst,val) snprintf(&(dst[strlen(dst)]),CDR_BROADCAST_LINELEN-strlen(dst)-1,dst[0]?",\"%d\"":"\"%d\"",(int)val); +#define DATAPPEND(dst,when) strncat(dst, dst[0]?",\"":"\"",CDR_BROADCAST_LINELEN-strlen(dst)-1); strftime(&(dst[strlen(dst)]), CDR_BROADCAST_LINELEN-strlen(dst)-1, DATE_FORMAT, &when); strncat(dst,"\"", CDR_BROADCAST_LINELEN-strlen(dst)-1); + +AST_MUTEX_DEFINE_STATIC(lock); + +static struct { + int enabled; + int multicast; + int socket; + struct sockaddr_in receiver; +} global_evt_broadcast_socket; + +static struct { + int enabled; + int multicast; + int socket; + struct sockaddr_in receiver; +} global_cdr_broadcast_socket; + +static struct manager_custom_hook global_evt_multicast_hook; + +static int evt_multicast(int category, const char* event, char * data) { + if (!global_evt_broadcast_socket.enabled) return 1; + + ast_mutex_lock(&lock); + if (SENDSTRING(global_evt_broadcast_socket, data) < 0) { + ast_log(LOG_ERROR, "%d -> %s\n", errno, strerror(errno)); + } + ast_mutex_unlock(&lock); + + return 0; +} + +static int cdr_multicast(struct ast_cdr *cdr) { + if (!global_cdr_broadcast_socket.enabled) return 1; + + struct tm tm; + char buffer[CDR_BROADCAST_LINELEN]; + buffer[0] = 0; + + STRAPPEND(buffer, cdr->accountcode); + STRAPPEND(buffer, cdr->src); + STRAPPEND(buffer, cdr->dst); + STRAPPEND(buffer, cdr->dcontext); + STRAPPEND(buffer, cdr->clid); + STRAPPEND(buffer, cdr->channel); + STRAPPEND(buffer, cdr->dstchannel); + STRAPPEND(buffer, cdr->lastapp); + STRAPPEND(buffer, cdr->lastdata); + gmtime_r(&(cdr->start.tv_sec), &tm); + DATAPPEND(buffer, tm); + gmtime_r(&(cdr->answer.tv_sec), &tm); + DATAPPEND(buffer, tm); + gmtime_r(&(cdr->end.tv_sec), &tm); + DATAPPEND(buffer, tm); + INTAPPEND(buffer, cdr->duration); + INTAPPEND(buffer, cdr->billsec); + STRAPPEND(buffer, ast_cdr_disp2str(cdr->disposition)); + STRAPPEND(buffer, ast_cdr_disp2str(cdr->amaflags)); + STRAPPEND(buffer, cdr->uniqueid); + STRAPPEND(buffer, cdr->userfield); + + ast_mutex_lock(&lock); + if (SENDSTRING(global_cdr_broadcast_socket, buffer) < 0) { + ast_log(LOG_ERROR, "%d -> %s\n", errno, strerror(errno)); + } else { + ast_log(LOG_WARNING, "%s\n", buffer); + } + ast_mutex_unlock(&lock); + + return 0; +} + +static int load_config(int reload) { + char *cat = NULL; + struct ast_config *cfg; + struct ast_variable *var; + + cfg = ast_config_load(CDR_BROADCAST_CONFIG); + + if (!cfg) { + ast_log(LOG_ERROR, "Invalid config file\n"); + return 1; + } + + ast_mutex_lock(&lock); + + while ((cat = ast_category_browse(cfg, cat))) { + if (!strcasecmp(cat,"events")) { + var = ast_variable_browse(cfg, cat); + while (var) { + if (var->value && !strcasecmp(var->name, "enabled")) { + global_evt_broadcast_socket.enabled = ((ast_true(var->value))?1:0); + } else if (var->value && !strcasecmp(var->name, "host")) { + global_evt_broadcast_socket.receiver.sin_addr.s_addr = STRHOST(var->value); + } else if (var->value && !strcasecmp(var->name, "port")) { + global_evt_broadcast_socket.receiver.sin_port = STRPORT(var->value); + } + var = var->next; + } + } else if (!strcasecmp(cat,"cdr")) { + var = ast_variable_browse(cfg, cat); + while (var) { + if (var->value && !strcasecmp(var->name, "enabled")) { + global_cdr_broadcast_socket.enabled = ((ast_true(var->value))?1:0); + } else if (var->value && !strcasecmp(var->name, "host")) { + global_cdr_broadcast_socket.receiver.sin_addr.s_addr = STRHOST(var->value); + } else if (var->value && !strcasecmp(var->name, "port")) { + global_cdr_broadcast_socket.receiver.sin_port = STRPORT(var->value); + } + var = var->next; + } + } + } + ast_mutex_unlock(&lock); + + return 0; +} + +static int unload_module(void) { + if (global_evt_broadcast_socket.enabled) { + ast_manager_unregister_hook(&global_evt_multicast_hook); + if (global_evt_broadcast_socket.socket > -1) close(global_evt_broadcast_socket.socket); + } + memset(&(global_evt_broadcast_socket.receiver), 0, sizeof(global_evt_broadcast_socket.receiver)); + global_evt_broadcast_socket.socket = -1; + global_evt_broadcast_socket.enabled = 0; + global_evt_broadcast_socket.multicast = 0; + global_evt_broadcast_socket.receiver.sin_family = AF_INET; + + if (global_cdr_broadcast_socket.enabled) { + ast_cdr_unregister(CDR_BROADCAST_FILE); + if (global_cdr_broadcast_socket.socket > -1) close(global_cdr_broadcast_socket.socket); + } + memset(&(global_cdr_broadcast_socket.receiver), 0, sizeof(global_cdr_broadcast_socket.receiver)); + global_cdr_broadcast_socket.socket = -1; + global_cdr_broadcast_socket.enabled = 0; + global_cdr_broadcast_socket.multicast = 0; + global_cdr_broadcast_socket.receiver.sin_family = AF_INET; + + return 0; +} + +static int load_module(void) { + int optval = 1; + global_evt_broadcast_socket.enabled = 0; + global_cdr_broadcast_socket.enabled = 0; + + if (unload_module()) return AST_MODULE_LOAD_DECLINE; + + if (load_config(0)) { + return AST_MODULE_LOAD_DECLINE; + } + + if (global_evt_broadcast_socket.enabled) { + global_evt_multicast_hook.file = CDR_BROADCAST_FILE; + global_evt_multicast_hook.helper = evt_multicast; + global_evt_broadcast_socket.socket = socket(PF_INET, SOCK_DGRAM, 0); + if (global_evt_broadcast_socket.socket < 0) { + ast_log(LOG_WARNING, "Error Opening Socket: %s\n", strerror(errno)); + global_evt_broadcast_socket.enabled = 0; + } else { + setsockopt(global_evt_broadcast_socket.socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval); + setsockopt(global_evt_broadcast_socket.socket, SOL_SOCKET, SO_BROADCAST, &optval, sizeof optval); + if (connect(global_evt_broadcast_socket.socket,(struct sockaddr *)&(global_evt_broadcast_socket.receiver),sizeof(struct sockaddr_in))) { + ast_log(LOG_ERROR, "%d -> %s\n", errno, strerror(errno)); + global_evt_broadcast_socket.enabled = 0; + } else { + ast_log(LOG_WARNING, "Connected to %s:%d\n", ast_inet_ntoa(global_evt_broadcast_socket.receiver.sin_addr), ntohs(global_evt_broadcast_socket.receiver.sin_port)); + ast_manager_register_hook(&global_evt_multicast_hook); + } + } + } + + if (global_cdr_broadcast_socket.enabled) { + global_cdr_broadcast_socket.socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (global_cdr_broadcast_socket.socket < 0) { + ast_log(LOG_WARNING, "Error Opening Socket: %s\n", strerror(errno)); + global_cdr_broadcast_socket.enabled = 0; + } else { + if (global_cdr_broadcast_socket.multicast) { + setsockopt(global_cdr_broadcast_socket.socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval); + setsockopt(global_cdr_broadcast_socket.socket, SOL_SOCKET, SO_BROADCAST, &optval, sizeof optval); + } + if (connect(global_cdr_broadcast_socket.socket,(struct sockaddr *)&(global_cdr_broadcast_socket.receiver),sizeof(struct sockaddr_in))) { + ast_log(LOG_ERROR, "%d -> %s\n", errno, strerror(errno)); + global_cdr_broadcast_socket.enabled = 0; + } else { + ast_cdr_register(CDR_BROADCAST_FILE, CDR_BROADCAST_DESC, cdr_multicast); + } + } + } + + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, CDR_BROADCAST_DESC, + .load = load_module, + .unload = unload_module, + .reload = load_module, +); diff -ur --unidirectional-new-file asterisk-1.4.24.1/configs/cdr_broadcast.conf.sample asterisk-1.4.24.1-new/configs/cdr_broadcast.conf.sample --- asterisk-1.4.24.1/configs/cdr_broadcast.conf.sample 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.4.24.1-new/configs/cdr_broadcast.conf.sample 2009-05-12 18:13:44.000000000 +0200 @@ -0,0 +1,16 @@ +; +; Sample CDR-Multicast Configuration +; +[events] +; Is the sending of Manager Events Enabled +enabled=off +; To which IP and Port are Manager Events sent +host=225.0.1.1 +port=1811 + +[cdr] +; Is the sending of CDR Events Enabled +enabled=off +; To which IP and Port are CDR Events sent +host=225.0.1.1 +port=1811 diff -ur --unidirectional-new-file asterisk-1.4.24.1/include/asterisk/manager.h asterisk-1.4.24.1-new/include/asterisk/manager.h --- asterisk-1.4.24.1/include/asterisk/manager.h 2009-02-16 00:37:03.000000000 +0100 +++ asterisk-1.4.24.1-new/include/asterisk/manager.h 2009-05-12 18:23:35.000000000 +0200 @@ -26,6 +26,7 @@ #include #include "asterisk/lock.h" +#include "asterisk/linkedlists.h" /*! \file @@ -81,6 +82,28 @@ struct manager_action *next; }; +/*! \brief Manager Helper Function */ +typedef int (*manager_hook_t)(int, const char *, char *); + +struct manager_custom_hook { + /*! Identifier */ + char *file; + /*! helper function */ + manager_hook_t helper; + /*! Linked list information */ + AST_RWLIST_ENTRY(manager_custom_hook) list; +}; + +/*! Add a custom hook to be called when an event is fired + \param hook struct manager_custom_hook object to add +*/ +void ast_manager_register_hook(struct manager_custom_hook *hook); + +/*! Delete a custom hook to be called when an event is fired + \param hook struct manager_custom_hook object to delete +*/ +void ast_manager_unregister_hook(struct manager_custom_hook *hook); + /* External routines may register/unregister manager callbacks this way */ #define ast_manager_register(a, b, c, d) ast_manager_register2(a, b, c, d, NULL) diff -ur --unidirectional-new-file asterisk-1.4.24.1/main/manager.c asterisk-1.4.24.1-new/main/manager.c --- asterisk-1.4.24.1/main/manager.c 2009-01-28 19:51:16.000000000 +0100 +++ asterisk-1.4.24.1-new/main/manager.c 2009-05-12 18:23:11.000000000 +0200 @@ -204,6 +204,26 @@ static struct manager_action *first_action; AST_RWLOCK_DEFINE_STATIC(actionlock); +static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook); + +/*! \brief Add a custom hook to be called when an event is fired */ +void ast_manager_register_hook(struct manager_custom_hook *hook) +{ + AST_RWLIST_WRLOCK(&manager_hooks); + AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list); + AST_RWLIST_UNLOCK(&manager_hooks); + return; +} + +/*! \brief Delete a custom hook to be called when an event is fired */ +void ast_manager_unregister_hook(struct manager_custom_hook *hook) +{ + AST_RWLIST_WRLOCK(&manager_hooks); + AST_RWLIST_REMOVE(&manager_hooks, hook, list); + AST_RWLIST_UNLOCK(&manager_hooks); + return; +} + /*! \brief Convert authority code to string with serveral options */ static char *authority_to_str(int authority, char *res, int reslen) { @@ -2509,6 +2529,7 @@ int manager_event(int category, const char *event, const char *fmt, ...) { struct mansession *s; + struct manager_custom_hook *hook; char auth[80]; va_list ap; struct timeval now; @@ -2556,6 +2577,12 @@ } AST_LIST_UNLOCK(&sessions); + AST_RWLIST_RDLOCK(&manager_hooks); + AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) { + hook->helper(category, event, buf->str); + } + AST_RWLIST_UNLOCK(&manager_hooks); + return 0; }