diff --git a/include/mod_asterisk_ami.h b/include/mod_asterisk_ami.h index 2a2543e..944c56d 100644 --- a/include/mod_asterisk_ami.h +++ b/include/mod_asterisk_ami.h @@ -17,6 +17,13 @@ * \author Naveen Albert */ +/*! + * \brief Get the global AMI session + * \return NULL if no AMI session active + * \return ami session + */ +struct ami_session *bbs_ami_session(void); + int __bbs_ami_callback_register(int (*callback)(struct ami_event *event, const char *eventname), void *mod); /*! diff --git a/modules/mod_asterisk_ami.c b/modules/mod_asterisk_ami.c index fb3196e..b15ee57 100644 --- a/modules/mod_asterisk_ami.c +++ b/modules/mod_asterisk_ami.c @@ -26,6 +26,13 @@ #include +#define MIN_VERSION_REQUIRED SEMVER_VERSION(0,2,0) +#if !defined(CAMI_VERSION_MAJOR) +#error "libcami version too old" +#elif SEMVER_VERSION(CAMI_VERSION_MAJOR, CAMI_VERSION_MINOR, CAMI_VERSION_PATCH) < MIN_VERSION_REQUIRED +#pragma message "libcami version " XSTR(CAMI_VERSION_MAJOR) "." XSTR(CAMI_VERSION_MINOR) "." XSTR(CAMI_VERSION_PATCH) " too old" +#endif + #include "include/module.h" #include "include/config.h" #include "include/alertpipe.h" @@ -35,6 +42,7 @@ #include "include/mod_asterisk_ami.h" static int asterisk_up = 0; +static struct ami_session *ami_session = NULL; struct ami_callback { int (*callback)(struct ami_event *event, const char *eventname); @@ -44,6 +52,11 @@ struct ami_callback { static RWLIST_HEAD_STATIC(callbacks, ami_callback); +struct ami_session *bbs_ami_session(void) +{ + return ami_session; +} + static void set_ami_status(int up) { asterisk_up = up; @@ -51,11 +64,13 @@ static void set_ami_status(int up) } /*! \brief Callback function executing asynchronously when new events are available */ -static void ami_callback(struct ami_event *event) +static void ami_callback(struct ami_session *ami, struct ami_event *event) { struct ami_callback *cb; const char *eventname; + UNUSED(ami); + eventname = ami_keyvalue(event, "Event"); bbs_assert_exists(eventname); @@ -138,7 +153,7 @@ static int cli_ami_loglevel(struct bbs_cli_args *a) bbs_dprintf(a->fdout, "Invalid log level: %d\n", newlevel); return -1; } - ami_set_debug_level(newlevel); + ami_set_debug_level(ami_session, newlevel); return 0; } @@ -158,10 +173,12 @@ static int load_config(int open_logfile); static int unloading = 0; static int reconnecting = 0; -static void ami_disconnect_callback(void) +static void ami_disconnect_callback(struct ami_session *ami) { int sleep_ms = 500; + UNUSED(ami); + set_ami_status(0); bbs_warning("Asterisk Manager Interface connection lost\n"); @@ -213,6 +230,7 @@ static int load_config(int open_logfile) char username[64]; char password[92]; char logfile[512]; + unsigned int loglevel = 0; cfg = bbs_config_load("mod_asterisk_ami.conf", 1); if (!cfg) { @@ -226,27 +244,30 @@ static int load_config(int open_logfile) if (open_logfile && !bbs_config_val_set_str(cfg, "logging", "logfile", logfile, sizeof(logfile))) { ami_log_fd = open(logfile, O_CREAT | O_APPEND); if (ami_log_fd != -1) { - unsigned int loglevel; bbs_config_val_set_uint(cfg, "logging", "loglevel", &loglevel); if (loglevel > 10) { bbs_warning("Maximum AMI debug level is 10\n"); } - ami_set_debug_level((int) loglevel); } else { bbs_error("Failed to open %s for AMI logging: %s\n", logfile, strerror(errno)); } } if (!res) { - if (ami_connect(hostname, 0, ami_callback, ami_disconnect_callback)) { + ami_session = ami_connect(hostname, 0, ami_callback, ami_disconnect_callback); + if (!ami_session) { bbs_error("AMI connection failed to %s\n", hostname); res = -1; - } else if (ami_action_login(username, password)) { + goto cleanup; + } + ami_set_debug_level(ami_session, (int) loglevel); + if (ami_action_login(ami_session, username, password)) { bbs_error("AMI login failed for user %s@%s\n", username, hostname); res = -1; } } +cleanup: /* Fully purge the password from memory */ bbs_memzero(password, strlen(password)); bbs_config_free(cfg); @@ -283,7 +304,8 @@ static int unload_module(void) } while (reconnecting); bbs_cli_unregister_multiple(cli_commands_ami); - ami_disconnect(); + ami_disconnect(ami_session); + ami_destroy(ami_session); close_if(ami_log_fd); bbs_alertpipe_close(ami_alert_pipe); return 0; diff --git a/modules/mod_asterisk_queues.c b/modules/mod_asterisk_queues.c index 33b06e8..efdc8e0 100644 --- a/modules/mod_asterisk_queues.c +++ b/modules/mod_asterisk_queues.c @@ -259,7 +259,7 @@ static int update_queue_stats(void) } /* If only one queue needs to be refreshed, then just ask for that one by name. */ - resp = ami_action("QueueStatus", stale_queues == 1 ? lastq->name : ""); + resp = ami_action(bbs_ami_session(), "QueueStatus", stale_queues == 1 ? lastq->name : ""); if (!resp || !resp->success) { RWLIST_UNLOCK(&queues); @@ -376,7 +376,7 @@ static void prune_dead_calls(int querydead) if (call->dead) { continue; } - val = ami_action_getvar("queueuniq", call->channel); + val = ami_action_getvar(bbs_ami_session(), "queueuniq", call->channel); if (!val) { call->dead = 1; bbs_debug(3, "Queue call %d is now dead\n", call->id); @@ -537,12 +537,12 @@ static int ami_callback(struct ami_event *e, const char *eventname) bbs_error("Missing mandatory fields\n"); return -1; } - queueid = ami_action_getvar(queue_id_var, channel); + queueid = ami_action_getvar(bbs_ami_session(), queue_id_var, channel); if (strlen_zero(queueid)) { return -1; } - ani2 = ami_action_getvar("CALLERID(ani2)", channel); - dnis = ami_action_getvar("CALLERID(DNID)", channel); + ani2 = ami_action_getvar(bbs_ami_session(), "CALLERID(ani2)", channel); + dnis = ami_action_getvar(bbs_ami_session(), "CALLERID(DNID)", channel); new_call(queue, atoi(queueid), atoi(S_IF(ani2)), channel, callerid, callername, dnis); free_if(queueid); free_if(ani2); @@ -610,7 +610,7 @@ static int ami_callback(struct ami_event *e, const char *eventname) static int update_member_stats(struct agent *agent) { int i; - struct ami_response *resp = ami_action("QueueStatus", "Member:%d", agent->id); + struct ami_response *resp = ami_action(bbs_ami_session(), "QueueStatus", "Member:%d", agent->id); /* We still need to initialize the agent-specific stats for all queues. * This response will be smaller (maybe much smaller) than asking for everything. */