Avoid crash in generic CC agent init if caller name or number is NULL.
[asterisk/asterisk.git] / main / ccss.c
index 4e61601..04fa92a 100644 (file)
@@ -98,8 +98,17 @@ static unsigned int global_cc_max_requests;
  */
 static int cc_request_count;
 
-#define cc_ref(obj, debug) ({ao2_t_ref((obj), +1, (debug)); (obj);})
-#define cc_unref(obj, debug) ({ao2_t_ref((obj), -1, (debug)); NULL;})
+static inline void *cc_ref(void *obj, const char *debug)
+{
+       ao2_t_ref(obj, +1, debug);
+       return obj;
+}
+
+static inline void *cc_unref(void *obj, const char *debug)
+{
+       ao2_t_ref(obj, -1, debug);
+       return NULL;
+}
 
 /*!
  * \since 1.8
@@ -965,7 +974,6 @@ static const struct ast_cc_agent_callbacks *find_agent_callbacks(struct ast_chan
 
 static int cc_generic_monitor_request_cc(struct ast_cc_monitor *monitor, int *available_timer_id);
 static int cc_generic_monitor_suspend(struct ast_cc_monitor *monitor);
-static int cc_generic_monitor_status_response(struct ast_cc_monitor *monitor, enum ast_device_state devstate);
 static int cc_generic_monitor_unsuspend(struct ast_cc_monitor *monitor);
 static int cc_generic_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, int *sched_id);
 static void cc_generic_monitor_destructor(void *private_data);
@@ -974,7 +982,6 @@ static struct ast_cc_monitor_callbacks generic_monitor_cbs = {
        .type = "generic",
        .request_cc = cc_generic_monitor_request_cc,
        .suspend = cc_generic_monitor_suspend,
-       .status_response = cc_generic_monitor_status_response,
        .unsuspend = cc_generic_monitor_unsuspend,
        .cancel_available_timer = cc_generic_monitor_cancel_available_timer,
        .destructor = cc_generic_monitor_destructor,
@@ -992,6 +999,22 @@ struct generic_monitor_instance {
 struct generic_monitor_instance_list {
        const char *device_name;
        enum ast_device_state current_state;
+       /* If there are multiple instances monitoring the
+        * same device and one should fail, we need to know
+        * whether to signal that the device can be recalled.
+        * The problem is that the device state is not enough
+        * to check. If a caller has requested CCNR, then the
+        * fact that the device is available does not indicate
+        * that the device is ready to be recalled. Instead, as
+        * soon as one instance of the monitor becomes available
+        * for a recall, we mark the entire list as being fit
+        * for recall. If a CCNR request comes in, then we will
+        * have to mark the list as unfit for recall since this
+        * is a clear indicator that the person at the monitored
+        * device has gone away and is actuall not fit to be
+        * recalled
+        */
+       int fit_for_recall;
        struct ast_event_sub *sub;
        AST_LIST_HEAD_NOLOCK(, generic_monitor_instance) list;
 };
@@ -1114,6 +1137,7 @@ static int generic_monitor_devstate_tp_cb(void *data)
                AST_LIST_TRAVERSE(&generic_list->list, generic_instance, next) {
                        if (!generic_instance->is_suspended && generic_instance->monitoring) {
                                generic_instance->monitoring = 0;
+                               generic_list->fit_for_recall = 1;
                                ast_cc_monitor_callee_available(generic_instance->core_id, "Generic monitored party has become available");
                                break;
                        }
@@ -1211,6 +1235,12 @@ static int cc_generic_monitor_request_cc(struct ast_cc_monitor *monitor, int *av
                cc_unref(generic_list, "Failed to schedule available timer. (generic_list)");
                return -1;
        }
+       /* If the new instance was created as CCNR, then that means this device is not currently
+        * fit for recall even if it previously was.
+        */
+       if (service == AST_CC_CCNR || service == AST_CC_CCNL) {
+               generic_list->fit_for_recall = 0;
+       }
        ast_cc_monitor_request_acked(monitor->core_id, "Generic monitor for %s subscribed to device state.",
                        monitor->interface->device_name);
        cc_unref(generic_list, "Finished with monitor instance reference in request cc callback");
@@ -1258,15 +1288,6 @@ static int cc_generic_monitor_suspend(struct ast_cc_monitor *monitor)
        return 0;
 }
 
-static int cc_generic_monitor_status_response(struct ast_cc_monitor *monitor, enum ast_device_state devstate)
-{
-       /* The generic monitor will never issue a status request of the other side's agent.
-        * If this somehow gets called, something really fishy is going on.
-        */
-       ast_log(LOG_WARNING, "Why has a generic monitor's status_response callback been called? CoreID is %d\n", monitor->core_id);
-       return 0;
-}
-
 static int cc_generic_monitor_unsuspend(struct ast_cc_monitor *monitor)
 {
        struct generic_monitor_instance *generic_instance;
@@ -1354,6 +1375,27 @@ static void cc_generic_monitor_destructor(void *private_data)
                 * list from the container
                 */
                ao2_t_unlink(generic_monitors, generic_list, "Generic list is empty. Unlink it from the container");
+       } else {
+               /* There are still instances for this particular device. The situation
+                * may be that we were attempting a CC recall and a failure occurred, perhaps
+                * on the agent side. If a failure happens here and the device being monitored
+                * is available, then we need to signal on the first unsuspended instance that
+                * the device is available for recall.
+                */
+
+               /* First things first. We don't even want to consider this action if
+                * the device in question isn't available right now.
+                */
+               if (generic_list->fit_for_recall && (generic_list->current_state == AST_DEVICE_NOT_INUSE ||
+                               generic_list->current_state == AST_DEVICE_UNKNOWN)) {
+                       AST_LIST_TRAVERSE(&generic_list->list, generic_instance, next) {
+                               if (!generic_instance->is_suspended && generic_instance->monitoring) {
+                                       ast_cc_monitor_callee_available(generic_instance->core_id, "Signaling generic monitor "
+                                                       "availability due to other instance's failure.");
+                                       break;
+                               }
+                       }
+               }
        }
        cc_unref(generic_list, "Done with generic list in generic monitor destructor");
        ast_free((char *)gen_mon_pvt->device_name);
@@ -2297,8 +2339,12 @@ static int cc_generic_agent_init(struct ast_cc_agent *agent, struct ast_channel
        }
 
        generic_pvt->offer_timer_id = -1;
-       ast_copy_string(generic_pvt->cid_num, chan->cid.cid_num, sizeof(generic_pvt->cid_num));
-       ast_copy_string(generic_pvt->cid_name, chan->cid.cid_name, sizeof(generic_pvt->cid_name));
+       if (chan->cid.cid_num) {
+               ast_copy_string(generic_pvt->cid_num, chan->cid.cid_num, sizeof(generic_pvt->cid_num));
+       }
+       if (chan->cid.cid_name) {
+               ast_copy_string(generic_pvt->cid_name, chan->cid.cid_name, sizeof(generic_pvt->cid_name));
+       }
        ast_copy_string(generic_pvt->exten, S_OR(chan->macroexten, chan->exten), sizeof(generic_pvt->exten));
        ast_copy_string(generic_pvt->context, S_OR(chan->macrocontext, chan->context), sizeof(generic_pvt->context));
        agent->private_data = generic_pvt;
@@ -2308,13 +2354,13 @@ static int cc_generic_agent_init(struct ast_cc_agent *agent, struct ast_channel
 
 static int offer_timer_expire(const void *data)
 {
-       const struct ast_cc_agent *agent = data;
+       struct ast_cc_agent *agent = (struct ast_cc_agent *) data;
        struct cc_generic_agent_pvt *agent_pvt = agent->private_data;
        ast_log_dynamic_level(cc_logger_level, "Core %d: Queuing change request because offer timer has expired.\n",
                        agent->core_id);
        agent_pvt->offer_timer_id = -1;
        ast_cc_failed(agent->core_id, "Generic agent %s offer timer expired", agent->device_name);
-       cc_unref((struct ast_cc_agent *)agent, "Remove scheduler's reference to the agent");
+       cc_unref(agent, "Remove scheduler's reference to the agent");
        return 0;
 }
 
@@ -2859,8 +2905,6 @@ static int cc_complete(struct cc_core_instance *core_instance, struct cc_state_c
 
 static int cc_failed(struct cc_core_instance *core_instance, struct cc_state_change_args *args, enum cc_state previous_state)
 {
-       /* Something along the way failed, call agent and monitor destructor functions
-        */
        manager_event(EVENT_FLAG_CC, "CCFailure",
                "CoreID: %d\r\n"
                "Caller: %s\r\n"