Newer
Older
* more information regarding the actual structure of the tree, see
* the documentation provided in include/asterisk/ccss.h
*/
static const struct ast_datastore_info dialed_cc_interfaces_info = {
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
.type = "Dial CC Interfaces",
.duplicate = dialed_cc_interfaces_duplicate,
.destroy = dialed_cc_interfaces_destroy,
};
static struct extension_monitor_pvt *extension_monitor_pvt_init(void)
{
struct extension_monitor_pvt *ext_pvt = ast_calloc(1, sizeof(*ext_pvt));
if (!ext_pvt) {
return NULL;
}
AST_LIST_HEAD_INIT_NOLOCK(&ext_pvt->child_dialstrings);
return ext_pvt;
}
void ast_cc_extension_monitor_add_dialstring(struct ast_channel *incoming, const char * const dialstring, const char * const device_name)
{
struct ast_datastore *cc_datastore;
struct dialed_cc_interfaces *cc_interfaces;
struct ast_cc_monitor *monitor;
struct extension_monitor_pvt *extension_pvt;
struct extension_child_dialstring *child_dialstring;
struct cc_monitor_tree *interface_tree;
int id;
ast_channel_lock(incoming);
if (!(cc_datastore = ast_channel_datastore_find(incoming, &dialed_cc_interfaces_info, NULL))) {
ast_channel_unlock(incoming);
return;
}
cc_interfaces = cc_datastore->data;
interface_tree = cc_interfaces->interface_tree;
id = cc_interfaces->dial_parent_id;
ast_channel_unlock(incoming);
AST_LIST_LOCK(interface_tree);
AST_LIST_TRAVERSE(interface_tree, monitor, next) {
if (monitor->id == id) {
break;
}
}
if (!monitor) {
AST_LIST_UNLOCK(interface_tree);
return;
}
extension_pvt = monitor->private_data;
if (!(child_dialstring = ast_calloc(1, sizeof(*child_dialstring)))) {
AST_LIST_UNLOCK(interface_tree);
return;
}
ast_copy_string(child_dialstring->original_dialstring, dialstring, sizeof(child_dialstring->original_dialstring));
ast_copy_string(child_dialstring->device_name, device_name, sizeof(child_dialstring->device_name));
child_dialstring->is_valid = 1;
AST_LIST_INSERT_TAIL(&extension_pvt->child_dialstrings, child_dialstring, next);
AST_LIST_UNLOCK(interface_tree);
}
static void cc_extension_monitor_change_is_valid(struct cc_core_instance *core_instance, unsigned int parent_id, const char * const device_name, int is_valid)
{
struct ast_cc_monitor *monitor_iter;
struct extension_monitor_pvt *extension_pvt;
struct extension_child_dialstring *child_dialstring;
AST_LIST_TRAVERSE(core_instance->monitors, monitor_iter, next) {
if (monitor_iter->id == parent_id) {
break;
}
}
if (!monitor_iter) {
return;
}
extension_pvt = monitor_iter->private_data;
AST_LIST_TRAVERSE(&extension_pvt->child_dialstrings, child_dialstring, next) {
if (!strcmp(child_dialstring->device_name, device_name)) {
child_dialstring->is_valid = is_valid;
break;
}
}
}
/*!
* \internal
* \brief Allocate and initialize an "extension" interface for CC purposes
*
* When app_dial starts, this function is called in order to set up the
* information about the extension in which this Dial is occurring. Any
* devices dialed will have this particular cc_monitor as a parent.
*
* \param exten Extension from which Dial is occurring
* \param context Context to which exten belongs
* \param parent_id What should we set the parent_id of this interface to?
* \retval NULL Memory allocation failure
* \retval non-NULL The newly-created cc_monitor for the extension
*/
static struct ast_cc_monitor *cc_extension_monitor_init(const char * const exten, const char * const context, const unsigned int parent_id)
{
struct ast_str *str = ast_str_alloca(2 * AST_MAX_EXTENSION);
struct ast_cc_interface *cc_interface;
struct ast_cc_monitor *monitor;
ast_str_set(&str, 0, "%s@%s", exten, context);
if (!(cc_interface = ao2_t_alloc(sizeof(*cc_interface) + ast_str_strlen(str), cc_interface_destroy,
"Allocating new ast_cc_interface"))) {
return NULL;
}
if (!(monitor = ao2_t_alloc(sizeof(*monitor), cc_monitor_destroy, "Allocating new ast_cc_monitor"))) {
cc_unref(cc_interface, "failed to allocate the monitor, so unref the interface");
return NULL;
}
if (!(monitor->private_data = extension_monitor_pvt_init())) {
cc_unref(monitor, "Failed to initialize extension monitor private data. uref monitor");
cc_unref(cc_interface, "Failed to initialize extension monitor private data. unref cc_interface");
}
monitor->id = ast_atomic_fetchadd_int(&dialed_cc_interface_counter, +1);
monitor->parent_id = parent_id;
cc_interface->monitor_type = "extension";
cc_interface->monitor_class = AST_CC_EXTENSION_MONITOR;
strcpy(cc_interface->device_name, ast_str_buffer(str));
monitor->interface = cc_interface;
ast_log_dynamic_level(cc_logger_level, "Created an extension cc interface for '%s' with id %u and parent %u\n", cc_interface->device_name, monitor->id, monitor->parent_id);
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
return monitor;
}
/*!
* \internal
* \brief allocate dialed_cc_interfaces datastore and initialize fields
*
* This function is called when Situation 1 occurs in ast_cc_call_init.
* See that function for more information on what Situation 1 is.
*
* In this particular case, we have to do a lot of memory allocation in order
* to create the datastore, the data for the datastore, the tree of interfaces
* that we'll be adding to, and the initial extension interface for this Dial
* attempt.
*
* \param chan The channel onto which the datastore should be added.
* \retval -1 An error occurred
* \retval 0 Success
*/
static int cc_interfaces_datastore_init(struct ast_channel *chan) {
struct dialed_cc_interfaces *interfaces;
struct ast_cc_monitor *monitor;
struct ast_datastore *dial_cc_datastore;
/*XXX This may be a bit controversial. In an attempt to not allocate
* extra resources, I make sure that a future request will be within
* limits. The problem here is that it is reasonable to think that
* even if we're not within the limits at this point, we may be by
* the time the requestor will have made his request. This may be
* deleted at some point.
*/
if (!ast_cc_request_is_within_limits()) {
return 0;
}
if (!(interfaces = ast_calloc(1, sizeof(*interfaces)))) {
return -1;
}
if (!(monitor = cc_extension_monitor_init(S_OR(ast_channel_macroexten(chan), ast_channel_exten(chan)), S_OR(ast_channel_macrocontext(chan), ast_channel_context(chan)), 0))) {
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
ast_free(interfaces);
return -1;
}
if (!(dial_cc_datastore = ast_datastore_alloc(&dialed_cc_interfaces_info, NULL))) {
cc_unref(monitor, "Could not allocate the dialed interfaces datastore. Unreffing monitor");
ast_free(interfaces);
return -1;
}
if (!(interfaces->interface_tree = ao2_t_alloc(sizeof(*interfaces->interface_tree), cc_interface_tree_destroy,
"Allocate monitor tree"))) {
ast_datastore_free(dial_cc_datastore);
cc_unref(monitor, "Could not allocate monitor tree on dialed interfaces datastore. Unreffing monitor");
ast_free(interfaces);
return -1;
}
/* Finally, all that allocation is done... */
AST_LIST_HEAD_INIT(interfaces->interface_tree);
AST_LIST_INSERT_TAIL(interfaces->interface_tree, monitor, next);
cc_ref(monitor, "List's reference to extension monitor");
dial_cc_datastore->data = interfaces;
dial_cc_datastore->inheritance = DATASTORE_INHERIT_FOREVER;
interfaces->dial_parent_id = monitor->id;
interfaces->core_id = monitor->core_id = ast_atomic_fetchadd_int(&core_id_counter, +1);
interfaces->is_original_caller = 1;
ast_channel_lock(chan);
ast_channel_datastore_add(chan, dial_cc_datastore);
ast_channel_unlock(chan);
cc_unref(monitor, "Unreffing allocation's reference");
return 0;
}
/*!
* \internal
* \brief Call a monitor's destructor before the monitor has been allocated
* \since 1.8
*
* \param monitor_type The type of monitor callbacks to use when calling the destructor
* \param private_data Data allocated by a channel driver that must be freed
*
* \details
* I'll admit, this is a bit evil.
*
* When a channel driver determines that it can offer a call completion service to
* a caller, it is very likely that the channel driver will need to allocate some
* data so that when the time comes to request CC, the channel driver will have the
* necessary data at hand.
*
* The problem is that there are many places where failures may occur before the monitor
* has been properly allocated and had its callbacks assigned to it. If one of these
* failures should occur, then we still need to let the channel driver know that it
* must destroy the data that it allocated.
*
* \return Nothing
*/
static void call_destructor_with_no_monitor(const char * const monitor_type, void *private_data)
{
const struct ast_cc_monitor_callbacks *monitor_callbacks = find_monitor_callbacks(monitor_type);
if (!monitor_callbacks) {
return;
}
monitor_callbacks->destructor(private_data);
}
/*!
* \internal
* \brief Allocate and intitialize a device cc_monitor
*
* For all intents and purposes, this is the same as
* cc_extension_monitor_init, except that there is only
* a single parameter used for naming the interface.
*
* This function is called when handling AST_CONTROL_CC frames.
* The device has reported that CC is possible, so we add it
* to the interface_tree.
*
* Note that it is not necessarily erroneous to add the same
* device to the tree twice. If the same device is called by
* two different extension during the same call, then
* that is a legitimate situation.
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
*
* \param device_name The name of the device being added to the tree
* \param dialstring The dialstring used to dial the device being added
* \param parent_id The parent of this new tree node.
* \retval NULL Memory allocation failure
* \retval non-NULL The new ast_cc_interface created.
*/
static struct ast_cc_monitor *cc_device_monitor_init(const char * const device_name, const char * const dialstring, const struct cc_control_payload *cc_data, int core_id)
{
struct ast_cc_interface *cc_interface;
struct ast_cc_monitor *monitor;
size_t device_name_len = strlen(device_name);
int parent_id = cc_data->parent_interface_id;
if (!(cc_interface = ao2_t_alloc(sizeof(*cc_interface) + device_name_len, cc_interface_destroy,
"Allocating new ast_cc_interface"))) {
return NULL;
}
if (!(cc_interface->config_params = ast_cc_config_params_init())) {
cc_unref(cc_interface, "Failed to allocate config params, unref interface");
return NULL;
}
if (!(monitor = ao2_t_alloc(sizeof(*monitor), cc_monitor_destroy, "Allocating new ast_cc_monitor"))) {
cc_unref(cc_interface, "Failed to allocate monitor, unref interface");
return NULL;
}
if (!(monitor->dialstring = ast_strdup(dialstring))) {
cc_unref(monitor, "Failed to copy dialable name. Unref monitor");
cc_unref(cc_interface, "Failed to copy dialable name");
return NULL;
}
if (!(monitor->callbacks = find_monitor_callbacks(cc_data->monitor_type))) {
cc_unref(monitor, "Failed to find monitor callbacks. Unref monitor");
cc_unref(cc_interface, "Failed to find monitor callbacks");
return NULL;
}
strcpy(cc_interface->device_name, device_name);
monitor->id = ast_atomic_fetchadd_int(&dialed_cc_interface_counter, +1);
monitor->parent_id = parent_id;
monitor->core_id = core_id;
monitor->service_offered = cc_data->service;
monitor->private_data = cc_data->private_data;
cc_interface->monitor_type = cc_data->monitor_type;
cc_interface->monitor_class = AST_CC_DEVICE_MONITOR;
monitor->interface = cc_interface;
monitor->available_timer_id = -1;
ast_cc_copy_config_params(cc_interface->config_params, &cc_data->config_params);
ast_log_dynamic_level(cc_logger_level, "Core %d: Created a device cc interface for '%s' with id %u and parent %u\n",
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
monitor->core_id, cc_interface->device_name, monitor->id, monitor->parent_id);
return monitor;
}
/*!
* \details
* Unless we are ignoring CC for some reason, we will always
* call this function when we read an AST_CONTROL_CC frame
* from an outbound channel.
*
* This function will call cc_device_monitor_init to
* create the new cc_monitor for the device from which
* we read the frame. In addition, the new device will be added
* to the monitor tree on the dialed_cc_interfaces datastore
* on the inbound channel.
*
* If this is the first AST_CONTROL_CC frame that we have handled
* for this call, then we will also initialize the CC core for
* this call.
*/
void ast_handle_cc_control_frame(struct ast_channel *inbound, struct ast_channel *outbound, void *frame_data)
{
char *device_name;
char *dialstring;
struct ast_cc_monitor *monitor;
struct ast_datastore *cc_datastore;
struct dialed_cc_interfaces *cc_interfaces;
struct cc_control_payload *cc_data = frame_data;
struct cc_core_instance *core_instance;
device_name = cc_data->device_name;
dialstring = cc_data->dialstring;
ast_channel_lock(inbound);
if (!(cc_datastore = ast_channel_datastore_find(inbound, &dialed_cc_interfaces_info, NULL))) {
ast_log(LOG_WARNING, "Unable to retrieve CC datastore while processing CC frame from '%s'. CC services will be unavailable.\n", device_name);
ast_channel_unlock(inbound);
call_destructor_with_no_monitor(cc_data->monitor_type, cc_data->private_data);
return;
}
cc_interfaces = cc_datastore->data;
if (cc_interfaces->ignore) {
ast_channel_unlock(inbound);
call_destructor_with_no_monitor(cc_data->monitor_type, cc_data->private_data);
return;
}
if (!cc_interfaces->is_original_caller) {
/* If the is_original_caller is not set on the *inbound* channel, then
* it must be a local channel. As such, we do not want to create a core instance
* or an agent for the local channel. Instead, we want to pass this along to the
* other side of the local channel so that the original caller can benefit.
*/
ast_channel_unlock(inbound);
ast_indicate_data(inbound, AST_CONTROL_CC, cc_data, sizeof(*cc_data));
return;
}
core_instance = find_cc_core_instance(cc_interfaces->core_id);
if (!core_instance) {
core_instance = cc_core_init_instance(inbound, cc_interfaces->interface_tree,
cc_interfaces->core_id, cc_data);
if (!core_instance) {
cc_interfaces->ignore = 1;
ast_channel_unlock(inbound);
call_destructor_with_no_monitor(cc_data->monitor_type, cc_data->private_data);
return;
}
}
ast_channel_unlock(inbound);
/* Yeah this kind of sucks, but luckily most people
* aren't dialing thousands of interfaces on every call
*
* This traversal helps us to not create duplicate monitors in
* case a device queues multiple CC control frames.
*/
AST_LIST_LOCK(cc_interfaces->interface_tree);
AST_LIST_TRAVERSE(cc_interfaces->interface_tree, monitor, next) {
if (!strcmp(monitor->interface->device_name, device_name)) {
ast_log_dynamic_level(cc_logger_level, "Core %d: Device %s sent us multiple CC control frames. Ignoring those beyond the first.\n",
core_instance->core_id, device_name);
AST_LIST_UNLOCK(cc_interfaces->interface_tree);
cc_unref(core_instance, "Returning early from ast_handle_cc_control_frame. Unref core_instance");
call_destructor_with_no_monitor(cc_data->monitor_type, cc_data->private_data);
return;
}
}
AST_LIST_UNLOCK(cc_interfaces->interface_tree);
if (!(monitor = cc_device_monitor_init(device_name, dialstring, cc_data, core_instance->core_id))) {
ast_log(LOG_WARNING, "Unable to create CC device interface for '%s'. CC services will be unavailable on this interface.\n", device_name);
cc_unref(core_instance, "Returning early from ast_handle_cc_control_frame. Unref core_instance");
call_destructor_with_no_monitor(cc_data->monitor_type, cc_data->private_data);
return;
}
AST_LIST_LOCK(cc_interfaces->interface_tree);
cc_ref(monitor, "monitor tree's reference to the monitor");
AST_LIST_INSERT_TAIL(cc_interfaces->interface_tree, monitor, next);
AST_LIST_UNLOCK(cc_interfaces->interface_tree);
cc_extension_monitor_change_is_valid(core_instance, monitor->parent_id, monitor->interface->device_name, 0);
cc_publish_available(cc_interfaces->core_id, device_name, cc_service_to_string(cc_data->service));
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
cc_unref(core_instance, "Done with core_instance after handling CC control frame");
cc_unref(monitor, "Unref reference from allocating monitor");
}
int ast_cc_call_init(struct ast_channel *chan, int *ignore_cc)
{
/* There are three situations to deal with here:
*
* 1. The channel does not have a dialed_cc_interfaces datastore on
* it. This means that this is the first time that Dial has
* been called. We need to create/initialize the datastore.
*
* 2. The channel does have a cc_interface datastore on it and
* the "ignore" indicator is 0. This means that a Local channel
* was called by a "parent" dial. We can check the datastore's
* parent field to see who the root of this particular dial tree
* is.
*
* 3. The channel does have a cc_interface datastore on it and
* the "ignore" indicator is 1. This means that a second Dial call
* is being made from an extension. In this case, we do not
* want to make any additions/modifications to the datastore. We
* will instead set a flag to indicate that CCSS is completely
* disabled for this Dial attempt.
*/
struct ast_datastore *cc_interfaces_datastore;
struct dialed_cc_interfaces *interfaces;
struct ast_cc_monitor *monitor;
struct ast_cc_config_params *cc_params;
ast_channel_lock(chan);
cc_params = ast_channel_get_cc_config_params(chan);
if (!cc_params) {
ast_channel_unlock(chan);
return -1;
}
if (ast_get_cc_agent_policy(cc_params) == AST_CC_AGENT_NEVER) {
/* We can't offer CC to this caller anyway, so don't bother with CC on this call
*/
*ignore_cc = 1;
ast_channel_unlock(chan);
ast_log_dynamic_level(cc_logger_level, "Agent policy for %s is 'never'. CC not possible\n", ast_channel_name(chan));
return 0;
}
if (!(cc_interfaces_datastore = ast_channel_datastore_find(chan, &dialed_cc_interfaces_info, NULL))) {
/* Situation 1 has occurred */
ast_channel_unlock(chan);
return cc_interfaces_datastore_init(chan);
}
interfaces = cc_interfaces_datastore->data;
ast_channel_unlock(chan);
if (interfaces->ignore) {
/* Situation 3 has occurred */
*ignore_cc = 1;
ast_log_dynamic_level(cc_logger_level, "Datastore is present with ignore flag set. Ignoring CC offers on this call\n");
return 0;
}
/* Situation 2 has occurred */
if (!(monitor = cc_extension_monitor_init(S_OR(ast_channel_macroexten(chan), ast_channel_exten(chan)),
S_OR(ast_channel_macrocontext(chan), ast_channel_context(chan)), interfaces->dial_parent_id))) {
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
return -1;
}
monitor->core_id = interfaces->core_id;
AST_LIST_LOCK(interfaces->interface_tree);
cc_ref(monitor, "monitor tree's reference to the monitor");
AST_LIST_INSERT_TAIL(interfaces->interface_tree, monitor, next);
AST_LIST_UNLOCK(interfaces->interface_tree);
interfaces->dial_parent_id = monitor->id;
cc_unref(monitor, "Unref monitor's allocation reference");
return 0;
}
int ast_cc_request_is_within_limits(void)
{
return cc_request_count < global_cc_max_requests;
}
int ast_cc_get_current_core_id(struct ast_channel *chan)
{
struct ast_datastore *datastore;
struct dialed_cc_interfaces *cc_interfaces;
int core_id_return;
ast_channel_lock(chan);
if (!(datastore = ast_channel_datastore_find(chan, &dialed_cc_interfaces_info, NULL))) {
ast_channel_unlock(chan);
return -1;
}
cc_interfaces = datastore->data;
core_id_return = cc_interfaces->ignore ? -1 : cc_interfaces->core_id;
ast_channel_unlock(chan);
return core_id_return;
}
static long count_agents(const char * const caller, const int core_id_exception)
{
struct count_agents_cb_data data = {.core_id_exception = core_id_exception,};
ao2_t_callback_data(cc_core_instances, OBJ_NODATA, count_agents_cb, (char *)caller, &data, "Counting agents");
ast_log_dynamic_level(cc_logger_level, "Counted %d agents\n", data.count);
return data.count;
}
static void kill_duplicate_offers(char *caller)
{
unsigned long match_flags = MATCH_NO_REQUEST;
struct ao2_iterator *dups_iter;
/*
* Must remove the ref that was in cc_core_instances outside of
* the container lock to prevent deadlock.
*/
dups_iter = ao2_t_callback_data(cc_core_instances, OBJ_MULTIPLE | OBJ_UNLINK,
match_agent, caller, &match_flags, "Killing duplicate offers");
if (dups_iter) {
/* Now actually unref any duplicate offers by simply destroying the iterator. */
ao2_iterator_destroy(dups_iter);
}
}
static void check_callback_sanity(const struct ast_cc_agent_callbacks *callbacks)
{
ast_assert(callbacks->init != NULL);
ast_assert(callbacks->start_offer_timer != NULL);
ast_assert(callbacks->stop_offer_timer != NULL);
ast_assert(callbacks->respond != NULL);
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
ast_assert(callbacks->status_request != NULL);
ast_assert(callbacks->start_monitoring != NULL);
ast_assert(callbacks->callee_available != NULL);
ast_assert(callbacks->destructor != NULL);
}
static void agent_destroy(void *data)
{
struct ast_cc_agent *agent = data;
if (agent->callbacks) {
agent->callbacks->destructor(agent);
}
ast_cc_config_params_destroy(agent->cc_params);
}
static struct ast_cc_agent *cc_agent_init(struct ast_channel *caller_chan,
const char * const caller_name, const int core_id,
struct cc_monitor_tree *interface_tree)
{
struct ast_cc_agent *agent;
struct ast_cc_config_params *cc_params;
if (!(agent = ao2_t_alloc(sizeof(*agent) + strlen(caller_name), agent_destroy,
"Allocating new ast_cc_agent"))) {
return NULL;
}
agent->core_id = core_id;
strcpy(agent->device_name, caller_name);
cc_params = ast_channel_get_cc_config_params(caller_chan);
if (!cc_params) {
cc_unref(agent, "Could not get channel config params.");
return NULL;
}
if (!(agent->cc_params = ast_cc_config_params_init())) {
cc_unref(agent, "Could not init agent config params.");
return NULL;
}
ast_cc_copy_config_params(agent->cc_params, cc_params);
if (!(agent->callbacks = find_agent_callbacks(caller_chan))) {
cc_unref(agent, "Could not find agent callbacks.");
return NULL;
}
check_callback_sanity(agent->callbacks);
if (agent->callbacks->init(agent, caller_chan)) {
cc_unref(agent, "Agent init callback failed.");
return NULL;
}
ast_log_dynamic_level(cc_logger_level, "Core %u: Created an agent for caller %s\n",
agent->core_id, agent->device_name);
return agent;
}
/* Generic agent callbacks */
static int cc_generic_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan);
static int cc_generic_agent_start_offer_timer(struct ast_cc_agent *agent);
static int cc_generic_agent_stop_offer_timer(struct ast_cc_agent *agent);
static void cc_generic_agent_respond(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason);
static int cc_generic_agent_status_request(struct ast_cc_agent *agent);
static int cc_generic_agent_stop_ringing(struct ast_cc_agent *agent);
static int cc_generic_agent_start_monitoring(struct ast_cc_agent *agent);
static int cc_generic_agent_recall(struct ast_cc_agent *agent);
static void cc_generic_agent_destructor(struct ast_cc_agent *agent);
static struct ast_cc_agent_callbacks generic_agent_callbacks = {
.type = "generic",
.init = cc_generic_agent_init,
.start_offer_timer = cc_generic_agent_start_offer_timer,
.stop_offer_timer = cc_generic_agent_stop_offer_timer,
.respond = cc_generic_agent_respond,
.status_request = cc_generic_agent_status_request,
.stop_ringing = cc_generic_agent_stop_ringing,
.start_monitoring = cc_generic_agent_start_monitoring,
.callee_available = cc_generic_agent_recall,
.destructor = cc_generic_agent_destructor,
};
struct cc_generic_agent_pvt {
/*!
* Subscription to device state
*
* Used in the CC_CALLER_BUSY state. The
* generic agent will subscribe to the
* device state of the caller in order to
* determine when we may move on
*/
struct stasis_subscription *sub;
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
/*!
* Scheduler id of offer timer.
*/
int offer_timer_id;
/*!
* Caller ID number
*
* When we re-call the caller, we need
* to provide this information to
* ast_request_and_dial so that the
* information will be present in the
* call to the callee
*/
char cid_num[AST_CHANNEL_NAME];
/*!
* Caller ID name
*
* See the description of cid_num.
* The same applies here, except this
* is the caller's name.
*/
char cid_name[AST_CHANNEL_NAME];
/*!
* Extension dialed
*
* The original extension dialed. This is used
* so that when performing a recall, we can
* call the proper extension.
*/
char exten[AST_CHANNEL_NAME];
/*!
* Context dialed
*
* The original context dialed. This is used
* so that when performaing a recall, we can
* call into the proper context
*/
char context[AST_CHANNEL_NAME];
};
static int cc_generic_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan)
{
struct cc_generic_agent_pvt *generic_pvt = ast_calloc(1, sizeof(*generic_pvt));
if (!generic_pvt) {
return -1;
}
generic_pvt->offer_timer_id = -1;
if (ast_channel_caller(chan)->id.number.valid && ast_channel_caller(chan)->id.number.str) {
ast_copy_string(generic_pvt->cid_num, ast_channel_caller(chan)->id.number.str, sizeof(generic_pvt->cid_num));
}
if (ast_channel_caller(chan)->id.name.valid && ast_channel_caller(chan)->id.name.str) {
ast_copy_string(generic_pvt->cid_name, ast_channel_caller(chan)->id.name.str, sizeof(generic_pvt->cid_name));
}
ast_copy_string(generic_pvt->exten, S_OR(ast_channel_macroexten(chan), ast_channel_exten(chan)), sizeof(generic_pvt->exten));
ast_copy_string(generic_pvt->context, S_OR(ast_channel_macrocontext(chan), ast_channel_context(chan)), sizeof(generic_pvt->context));
agent->private_data = generic_pvt;
ast_set_flag(agent, AST_CC_AGENT_SKIP_OFFER);
return 0;
}
static int offer_timer_expire(const void *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 %u: 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(agent, "Remove scheduler's reference to the agent");
return 0;
}
static int cc_generic_agent_start_offer_timer(struct ast_cc_agent *agent)
{
int when;
int sched_id;
struct cc_generic_agent_pvt *generic_pvt = agent->private_data;
ast_assert(cc_sched_context != NULL);
ast_assert(agent->cc_params != NULL);
when = ast_get_cc_offer_timer(agent->cc_params) * 1000;
ast_log_dynamic_level(cc_logger_level, "Core %u: About to schedule offer timer expiration for %d ms\n",
if ((sched_id = ast_sched_add(cc_sched_context, when, offer_timer_expire, cc_ref(agent, "Give scheduler an agent ref"))) == -1) {
return -1;
}
generic_pvt->offer_timer_id = sched_id;
return 0;
}
static int cc_generic_agent_stop_offer_timer(struct ast_cc_agent *agent)
{
struct cc_generic_agent_pvt *generic_pvt = agent->private_data;
if (generic_pvt->offer_timer_id != -1) {
if (!ast_sched_del(cc_sched_context, generic_pvt->offer_timer_id)) {
cc_unref(agent, "Remove scheduler's reference to the agent");
}
generic_pvt->offer_timer_id = -1;
}
return 0;
}
static void cc_generic_agent_respond(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason)
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
{
/* The generic agent doesn't have to do anything special to
* acknowledge a CC request. Just return.
*/
return;
}
static int cc_generic_agent_status_request(struct ast_cc_agent *agent)
{
ast_cc_agent_status_response(agent->core_id, ast_device_state(agent->device_name));
return 0;
}
static int cc_generic_agent_stop_ringing(struct ast_cc_agent *agent)
{
struct ast_channel *recall_chan = ast_channel_get_by_name_prefix(agent->device_name, strlen(agent->device_name));
if (!recall_chan) {
return 0;
}
ast_softhangup(recall_chan, AST_SOFTHANGUP_EXPLICIT);
return 0;
}
static void generic_agent_devstate_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
struct ast_cc_agent *agent = userdata;
enum ast_device_state new_state;
struct ast_device_state_message *dev_state;
struct cc_generic_agent_pvt *generic_pvt = agent->private_data;
if (stasis_subscription_final_message(sub, msg)) {
cc_unref(agent, "Done holding ref for subscription");
return;
} else if (ast_device_state_message_type() != stasis_message_type(msg)) {
return;
dev_state = stasis_message_data(msg);
if (dev_state->eid) {
/* ignore non-aggregate states */
return;
}
new_state = dev_state->state;
if (!cc_generic_is_device_available(new_state)) {
/* Not interested in this new state of the device. It is still busy. */
return;
}
generic_pvt->sub = stasis_unsubscribe(sub);
ast_cc_agent_caller_available(agent->core_id, "%s is no longer busy", agent->device_name);
}
static int cc_generic_agent_start_monitoring(struct ast_cc_agent *agent)
{
struct cc_generic_agent_pvt *generic_pvt = agent->private_data;
struct ast_str *str = ast_str_alloca(128);
struct stasis_topic *device_specific_topic;
ast_assert(generic_pvt->sub == NULL);
ast_str_set(&str, 0, "Agent monitoring %s device state since it is busy\n",
agent->device_name);
device_specific_topic = ast_device_state_topic(agent->device_name);
if (!device_specific_topic) {
return -1;
}
if (!(generic_pvt->sub = stasis_subscribe(device_specific_topic, generic_agent_devstate_cb, agent))) {
cc_ref(agent, "Ref agent for subscription");
return 0;
}
static void *generic_recall(void *data)
{
struct ast_cc_agent *agent = data;
struct cc_generic_agent_pvt *generic_pvt = agent->private_data;
const char *interface = S_OR(ast_get_cc_agent_dialstring(agent->cc_params), ast_strdupa(agent->device_name));
const char *tech;
char *target;
int reason;
struct ast_channel *chan;
const char *callback_macro = ast_get_cc_callback_macro(agent->cc_params);
const char *callback_sub = ast_get_cc_callback_sub(agent->cc_params);
unsigned int recall_timer = ast_get_cc_recall_timer(agent->cc_params) * 1000;
struct ast_format_cap *tmp_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
if (!tmp_cap) {
return NULL;
}
tech = interface;
if ((target = strchr(interface, '/'))) {
*target++ = '\0';
}
ast_format_cap_append(tmp_cap, ast_format_slin, 0);
if (!(chan = ast_request_and_dial(tech, tmp_cap, NULL, NULL, target, recall_timer, &reason, generic_pvt->cid_num, generic_pvt->cid_name))) {
/* Hmm, no channel. Sucks for you, bud.
*/
ast_log_dynamic_level(cc_logger_level, "Core %u: Failed to call back %s for reason %d\n",
agent->core_id, agent->device_name, reason);
ast_cc_failed(agent->core_id, "Failed to call back device %s/%s", tech, target);
ao2_ref(tmp_cap, -1);
ao2_ref(tmp_cap, -1);
/* We have a channel. It's time now to set up the datastore of recalled CC interfaces.
* This will be a common task for all recall functions. If it were possible, I'd have
* the core do it automatically, but alas I cannot. Instead, I will provide a public
* function to do so.
*/
ast_setup_cc_recall_datastore(chan, agent->core_id);
ast_cc_agent_set_interfaces_chanvar(chan);
ast_channel_exten_set(chan, generic_pvt->exten);
ast_channel_context_set(chan, generic_pvt->context);
ast_channel_priority_set(chan, 1);
pbx_builtin_setvar_helper(chan, "CC_EXTEN", generic_pvt->exten);
pbx_builtin_setvar_helper(chan, "CC_CONTEXT", generic_pvt->context);
if (!ast_strlen_zero(callback_macro)) {
ast_log_dynamic_level(cc_logger_level, "Core %u: There's a callback macro configured for agent %s\n",
agent->core_id, agent->device_name);
if (ast_app_exec_macro(NULL, chan, callback_macro)) {
ast_cc_failed(agent->core_id, "Callback macro to %s failed. Maybe a hangup?", agent->device_name);
ast_hangup(chan);
return NULL;
}
}
if (!ast_strlen_zero(callback_sub)) {
ast_log_dynamic_level(cc_logger_level, "Core %u: There's a callback subroutine configured for agent %s\n",
agent->core_id, agent->device_name);
if (ast_app_exec_sub(NULL, chan, callback_sub, 0)) {
ast_cc_failed(agent->core_id, "Callback subroutine to %s failed. Maybe a hangup?", agent->device_name);
ast_hangup(chan);
return NULL;
}
}
if (ast_pbx_start(chan)) {
ast_cc_failed(agent->core_id, "PBX failed to start for %s.", agent->device_name);
ast_hangup(chan);
return NULL;
}
ast_cc_agent_recalling(agent->core_id, "Generic agent %s is recalling",
agent->device_name);
return NULL;
}
static int cc_generic_agent_recall(struct ast_cc_agent *agent)
{
pthread_t clotho;
enum ast_device_state current_state = ast_device_state(agent->device_name);
if (!cc_generic_is_device_available(current_state)) {
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
/* We can't try to contact the device right now because he's not available
* Let the core know he's busy.
*/
ast_cc_agent_caller_busy(agent->core_id, "Generic agent caller %s is busy", agent->device_name);
return 0;
}
ast_pthread_create_detached_background(&clotho, NULL, generic_recall, agent);
return 0;
}
static void cc_generic_agent_destructor(struct ast_cc_agent *agent)
{
struct cc_generic_agent_pvt *agent_pvt = agent->private_data;
if (!agent_pvt) {
/* The agent constructor probably failed. */
return;
}
cc_generic_agent_stop_offer_timer(agent);
if (agent_pvt->sub) {
agent_pvt->sub = stasis_unsubscribe(agent_pvt->sub);
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
}
ast_free(agent_pvt);
}
static void cc_core_instance_destructor(void *data)
{
struct cc_core_instance *core_instance = data;
ast_log_dynamic_level(cc_logger_level, "Core %d: Destroying core instance\n", core_instance->core_id);
if (core_instance->agent) {
cc_unref(core_instance->agent, "Core instance is done with the agent now");
}
if (core_instance->monitors) {
core_instance->monitors = cc_unref(core_instance->monitors, "Core instance is done with interface list");
}
}
static struct cc_core_instance *cc_core_init_instance(struct ast_channel *caller_chan,
struct cc_monitor_tree *called_tree, const int core_id, struct cc_control_payload *cc_data)
{
char caller[AST_CHANNEL_NAME];
struct cc_core_instance *core_instance;
struct ast_cc_config_params *cc_params;
long agent_count;
int recall_core_id;
ast_channel_get_device_name(caller_chan, caller, sizeof(caller));
cc_params = ast_channel_get_cc_config_params(caller_chan);
if (!cc_params) {
ast_log_dynamic_level(cc_logger_level, "Could not get CC parameters for %s\n",
caller);
return NULL;
}
/* First, we need to kill off other pending CC offers from caller. If the caller is going
* to request a CC service, it may only be for the latest call he made.
*/
if (ast_get_cc_agent_policy(cc_params) == AST_CC_AGENT_GENERIC) {
kill_duplicate_offers(caller);
}
ast_cc_is_recall(caller_chan, &recall_core_id, NULL);
agent_count = count_agents(caller, recall_core_id);
if (agent_count >= ast_get_cc_max_agents(cc_params)) {
ast_log_dynamic_level(cc_logger_level, "Caller %s already has the maximum number of agents configured\n", caller);
return NULL;
}
/* Generic agents can only have a single outstanding CC request per caller. */
if (agent_count > 0 && ast_get_cc_agent_policy(cc_params) == AST_CC_AGENT_GENERIC) {
ast_log_dynamic_level(cc_logger_level, "Generic agents can only have a single outstanding request\n");
return NULL;
}
/* Next, we need to create the core instance for this call */
if (!(core_instance = ao2_t_alloc(sizeof(*core_instance), cc_core_instance_destructor, "Creating core instance for CC"))) {
return NULL;
}
core_instance->core_id = core_id;
if (!(core_instance->agent = cc_agent_init(caller_chan, caller, core_instance->core_id, called_tree))) {
cc_unref(core_instance, "Couldn't allocate agent, unref core_instance");
return NULL;
}
core_instance->monitors = cc_ref(called_tree, "Core instance getting ref to monitor tree");