Index: channels/chan_agent.c =================================================================== --- channels/chan_agent.c (revision 381085) +++ channels/chan_agent.c (working copy) @@ -2062,6 +2062,7 @@ if (!res) { long logintime; char agent[AST_MAX_AGENT]; + struct ast_event *event; snprintf(agent, sizeof(agent), "Agent/%s", p->agent); @@ -2095,7 +2096,13 @@ p->agent, ast_channel_name(chan), ast_channel_uniqueid(chan)); if (update_cdr && ast_channel_cdr(chan)) snprintf(ast_channel_cdr(chan)->channel, sizeof(ast_channel_cdr(chan)->channel), "%s", agent); - ast_queue_log("NONE", ast_channel_uniqueid(chan), agent, "AGENTLOGIN", "%s", ast_channel_name(chan)); + event = ast_event_new(AST_EVENT_Q_AGENT_LOGON, AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, ast_channel_uniqueid(chan), + AST_EVENT_IE_CHANNAME, AST_EVENT_IE_PLTYPE_STR, ast_channel_name(chan), + AST_EVENT_IE_AGENTNAME, AST_EVENT_IE_PLTYPE_STR, p->agent, + AST_EVENT_IE_END); + if (event && ast_event_queue(event)) { + ast_event_destroy(event); + } ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", p->agent, ast_getformatname(ast_channel_readformat(chan)), ast_getformatname(ast_channel_writeformat(chan))); @@ -2192,7 +2199,14 @@ "Logintime: %ld\r\n" "Uniqueid: %s\r\n", p->agent, logintime, ast_channel_uniqueid(chan)); - ast_queue_log("NONE", ast_channel_uniqueid(chan), agent, "AGENTLOGOFF", "%s|%ld", ast_channel_name(chan), logintime); + event = ast_event_new(AST_EVENT_Q_AGENT_LOGON, AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, ast_channel_uniqueid(chan), + AST_EVENT_IE_CHANNAME, AST_EVENT_IE_PLTYPE_STR, ast_channel_name(chan), + AST_EVENT_IE_AGENTNAME, AST_EVENT_IE_PLTYPE_STR, p->agent, + AST_EVENT_IE_LOGTIME, AST_EVENT_IE_PLTYPE_UINT, logintime, + AST_EVENT_IE_END); + if (event && ast_event_queue(event)) { + ast_event_destroy(event); + } ast_verb(2, "Agent '%s' logged out\n", p->agent); /* If there is no owner, go ahead and kill it now */ Index: apps/app_queue.c =================================================================== --- apps/app_queue.c (revision 381085) +++ apps/app_queue.c (working copy) @@ -98,6 +98,7 @@ #include "asterisk/stringfields.h" #include "asterisk/event.h" #include "asterisk/astobj2.h" +#include "asterisk/hashtab.h" #include "asterisk/strings.h" #include "asterisk/global_datastores.h" #include "asterisk/taskprocessor.h" @@ -1039,6 +1040,18 @@ /*! \brief Subscription to device state change events */ static struct ast_event_sub *device_state_sub; +/*! \brief Subscription to device state change events */ +static struct ast_event_sub *moh_start_sub; + +/*! \brief Subscription to device state change events */ +static struct ast_event_sub *moh_stop_sub; + +/*! \brief Subscription to device state change events */ +static struct ast_event_sub *agent_logon_sub; + +/*! \brief Subscription to device state change events */ +static struct ast_event_sub *agent_logoff_sub; + /*! \brief queues.conf [general] option */ static int update_cdr = 0; @@ -1081,6 +1094,137 @@ TIMEOUT_PRIORITY_CONF, }; +struct connected_member_info { + char *name; + char *queuename; + char *membername; +}; + +static struct ast_hashtab *connected_members; /* to track connected members for the logging of OHHOLD events to the queue log */ + +static int connected_member_compare(const void *a, const void *b) +{ + const struct connected_member_info *aa = a; + const struct connected_member_info *ab = b; + if (!aa || !ab) { + return 1; + } + return strcmp(aa->name, ab->name); +} + +static unsigned int connected_member_hash(const void *obj) +{ + const struct connected_member_info *a = obj; + return ast_hashtab_hash_string(a->name); +} + +static void connected_member_destroy(void *obj) +{ + struct connected_member_info *info = obj; + if (info) { + if (info->name) { + ast_free(info->name); + } + if (info->queuename) { + ast_free(info->queuename); + } + if (info->membername) { + ast_free(info->membername); + } + ast_free(info); + } +} + +static void connected_member_add(char *name, char *queuename, char *membername) +{ + struct connected_member_info *cmi = ast_calloc(sizeof(struct connected_member_info),1); + cmi->name = ast_strdup(name); + cmi->queuename = ast_strdup(queuename); + cmi->membername = ast_strdup(membername); + if (ast_hashtab_insert_safe(connected_members, cmi) == 0) { + ast_free(cmi->name); + ast_free(cmi->queuename); + ast_free(cmi->membername); + ast_free(cmi); + } +} + +static void connected_member_remove(char *name) +{ + struct connected_member_info temp; + temp.name = name; + ast_hashtab_remove_object_via_lookup(connected_members, &temp); +} + +static void moh_start(const struct ast_event *event, void *unused) +{ + struct connected_member_info *found, temp; + const char *mohclass_name; + const char *chanuniqueid; + + mohclass_name = ast_event_get_ie_str(event, AST_EVENT_IE_MOHCLASS); + chanuniqueid = ast_event_get_ie_str(event, AST_EVENT_IE_CEL_UNIQUEID); + /* DEBUG! */ +#ifdef NOTNOW_FORDEBUG + { + int bbsize, resize_count, numobjs, numbuckets; + ast_hashtab_get_stats(connected_members, &bbsize, &resize_count, &numobjs, &numbuckets); + ast_log(LOG_NOTICE, "Chanuid: %s, Hash stats: biggest bucket size: %d, resize_count=%d, numObjs=%d, numBuckets=%d\n", + chanuniqueid, bbsize, resize_count, numobjs, numbuckets); + } +#endif + /* END DEBUG */ + /* first of all, is this channel uniqueid in the hashtab? */ + temp.name = (char *)chanuniqueid; + found = ast_hashtab_lookup(connected_members, &temp); + if (found) { + ast_queue_log(found->queuename, chanuniqueid, found->membername, "CALLERONHOLD", "%s", mohclass_name); + } +} + +static void moh_stop(const struct ast_event *event, void *unused) +{ + struct connected_member_info *found, temp; + const char *chanuniqueid; + + chanuniqueid = ast_event_get_ie_str(event, AST_EVENT_IE_CEL_UNIQUEID); + /* first of all, is this channel uniqueid in the hashtab? */ + temp.name = (char *)chanuniqueid; + found = ast_hashtab_lookup(connected_members, &temp); + if (found) { + ast_queue_log(found->queuename, chanuniqueid, found->membername, "CALLEROFFHOLD", "%s", ""); + } +} + +static void agent_login(const struct ast_event *event, void *unused) /* log all calls! */ +{ + const char *chanuniqueid; + const char *channame; + const char *agent; + + chanuniqueid = ast_event_get_ie_str(event, AST_EVENT_IE_CEL_UNIQUEID); + channame = ast_event_get_ie_str(event, AST_EVENT_IE_CHANNAME); + agent = ast_event_get_ie_str(event, AST_EVENT_IE_AGENTNAME); + + ast_queue_log("NONE", chanuniqueid, agent, "AGENTLOGIN", "%s", channame); +} + +static void agent_logoff(const struct ast_event *event, void *unused) /* log all calls! */ +{ + const char *chanuniqueid; + const char *channame; + const char *agent; + long logintime; + + chanuniqueid = ast_event_get_ie_str(event, AST_EVENT_IE_CEL_UNIQUEID); + channame = ast_event_get_ie_str(event, AST_EVENT_IE_CHANNAME); + agent = ast_event_get_ie_str(event, AST_EVENT_IE_AGENTNAME); + logintime = ast_event_get_ie_uint(event, AST_EVENT_IE_LOGTIME); + + ast_queue_log("NONE", chanuniqueid, agent, "AGENTLOGOFF", "%s|%ld", channame, logintime); +} + + /*! \brief We define a custom "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. @@ -2594,7 +2738,7 @@ ast_string_field_free_memory(q); for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) { if (q->sound_periodicannounce[i]) { - free(q->sound_periodicannounce[i]); + ast_free(q->sound_periodicannounce[i]); } } ao2_ref(q->members, -1); @@ -4927,6 +5071,8 @@ char *vars, size_t vars_len, enum agent_complete_reason rsn) { const char *reason = NULL; /* silence dumb compilers */ + + connected_member_remove((char*)ast_channel_uniqueid(qe->chan)); if (!qe->parent->eventwhencalled) { return; @@ -5813,6 +5959,8 @@ ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, ast_channel_uniqueid(peer), (long)(orig - to > 0 ? (orig - to) / 1000 : 0)); + connected_member_add((char*)ast_channel_uniqueid(qe->chan), queuename, member->membername); /* and here we add an entry to a hashtab for MusicOnHold events to get logged. */ + if (ast_channel_cdr(qe->chan)) { struct ast_cdr *cdr; struct ast_cdr *newcdr; @@ -9869,9 +10017,28 @@ res |= ast_data_unregister(NULL); - if (device_state_sub) + if (device_state_sub) { ast_event_unsubscribe(device_state_sub); + } + if (moh_start_sub) { + ast_event_unsubscribe(moh_start_sub); + } + + if (moh_stop_sub) { + ast_event_unsubscribe(moh_stop_sub); + } + + if (agent_logon_sub) { + ast_event_unsubscribe(agent_logon_sub); + } + + if (agent_logoff_sub) { + ast_event_unsubscribe(agent_logoff_sub); + } + + ast_hashtab_destroy(connected_members, connected_member_destroy); + ast_extension_state_del(0, extension_state_cb); q_iter = ao2_iterator_init(queues, 0); @@ -9904,6 +10071,8 @@ queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb); + connected_members = ast_hashtab_create(101, connected_member_compare, NULL, NULL, connected_member_hash, 1); + use_weight = 0; if (reload_handler(0, &mask, NULL)) @@ -9950,6 +10119,22 @@ res = -1; } + if (!(moh_start_sub = ast_event_subscribe(AST_EVENT_MOH_START, moh_start, "AppQueue Track On Hold", NULL, AST_EVENT_IE_END))) { + res = -1; + } + + if (!(moh_stop_sub = ast_event_subscribe(AST_EVENT_MOH_STOP, moh_stop, "AppQueue Track Off Hold", NULL, AST_EVENT_IE_END))) { + res = -1; + } + + if (!(agent_logon_sub = ast_event_subscribe(AST_EVENT_Q_AGENT_LOGON, agent_login, "AppQueue Track Agent Login", NULL, AST_EVENT_IE_END))) { + res = -1; + } + + if (!(agent_logoff_sub = ast_event_subscribe(AST_EVENT_Q_AGENT_LOGOFF, agent_logoff, "AppQueue Track Agent Logoff", NULL, AST_EVENT_IE_END))) { + res = -1; + } + ast_extension_state_add(NULL, NULL, extension_state_cb, NULL); ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL); Index: include/asterisk/event_defs.h =================================================================== --- include/asterisk/event_defs.h (revision 381085) +++ include/asterisk/event_defs.h (working copy) @@ -60,8 +60,16 @@ AST_EVENT_ACL_CHANGE = 0x0b, /*! Send out a ping for debugging distributed events */ AST_EVENT_PING = 0x0c, + /*! Music on Hold started event */ + AST_EVENT_MOH_START = 0x0d, + /*! Music on Hold stopped event */ + AST_EVENT_MOH_STOP = 0x0e, + /*! Agent logged in event */ + AST_EVENT_Q_AGENT_LOGON = 0x0f, + /*! Agent Logged out events */ + AST_EVENT_Q_AGENT_LOGOFF = 0x10, /*! Number of event types. This should be the last event type + 1 */ - AST_EVENT_TOTAL = 0x0d, + AST_EVENT_TOTAL = 0x11, }; /*! \brief Event Information Element types */ @@ -304,8 +312,33 @@ * Payload type: UINT */ AST_EVENT_IE_CACHABLE = 0x003d, + + /*! + * \brief Agent name associated with agent log out/in events + * Used by: AST_EVENT_Q_AGENT_LOGON, AST_EVENT_Q_AGENT_LOGOFF + * Payload type: STR + */ + AST_EVENT_IE_AGENTNAME = 0x003e, + /*! + * \brief MOHCLASS value + * Used by: AST_EVENT_MOH_START + * Payload type: STR + */ + AST_EVENT_IE_MOHCLASS = 0x003f, + /*! + * \brief Q Login time + * Used by: AST_EVENT_Q_AGENT_LOGOFF + * Payload type: UINT + */ + AST_EVENT_IE_LOGTIME = 0x0040, + /*! + * \brief ChanName + * Used by: AST_EVENT_Q_AGENT_LOGON, AST_EVENT_Q_AGENT_LOGOFF + * Payload type: STR + */ + AST_EVENT_IE_CHANNAME = 0x0041, /*! \brief Must be the last IE value +1 */ - AST_EVENT_IE_TOTAL = 0x003e, + AST_EVENT_IE_TOTAL = 0x0042, }; /*! Index: res/res_musiconhold.c =================================================================== --- res/res_musiconhold.c (revision 381085) +++ res/res_musiconhold.c (working copy) @@ -58,6 +58,7 @@ #include "asterisk/channel.h" #include "asterisk/pbx.h" #include "asterisk/app.h" +#include "asterisk/event.h" #include "asterisk/module.h" #include "asterisk/translate.h" #include "asterisk/say.h" @@ -1375,6 +1376,7 @@ struct ast_variable *var = NULL; int res; int realtime_possible = ast_check_realtime("musiconhold"); + struct ast_event *event; /* The following is the order of preference for which class to use: * 1) The channels explicitly set musicclass, which should *only* be @@ -1574,7 +1576,12 @@ "Class: %s\r\n", ast_channel_name(chan), ast_channel_uniqueid(chan), mohclass->name); - + event = ast_event_new(AST_EVENT_MOH_START, AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, ast_channel_uniqueid(chan), + AST_EVENT_IE_MOHCLASS, AST_EVENT_IE_PLTYPE_STR, mohclass->name, + AST_EVENT_IE_END); + if (event && ast_event_queue(event)) { + ast_event_destroy(event); + } ast_set_flag(ast_channel_flags(chan), AST_FLAG_MOH); if (mohclass->total_files) { @@ -1590,6 +1597,7 @@ static void local_ast_moh_stop(struct ast_channel *chan) { + struct ast_event *event; ast_clear_flag(ast_channel_flags(chan), AST_FLAG_MOH); ast_deactivate_generator(chan); @@ -1606,6 +1614,11 @@ "Channel: %s\r\n" "UniqueID: %s\r\n", ast_channel_name(chan), ast_channel_uniqueid(chan)); + event = ast_event_new(AST_EVENT_MOH_STOP, AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, ast_channel_uniqueid(chan), + AST_EVENT_IE_END); + if (event && ast_event_queue(event)) { + ast_event_destroy(event); + } ast_channel_unlock(chan); } @@ -1619,7 +1632,7 @@ ao2_lock(class); while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) { - free(member); + ast_free(member); } ao2_unlock(class); @@ -1680,9 +1693,9 @@ if (class->filearray) { int i; for (i = 0; i < class->total_files; i++) { - free(class->filearray[i]); + ast_free(class->filearray[i]); } - free(class->filearray); + ast_free(class->filearray); class->filearray = NULL; }