Asterisk - The Open Source Telephony Project GIT-master-7e7a603
app_bridgewait.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2013, Digium, Inc.
5 *
6 * Mark Spencer <markster@digium.com>
7 *
8 * Author: Jonathan Rose <jrose@digium.com>
9 *
10 * See http://www.asterisk.org for more information about
11 * the Asterisk project. Please do not directly contact
12 * any of the maintainers of this project for assistance;
13 * the project provides a web site, mailing lists and IRC
14 * channels for your use.
15 *
16 * This program is free software, distributed under the terms of
17 * the GNU General Public License Version 2. See the LICENSE file
18 * at the top of the source tree.
19 */
20
21/*! \file
22 *
23 * \brief Application to place the channel into a holding Bridge
24 *
25 * \author Jonathan Rose <jrose@digium.com>
26 *
27 * \ingroup applications
28 */
29
30/*** MODULEINFO
31 <depend>bridge_holding</depend>
32 <support_level>core</support_level>
33 ***/
34
35#include "asterisk.h"
36
37#include "asterisk/file.h"
38#include "asterisk/channel.h"
39#include "asterisk/pbx.h"
40#include "asterisk/module.h"
41#include "asterisk/features.h"
42#include "asterisk/say.h"
43#include "asterisk/lock.h"
44#include "asterisk/utils.h"
45#include "asterisk/app.h"
46#include "asterisk/bridge.h"
48#include "asterisk/astobj2.h"
49#include "asterisk/causes.h"
50
51/*** DOCUMENTATION
52 <application name="BridgeWait" language="en_US">
53 <synopsis>
54 Put a call into the holding bridge.
55 </synopsis>
56 <syntax>
57 <parameter name="name">
58 <para>Name of the holding bridge to join. This is a handle for <literal>BridgeWait</literal>
59 only and does not affect the actual bridges that are created. If not provided,
60 the reserved name <literal>default</literal> will be used.
61 </para>
62 </parameter>
63 <parameter name="role" required="false">
64 <para>Defines the channel's purpose for entering the holding bridge. Values are case sensitive.
65 </para>
66 <enumlist>
67 <enum name="participant">
68 <para>The channel will enter the holding bridge to be placed on hold
69 until it is removed from the bridge for some reason. (default)</para>
70 </enum>
71 <enum name="announcer">
72 <para>The channel will enter the holding bridge to make announcements
73 to channels that are currently in the holding bridge. While an
74 announcer is present, holding for the participants will be
75 suspended.</para>
76 </enum>
77 </enumlist>
78 </parameter>
79 <parameter name="options">
80 <optionlist>
81 <option name="m">
82 <argument name="class" required="true" />
83 <para>The specified MOH class will be used/suggested for
84 music on hold operations. This option will only be useful for
85 entertainment modes that use it (m and h).</para>
86 </option>
87 <option name="e">
88 <para>Which entertainment mechanism should be used while on hold
89 in the holding bridge. Only the first letter is read.</para>
90 <enumlist>
91 <enum name="m"><para>Play music on hold (default)</para></enum>
92 <enum name="r"><para>Ring without pause</para></enum>
93 <enum name="s"><para>Generate silent audio</para></enum>
94 <enum name="h"><para>Put the channel on hold</para></enum>
95 <enum name="n"><para>No entertainment</para></enum>
96 </enumlist>
97 </option>
98 <option name="S">
99 <argument name="duration" required="true" />
100 <para>Automatically exit the bridge and return to the PBX after
101 <emphasis>duration</emphasis> seconds.</para>
102 </option>
103 <option name="n">
104 <para>Do not automatically answer the channel.</para>
105 </option>
106 </optionlist>
107 </parameter>
108 </syntax>
109 <description>
110 <para>This application places the incoming channel into a holding bridge.
111 The channel will then wait in the holding bridge until some event occurs
112 which removes it from the holding bridge.</para>
113 <note><para>This application will answer calls which haven't already
114 been answered, unless the n option is provided.</para></note>
115 </description>
116 </application>
117 ***/
118
119#define APP_NAME "BridgeWait"
120#define DEFAULT_BRIDGE_NAME "default"
121
123
125 struct ast_bridge *bridge; /*!< Bridge being wrapped by this wrapper */
126 char name[0]; /*!< Name of the holding bridge wrapper */
127};
128
129static void wait_bridge_wrapper_destructor(void *obj)
130{
131 struct wait_bridge_wrapper *wrapper = obj;
132
133 if (wrapper->bridge) {
134 ast_bridge_destroy(wrapper->bridge, 0);
135 }
136}
137
138static struct wait_bridge_wrapper *wait_bridge_wrapper_find_by_name(const char *bridge_name)
139{
140 return ao2_find(wait_bridge_wrappers, bridge_name, OBJ_KEY);
141}
142
143static int wait_bridge_hash_fn(const void *obj, const int flags)
144{
145 const struct wait_bridge_wrapper *entry;
146 const char *key;
147
148 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
149 case OBJ_KEY:
150 key = obj;
151 return ast_str_hash(key);
152 case OBJ_POINTER:
153 entry = obj;
154 return ast_str_hash(entry->name);
155 default:
156 /* Hash can only work on something with a full key. */
157 ast_assert(0);
158 return 0;
159 }
160}
161
162static int wait_bridge_sort_fn(const void *obj_left, const void *obj_right, const int flags)
163{
164 const struct wait_bridge_wrapper *left = obj_left;
165 const struct wait_bridge_wrapper *right = obj_right;
166 const char *right_key = obj_right;
167 int cmp;
168
169 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
170 case OBJ_POINTER:
171 right_key = right->name;
172 /* Fall through */
173 case OBJ_KEY:
174 cmp = strcmp(left->name, right_key);
175 break;
176 case OBJ_PARTIAL_KEY:
177 cmp = strncmp(left->name, right_key, strlen(right_key));
178 break;
179 default:
180 /* Sort can only work on something with a full or partial key. */
181 ast_assert(0);
182 cmp = 0;
183 break;
184 }
185 return cmp;
186}
187
191 MUXFLAG_TIMEOUT = (1 << 2),
193};
194
199 OPT_ARG_ARRAY_SIZE, /* Always the last element of the enum */
200};
201
207});
208
209static int bridgewait_timeout_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
210{
211 ast_verb(3, "Channel %s timed out.\n", ast_channel_name(bridge_channel->chan));
214 return -1;
215}
216
217static int apply_option_timeout(struct ast_bridge_features *features, char *duration_arg)
218{
219 unsigned int duration;
220
221 if (ast_strlen_zero(duration_arg)) {
222 ast_log(LOG_ERROR, "Timeout option 'S': No value provided.\n");
223 return -1;
224 }
225 if (sscanf(duration_arg, "%u", &duration) != 1 || duration == 0) {
226 ast_log(LOG_ERROR, "Timeout option 'S': Invalid value provided '%s'.\n",
227 duration_arg);
228 return -1;
229 }
230
231 duration *= 1000;
232 if (ast_bridge_interval_hook(features, 0, duration, bridgewait_timeout_callback,
234 ast_log(LOG_ERROR, "Timeout option 'S': Could not create timer.\n");
235 return -1;
236 }
237
238 return 0;
239}
240
241static int apply_option_moh(struct ast_channel *chan, const char *class_arg)
242{
243 return ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", class_arg);
244}
245
246static int apply_option_entertainment(struct ast_channel *chan, const char *entertainment_arg)
247{
248 char entertainment = entertainment_arg[0];
249
250 switch (entertainment) {
251 case 'm':
252 return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold");
253 case 'r':
254 return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "ringing");
255 case 's':
256 return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "silence");
257 case 'h':
258 return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "hold");
259 case 'n':
260 return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "none");
261 default:
262 ast_log(LOG_ERROR, "Invalid argument for BridgeWait entertainment '%s'\n", entertainment_arg);
263 return -1;
264 }
265}
266
271};
272
273static int process_options(struct ast_channel *chan, struct ast_flags *flags, char **opts, struct ast_bridge_features *features, enum wait_bridge_roles role)
274{
275 if (ast_test_flag(flags, MUXFLAG_TIMEOUT)) {
276 if (apply_option_timeout(features, opts[OPT_ARG_TIMEOUT])) {
277 return -1;
278 }
279 }
280
281 switch (role) {
282 case ROLE_PARTICIPANT:
283 if (ast_channel_add_bridge_role(chan, "holding_participant")) {
284 return -1;
285 }
286
287 if (ast_test_flag(flags, MUXFLAG_MOHCLASS)) {
288 if (apply_option_moh(chan, opts[OPT_ARG_MOHCLASS])) {
289 return -1;
290 }
291 }
292
295 return -1;
296 }
297 }
298
299 break;
300 case ROLE_ANNOUNCER:
301 if (ast_channel_add_bridge_role(chan, "announcer")) {
302 return -1;
303 }
304 break;
305 case ROLE_INVALID:
306 ast_assert(0);
307 return -1;
308 }
309
310 return 0;
311}
312
313/*!
314 * \internal
315 * \since 12.0.0
316 * \brief Allocate a new holding bridge wrapper with the given bridge name and bridge ID.
317 *
318 * \param bridge_name name of the bridge wrapper
319 * \param bridge the bridge being wrapped
320 *
321 * \retval Pointer to the newly allocated holding bridge wrapper
322 * \retval NULL if allocation failed. The bridge will be destroyed if this function fails.
323 */
324static struct wait_bridge_wrapper *wait_bridge_wrapper_alloc(const char *bridge_name, struct ast_bridge *bridge)
325{
326 struct wait_bridge_wrapper *bridge_wrapper;
327
328 bridge_wrapper = ao2_alloc_options(sizeof(*bridge_wrapper) + strlen(bridge_name) + 1,
330 if (!bridge_wrapper) {
332 return NULL;
333 }
334
335 strcpy(bridge_wrapper->name, bridge_name);
336 bridge_wrapper->bridge = bridge;
337
338 if (!ao2_link(wait_bridge_wrappers, bridge_wrapper)) {
339 ao2_cleanup(bridge_wrapper);
340 return NULL;
341 }
342
343 return bridge_wrapper;
344}
345
346static struct wait_bridge_wrapper *get_wait_bridge_wrapper(const char *bridge_name)
347{
348 struct wait_bridge_wrapper * wrapper;
349 struct ast_bridge *bridge = NULL;
350
352
353 if ((wrapper = wait_bridge_wrapper_find_by_name(bridge_name))) {
354 return wrapper;
355 }
356
357 /*
358 * Holding bridges can allow local channel move/swap
359 * optimization to the bridge. However, we cannot allow it for
360 * this holding bridge because the call will lose the channel
361 * roles and dialplan location as a result.
362 */
367
368 if (!bridge) {
369 return NULL;
370 }
371
372 /* The bridge reference is unconditionally passed. */
373 return wait_bridge_wrapper_alloc(bridge_name, bridge);
374}
375
376/*!
377 * \internal
378 * \since 12.0.0
379 * \brief If we are down to the last reference of a wrapper and it's still contained within the list, remove it from the list.
380 *
381 * \param wrapper reference to wait bridge wrapper being checked for list culling - will be cleared on exit
382 */
383static void wait_wrapper_removal(struct wait_bridge_wrapper *wrapper)
384{
385 if (!wrapper) {
386 return;
387 }
388
390 if (ao2_ref(wrapper, 0) == 2) {
391 /* Either we have the only real reference or else wrapper isn't in the container anyway. */
393 }
395
396 ao2_cleanup(wrapper);
397}
398
399static enum wait_bridge_roles validate_role(const char *role)
400{
401 if (!strcmp(role, "participant")) {
402 return ROLE_PARTICIPANT;
403 } else if (!strcmp(role, "announcer")) {
404 return ROLE_ANNOUNCER;
405 } else {
406 return ROLE_INVALID;
407 }
408}
409
410/*!
411 * \internal
412 * \since 12.0.0
413 * \brief Application callback for the bridgewait application
414 *
415 * \param chan channel running the application
416 * \param data Arguments to the application
417 *
418 * \retval 0 Ran successfully and the call didn't hang up
419 * \retval -1 Failed or the call was hung up by the time the channel exited the holding bridge
420 */
421static int bridgewait_exec(struct ast_channel *chan, const char *data)
422{
423 char *bridge_name = DEFAULT_BRIDGE_NAME;
424 struct ast_bridge_features chan_features;
425 struct ast_flags flags = { 0 };
426 char *parse;
428 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
429 struct wait_bridge_wrapper *bridge_wrapper;
430 int res;
431
434 AST_APP_ARG(role);
436 AST_APP_ARG(other); /* Any remaining unused arguments */
437 );
438
439 parse = ast_strdupa(data);
441
442 if (!ast_strlen_zero(args.name)) {
443 bridge_name = args.name;
444 }
445
446 if (!ast_strlen_zero(args.role)) {
447 role = validate_role(args.role);
448 if (role == ROLE_INVALID) {
449 ast_log(LOG_ERROR, "Requested waiting bridge role '%s' is invalid.\n", args.role);
450 return -1;
451 }
452 }
453
454 if (ast_bridge_features_init(&chan_features)) {
455 ast_bridge_features_cleanup(&chan_features);
456 ast_log(LOG_ERROR, "'%s' failed to enter the waiting bridge - could not set up channel features\n",
457 ast_channel_name(chan));
458 return -1;
459 }
460
461 if (args.options) {
462 ast_app_parse_options(bridgewait_opts, &flags, opts, args.options);
463 }
464
465 /* Answer the channel if needed */
467 ast_answer(chan);
468 }
469
470 if (process_options(chan, &flags, opts, &chan_features, role)) {
471 ast_bridge_features_cleanup(&chan_features);
472 return -1;
473 }
474
475 bridge_wrapper = get_wait_bridge_wrapper(bridge_name);
476 if (!bridge_wrapper) {
477 ast_log(LOG_WARNING, "Failed to find or create waiting bridge '%s' for '%s'.\n", bridge_name, ast_channel_name(chan));
478 ast_bridge_features_cleanup(&chan_features);
479 return -1;
480 }
481
482 ast_verb(3, "%s is entering waiting bridge %s:%s\n", ast_channel_name(chan), bridge_name, bridge_wrapper->bridge->uniqueid);
483 res = ast_bridge_join(bridge_wrapper->bridge, chan, NULL, &chan_features, NULL, 0);
484 wait_wrapper_removal(bridge_wrapper);
485 ast_bridge_features_cleanup(&chan_features);
486
487 if (res) {
488 /* For the lifetime of the bridge wrapper the bridge itself will be valid, if an error occurs it is because
489 * of extreme situations.
490 */
491 ast_log(LOG_WARNING, "Failed to join waiting bridge '%s' for '%s'.\n", bridge_name, ast_channel_name(chan));
492 }
493
494 return (res || ast_check_hangup_locked(chan)) ? -1 : 0;
495}
496
497static int unload_module(void)
498{
500
502}
503
504static int load_module(void)
505{
509
511 return -1;
512 }
513
515}
516
517AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Place the channel into a holding bridge application");
static enum wait_bridge_roles validate_role(const char *role)
static int apply_option_timeout(struct ast_bridge_features *features, char *duration_arg)
static int apply_option_moh(struct ast_channel *chan, const char *class_arg)
static int wait_bridge_hash_fn(const void *obj, const int flags)
static int apply_option_entertainment(struct ast_channel *chan, const char *entertainment_arg)
static int bridgewait_exec(struct ast_channel *chan, const char *data)
bridgewait_args
@ OPT_ARG_MOHCLASS
@ OPT_ARG_ENTERTAINMENT
@ OPT_ARG_TIMEOUT
@ OPT_ARG_ARRAY_SIZE
#define DEFAULT_BRIDGE_NAME
static int process_options(struct ast_channel *chan, struct ast_flags *flags, char **opts, struct ast_bridge_features *features, enum wait_bridge_roles role)
wait_bridge_roles
@ ROLE_ANNOUNCER
@ ROLE_INVALID
@ ROLE_PARTICIPANT
static const struct ast_app_option bridgewait_opts[128]
static struct wait_bridge_wrapper * wait_bridge_wrapper_find_by_name(const char *bridge_name)
static void wait_bridge_wrapper_destructor(void *obj)
bridgewait_flags
@ MUXFLAG_ENTERTAINMENT
@ MUXFLAG_NOANSWER
@ MUXFLAG_TIMEOUT
@ MUXFLAG_MOHCLASS
static int bridgewait_timeout_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
static struct wait_bridge_wrapper * get_wait_bridge_wrapper(const char *bridge_name)
static struct ao2_container * wait_bridge_wrappers
static int load_module(void)
static int unload_module(void)
static int wait_bridge_sort_fn(const void *obj_left, const void *obj_right, const int flags)
static struct wait_bridge_wrapper * wait_bridge_wrapper_alloc(const char *bridge_name, struct ast_bridge *bridge)
#define APP_NAME
static void wait_wrapper_removal(struct wait_bridge_wrapper *wrapper)
ast_mutex_t lock
Definition: app_sla.c:331
Asterisk main include file. File version handling, generic pbx functions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
#define OBJ_KEY
Definition: astobj2.h:1151
#define OBJ_POINTER
Definition: astobj2.h:1150
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition: astobj2.h:367
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_unlink(container, obj)
Remove an object from a container.
Definition: astobj2.h:1578
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
#define ao2_unlock(a)
Definition: astobj2.h:729
#define ao2_lock(a)
Definition: astobj2.h:717
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define OBJ_PARTIAL_KEY
Definition: astobj2.h:1152
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition: astobj2.h:404
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
@ AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT
Reject objects with duplicate keys in container.
Definition: astobj2.h:1188
Bridging API.
struct ast_bridge * ast_bridge_base_new(uint32_t capabilities, unsigned int flags, const char *creator, const char *name, const char *id)
Create a new base class bridge.
Definition: bridge.c:934
int ast_bridge_join(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, struct ast_bridge_tech_optimizations *tech_args, enum ast_bridge_join_flags flags)
Join a channel to a bridge (blocking)
Definition: bridge.c:1621
int ast_bridge_destroy(struct ast_bridge *bridge, int cause)
Destroy a bridge.
Definition: bridge.c:944
@ AST_BRIDGE_CAPABILITY_HOLDING
Definition: bridge.h:86
@ BRIDGE_CHANNEL_STATE_END
void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state, int cause)
Set bridge channel state to leave bridge (if not leaving already).
int ast_bridge_features_init(struct ast_bridge_features *features)
Initialize bridge features structure.
Definition: bridge.c:3620
int ast_bridge_interval_hook(struct ast_bridge_features *features, enum ast_bridge_hook_timer_option flags, unsigned int interval, ast_bridge_hook_callback callback, void *hook_pvt, ast_bridge_hook_pvt_destructor destructor, enum ast_bridge_hook_remove_flags remove_flags)
Attach an interval hook to a bridge features structure.
Definition: bridge.c:3319
@ AST_BRIDGE_HOOK_REMOVE_ON_PULL
@ AST_BRIDGE_FLAG_SWAP_INHIBIT_TO
@ AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM
@ AST_BRIDGE_FLAG_TRANSFER_PROHIBITED
@ AST_BRIDGE_FLAG_MERGE_INHIBIT_TO
@ AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
void ast_bridge_features_cleanup(struct ast_bridge_features *features)
Clean up the contents of a bridge features structure.
Definition: bridge.c:3653
int ast_channel_set_bridge_role_option(struct ast_channel *channel, const char *role_name, const char *option, const char *value)
Set a role option on a channel.
Definition: bridge_roles.c:375
int ast_channel_add_bridge_role(struct ast_channel *chan, const char *role_name)
Adds a bridge role to a channel.
Definition: bridge_roles.c:313
Internal Asterisk hangup causes.
#define AST_CAUSE_NORMAL_CLEARING
Definition: causes.h:106
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
int ast_check_hangup_locked(struct ast_channel *chan)
Definition: channel.c:459
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2805
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_UP
Definition: channelstate.h:42
Call Parking and Pickup API Includes code and algorithms from the Zapata library.
Generic File Format Support. Should be included by clients of the file handling routines....
static const char name[]
Definition: format_mp3.c:68
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3056
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_WARNING
Asterisk locking-related definitions:
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
Definition: lock.h:604
Asterisk module definitions.
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:567
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
Music on hold handling.
Core PBX routines and definitions.
#define NULL
Definition: resample.c:96
Say numbers and dates (maybe words one day too)
static force_inline int attribute_pure ast_str_hash(const char *str)
Compute a hash value on a string.
Definition: strings.h:1259
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Generic container type.
Structure that contains information regarding a channel in a bridge.
struct ast_channel * chan
Structure that contains features information.
Structure that contains information about a bridge.
Definition: bridge.h:349
const ast_string_field uniqueid
Definition: bridge.h:401
Main Channel structure associated with a channel.
Structure used to handle boolean flags.
Definition: utils.h:199
unsigned int flags
Definition: utils.h:200
Definition: search.h:40
struct ast_bridge * bridge
const char * args
static struct test_options options
Utility functions.
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define ast_assert(a)
Definition: utils.h:739