return empty;
}
+struct statechange {
+ int state;
+ char dev[0];
+};
+
+static void *changethread(void *data)
+{
+ struct ast_call_queue *q;
+ struct statechange *sc = data;
+ struct member *cur;
+ char *loc;
+ loc = strchr(sc->dev, '/');
+ if (loc) {
+ *loc = '\0';
+ loc++;
+ } else {
+ ast_log(LOG_WARNING, "Can't change device with no technology!\n");
+ free(sc);
+ return NULL;
+ }
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d'\n", sc->dev, loc, sc->state);
+ ast_mutex_lock(&qlock);
+ for (q = queues; q; q = q->next) {
+ ast_mutex_lock(&q->lock);
+ cur = q->members;
+ while(cur) {
+ if (!strcasecmp(sc->dev, cur->tech) && !strcmp(loc, cur->loc)) {
+ if (cur->status != sc->state) {
+ cur->status = sc->state;
+ manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
+ "Queue: %s\r\n"
+ "Location: %s/%s\r\n"
+ "Membership: %s\r\n"
+ "Penalty: %d\r\n"
+ "CallsTaken: %d\r\n"
+ "LastCall: %ld\r\n"
+ "Status: %d\r\n",
+ q->name, cur->tech, cur->loc, cur->dynamic ? "dynamic" : "static",
+ cur->penalty, cur->calls, cur->lastcall, cur->status);
+ }
+ }
+ cur = cur->next;
+ }
+ ast_mutex_unlock(&q->lock);
+ }
+ ast_mutex_unlock(&qlock);
+ ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d'\n", sc->dev, loc, sc->state);
+ free(sc);
+ return NULL;
+}
+
+static int statechange_queue(const char *dev, int state, void *ign)
+{
+ /* Avoid potential for deadlocks by spawning a new thread to handle
+ the event */
+ struct statechange *sc;
+ pthread_t t;
+ pthread_attr_t attr;
+ sc = malloc(sizeof(struct statechange) + strlen(dev) + 1);
+ if (sc) {
+ sc->state = state;
+ strcpy(sc->dev, dev);
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (ast_pthread_create(&t, &attr, changethread, sc)) {
+ ast_log(LOG_WARNING, "Failed to create update thread!\n");
+ free(sc);
+ }
+ }
+ return 0;
+}
+
static int join_queue(char *queuename, struct queue_ent *qe)
{
struct ast_call_queue *q;
if (!strcasecmp(q->name, queuename)) {
/* This is our one */
ast_mutex_lock(&q->lock);
- if ((!has_no_members(q) || q->joinempty || !q->head) && (!q->maxlen || (q->count < q->maxlen))) {
+ if ((!has_no_members(q) || q->joinempty) && (!q->maxlen || (q->count < q->maxlen))) {
/* There's space for us, put us at the right position inside
* the queue.
* Take into account the priority of the calling user */
"Penalty: %d\r\n"
"CallsTaken: %d\r\n"
"LastCall: %ld\r\n"
- "Status: %d\r\n"
- "\r\n",
+ "Status: %d\r\n",
q->name, cur->tech, cur->loc, cur->dynamic ? "dynamic" : "static",
cur->penalty, cur->calls, cur->lastcall, cur->status);
break;
return 0;
}
+static int update_dial_status(struct ast_call_queue *q, struct member *member, int status)
+{
+ if (status == AST_CAUSE_BUSY)
+ status = AST_DEVICE_BUSY;
+ else if (status == AST_CAUSE_UNREGISTERED)
+ status = AST_DEVICE_UNAVAILABLE;
+ else if (status == AST_CAUSE_NOSUCHDRIVER)
+ status = AST_DEVICE_INVALID;
+ else
+ status = AST_DEVICE_UNKNOWN;
+ return update_status(q, member, status);
+}
+
static int ring_entry(struct queue_ent *qe, struct localuser *tmp)
{
int res;
if (qe->chan->cdr)
ast_cdr_busy(qe->chan->cdr);
tmp->stillgoing = 0;
- update_status(qe->parent, tmp->member, status);
+ update_dial_status(qe->parent, tmp->member, status);
return 0;
} else if (status != tmp->oldstatus)
- update_status(qe->parent, tmp->member, status);
+ update_dial_status(qe->parent, tmp->member, status);
tmp->chan->appl = "AppQueue";
tmp->chan->data = "(Outgoing Line)";
/* Setup parameters */
o->chan = ast_request(tech, in->nativeformats, stuff, &status);
if (status != o->oldstatus)
- update_status(qe->parent, o->member, status);
+ update_dial_status(qe->parent, o->member, status);
if (!o->chan) {
ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
o->stillgoing = 0;
free(q);
} else
ast_log(LOG_WARNING, "XXX Leaking a little memory :( XXX\n");
- } else
+ } else {
+ char tmp[256];
+ cur = q->members;
+ while(cur) {
+ snprintf(tmp, sizeof(tmp), "%s/%s", cur->tech, cur->loc);
+ cur->status = ast_device_state(tmp);
+ cur = cur->next;
+ }
ql = q;
+ }
q = qn;
}
ast_mutex_unlock(&qlock);
static char *status2str(int status, char *buf, int buflen)
{
switch(status) {
- case AST_CAUSE_BUSY:
- strncpy(buf, "busy", buflen - 1);
+ case AST_DEVICE_UNKNOWN:
+ strncpy(buf, "unknown", buflen - 1);
break;
- case AST_CAUSE_CONGESTION:
- strncpy(buf, "congestion", buflen - 1);
+ case AST_DEVICE_NOT_INUSE:
+ strncpy(buf, "notinuse", buflen - 1);
break;
- case AST_CAUSE_FAILURE:
- strncpy(buf, "failure", buflen - 1);
+ case AST_DEVICE_INUSE:
+ strncpy(buf, "inuse", buflen - 1);
+ break;
+ case AST_DEVICE_BUSY:
+ strncpy(buf, "busy", buflen - 1);
break;
- case AST_CAUSE_UNREGISTERED:
- strncpy(buf, "unregistered", buflen - 1);
+ case AST_DEVICE_INVALID:
+ strncpy(buf, "invalid", buflen - 1);
break;
- case AST_CAUSE_NOSUCHDRIVER:
- strncpy(buf, "nosuchdriver", buflen - 1);
+ case AST_DEVICE_UNAVAILABLE:
+ strncpy(buf, "unavailable", buflen - 1);
break;
default:
snprintf(buf, buflen, "unknown status %d", status);
ast_manager_unregister("QueueStatus");
ast_manager_unregister("QueueAdd");
ast_manager_unregister("QueueRemove");
+ ast_devstate_del(statechange_queue, NULL);
ast_unregister_application(app_aqm);
ast_unregister_application(app_rqm);
return ast_unregister_application(app);
ast_cli_register(&cli_show_queues);
ast_cli_register(&cli_add_queue_member);
ast_cli_register(&cli_remove_queue_member);
+ ast_devstate_add(statechange_queue, NULL);
ast_manager_register( "Queues", 0, manager_queues_show, "Queues" );
ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" );
ast_manager_register( "QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue." );
snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
ast_queue_log("NONE", ast->uniqueid, agent, "AGENTCALLBACKLOGOFF", "%s|%ld|%s", p->loginchan, logintime, "Autologoff");
p->loginchan[0] = '\0';
+ ast_device_state_changed("Agent/%s", p->agent);
}
} else if (p->dead) {
ast_mutex_lock(&p->chan->lock);
ast_queue_log("NONE", chan->uniqueid, agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan);
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan);
+ ast_device_state_changed("Agent/%s", p->agent);
} else {
logintime = time(NULL) - p->loginstart;
p->loginstart = 0;
ast_queue_log("NONE", chan->uniqueid, agent, "AGENTCALLBACKLOGOFF", "%s|%ld|", last_loginchan, logintime);
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Callback Agent '%s' logged out\n", p->agent);
+ ast_device_state_changed("Agent/%s", p->agent);
}
ast_mutex_unlock(&agentlock);
if (!res)
check_availability(p, 0);
ast_mutex_unlock(&p->lock);
ast_mutex_unlock(&agentlock);
+ ast_device_state_changed("Agent/%s", p->agent);
while (res >= 0) {
ast_mutex_lock(&p->lock);
if (p->chan != chan)
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Agent '%s' logged out\n", p->agent);
/* If there is no owner, go ahead and kill it now */
+ ast_device_state_changed("Agent/%s", p->agent);
if (p->dead && !p->owner) {
ast_mutex_destroy(&p->lock);
ast_mutex_destroy(&p->app_lock);
while(p) {
ast_mutex_lock(&p->lock);
if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
- res = AST_DEVICE_UNKNOWN;
if (p->owner) {
if (res != AST_DEVICE_INUSE)
res = AST_DEVICE_BUSY;
struct ast_state_cb *next;
};
+/* ast_state_cb: An extension state notify */
+struct ast_devstate_cb {
+ void *data;
+ ast_devstate_cb_type callback;
+ struct ast_devstate_cb *next;
+};
+
+static struct ast_devstate_cb *devcbs;
+
struct ast_hint {
struct ast_exten *exten;
int laststate;
{
struct ast_hint *list;
struct ast_state_cb *cblist;
+ struct ast_devstate_cb *devcb;
char hint[AST_MAX_EXTENSION] = "";
char device[AST_MAX_EXTENSION];
char *cur, *rest;
*rest = 0;
}
+ state = ast_device_state(device);
+
ast_mutex_lock(&hintlock);
+ devcb = devcbs;
+ while(devcb) {
+ if (devcb->callback)
+ devcb->callback(device, state, devcb->data);
+ devcb = devcb->next;
+ }
list = hints;
while (list) {
return 1;
}
+int ast_devstate_add(ast_devstate_cb_type callback, void *data)
+{
+ struct ast_devstate_cb *devcb;
+ devcb = malloc(sizeof(struct ast_devstate_cb));
+ if (devcb) {
+ memset(devcb, 0, sizeof(struct ast_devstate_cb));
+ ast_mutex_lock(&hintlock);
+ devcb->data = data;
+ devcb->callback = callback;
+ devcb->next = devcbs;
+ devcbs = devcb;
+ ast_mutex_unlock(&hintlock);
+ }
+ return 0;
+}
+
+void ast_devstate_del(ast_devstate_cb_type callback, void *data)
+{
+ struct ast_devstate_cb *devcb, *prev = NULL, *next;
+ ast_mutex_lock(&hintlock);
+ devcb = devcbs;
+ while(devcb) {
+ next = devcb->next;
+ if ((devcb->data == data) && (devcb->callback == callback)) {
+ if (prev)
+ prev->next = next;
+ else
+ devcbs = next;
+ free(devcb);
+ } else
+ prev = devcb;
+ devcb = next;
+ }
+ ast_mutex_unlock(&hintlock);
+}
+
int ast_extension_state_add(const char *context, const char *exten,
ast_state_cb_type callback, void *data)
{