summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/app_queue.c63
-rw-r--r--include/asterisk/time.h10
-rw-r--r--main/devicestate.c16
-rw-r--r--main/manager.c2
-rw-r--r--main/pbx.c595
-rw-r--r--pbx/pbx_dundi.c21
-rw-r--r--res/res_calendar.c2
-rw-r--r--res/res_crypto.c8
-rw-r--r--res/res_pjsip/location.c8
-rw-r--r--res/res_rtp_asterisk.c53
10 files changed, 392 insertions, 386 deletions
diff --git a/apps/app_queue.c b/apps/app_queue.c
index c2450765c..e77b2be23 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -982,6 +982,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<parameter name="LastCall">
<para>The time this member last took a call, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
</parameter>
+ <parameter name="InCall">
+ <para>Set to 1 if member is in call. Set to 0 after LastCall time is updated.</para>
+ <enumlist>
+ <enum name="0"/>
+ <enum name="1"/>
+ </enumlist>
+ </parameter>
<parameter name="Status">
<para>The numeric device state status of the queue member.</para>
<enumlist>
@@ -1499,6 +1506,7 @@ struct member {
char reason_paused[80]; /*!< Reason of paused if member is paused */
int queuepos; /*!< In what order (pertains to certain strategies) should this member be called? */
time_t lastcall; /*!< When last successful call was hungup */
+ unsigned int in_call:1; /*!< True if member is still in call. (so lastcall is not actual) */
struct call_queue *lastqueue; /*!< Last queue we received a call */
unsigned int dead:1; /*!< Used to detect members deleted in realtime */
unsigned int delme:1; /*!< Flag to delete entry on reload */
@@ -2159,7 +2167,7 @@ static void queue_publish_member_blob(struct stasis_message_type *type, struct a
static struct ast_json *queue_member_blob_create(struct call_queue *q, struct member *mem)
{
- return ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: i, s: i, s: i, s: i, s: i, s: s, s: i}",
+ return ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: i, s: i, s: i, s: i, s: i, s: i, s: s, s: i}",
"Queue", q->name,
"MemberName", mem->membername,
"Interface", mem->interface,
@@ -2168,6 +2176,7 @@ static struct ast_json *queue_member_blob_create(struct call_queue *q, struct me
"Penalty", mem->penalty,
"CallsTaken", mem->calls,
"LastCall", (int)mem->lastcall,
+ "InCall", mem->in_call,
"Status", mem->status,
"Paused", mem->paused,
"PausedReason", mem->reason_paused,
@@ -2231,6 +2240,10 @@ static int get_member_status(struct call_queue *q, int max_penalty, int min_pena
if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
break;
+ } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->in_call && q->wrapuptime) {
+ ast_debug(4, "%s is unavailable because still in call, so we can`t check "
+ "wrapuptime (%d)\n", member->membername, q->wrapuptime);
+ break;
} else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) {
ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n", member->membername, (int) (time(NULL) - member->lastcall), q->wrapuptime);
break;
@@ -2298,6 +2311,9 @@ static int is_member_available(struct call_queue *q, struct member *mem)
}
/* Let wrapuptimes override device state availability */
+ if (q->wrapuptime && mem->in_call) {
+ available = 0; /* member is still in call, cant check wrapuptime to lastcall time */
+ }
if (mem->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < mem->lastcall)) {
available = 0;
}
@@ -2650,6 +2666,7 @@ static void clear_queue(struct call_queue *q)
while ((mem = ao2_iterator_next(&mem_iter))) {
mem->calls = 0;
mem->lastcall = 0;
+ mem->in_call = 0;
ao2_ref(mem, -1);
}
ao2_iterator_destroy(&mem_iter);
@@ -4147,6 +4164,12 @@ static int can_ring_entry(struct queue_ent *qe, struct callattempt *call)
return 0;
}
+ if (call->member->in_call && call->lastqueue->wrapuptime) {
+ ast_debug(1, "%s is in call, so not available (wrapuptime %d)\n",
+ call->interface, call->lastqueue->wrapuptime);
+ return 0;
+ }
+
if ((call->lastqueue && call->lastqueue->wrapuptime && (time(NULL) - call->lastcall < call->lastqueue->wrapuptime))
|| (!call->lastqueue && qe->parent->wrapuptime && (time(NULL) - call->lastcall < qe->parent->wrapuptime))) {
ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
@@ -5369,6 +5392,9 @@ static int update_queue(struct call_queue *q, struct member *member, int callcom
time(&mem->lastcall);
mem->calls++;
mem->lastqueue = q;
+ mem->in_call = 0;
+ ast_debug(4, "Marked member %s as NOT in_call. Lastcall time: %ld \n",
+ mem->membername, (long)mem->lastcall);
ao2_ref(mem, -1);
}
ao2_unlock(qtmp);
@@ -5380,6 +5406,9 @@ static int update_queue(struct call_queue *q, struct member *member, int callcom
time(&member->lastcall);
member->calls++;
member->lastqueue = q;
+ member->in_call = 0;
+ ast_debug(4, "Marked member %s as NOT in_call. Lastcall time: %ld \n",
+ member->membername, (long)member->lastcall);
ao2_unlock(q);
}
ao2_lock(q);
@@ -6358,6 +6387,9 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
int callcompletedinsl;
struct ao2_iterator memi;
struct queue_end_bridge *queue_end_bridge = NULL;
+ struct ao2_iterator queue_iter; /* to iterate through all queues (for shared_lastcall)*/
+ struct member *mem;
+ struct call_queue *queuetmp;
memset(&bridge_config, 0, sizeof(bridge_config));
tmpid[0] = 0;
@@ -6783,6 +6815,28 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
}
}
qe->handled++;
+
+ /** mark member as "in_call" in all queues */
+ if (shared_lastcall) {
+ queue_iter = ao2_iterator_init(queues, 0);
+ while ((queuetmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
+ ao2_lock(queuetmp);
+ if ((mem = ao2_find(queuetmp->members, member, OBJ_POINTER))) {
+ mem->in_call = 1;
+ ast_debug(4, "Marked member %s as in_call \n", mem->membername);
+ ao2_ref(mem, -1);
+ }
+ ao2_unlock(queuetmp);
+ queue_t_unref(queuetmp, "Done with iterator");
+ }
+ ao2_iterator_destroy(&queue_iter);
+ } else {
+ ao2_lock(qe->parent);
+ member->in_call = 1;
+ ast_debug(4, "Marked member %s as in_call \n", member->membername);
+ ao2_unlock(qe->parent);
+ }
+
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));
@@ -9153,10 +9207,11 @@ static char *__queues_show(struct mansession *s, int fd, int argc, const char *
ast_str_append(&out, 0, " (ringinuse %s)", mem->ringinuse ? "enabled" : "disabled");
- ast_str_append(&out, 0, "%s%s%s%s%s%s%s%s%s (%s%s%s)",
+ ast_str_append(&out, 0, "%s%s%s%s%s%s%s%s%s%s%s%s (%s%s%s)",
mem->dynamic ? ast_term_color(COLOR_CYAN, COLOR_BLACK) : "", mem->dynamic ? " (dynamic)" : "", ast_term_reset(),
mem->realtime ? ast_term_color(COLOR_MAGENTA, COLOR_BLACK) : "", mem->realtime ? " (realtime)" : "", ast_term_reset(),
mem->paused ? ast_term_color(COLOR_BROWN, COLOR_BLACK) : "", mem->paused ? " (paused)" : "", ast_term_reset(),
+ mem->in_call ? ast_term_color(COLOR_BROWN, COLOR_BLACK) : "", mem->in_call ? " (in call)" : "", ast_term_reset(),
ast_term_color(
mem->status == AST_DEVICE_UNAVAILABLE || mem->status == AST_DEVICE_UNKNOWN ?
COLOR_RED : COLOR_GREEN, COLOR_BLACK),
@@ -9524,13 +9579,15 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
"Penalty: %d\r\n"
"CallsTaken: %d\r\n"
"LastCall: %d\r\n"
+ "InCall: %d\r\n"
"Status: %d\r\n"
"Paused: %d\r\n"
"PausedReason: %s\r\n"
"%s"
"\r\n",
q->name, mem->membername, mem->interface, mem->state_interface, mem->dynamic ? "dynamic" : "static",
- mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, mem->reason_paused, idText);
+ mem->penalty, mem->calls, (int)mem->lastcall, mem->in_call, mem->status,
+ mem->paused, mem->reason_paused, idText);
++q_items;
}
ao2_ref(mem, -1);
diff --git a/include/asterisk/time.h b/include/asterisk/time.h
index f2382df33..408325b6f 100644
--- a/include/asterisk/time.h
+++ b/include/asterisk/time.h
@@ -31,10 +31,12 @@
/* We have to let the compiler learn what types to use for the elements of a
struct timeval since on linux, it's time_t and suseconds_t, but on *BSD,
- they are just a long. */
-extern struct timeval tv;
-typedef typeof(tv.tv_sec) ast_time_t;
-typedef typeof(tv.tv_usec) ast_suseconds_t;
+ they are just a long.
+ note:dummy_tv_var_for_types never actually gets exported, only used as
+ local place holder. */
+extern struct timeval dummy_tv_var_for_types;
+typedef typeof(dummy_tv_var_for_types.tv_sec) ast_time_t;
+typedef typeof(dummy_tv_var_for_types.tv_usec) ast_suseconds_t;
/*!
* \brief Computes the difference (in seconds) between two \c struct \c timeval instances.
diff --git a/main/devicestate.c b/main/devicestate.c
index 2983ee992..faba144aa 100644
--- a/main/devicestate.c
+++ b/main/devicestate.c
@@ -214,6 +214,7 @@ static pthread_t change_thread = AST_PTHREADT_NULL;
/*! \brief Flag for the queue */
static ast_cond_t change_pending;
+static volatile int shuttingdown;
struct stasis_subscription *devstate_message_sub;
@@ -548,7 +549,7 @@ static void *do_devstate_changes(void *data)
{
struct state_change *next, *current;
- for (;;) {
+ while (!shuttingdown) {
/* This basically pops off any state change entries, resets the list back to NULL, unlocks, and processes each state change */
AST_LIST_LOCK(&state_changes);
if (AST_LIST_EMPTY(&state_changes))
@@ -626,6 +627,18 @@ static void devstate_change_cb(void *data, struct stasis_subscription *sub, stru
device_state->cachable, NULL);
}
+static void device_state_engine_cleanup(void)
+{
+ shuttingdown = 1;
+ AST_LIST_LOCK(&state_changes);
+ ast_cond_signal(&change_pending);
+ AST_LIST_UNLOCK(&state_changes);
+
+ if (change_thread != AST_PTHREADT_NULL) {
+ pthread_join(change_thread, NULL);
+ }
+}
+
/*! \brief Initialize the device state engine in separate thread */
int ast_device_state_engine_init(void)
{
@@ -634,6 +647,7 @@ int ast_device_state_engine_init(void)
ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
return -1;
}
+ ast_register_cleanup(device_state_engine_cleanup);
return 0;
}
diff --git a/main/manager.c b/main/manager.c
index 455a49c4e..8478730c8 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -8512,6 +8512,8 @@ static void manager_shutdown(void)
manager_free_user(user);
}
acl_change_stasis_unsubscribe();
+
+ ast_free(manager_channelvars);
}
diff --git a/main/pbx.c b/main/pbx.c
index 08ae5b6c3..b166978a1 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -370,6 +370,8 @@ struct ast_hint {
AST_VECTOR(, char *) devices; /*!< Devices associated with the hint */
};
+STASIS_MESSAGE_TYPE_DEFN_LOCAL(hint_change_message_type);
+
#define HINTDEVICE_DATA_LENGTH 16
AST_THREADSTORAGE(hintdevice_data);
@@ -3246,17 +3248,256 @@ static void get_device_state_causing_channels(struct ao2_container *c)
ao2_iterator_destroy(&iter);
}
+static void device_state_notify_callbacks(struct ast_hint *hint, struct ast_str **hint_app)
+{
+ struct ao2_iterator cb_iter;
+ struct ast_state_cb *state_cb;
+ int state, same_state;
+ struct ao2_container *device_state_info;
+ int first_extended_cb_call = 1;
+ char context_name[AST_MAX_CONTEXT];
+ char exten_name[AST_MAX_EXTENSION];
+
+ ao2_lock(hint);
+ if (!hint->exten) {
+ /* The extension has already been destroyed */
+ ao2_unlock(hint);
+ return;
+ }
+
+ /*
+ * Save off strings in case the hint extension gets destroyed
+ * while we are notifying the watchers.
+ */
+ ast_copy_string(context_name,
+ ast_get_context_name(ast_get_extension_context(hint->exten)),
+ sizeof(context_name));
+ ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
+ sizeof(exten_name));
+ ast_str_set(hint_app, 0, "%s", ast_get_extension_app(hint->exten));
+ ao2_unlock(hint);
+
+ /*
+ * Get device state for this hint.
+ *
+ * NOTE: We cannot hold any locks while determining the hint
+ * device state or notifying the watchers without causing a
+ * deadlock. (conlock, hints, and hint)
+ */
+
+ /* Make a container so state3 can fill it if we wish.
+ * If that failed we simply do not provide the extended state info.
+ */
+ device_state_info = alloc_device_state_info();
+
+ state = ast_extension_state3(*hint_app, device_state_info);
+ if ((same_state = state == hint->laststate) && (~state & AST_EXTENSION_RINGING)) {
+ ao2_cleanup(device_state_info);
+ return;
+ }
+
+ /* Device state changed since last check - notify the watchers. */
+ hint->laststate = state; /* record we saw the change */
+
+ /* For general callbacks */
+ cb_iter = ao2_iterator_init(statecbs, 0);
+ for (; !same_state && (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+ execute_state_callback(state_cb->change_cb,
+ context_name,
+ exten_name,
+ state_cb->data,
+ AST_HINT_UPDATE_DEVICE,
+ hint,
+ NULL);
+ }
+ ao2_iterator_destroy(&cb_iter);
+
+ /* For extension callbacks */
+ /* extended callbacks are called when the state changed or when AST_STATE_RINGING is
+ * included. Normal callbacks are only called when the state changed.
+ */
+ cb_iter = ao2_iterator_init(hint->callbacks, 0);
+ for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+ if (state_cb->extended && first_extended_cb_call) {
+ /* Fill detailed device_state_info now that we know it is used by extd. callback */
+ first_extended_cb_call = 0;
+ get_device_state_causing_channels(device_state_info);
+ }
+ if (state_cb->extended || !same_state) {
+ execute_state_callback(state_cb->change_cb,
+ context_name,
+ exten_name,
+ state_cb->data,
+ AST_HINT_UPDATE_DEVICE,
+ hint,
+ state_cb->extended ? device_state_info : NULL);
+ }
+ }
+ ao2_iterator_destroy(&cb_iter);
+
+ ao2_cleanup(device_state_info);
+}
+
+static void presence_state_notify_callbacks(
+ struct stasis_message *msg, struct ast_hint *hint, struct ast_str **hint_app,
+ struct ast_presence_state_message *presence_state)
+{
+ struct ao2_iterator cb_iter;
+ struct ast_state_cb *state_cb;
+ char context_name[AST_MAX_CONTEXT];
+ char exten_name[AST_MAX_EXTENSION];
+
+ ao2_lock(hint);
+
+ if (!hint->exten) {
+ /* The extension has already been destroyed */
+ ao2_unlock(hint);
+ return;
+ }
+
+ if (hint_change_message_type() != stasis_message_type(msg)) {
+ const char *app;
+ char *parse;
+
+ /* Does this hint monitor the device that changed state? */
+ app = ast_get_extension_app(hint->exten);
+ if (ast_strlen_zero(app)) {
+ /* The hint does not monitor presence at all. */
+ ao2_unlock(hint);
+ return;
+ }
+
+ ast_str_set(hint_app, 0, "%s", app);
+ parse = parse_hint_presence(*hint_app);
+ if (ast_strlen_zero(parse)) {
+ ao2_unlock(hint);
+ return;
+ }
+ if (strcasecmp(parse, presence_state->provider)) {
+ /* The hint does not monitor the presence provider. */
+ ao2_unlock(hint);
+ return;
+ }
+ }
+
+ /*
+ * Save off strings in case the hint extension gets destroyed
+ * while we are notifying the watchers.
+ */
+ ast_copy_string(context_name,
+ ast_get_context_name(ast_get_extension_context(hint->exten)),
+ sizeof(context_name));
+ ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
+ sizeof(exten_name));
+ ast_str_set(hint_app, 0, "%s", ast_get_extension_app(hint->exten));
+
+ /* Check to see if update is necessary */
+ if ((hint->last_presence_state == presence_state->state) &&
+ ((hint->last_presence_subtype && presence_state->subtype &&
+ !strcmp(hint->last_presence_subtype, presence_state->subtype)) ||
+ (!hint->last_presence_subtype && !presence_state->subtype)) &&
+ ((hint->last_presence_message && presence_state->message &&
+ !strcmp(hint->last_presence_message, presence_state->message)) ||
+ (!hint->last_presence_message && !presence_state->message))) {
+ /* this update is the same as the last, do nothing */
+ ao2_unlock(hint);
+ return;
+ }
+
+ /* update new values */
+ ast_free(hint->last_presence_subtype);
+ ast_free(hint->last_presence_message);
+ hint->last_presence_state = presence_state->state;
+ hint->last_presence_subtype = presence_state->subtype ? ast_strdup(presence_state->subtype) : NULL;
+ hint->last_presence_message = presence_state->message ? ast_strdup(presence_state->message) : NULL;
+
+ /*
+ * NOTE: We cannot hold any locks while notifying
+ * the watchers without causing a deadlock.
+ * (conlock, hints, and hint)
+ */
+ ao2_unlock(hint);
+
+ /* For general callbacks */
+ cb_iter = ao2_iterator_init(statecbs, 0);
+ for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+ execute_state_callback(state_cb->change_cb,
+ context_name,
+ exten_name,
+ state_cb->data,
+ AST_HINT_UPDATE_PRESENCE,
+ hint,
+ NULL);
+ }
+ ao2_iterator_destroy(&cb_iter);
+
+ /* For extension callbacks */
+ cb_iter = ao2_iterator_init(hint->callbacks, 0);
+ for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_cleanup(state_cb)) {
+ execute_state_callback(state_cb->change_cb,
+ context_name,
+ exten_name,
+ state_cb->data,
+ AST_HINT_UPDATE_PRESENCE,
+ hint,
+ NULL);
+ }
+ ao2_iterator_destroy(&cb_iter);
+}
+
+static int handle_hint_change_message_type(struct stasis_message *msg, enum ast_state_cb_update_reason reason)
+{
+ struct ast_hint *hint;
+ struct ast_str *hint_app;
+
+ if (hint_change_message_type() != stasis_message_type(msg)) {
+ return 0;
+ }
+
+ if (!(hint_app = ast_str_create(1024))) {
+ return -1;
+ }
+
+ hint = stasis_message_data(msg);
+
+ if (reason == AST_HINT_UPDATE_DEVICE) {
+ device_state_notify_callbacks(hint, &hint_app);
+ } else if (reason == AST_HINT_UPDATE_PRESENCE) {
+ char *presence_subtype = NULL;
+ char *presence_message = NULL;
+ int state;
+
+ state = extension_presence_state_helper(
+ hint->exten, &presence_subtype, &presence_message);
+
+ {
+ struct ast_presence_state_message presence_state = {
+ .state = state > 0 ? state : AST_PRESENCE_INVALID,
+ .subtype = presence_subtype,
+ .message = presence_message
+ };
+ presence_state_notify_callbacks(msg, hint, &hint_app, &presence_state);
+ }
+
+ ast_free(presence_subtype);
+ ast_free(presence_message);
+ }
+
+ ast_free(hint_app);
+ return 1;
+}
+
static void device_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
{
struct ast_device_state_message *dev_state;
- struct ast_hint *hint;
struct ast_str *hint_app;
struct ast_hintdevice *device;
struct ast_hintdevice *cmpdevice;
struct ao2_iterator *dev_iter;
- struct ao2_iterator cb_iter;
- char context_name[AST_MAX_CONTEXT];
- char exten_name[AST_MAX_EXTENSION];
+
+ if (handle_hint_change_message_type(msg, AST_HINT_UPDATE_DEVICE)) {
+ return;
+ }
if (ast_device_state_message_type() != stasis_message_type(msg)) {
return;
@@ -3294,94 +3535,9 @@ static void device_state_cb(void *unused, struct stasis_subscription *sub, struc
}
for (; (device = ao2_iterator_next(dev_iter)); ao2_t_ref(device, -1, "Next device")) {
- struct ast_state_cb *state_cb;
- int state;
- int same_state;
- struct ao2_container *device_state_info;
- int first_extended_cb_call = 1;
-
- if (!device->hint) {
- /* Should never happen. */
- continue;
- }
- hint = device->hint;
-
- ao2_lock(hint);
- if (!hint->exten) {
- /* The extension has already been destroyed */
- ao2_unlock(hint);
- continue;
- }
-
- /*
- * Save off strings in case the hint extension gets destroyed
- * while we are notifying the watchers.
- */
- ast_copy_string(context_name,
- ast_get_context_name(ast_get_extension_context(hint->exten)),
- sizeof(context_name));
- ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
- sizeof(exten_name));
- ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(hint->exten));
- ao2_unlock(hint);
-
- /*
- * Get device state for this hint.
- *
- * NOTE: We cannot hold any locks while determining the hint
- * device state or notifying the watchers without causing a
- * deadlock. (conlock, hints, and hint)
- */
- /* Make a container so state3 can fill it if we wish.
- * If that failed we simply do not provide the extended state info.
- */
- device_state_info = alloc_device_state_info();
- state = ast_extension_state3(hint_app, device_state_info);
- if ((same_state = state == hint->laststate) && (~state & AST_EXTENSION_RINGING)) {
- ao2_cleanup(device_state_info);
- continue;
- }
-
- /* Device state changed since last check - notify the watchers. */
- hint->laststate = state; /* record we saw the change */
-
- /* For general callbacks */
- cb_iter = ao2_iterator_init(statecbs, 0);
- for (; !same_state && (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
- execute_state_callback(state_cb->change_cb,
- context_name,
- exten_name,
- state_cb->data,
- AST_HINT_UPDATE_DEVICE,
- hint,
- NULL);
- }
- ao2_iterator_destroy(&cb_iter);
-
- /* For extension callbacks */
- /* extended callbacks are called when the state changed or when AST_STATE_RINGING is
- * included. Normal callbacks are only called when the state changed.
- */
- cb_iter = ao2_iterator_init(hint->callbacks, 0);
- for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
- if (state_cb->extended && first_extended_cb_call) {
- /* Fill detailed device_state_info now that we know it is used by extd. callback */
- first_extended_cb_call = 0;
- get_device_state_causing_channels(device_state_info);
- }
- if (state_cb->extended || !same_state) {
- execute_state_callback(state_cb->change_cb,
- context_name,
- exten_name,
- state_cb->data,
- AST_HINT_UPDATE_DEVICE,
- hint,
- state_cb->extended ? device_state_info : NULL);
- }
+ if (device->hint) {
+ device_state_notify_callbacks(device->hint, &hint_app);
}
- ao2_iterator_destroy(&cb_iter);
-
- ao2_cleanup(device_state_info);
}
ast_mutex_unlock(&context_merge_lock);
@@ -3763,30 +3919,37 @@ static int ast_add_hint(struct ast_exten *e)
return 0;
}
-/*! \brief Change hint for an extension */
-static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
+/*! \brief Publish a hint changed event */
+static int publish_hint_change(struct ast_hint *hint, struct ast_exten *ne)
{
- struct ast_str *hint_app;
- struct ast_hint *hint;
- int previous_device_state;
- char *previous_message = NULL;
- char *message = NULL;
- char *previous_subtype = NULL;
- char *subtype = NULL;
- int previous_presence_state;
- int presence_state;
- int presence_state_changed = 0;
+ struct stasis_message *message;
- if (!oe || !ne) {
+ if (!hint_change_message_type()) {
return -1;
}
- hint_app = ast_str_create(1024);
- if (!hint_app) {
+ /* The message is going to be published to two topics so the hint needs two refs */
+ if (!(message = stasis_message_create(hint_change_message_type(), ao2_bump(hint)))) {
+ ao2_ref(hint, -1);
return -1;
}
- ast_mutex_lock(&context_merge_lock); /* Hold off ast_merge_contexts_and_delete and state changes */
+ stasis_publish(ast_device_state_topic_all(), message);
+ stasis_publish(ast_presence_state_topic_all(), message);
+
+ ao2_ref(message, -1);
+
+ return 0;
+}
+
+/*! \brief Change hint for an extension */
+static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
+{
+ struct ast_hint *hint;
+
+ if (!oe || !ne) {
+ return -1;
+ }
ao2_lock(hints);/* Locked to hold off others while we move the hint around. */
@@ -3798,7 +3961,6 @@ static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
if (!hint) {
ao2_unlock(hints);
ast_mutex_unlock(&context_merge_lock);
- ast_free(hint_app);
return -1;
}
@@ -3808,25 +3970,6 @@ static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
ao2_lock(hint);
hint->exten = ne;
- /* Store the previous states so we know whether we need to notify state callbacks */
- previous_device_state = hint->laststate;
- previous_presence_state = hint->last_presence_state;
- previous_message = hint->last_presence_message;
- previous_subtype = hint->last_presence_subtype;
-
- /* Update the saved device and presence state with the new extension */
- hint->laststate = ast_extension_state2(ne, NULL);
- hint->last_presence_state = AST_PRESENCE_INVALID;
- hint->last_presence_subtype = NULL;
- hint->last_presence_message = NULL;
-
- presence_state = extension_presence_state_helper(ne, &subtype, &message);
- if (presence_state > 0) {
- hint->last_presence_state = presence_state;
- hint->last_presence_subtype = subtype;
- hint->last_presence_message = message;
- }
-
ao2_unlock(hint);
ao2_link(hints, hint);
@@ -3835,104 +3978,15 @@ static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
ast_get_extension_name(ne),
ast_get_context_name(ast_get_extension_context(ne)));
}
-
ao2_unlock(hints);
- /* Locking for state callbacks is respected here and only the context_merge_lock lock is
- * held during the state callback invocation. This will stop the normal state callback
- * thread from being able to handle incoming state changes if they occur.
- */
-
- /* Determine if presence state has changed due to the change of the hint extension */
- if ((hint->last_presence_state != previous_presence_state) ||
- strcmp(S_OR(hint->last_presence_subtype, ""), S_OR(previous_subtype, "")) ||
- strcmp(S_OR(hint->last_presence_message, ""), S_OR(previous_message, ""))) {
- presence_state_changed = 1;
- }
-
- /* Notify any existing state callbacks if the device or presence state has changed */
- if ((hint->laststate != previous_device_state) || presence_state_changed) {
- struct ao2_iterator cb_iter;
- struct ast_state_cb *state_cb;
- struct ao2_container *device_state_info;
- int first_extended_cb_call = 1;
-
- /* For general callbacks */
- cb_iter = ao2_iterator_init(statecbs, 0);
- for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
- /* Unlike the normal state callbacks since something has explicitly provided us this extension
- * it will remain valid and unchanged for the lifetime of this function invocation.
- */
- if (hint->laststate != previous_device_state) {
- execute_state_callback(state_cb->change_cb,
- ast_get_context_name(ast_get_extension_context(ne)),
- ast_get_extension_name(ne),
- state_cb->data,
- AST_HINT_UPDATE_DEVICE,
- hint,
- NULL);
- }
- if (presence_state_changed) {
- execute_state_callback(state_cb->change_cb,
- ast_get_context_name(ast_get_extension_context(ne)),
- ast_get_extension_name(ne),
- state_cb->data,
- AST_HINT_UPDATE_PRESENCE,
- hint,
- NULL);
- }
- }
- ao2_iterator_destroy(&cb_iter);
-
- ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(ne));
-
- device_state_info = alloc_device_state_info();
- ast_extension_state3(hint_app, device_state_info);
-
- /* For extension callbacks */
- cb_iter = ao2_iterator_init(hint->callbacks, 0);
- for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
- if (hint->laststate != previous_device_state) {
- if (state_cb->extended && first_extended_cb_call) {
- /* Fill detailed device_state_info now that we know it is used by extd. callback */
- first_extended_cb_call = 0;
- get_device_state_causing_channels(device_state_info);
- }
- execute_state_callback(state_cb->change_cb,
- ast_get_context_name(ast_get_extension_context(ne)),
- ast_get_extension_name(ne),
- state_cb->data,
- AST_HINT_UPDATE_DEVICE,
- hint,
- state_cb->extended ? device_state_info : NULL);
- }
- if (presence_state_changed) {
- execute_state_callback(state_cb->change_cb,
- ast_get_context_name(ast_get_extension_context(ne)),
- ast_get_extension_name(ne),
- state_cb->data,
- AST_HINT_UPDATE_PRESENCE,
- hint,
- NULL);
- }
- }
- ao2_iterator_destroy(&cb_iter);
-
- ao2_cleanup(device_state_info);
- }
+ publish_hint_change(hint, ne);
ao2_ref(hint, -1);
- ast_mutex_unlock(&context_merge_lock);
-
- ast_free(hint_app);
- ast_free(previous_message);
- ast_free(previous_subtype);
-
return 0;
}
-
/*! \brief Get hint for channel */
int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten)
{
@@ -7943,13 +7997,13 @@ int pbx_checkcondition(const char *condition)
static void presence_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
{
- struct ast_presence_state_message *presence_state = stasis_message_data(msg);
struct ast_hint *hint;
struct ast_str *hint_app = NULL;
struct ao2_iterator hint_iter;
- struct ao2_iterator cb_iter;
- char context_name[AST_MAX_CONTEXT];
- char exten_name[AST_MAX_EXTENSION];
+
+ if (handle_hint_change_message_type(msg, AST_HINT_UPDATE_PRESENCE)) {
+ return;
+ }
if (stasis_message_type(msg) != ast_presence_state_message_type()) {
return;
@@ -7963,98 +8017,7 @@ static void presence_state_cb(void *unused, struct stasis_subscription *sub, str
ast_mutex_lock(&context_merge_lock);/* Hold off ast_merge_contexts_and_delete */
hint_iter = ao2_iterator_init(hints, 0);
for (; (hint = ao2_iterator_next(&hint_iter)); ao2_cleanup(hint)) {
- struct ast_state_cb *state_cb;
- const char *app;
- char *parse;
- ao2_lock(hint);
-
- if (!hint->exten) {
- /* The extension has already been destroyed */
- ao2_unlock(hint);
- continue;
- }
-
- /* Does this hint monitor the device that changed state? */
- app = ast_get_extension_app(hint->exten);
- if (ast_strlen_zero(app)) {
- /* The hint does not monitor presence at all. */
- ao2_unlock(hint);
- continue;
- }
-
- ast_str_set(&hint_app, 0, "%s", app);
- parse = parse_hint_presence(hint_app);
- if (ast_strlen_zero(parse)) {
- ao2_unlock(hint);
- continue;
- }
- if (strcasecmp(parse, presence_state->provider)) {
- /* The hint does not monitor the presence provider. */
- ao2_unlock(hint);
- continue;
- }
-
- /*
- * Save off strings in case the hint extension gets destroyed
- * while we are notifying the watchers.
- */
- ast_copy_string(context_name,
- ast_get_context_name(ast_get_extension_context(hint->exten)),
- sizeof(context_name));
- ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
- sizeof(exten_name));
- ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(hint->exten));
-
- /* Check to see if update is necessary */
- if ((hint->last_presence_state == presence_state->state) &&
- ((hint->last_presence_subtype && presence_state->subtype && !strcmp(hint->last_presence_subtype, presence_state->subtype)) || (!hint->last_presence_subtype && !presence_state->subtype)) &&
- ((hint->last_presence_message && presence_state->message && !strcmp(hint->last_presence_message, presence_state->message)) || (!hint->last_presence_message && !presence_state->message))) {
-
- /* this update is the same as the last, do nothing */
- ao2_unlock(hint);
- continue;
- }
-
- /* update new values */
- ast_free(hint->last_presence_subtype);
- ast_free(hint->last_presence_message);
- hint->last_presence_state = presence_state->state;
- hint->last_presence_subtype = presence_state->subtype ? ast_strdup(presence_state->subtype) : NULL;
- hint->last_presence_message = presence_state->message ? ast_strdup(presence_state->message) : NULL;
- /*
- * (Copied from device_state_cb)
- *
- * NOTE: We cannot hold any locks while notifying
- * the watchers without causing a deadlock.
- * (conlock, hints, and hint)
- */
- ao2_unlock(hint);
-
- /* For general callbacks */
- cb_iter = ao2_iterator_init(statecbs, 0);
- for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
- execute_state_callback(state_cb->change_cb,
- context_name,
- exten_name,
- state_cb->data,
- AST_HINT_UPDATE_PRESENCE,
- hint,
- NULL);
- }
- ao2_iterator_destroy(&cb_iter);
-
- /* For extension callbacks */
- cb_iter = ao2_iterator_init(hint->callbacks, 0);
- for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_cleanup(state_cb)) {
- execute_state_callback(state_cb->change_cb,
- context_name,
- exten_name,
- state_cb->data,
- AST_HINT_UPDATE_PRESENCE,
- hint,
- NULL);
- }
- ao2_iterator_destroy(&cb_iter);
+ presence_state_notify_callbacks(msg, hint, &hint_app, stasis_message_data(msg));
}
ao2_iterator_destroy(&hint_iter);
ast_mutex_unlock(&context_merge_lock);
@@ -8568,6 +8531,8 @@ static int statecbs_cmp(void *obj, void *arg, int flags)
*/
static void pbx_shutdown(void)
{
+ STASIS_MESSAGE_TYPE_CLEANUP(hint_change_message_type);
+
if (hints) {
ao2_container_unregister("hints");
ao2_ref(hints, -1);
@@ -8638,5 +8603,9 @@ int ast_pbx_init(void)
ast_register_cleanup(pbx_shutdown);
+ if (STASIS_MESSAGE_TYPE_INIT(hint_change_message_type) != 0) {
+ return -1;
+ }
+
return (hints && hintdevices && statecbs) ? 0 : -1;
}
diff --git a/pbx/pbx_dundi.c b/pbx/pbx_dundi.c
index 04da24788..51801f45f 100644
--- a/pbx/pbx_dundi.c
+++ b/pbx/pbx_dundi.c
@@ -5014,30 +5014,31 @@ static int load_module(void)
io = io_context_create();
sched = ast_sched_context_create();
- if (!io || !sched)
- return AST_MODULE_LOAD_DECLINE;
+ if (!io || !sched) {
+ goto declined;
+ }
- if (set_config("dundi.conf", &sin, 0))
- return AST_MODULE_LOAD_DECLINE;
+ if (set_config("dundi.conf", &sin, 0)) {
+ goto declined;
+ }
netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (netsocket < 0) {
ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
- return AST_MODULE_LOAD_DECLINE;
+ goto declined;
}
if (bind(netsocket, (struct sockaddr *) &sin, sizeof(sin))) {
ast_log(LOG_ERROR, "Unable to bind to %s port %d: %s\n",
ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), strerror(errno));
- return AST_MODULE_LOAD_DECLINE;
+ goto declined;
}
ast_set_qos(netsocket, tos, 0, "DUNDi");
if (start_network_thread()) {
ast_log(LOG_ERROR, "Unable to start network thread\n");
- close(netsocket);
- return AST_MODULE_LOAD_DECLINE;
+ goto declined;
}
ast_cli_register_multiple(cli_dundi, ARRAY_LEN(cli_dundi));
@@ -5050,6 +5051,10 @@ static int load_module(void)
ast_verb(2, "DUNDi Ready and Listening on %s port %d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
return AST_MODULE_LOAD_SUCCESS;
+
+declined:
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Distributed Universal Number Discovery (DUNDi)",
diff --git a/res/res_calendar.c b/res/res_calendar.c
index 5b911ca5b..264165470 100644
--- a/res/res_calendar.c
+++ b/res/res_calendar.c
@@ -1853,6 +1853,8 @@ static int unload_module(void)
ast_mutex_unlock(&refreshlock);
pthread_join(refresh_thread, NULL);
+ ast_sched_context_destroy(sched);
+
AST_LIST_LOCK(&techs);
AST_LIST_TRAVERSE_SAFE_BEGIN(&techs, tech, list) {
ast_unload_resource(tech->module, 0);
diff --git a/res/res_crypto.c b/res/res_crypto.c
index 78b8df209..168342791 100644
--- a/res/res_crypto.c
+++ b/res/res_crypto.c
@@ -652,13 +652,17 @@ static int load_module(void)
} else {
crypto_load(-1, -1);
}
+
+ /* This prevents dlclose from ever running, but allows CLI cleanup at shutdown. */
+ ast_module_shutdown_ref(ast_module_info->self);
return AST_MODULE_LOAD_SUCCESS;
}
static int unload_module(void)
{
- /* Can't unload this once we're loaded */
- return -1;
+ ast_cli_unregister_multiple(cli_crypto, ARRAY_LEN(cli_crypto));
+
+ return 0;
}
/* needs usecount semantics defined */
diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c
index a34e5cdf3..2908f6f70 100644
--- a/res/res_pjsip/location.c
+++ b/res/res_pjsip/location.c
@@ -322,14 +322,6 @@ int ast_sip_location_update_contact(struct ast_sip_contact *contact)
int ast_sip_location_delete_contact(struct ast_sip_contact *contact)
{
- void *contact_status_obj;
-
- contact_status_obj = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact));
- if (contact_status_obj) {
- ast_sorcery_delete(ast_sip_get_sorcery(), contact_status_obj);
- ao2_ref(contact_status_obj, -1);
- }
-
return ast_sorcery_delete(ast_sip_get_sorcery(), contact);
}
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index 3c630fbe3..f6bf34211 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -444,7 +444,6 @@ static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level);
#ifdef HAVE_OPENSSL_SRTP
static int ast_rtp_activate(struct ast_rtp_instance *instance);
static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp);
-static void dtls_srtp_flush_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp);
static void dtls_srtp_start_timeout_timer(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp);
static void dtls_srtp_stop_timeout_timer(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp);
#endif
@@ -1684,20 +1683,15 @@ static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status)
if (rtp->rtcp) {
update_address_with_ice_candidate(rtp, AST_RTP_ICE_COMPONENT_RTCP, &rtp->rtcp->them);
}
+ }
#ifdef HAVE_OPENSSL_SRTP
- if (rtp->dtls.dtls_setup != AST_RTP_DTLS_SETUP_PASSIVE) {
- dtls_perform_handshake(instance, &rtp->dtls, 0);
- }
- else {
- dtls_srtp_flush_pending(instance, rtp); /* this flushes pending BIO for both rtp & rtcp as needed. */
- }
+ dtls_perform_handshake(instance, &rtp->dtls, 0);
- if (rtp->rtcp && rtp->rtcp->dtls.dtls_setup != AST_RTP_DTLS_SETUP_PASSIVE) {
- dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1);
- }
-#endif
+ if (rtp->rtcp) {
+ dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1);
}
+#endif
if (!strictrtp) {
return;
@@ -1892,23 +1886,6 @@ static void dtls_srtp_stop_timeout_timer(struct ast_rtp_instance *instance, stru
AST_SCHED_DEL_UNREF(rtp->sched, dtls->timeout_timer, ao2_ref(instance, -1));
}
-static void dtls_srtp_flush_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp)
-{
- struct dtls_details *dtls;
-
- dtls = &rtp->dtls;
- ast_mutex_lock(&dtls->lock);
- dtls_srtp_check_pending(instance, rtp, 0);
- ast_mutex_unlock(&dtls->lock);
-
- if (rtp->rtcp) {
- dtls = &rtp->rtcp->dtls;
- ast_mutex_lock(&dtls->lock);
- dtls_srtp_check_pending(instance, rtp, 1);
- ast_mutex_unlock(&dtls->lock);
- }
-}
-
static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp)
{
struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls;
@@ -2141,8 +2118,6 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
SSL_set_accept_state(dtls->ssl);
}
- ast_mutex_lock(&dtls->lock);
-
dtls_srtp_check_pending(instance, rtp, rtcp);
BIO_write(dtls->read_bio, buf, len);
@@ -2153,7 +2128,6 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
unsigned long error = ERR_get_error();
ast_log(LOG_ERROR, "DTLS failure occurred on RTP instance '%p' due to reason '%s', terminating\n",
instance, ERR_reason_error_string(error));
- ast_mutex_unlock(&dtls->lock);
return -1;
}
@@ -2171,8 +2145,6 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
dtls_srtp_start_timeout_timer(instance, rtp, rtcp);
}
- ast_mutex_unlock(&dtls->lock);
-
return res;
}
#endif
@@ -4862,20 +4834,7 @@ static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct
rtp_learning_seq_init(&rtp->rtp_source_learn, rtp->seqno);
}
-#ifdef HAVE_OPENSSL_SRTP
- /* Trigger pending outbound DTLS packets received before the address was set. Avoid unnecessary locking
- * by checking if we're passive. Without this, we only send the pending packets once a new SSL packet is
- * received in __rtp_recvfrom. If rtp->ice, this is instead done on_ice_complete
- */
-#ifdef HAVE_PJPROJECT
- if (rtp->ice) {
- return;
- }
-#endif
- if (rtp->dtls.dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) {
- dtls_srtp_flush_pending(instance, rtp);
- }
-#endif
+ return;
}
/*! \brief Write t140 redundacy frame