Commit ed8d5c6430 for asterisk.org

commit ed8d5c6430fb4ebaef79834ff8268c831b14de04
Author: George Joseph <gjoseph@sangoma.com>
Date:   Wed Nov 19 14:57:46 2025 -0700

    ccss:  Add option to ccss.conf to globally disable it.

    The Call Completion Supplementary Service feature is rarely used but many of
    it's functions are called by app_dial and channel.c "just in case".  These
    functions lock and unlock the channel just to see if CCSS is enabled on it,
    which it isn't 99.99% of the time.

    UserNote: A new "enabled" parameter has been added to ccss.conf.  It defaults
    to "yes" to preserve backwards compatibility but CCSS is rarely used so
    setting "enabled = no" in the "general" section can save some unneeded channel
    locking operations and log message spam.  Disabling ccss will also prevent
    the func_callcompletion and chan_dahdi modules from loading.

    DeveloperNote: A new API ast_is_cc_enabled() has been added.  It should be
    used to ensure that CCSS is enabled before making any other ast_cc_* calls.

diff --git a/apps/app_dial.c b/apps/app_dial.c
index ec39ab6504..3c2b6a6684 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -3615,5 +3615,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Dialing Application",
 	.support_level = AST_MODULE_SUPPORT_CORE,
 	.load = load_module,
 	.unload = unload_module,
-	.requires = "ccss",
 );
diff --git a/configs/samples/ccss.conf.sample b/configs/samples/ccss.conf.sample
index efb24928ae..5723e196cd 100644
--- a/configs/samples/ccss.conf.sample
+++ b/configs/samples/ccss.conf.sample
@@ -6,6 +6,11 @@
 ;

 [general]
+;
+;enabled = yes     ; CCSS is rarely used so setting this to "no"
+;                  ; will globally disable it and save some unneeded
+;                  ; channel locking and log messages.
+;
 ; The cc_max_requests option is a global limit on the number of
 ; CC requests that may be in the Asterisk system at any time.
 ;
diff --git a/include/asterisk/ccss.h b/include/asterisk/ccss.h
index ef821d9eb0..63f37e3107 100644
--- a/include/asterisk/ccss.h
+++ b/include/asterisk/ccss.h
@@ -1578,4 +1578,15 @@ typedef void (*ast_cc_callback_fn)(struct ast_channel *chan, struct ast_cc_confi
  */
 int ast_cc_callback(struct ast_channel *inbound, const char * const tech, const char * const dest, ast_cc_callback_fn callback);

+/*!
+ * \since 23.2.0
+ * \since 22.8.0
+ * \since 20.18.0
+ *
+ * \brief Determine if CCSS is enabled.
+ * \retval 0 Not enabled.
+ * \retval 1 Enabled
+ */
+int ast_cc_is_enabled(void);
+
 #endif /* _ASTERISK_CCSS_H */
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 276037b25d..7e8b3e353e 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -3996,10 +3996,13 @@ int ast_channel_redirecting_sub(struct ast_channel *autoservice_chan, struct ast
  * This function makes use of datastore operations on the channel, so
  * it is important to lock the channel before calling this function.
  *
+ * \warning You should call this function only if \ref ast_cc_is_enabled()
+ * returns true.
+ *
  * \param chan The channel to create the datastore on
  * \param base_params CCSS parameters we wish to copy into the channel
  * \retval 0 Success
- * \retval -1 Failure
+ * \retval -1 Failure or CCSS is globally disabled.
  */
 int ast_channel_cc_params_init(struct ast_channel *chan,
 		const struct ast_cc_config_params *base_params);
@@ -4012,8 +4015,11 @@ int ast_channel_cc_params_init(struct ast_channel *chan,
  * This function makes use of datastore operations on the channel, so
  * it is important to lock the channel before calling this function.
  *
+ * \warning You should call this function only if \ref ast_cc_is_enabled()
+ * returns true.
+ *
  * \param chan Channel to retrieve parameters from
- * \retval NULL Failure
+ * \retval NULL Failure or CCSS is globally disabled.
  * \retval non-NULL The parameters desired
  */
 struct ast_cc_config_params *ast_channel_get_cc_config_params(struct ast_channel *chan);
diff --git a/main/ccss.c b/main/ccss.c
index 5ec67cd289..49b40f7dde 100644
--- a/main/ccss.c
+++ b/main/ccss.c
@@ -115,6 +115,16 @@
  * them here at the top is easiest.
  */

+static int global_enabled;
+
+#define RETURN_IF_NOT_ENABLED(rc) \
+({ \
+	if (!global_enabled) { \
+		ast_debug(3, "CCSS disabled globally\n"); \
+		return rc; \
+	} \
+})
+
 /*!
  * The ast_sched_context used for all generic CC timeouts
  */
@@ -463,6 +473,9 @@ struct ast_cc_agent *ast_cc_agent_callback(int flags, ao2_callback_fn *function,
 {
 	struct cc_callback_helper helper = {.function = function, .args = args, .type = type};
 	struct cc_core_instance *core_instance;
+
+	RETURN_IF_NOT_ENABLED(NULL);
+
 	if ((core_instance = ao2_t_callback(cc_core_instances, flags, cc_agent_callback_helper, &helper,
 					"Calling provided agent callback function"))) {
 		struct ast_cc_agent *agent = cc_ref(core_instance->agent, "An outside entity needs the agent");
@@ -685,8 +698,11 @@ void ast_cc_default_config_params(struct ast_cc_config_params *params)

 struct ast_cc_config_params *__ast_cc_config_params_init(const char *file, int line, const char *function)
 {
-	struct ast_cc_config_params *params = __ast_malloc(sizeof(*params), file, line, function);
+	struct ast_cc_config_params *params;
+
+	RETURN_IF_NOT_ENABLED(NULL);

+	params = __ast_malloc(sizeof(*params), file, line, function);
 	if (!params) {
 		return NULL;
 	}
@@ -859,7 +875,9 @@ int ast_cc_is_config_param(const char * const name)

 void ast_cc_copy_config_params(struct ast_cc_config_params *dest, const struct ast_cc_config_params *src)
 {
-	*dest = *src;
+	if (dest && src) {
+		*dest = *src;
+	}
 }

 enum ast_cc_agent_policies ast_get_cc_agent_policy(struct ast_cc_config_params *config)
@@ -1167,8 +1185,11 @@ AST_RWLIST_HEAD_STATIC(cc_monitor_backends, cc_monitor_backend);

 int ast_cc_monitor_register(const struct ast_cc_monitor_callbacks *callbacks)
 {
-	struct cc_monitor_backend *backend = ast_calloc(1, sizeof(*backend));
+	struct cc_monitor_backend *backend;
+
+	RETURN_IF_NOT_ENABLED(0);

+	backend = ast_calloc(1, sizeof(*backend));
 	if (!backend) {
 		return -1;
 	}
@@ -1201,6 +1222,9 @@ static const struct ast_cc_monitor_callbacks *find_monitor_callbacks(const char
 void ast_cc_monitor_unregister(const struct ast_cc_monitor_callbacks *callbacks)
 {
 	struct cc_monitor_backend *backend;
+
+	RETURN_IF_NOT_ENABLED();
+
 	AST_RWLIST_WRLOCK(&cc_monitor_backends);
 	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&cc_monitor_backends, backend, next) {
 		if (backend->callbacks == callbacks) {
@@ -1222,8 +1246,11 @@ AST_RWLIST_HEAD_STATIC(cc_agent_backends, cc_agent_backend);

 int ast_cc_agent_register(const struct ast_cc_agent_callbacks *callbacks)
 {
-	struct cc_agent_backend *backend = ast_calloc(1, sizeof(*backend));
+	struct cc_agent_backend *backend;

+	RETURN_IF_NOT_ENABLED(0);
+
+	backend = ast_calloc(1, sizeof(*backend));
 	if (!backend) {
 		return -1;
 	}
@@ -1238,6 +1265,9 @@ int ast_cc_agent_register(const struct ast_cc_agent_callbacks *callbacks)
 void ast_cc_agent_unregister(const struct ast_cc_agent_callbacks *callbacks)
 {
 	struct cc_agent_backend *backend;
+
+	RETURN_IF_NOT_ENABLED();
+
 	AST_RWLIST_WRLOCK(&cc_agent_backends);
 	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&cc_agent_backends, backend, next) {
 		if (backend->callbacks == callbacks) {
@@ -1494,6 +1524,9 @@ int ast_cc_available_timer_expire(const void *data)
 {
 	struct ast_cc_monitor *monitor = (struct ast_cc_monitor *) data;
 	int res;
+
+	RETURN_IF_NOT_ENABLED(0);
+
 	monitor->available_timer_id = -1;
 	res = ast_cc_monitor_failed(monitor->core_id, monitor->interface->device_name, "Available timer expired for monitor");
 	cc_unref(monitor, "Unref reference from scheduler\n");
@@ -1996,6 +2029,8 @@ void ast_cc_extension_monitor_add_dialstring(struct ast_channel *incoming, const
 	struct cc_monitor_tree *interface_tree;
 	int id;

+	RETURN_IF_NOT_ENABLED();
+
 	ast_channel_lock(incoming);
 	if (!(cc_datastore = ast_channel_datastore_find(incoming, &dialed_cc_interfaces_info, NULL))) {
 		ast_channel_unlock(incoming);
@@ -2306,6 +2341,8 @@ void ast_handle_cc_control_frame(struct ast_channel *inbound, struct ast_channel
 	struct cc_control_payload *cc_data = frame_data;
 	struct cc_core_instance *core_instance;

+	RETURN_IF_NOT_ENABLED();
+
 	device_name = cc_data->device_name;
 	dialstring = cc_data->dialstring;

@@ -2416,6 +2453,12 @@ int ast_cc_call_init(struct ast_channel *chan, int *ignore_cc)
 	struct ast_cc_monitor *monitor;
 	struct ast_cc_config_params *cc_params;

+	if (!global_enabled) {
+		ast_debug(3, "CCSS disabled globally\n");
+		*ignore_cc = 1;
+		return 0;
+	}
+
 	ast_channel_lock(chan);

 	cc_params = ast_channel_get_cc_config_params(chan);
@@ -2474,6 +2517,8 @@ int ast_cc_get_current_core_id(struct ast_channel *chan)
 	struct dialed_cc_interfaces *cc_interfaces;
 	int core_id_return;

+	RETURN_IF_NOT_ENABLED(-1);
+
 	ast_channel_lock(chan);
 	if (!(datastore = ast_channel_datastore_find(chan, &dialed_cc_interfaces_info, NULL))) {
 		ast_channel_unlock(chan);
@@ -3377,10 +3422,13 @@ static const struct ast_datastore_info recall_ds_info = {

 int ast_setup_cc_recall_datastore(struct ast_channel *chan, const int core_id)
 {
-	struct ast_datastore *recall_datastore = ast_datastore_alloc(&recall_ds_info, NULL);
+	struct ast_datastore *recall_datastore;
 	struct cc_recall_ds_data *recall_data;
 	struct cc_core_instance *core_instance;

+	RETURN_IF_NOT_ENABLED(0);
+
+	recall_datastore = ast_datastore_alloc(&recall_ds_info, NULL);
 	if (!recall_datastore) {
 		return -1;
 	}
@@ -3417,6 +3465,8 @@ int ast_cc_is_recall(struct ast_channel *chan, int *core_id, const char * const
 	struct ast_cc_monitor *device_monitor;
 	int core_id_candidate;

+	RETURN_IF_NOT_ENABLED(0);
+
 	ast_assert(core_id != NULL);

 	*core_id = -1;
@@ -3494,6 +3544,8 @@ struct ast_cc_monitor *ast_cc_get_monitor_by_recall_core_id(const int core_id, c
 	struct cc_core_instance *core_instance = find_cc_core_instance(core_id);
 	struct ast_cc_monitor *monitor_iter;

+	RETURN_IF_NOT_ENABLED(NULL);
+
 	if (!core_instance) {
 		return NULL;
 	}
@@ -3608,6 +3660,8 @@ int ast_cc_agent_set_interfaces_chanvar(struct ast_channel *chan)
 	struct ast_str *str = ast_str_create(64);
 	int core_id;

+	RETURN_IF_NOT_ENABLED(0);
+
 	if (!str) {
 		return -1;
 	}
@@ -3645,6 +3699,8 @@ int ast_set_cc_interfaces_chanvar(struct ast_channel *chan, const char * const e
 	struct ast_str *str = ast_str_create(64);
 	int core_id;

+	RETURN_IF_NOT_ENABLED(0);
+
 	if (!str) {
 		return -1;
 	}
@@ -3695,6 +3751,8 @@ void ast_ignore_cc(struct ast_channel *chan)
 	struct dialed_cc_interfaces *cc_interfaces;
 	struct cc_recall_ds_data *recall_cc_data;

+	RETURN_IF_NOT_ENABLED();
+
 	ast_channel_lock(chan);
 	if ((cc_datastore = ast_channel_datastore_find(chan, &dialed_cc_interfaces_info, NULL))) {
 		cc_interfaces = cc_datastore->data;
@@ -3727,6 +3785,8 @@ int ast_cc_offer(struct ast_channel *caller_chan)
 	struct dialed_cc_interfaces *cc_interfaces;
 	char cc_is_offerable;

+	RETURN_IF_NOT_ENABLED(0);
+
 	ast_channel_lock(caller_chan);
 	if (!(datastore = ast_channel_datastore_find(caller_chan, &dialed_cc_interfaces_info, NULL))) {
 		ast_channel_unlock(caller_chan);
@@ -3749,6 +3809,8 @@ int ast_cc_agent_accept_request(int core_id, const char * const debug, ...)
 	va_list ap;
 	int res;

+	RETURN_IF_NOT_ENABLED(0);
+
 	va_start(ap, debug);
 	res = cc_request_state_change(CC_CALLER_REQUESTED, core_id, debug, ap);
 	va_end(ap);
@@ -3760,6 +3822,8 @@ int ast_cc_monitor_request_acked(int core_id, const char * const debug, ...)
 	va_list ap;
 	int res;

+	RETURN_IF_NOT_ENABLED(0);
+
 	va_start(ap, debug);
 	res = cc_request_state_change(CC_ACTIVE, core_id, debug, ap);
 	va_end(ap);
@@ -3771,6 +3835,8 @@ int ast_cc_monitor_callee_available(const int core_id, const char * const debug,
 	va_list ap;
 	int res;

+	RETURN_IF_NOT_ENABLED(0);
+
 	va_start(ap, debug);
 	res = cc_request_state_change(CC_CALLEE_READY, core_id, debug, ap);
 	va_end(ap);
@@ -3782,6 +3848,8 @@ int ast_cc_agent_caller_busy(int core_id, const char * debug, ...)
 	va_list ap;
 	int res;

+	RETURN_IF_NOT_ENABLED(0);
+
 	va_start(ap, debug);
 	res = cc_request_state_change(CC_CALLER_BUSY, core_id, debug, ap);
 	va_end(ap);
@@ -3793,6 +3861,8 @@ int ast_cc_agent_caller_available(int core_id, const char * const debug, ...)
 	va_list ap;
 	int res;

+	RETURN_IF_NOT_ENABLED(0);
+
 	va_start(ap, debug);
 	res = cc_request_state_change(CC_ACTIVE, core_id, debug, ap);
 	va_end(ap);
@@ -3804,6 +3874,8 @@ int ast_cc_agent_recalling(int core_id, const char * const debug, ...)
 	va_list ap;
 	int res;

+	RETURN_IF_NOT_ENABLED(0);
+
 	va_start(ap, debug);
 	res = cc_request_state_change(CC_RECALLING, core_id, debug, ap);
 	va_end(ap);
@@ -3818,6 +3890,8 @@ int ast_cc_completed(struct ast_channel *chan, const char * const debug, ...)
 	va_list ap;
 	int res;

+	RETURN_IF_NOT_ENABLED(0);
+
 	ast_channel_lock(chan);
 	if (!(recall_datastore = ast_channel_datastore_find(chan, &recall_ds_info, NULL))) {
 		/* Silly! Why did you call this function if there's no recall DS? */
@@ -3852,6 +3926,8 @@ int ast_cc_failed(int core_id, const char * const debug, ...)
 	va_list ap;
 	int res;

+	RETURN_IF_NOT_ENABLED(0);
+
 	va_start(ap, debug);
 	res = cc_request_state_change(CC_FAILED, core_id, debug, ap);
 	va_end(ap);
@@ -3915,6 +3991,8 @@ int ast_cc_monitor_failed(int core_id, const char *const monitor_name, const cha
 	int res;
 	va_list ap;

+	RETURN_IF_NOT_ENABLED(0);
+
 	if (!(failure_data = ast_calloc(1, sizeof(*failure_data)))) {
 		return -1;
 	}
@@ -3959,6 +4037,8 @@ int ast_cc_monitor_status_request(int core_id)
 	int res;
 	struct cc_core_instance *core_instance = find_cc_core_instance(core_id);

+	RETURN_IF_NOT_ENABLED(0);
+
 	if (!core_instance) {
 		return -1;
 	}
@@ -3996,6 +4076,8 @@ int ast_cc_monitor_stop_ringing(int core_id)
 	int res;
 	struct cc_core_instance *core_instance = find_cc_core_instance(core_id);

+	RETURN_IF_NOT_ENABLED(0);
+
 	if (!core_instance) {
 		return -1;
 	}
@@ -4024,6 +4106,8 @@ int ast_cc_monitor_party_b_free(int core_id)
 	int res;
 	struct cc_core_instance *core_instance = find_cc_core_instance(core_id);

+	RETURN_IF_NOT_ENABLED(0);
+
 	if (!core_instance) {
 		return -1;
 	}
@@ -4067,6 +4151,8 @@ int ast_cc_agent_status_response(int core_id, enum ast_device_state devstate)
 	struct cc_core_instance *core_instance;
 	int res;

+	RETURN_IF_NOT_ENABLED(0);
+
 	args = ast_calloc(1, sizeof(*args));
 	if (!args) {
 		return -1;
@@ -4125,6 +4211,8 @@ int ast_queue_cc_frame(struct ast_channel *chan, const char *monitor_type,
 	int retval;
 	struct ast_cc_config_params *cc_params;

+	RETURN_IF_NOT_ENABLED(0);
+
 	cc_params = ast_channel_get_cc_config_params(chan);
 	if (!cc_params) {
 		return -1;
@@ -4149,8 +4237,11 @@ int ast_cc_build_frame(struct ast_channel *chan, struct ast_cc_config_params *cc
 	const char * const dialstring, enum ast_cc_service_type service, void *private_data,
 	struct ast_frame *frame)
 {
-	struct cc_control_payload *payload = ast_calloc(1, sizeof(*payload));
+	struct cc_control_payload *payload;
+
+	RETURN_IF_NOT_ENABLED(0);

+	payload = ast_calloc(1, sizeof(*payload));
 	if (!payload) {
 		return -1;
 	}
@@ -4173,6 +4264,8 @@ void ast_cc_call_failed(struct ast_channel *incoming, struct ast_channel *outgoi
 	struct cc_control_payload payload;
 	struct ast_cc_config_params *cc_params;

+	RETURN_IF_NOT_ENABLED();
+
 	if (ast_channel_hangupcause(outgoing) != AST_CAUSE_BUSY && ast_channel_hangupcause(outgoing) != AST_CAUSE_CONGESTION) {
 		/* It doesn't make sense to try to offer CCBS to the caller if the reason for ast_call
 		 * failing is something other than busy or congestion
@@ -4204,6 +4297,9 @@ void ast_cc_busy_interface(struct ast_channel *inbound, struct ast_cc_config_par
 	const char *monitor_type, const char * const device_name, const char * const dialstring, void *private_data)
 {
 	struct cc_control_payload payload;
+
+	RETURN_IF_NOT_ENABLED();
+
 	if (cc_build_payload(inbound, cc_params, monitor_type, device_name, dialstring, AST_CC_CCBS, private_data, &payload)) {
 		/* Something screwed up. Don't try to handle this payload */
 		call_destructor_with_no_monitor(monitor_type, private_data);
@@ -4214,8 +4310,11 @@ void ast_cc_busy_interface(struct ast_channel *inbound, struct ast_cc_config_par

 int ast_cc_callback(struct ast_channel *inbound, const char * const tech, const char * const dest, ast_cc_callback_fn callback)
 {
-	const struct ast_channel_tech *chantech = ast_get_channel_tech(tech);
+	const struct ast_channel_tech *chantech;
+
+	RETURN_IF_NOT_ENABLED(0);

+	chantech = ast_get_channel_tech(tech);
 	if (chantech && chantech->cc_callback) {
 		chantech->cc_callback(inbound, dest, callback);
 	}
@@ -4340,27 +4439,19 @@ int ast_cc_monitor_count(const char * const name, const char * const type)
 {
 	struct count_monitors_cb_data data = {.device_name = name, .monitor_type = type,};

+	RETURN_IF_NOT_ENABLED(0);
+
 	ao2_t_callback(cc_core_instances, OBJ_NODATA, count_monitors_cb, &data, "Counting agents");
 	ast_log_dynamic_level(cc_logger_level, "Counted %d monitors\n", data.count);
 	return data.count;
 }

-static void initialize_cc_max_requests(void)
+static void initialize_cc_max_requests(struct ast_config *cc_config)
 {
-	struct ast_config *cc_config;
 	const char *cc_max_requests_str;
-	struct ast_flags config_flags = {0,};
 	char *endptr;

-	cc_config = ast_config_load2("ccss.conf", "ccss", config_flags);
-	if (!cc_config || cc_config == CONFIG_STATUS_FILEINVALID) {
-		ast_log(LOG_WARNING, "Could not find valid ccss.conf file. Using cc_max_requests default\n");
-		global_cc_max_requests = GLOBAL_CC_MAX_REQUESTS_DEFAULT;
-		return;
-	}
-
 	if (!(cc_max_requests_str = ast_variable_retrieve(cc_config, "general", "cc_max_requests"))) {
-		ast_config_destroy(cc_config);
 		global_cc_max_requests = GLOBAL_CC_MAX_REQUESTS_DEFAULT;
 		return;
 	}
@@ -4372,7 +4463,6 @@ static void initialize_cc_max_requests(void)
 		global_cc_max_requests = GLOBAL_CC_MAX_REQUESTS_DEFAULT;
 	}

-	ast_config_destroy(cc_config);
 	return;
 }

@@ -4404,18 +4494,8 @@ static void initialize_cc_devstate_map_helper(struct ast_config *cc_config, enum
  * should use any valid device state form that is recognized by
  * ast_devstate_val() function.
  */
-static void initialize_cc_devstate_map(void)
+static void initialize_cc_devstate_map(struct ast_config *cc_config)
 {
-	struct ast_config *cc_config;
-	struct ast_flags config_flags = { 0, };
-
-	cc_config = ast_config_load2("ccss.conf", "ccss", config_flags);
-	if (!cc_config || cc_config == CONFIG_STATUS_FILEINVALID) {
-		ast_log(LOG_WARNING,
-			"Could not find valid ccss.conf file. Using cc_[state]_devstate defaults\n");
-		return;
-	}
-
 	initialize_cc_devstate_map_helper(cc_config, CC_AVAILABLE, "cc_available_devstate");
 	initialize_cc_devstate_map_helper(cc_config, CC_CALLER_OFFERED, "cc_caller_offered_devstate");
 	initialize_cc_devstate_map_helper(cc_config, CC_CALLER_REQUESTED, "cc_caller_requested_devstate");
@@ -4426,7 +4506,40 @@ static void initialize_cc_devstate_map(void)
 	initialize_cc_devstate_map_helper(cc_config, CC_COMPLETE, "cc_complete_devstate");
 	initialize_cc_devstate_map_helper(cc_config, CC_FAILED, "cc_failed_devstate");

+}
+
+static int load_config(void)
+{
+	struct ast_flags config_flags = { 0, };
+	const char *enabled = NULL;
+	struct ast_config *cc_config = ast_config_load2("ccss.conf", "ccss", config_flags);
+
+	global_enabled = 1;
+
+	if (!cc_config || cc_config == CONFIG_STATUS_FILEINVALID) {
+		ast_log(LOG_WARNING, "Could not find valid ccss.conf file. Using cc_max_requests and devstate defaults\n");
+		global_cc_max_requests = GLOBAL_CC_MAX_REQUESTS_DEFAULT;
+		return 0;
+	}
+
+	if ((enabled = ast_variable_retrieve(cc_config, "general", "enabled"))) {
+		global_enabled = ast_true(enabled);
+	}
+
+	if (global_enabled) {
+		initialize_cc_max_requests(cc_config);
+		initialize_cc_devstate_map(cc_config);
+	} else {
+		ast_log(LOG_NOTICE, "CCSS disabled globally\n");
+	}
 	ast_config_destroy(cc_config);
+
+	return !global_enabled;
+}
+
+int ast_cc_is_enabled(void)
+{
+	return global_enabled;
 }

 static void cc_cli_print_monitor_stats(struct ast_cc_monitor *monitor, int fd, int parent_id)
@@ -4593,6 +4706,10 @@ static struct ast_cli_entry cc_cli[] = {

 static int unload_module(void)
 {
+	if (!global_enabled) {
+		return 0;
+	}
+
 	ast_devstate_prov_del("ccss");
 	ast_cc_agent_unregister(&generic_agent_callbacks);
 	ast_cc_monitor_unregister(&generic_monitor_cbs);
@@ -4625,6 +4742,11 @@ static int load_module(void)
 {
 	int res;

+	res = load_config();
+	if (res != 0) {
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
 	cc_core_instances = ao2_t_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
 		CC_CORE_INSTANCES_BUCKETS,
 		cc_core_instance_hash_fn, NULL, cc_core_instance_cmp_fn,
@@ -4657,10 +4779,6 @@ static int load_module(void)
 	ast_cli_register_multiple(cc_cli, ARRAY_LEN(cc_cli));
 	cc_logger_level = ast_logger_register_level(CC_LOGGER_LEVEL_NAME);
 	dialed_cc_interface_counter = 1;
-	initialize_cc_max_requests();
-
-	/* Read the map and register the device state callback for generic agents */
-	initialize_cc_devstate_map();
 	res |= ast_devstate_prov_add("ccss", ccss_device_state);

 	return res ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS;
diff --git a/main/channel.c b/main/channel.c
index 787bdb8886..8121b5c3d7 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -10519,6 +10519,10 @@ struct ast_cc_config_params *ast_channel_get_cc_config_params(struct ast_channel
 {
 	struct ast_datastore *cc_datastore;

+	if (!ast_cc_is_enabled()) {
+		return NULL;
+	}
+
 	if (!(cc_datastore = ast_channel_datastore_find(chan, &cc_channel_datastore_info, NULL))) {
 		/* If we can't find the datastore, it almost definitely means that the channel type being
 		 * used has not had its driver modified to parse CC config parameters. The best action
diff --git a/main/core_local.c b/main/core_local.c
index 32625bccba..af38dd7bbb 100644
--- a/main/core_local.c
+++ b/main/core_local.c
@@ -732,7 +732,9 @@ static int local_call(struct ast_channel *ast, const char *dest, int timeout)
 	if ((slash = strrchr(reduced_dest, '/'))) {
 		*slash = '\0';
 	}
-	ast_set_cc_interfaces_chanvar(chan, reduced_dest);
+	if (ast_cc_is_enabled()) {
+		ast_set_cc_interfaces_chanvar(chan, reduced_dest);
+	}

 	ao2_unlock(p);
 	pvt_locked = 0;
diff --git a/main/core_unreal.c b/main/core_unreal.c
index fd0f05b4e3..b9cbaebfd4 100644
--- a/main/core_unreal.c
+++ b/main/core_unreal.c
@@ -896,7 +896,9 @@ void ast_unreal_call_setup(struct ast_channel *semi1, struct ast_channel *semi2)
 	ast_channel_accountcode_set(semi2, ast_channel_peeraccount(semi1));
 	ast_channel_peeraccount_set(semi2, ast_channel_accountcode(semi1));

-	ast_channel_cc_params_init(semi2, ast_channel_get_cc_config_params(semi1));
+	if (ast_cc_is_enabled()) {
+		ast_channel_cc_params_init(semi2, ast_channel_get_cc_config_params(semi1));
+	}

 	/*
 	 * Make sure we inherit the AST_CAUSE_ANSWERED_ELSEWHERE if it's
@@ -1259,16 +1261,17 @@ struct ast_channel *ast_unreal_new_channels(struct ast_unreal_pvt *p,

 	ast_jb_configure(owner, &p->jb_conf);

-	if (ast_channel_cc_params_init(owner, requestor
-		? ast_channel_get_cc_config_params((struct ast_channel *) requestor) : NULL)) {
-		ast_channel_tech_pvt_set(owner, NULL);
-		ao2_ref(p, -1);
-		ast_channel_tech_pvt_set(owner, NULL);
-		ast_channel_unlock(owner);
-		ast_channel_release(owner);
-		return NULL;
+	if (ast_cc_is_enabled()) {
+		if (ast_channel_cc_params_init(owner, requestor
+			? ast_channel_get_cc_config_params((struct ast_channel *) requestor) : NULL)) {
+			ast_channel_tech_pvt_set(owner, NULL);
+			ao2_ref(p, -1);
+			ast_channel_tech_pvt_set(owner, NULL);
+			ast_channel_unlock(owner);
+			ast_channel_release(owner);
+			return NULL;
+		}
 	}
-
 	p->owner = owner;
 	ast_channel_unlock(owner);