diff options
-rw-r--r-- | apps/app_queue.c | 63 | ||||
-rw-r--r-- | include/asterisk/time.h | 10 | ||||
-rw-r--r-- | main/devicestate.c | 16 | ||||
-rw-r--r-- | main/manager.c | 2 | ||||
-rw-r--r-- | main/pbx.c | 595 | ||||
-rw-r--r-- | pbx/pbx_dundi.c | 21 | ||||
-rw-r--r-- | res/res_calendar.c | 2 | ||||
-rw-r--r-- | res/res_crypto.c | 8 | ||||
-rw-r--r-- | res/res_pjsip/location.c | 8 | ||||
-rw-r--r-- | res/res_rtp_asterisk.c | 53 |
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 |