Asterisk - The Open Source Telephony Project  21.4.1
Typedefs | Functions
stasis_app_impl.h File Reference

Backend API for implementing components of res_stasis. More...

#include "asterisk/stasis_app.h"

Go to the source code of this file.

Typedefs

typedef void(* command_data_destructor_fn) (void *data)
 Typedef for data destructor for stasis app commands. More...
 
typedef int(* stasis_app_command_cb) (struct stasis_app_control *control, struct ast_channel *chan, void *data)
 

Functions

int stasis_app_exec (struct ast_channel *chan, const char *app_name, int argc, char *argv[])
 Control a channel using stasis_app. More...
 
int stasis_app_send_command (struct stasis_app_control *control, stasis_app_command_cb command, void *data, command_data_destructor_fn data_destructor)
 Invokes a command on a control's channel. More...
 
int stasis_app_send_command_async (struct stasis_app_control *control, stasis_app_command_cb command, void *data, command_data_destructor_fn data_destructor)
 Asynchronous version of stasis_app_send_command(). More...
 

Detailed Description

Backend API for implementing components of res_stasis.

Author
David M. Lee, II dlee@.nosp@m.digi.nosp@m.um.co.nosp@m.m
Since
12

This file defines functions useful for defining new commands to execute on channels while they are in Stasis.

Definition in file stasis_app_impl.h.

Typedef Documentation

typedef void(* command_data_destructor_fn) (void *data)

Typedef for data destructor for stasis app commands.

This is called during destruction of the command or if we fail to schedule a command. It is passed a pointer to the user-defined data of the command.

Parameters
dataData to destroy.

Definition at line 59 of file stasis_app_impl.h.

typedef int(* stasis_app_command_cb) (struct stasis_app_control *control, struct ast_channel *chan, void *data)

Callback type for stasis app commands

Definition at line 62 of file stasis_app_impl.h.

Function Documentation

int stasis_app_exec ( struct ast_channel chan,
const char *  app_name,
int  argc,
char *  argv[] 
)

Control a channel using stasis_app.

Since
12 This function blocks until the channel hangs up, or stasis_app_control_continue() is called on the channel's stasis_app_control struct.
Parameters
chanChannel to control with Stasis.
app_nameApplication controlling the channel.
argcNumber of arguments for the application.
argvArguments for the application.

Control a channel using stasis_app.

Definition at line 1327 of file res_stasis.c.

References ao2_bump, ao2_link, ao2_ref, app_is_active(), app_send(), app_send_end_msg(), app_subscribe_bridge(), app_unsubscribe_bridge(), ast_bridge_depart(), ast_channel_clear_softhangup(), ast_channel_snapshot_get_latest(), ast_channel_snapshot_to_json(), ast_check_hangup(), AST_CONTROL_HANGUP, ast_debug, AST_FRAME_CONTROL, ast_frame_dtor(), ast_json_array_append(), ast_json_object_get(), ast_json_pack(), ast_json_string_create(), ast_json_timeval(), ast_json_unref(), ast_pbx_run_args(), ast_read(), AST_SOFTHANGUP_ASYNCGOTO, ast_tvnow(), ast_waitfor(), cleanup(), control_app(), control_create(), control_dispatch_all(), control_flush_queue(), control_is_done(), control_move_cleanup(), control_next_app(), control_next_app_args(), control_next_app_args_size(), control_prestart_dispatch_all(), control_set_app(), control_set_thread(), control_silence_stop_now(), control_unlink(), control_wait(), MAX_WAIT_MS, ast_pbx_args::no_hangup_chan, OBJ_SEARCH_KEY, RAII_VAR, stasis_app_channel_is_stasis_end_published(), stasis_app_get_bridge(), and stasis_app_name().

Referenced by app_exec().

1329 {
1330  RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup);
1331  RAII_VAR(struct stasis_app_control *, control, NULL, control_unlink);
1332  struct ast_bridge *bridge = NULL;
1333  int res = 0;
1334  int needs_depart;
1335 
1336  ast_assert(chan != NULL);
1337 
1338  /* Just in case there's a lingering indication that the channel has had a stasis
1339  * end published on it, remove that now.
1340  */
1341  remove_stasis_end_published(chan);
1342 
1343  if (!apps_registry) {
1344  return -1;
1345  }
1346 
1347  app = ao2_find(apps_registry, app_name, OBJ_SEARCH_KEY);
1348  if (!app) {
1349  ast_log(LOG_ERROR,
1350  "Stasis app '%s' not registered\n", app_name);
1351  return -1;
1352  }
1353  if (!app_is_active(app)) {
1354  ast_log(LOG_ERROR,
1355  "Stasis app '%s' not active\n", app_name);
1356  return -1;
1357  }
1358 
1359  control = control_create(chan, app);
1360  if (!control) {
1361  ast_log(LOG_ERROR, "Control allocation failed or Stasis app '%s' not registered\n", app_name);
1362  return -1;
1363  }
1364 
1365  if (!control_app(control)) {
1366  ast_log(LOG_ERROR, "Stasis app '%s' not registered\n", app_name);
1367  return -1;
1368  }
1369 
1370  if (!app_is_active(control_app(control))) {
1371  ast_log(LOG_ERROR, "Stasis app '%s' not active\n", app_name);
1372  return -1;
1373  }
1374  ao2_link(app_controls, control);
1375 
1376  if (add_masquerade_store(chan)) {
1377  ast_log(LOG_ERROR, "Failed to attach masquerade detector\n");
1378  return -1;
1379  }
1380 
1381  res = send_start_msg(control_app(control), chan, argc, argv);
1382  if (res != 0) {
1383  ast_log(LOG_ERROR,
1384  "Error sending start message to '%s'\n", app_name);
1385  remove_masquerade_store(chan);
1386  return -1;
1387  }
1388 
1389  /* Pull queued prestart commands and execute */
1390  control_prestart_dispatch_all(control, chan);
1391 
1392  while (!control_is_done(control)) {
1393  RAII_VAR(struct ast_frame *, f, NULL, ast_frame_dtor);
1394  int r;
1395  int command_count;
1396  RAII_VAR(struct ast_bridge *, last_bridge, NULL, ao2_cleanup);
1397 
1398  /* Check to see if a bridge absorbed our hangup frame */
1399  if (ast_check_hangup_locked(chan)) {
1400  control_mark_done(control);
1401  break;
1402  }
1403 
1404  /* control->next_app is only modified within the control thread, so this is safe */
1405  if (control_next_app(control)) {
1406  struct stasis_app *next_app = ao2_find(apps_registry, control_next_app(control), OBJ_SEARCH_KEY);
1407 
1408  if (next_app && app_is_active(next_app)) {
1409  int idx;
1410  int next_argc;
1411  char **next_argv;
1412 
1413  /* If something goes wrong in this conditional, res will need to be non-zero
1414  * so that the code below the exec loop knows something went wrong during a move.
1415  */
1417  res = has_masquerade_store(chan) && app_send_end_msg(control_app(control), chan);
1418  if (res != 0) {
1419  ast_log(LOG_ERROR,
1420  "Error sending end message to %s\n", stasis_app_name(control_app(control)));
1421  control_mark_done(control);
1422  ao2_ref(next_app, -1);
1423  break;
1424  }
1425  } else {
1426  remove_stasis_end_published(chan);
1427  }
1428 
1429  /* This will ao2_bump next_app, and unref the previous app by 1 */
1430  control_set_app(control, next_app);
1431 
1432  /* There's a chance that the previous application is ready for clean up, so go ahead
1433  * and do that now.
1434  */
1435  cleanup();
1436 
1437  /* We need to add another masquerade store, otherwise the leave message will
1438  * not show up for the correct application.
1439  */
1440  if (add_masquerade_store(chan)) {
1441  ast_log(LOG_ERROR, "Failed to attach masquerade detector\n");
1442  res = -1;
1443  control_mark_done(control);
1444  ao2_ref(next_app, -1);
1445  break;
1446  }
1447 
1448  /* We MUST get the size before the list, as control_next_app_args steals the elements
1449  * from the string vector.
1450  */
1451  next_argc = control_next_app_args_size(control);
1452  next_argv = control_next_app_args(control);
1453 
1454  res = send_start_msg(control_app(control), chan, next_argc, next_argv);
1455 
1456  /* Even if res != 0, we still need to free the memory we got from control_argv */
1457  if (next_argv) {
1458  for (idx = 0; idx < next_argc; idx++) {
1459  ast_free(next_argv[idx]);
1460  }
1461  ast_free(next_argv);
1462  }
1463 
1464  if (res != 0) {
1465  ast_log(LOG_ERROR,
1466  "Error sending start message to '%s'\n", stasis_app_name(control_app(control)));
1467  remove_masquerade_store(chan);
1468  control_mark_done(control);
1469  ao2_ref(next_app, -1);
1470  break;
1471  }
1472 
1473  /* Done switching applications, free memory and clean up */
1474  control_move_cleanup(control);
1475  } else {
1476  /* If we can't switch applications, do nothing */
1477  struct ast_json *msg;
1478  RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
1479 
1480  if (!next_app) {
1481  ast_log(LOG_ERROR, "Could not move to Stasis app '%s' - not registered\n",
1482  control_next_app(control));
1483  } else {
1484  ast_log(LOG_ERROR, "Could not move to Stasis app '%s' - not active\n",
1485  control_next_app(control));
1486  }
1487 
1488  snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan));
1489  if (!snapshot) {
1490  ast_log(LOG_ERROR, "Could not get channel shapshot for '%s'\n",
1491  ast_channel_name(chan));
1492  } else {
1493  struct ast_json *json_args;
1494  int next_argc = control_next_app_args_size(control);
1495  char **next_argv = control_next_app_args(control);
1496 
1497  msg = ast_json_pack("{s: s, s: o, s: o, s: s, s: []}",
1498  "type", "ApplicationMoveFailed",
1499  "timestamp", ast_json_timeval(ast_tvnow(), NULL),
1500  "channel", ast_channel_snapshot_to_json(snapshot, NULL),
1501  "destination", control_next_app(control),
1502  "args");
1503  if (!msg) {
1504  ast_log(LOG_ERROR, "Failed to pack JSON for ApplicationMoveFailed message\n");
1505  } else {
1506  json_args = ast_json_object_get(msg, "args");
1507  if (!json_args) {
1508  ast_log(LOG_ERROR, "Could not get args json array");
1509  } else {
1510  int r = 0;
1511  int idx;
1512  for (idx = 0; idx < next_argc; ++idx) {
1513  r = ast_json_array_append(json_args,
1514  ast_json_string_create(next_argv[idx]));
1515  if (r != 0) {
1516  ast_log(LOG_ERROR, "Error appending to ApplicationMoveFailed message\n");
1517  break;
1518  }
1519  }
1520  if (r == 0) {
1521  app_send(control_app(control), msg);
1522  }
1523  }
1524  ast_json_unref(msg);
1525  }
1526  }
1527  }
1528  control_move_cleanup(control);
1529  ao2_cleanup(next_app);
1530  }
1531 
1532  last_bridge = bridge;
1533  bridge = ao2_bump(stasis_app_get_bridge(control));
1534 
1535  if (bridge != last_bridge) {
1536  if (last_bridge) {
1537  app_unsubscribe_bridge(control_app(control), last_bridge);
1538  }
1539  if (bridge) {
1540  app_subscribe_bridge(control_app(control), bridge);
1541  }
1542  }
1543 
1544  if (bridge) {
1545  /* Bridge/dial is handling channel frames */
1546  control_wait(control);
1547  control_dispatch_all(control, chan);
1548  continue;
1549  }
1550 
1551  /* Set this thread's id as the control thread id so that any
1552  new commands can signal out of this wait */
1553  control_set_thread(control, pthread_self());
1554  r = ast_waitfor(chan, MAX_WAIT_MS);
1555  control_set_thread(control, AST_PTHREADT_NULL);
1556 
1557  if (r < 0) {
1558  ast_debug(3, "%s: Poll error\n",
1559  ast_channel_uniqueid(chan));
1560  control_mark_done(control);
1561  break;
1562  }
1563 
1564  command_count = control_dispatch_all(control, chan);
1565 
1566  if (command_count > 0 && ast_channel_fdno(chan) == -1) {
1567  /* Command drained the channel; wait for next frame */
1568  continue;
1569  }
1570 
1571  if (r == 0) {
1572  /* Timeout */
1573  continue;
1574  }
1575 
1576  f = ast_read(chan);
1577  if (!f) {
1578  /* Continue on in the dialplan */
1579  ast_debug(3, "%s: Hangup (no more frames)\n",
1580  ast_channel_uniqueid(chan));
1581  control_mark_done(control);
1582  break;
1583  }
1584 
1585  if (f->frametype == AST_FRAME_CONTROL) {
1586  if (f->subclass.integer == AST_CONTROL_HANGUP) {
1587  /* Continue on in the dialplan */
1588  ast_debug(3, "%s: Hangup\n",
1589  ast_channel_uniqueid(chan));
1590  control_mark_done(control);
1591  break;
1592  }
1593  }
1594  }
1595 
1596  ast_channel_lock(chan);
1597  needs_depart = (ast_channel_internal_bridge_channel(chan) != NULL);
1598  ast_channel_unlock(chan);
1599  if (needs_depart) {
1600  ast_bridge_depart(chan);
1601  }
1602 
1603  if (stasis_app_get_bridge(control)) {
1605  }
1606  ao2_cleanup(bridge);
1607 
1608  /* Only publish a stasis_end event if it hasn't already been published */
1609  if (!res && !stasis_app_channel_is_stasis_end_published(chan)) {
1610  /* A masquerade has occurred and this message will be wrong so it
1611  * has already been sent elsewhere. */
1612  res = has_masquerade_store(chan) && app_send_end_msg(control_app(control), chan);
1613  if (res != 0) {
1614  ast_log(LOG_ERROR,
1615  "Error sending end message to %s\n", stasis_app_name(control_app(control)));
1616  return res;
1617  }
1618  } else {
1619  remove_stasis_end_published(chan);
1620  }
1621 
1622  control_flush_queue(control);
1623 
1624  /* Stop any lingering silence generator */
1625  control_silence_stop_now(control);
1626 
1627  /* There's an off chance that app is ready for cleanup. Go ahead
1628  * and clean up, just in case
1629  */
1630  cleanup();
1631 
1632  /* The control needs to be removed from the controls container in
1633  * case a new PBX is started and ends up coming back into Stasis.
1634  */
1635  control_unlink(control);
1636  control = NULL;
1637 
1638  if (!res && !ast_channel_pbx(chan)) {
1639  int chan_hungup;
1640 
1641  /* The ASYNCGOTO softhangup flag may have broken the channel out of
1642  * its bridge to run dialplan, so if there's no pbx on the channel
1643  * let it run dialplan here. Otherwise, it will run when this
1644  * application exits. */
1645  ast_channel_lock(chan);
1647  chan_hungup = ast_check_hangup(chan);
1648  ast_channel_unlock(chan);
1649 
1650  if (!chan_hungup) {
1651  struct ast_pbx_args pbx_args;
1652 
1653  memset(&pbx_args, 0, sizeof(pbx_args));
1654  pbx_args.no_hangup_chan = 1;
1655 
1656  res = ast_pbx_run_args(chan, &pbx_args);
1657  }
1658  }
1659 
1660  return res;
1661 }
struct ast_bridge * stasis_app_get_bridge(struct stasis_app_control *control)
Gets the bridge currently associated with a control object.
Definition: control.c:953
Options for ast_pbx_run()
Definition: pbx.h:407
char * control_next_app(struct stasis_app_control *control)
Returns the name of the application we are moving to.
Definition: control.c:1719
char ** control_next_app_args(struct stasis_app_control *control)
Returns the list of arguments to pass to the application we are moving to.
Definition: control.c:1732
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1101
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
void control_flush_queue(struct stasis_app_control *control)
Flush the control command queue.
Definition: control.c:1504
Structure representing a snapshot of channel state.
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4257
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
struct stasis_app * control_app(struct stasis_app_control *control)
Returns the pointer (non-reffed) to the app associated with this control.
Definition: control.c:1585
int app_is_active(struct stasis_app *app)
Checks whether an app is active.
int control_dispatch_all(struct stasis_app_control *control, struct ast_channel *chan)
Dispatch all commands enqueued to this control.
Definition: control.c:1517
int app_subscribe_bridge(struct stasis_app *app, struct ast_bridge *bridge)
Add a bridge subscription to an existing channel subscription.
void ast_frame_dtor(struct ast_frame *frame)
NULL-safe wrapper for ast_frfree, good for RAII_VAR.
Definition: main/frame.c:187
int app_send_end_msg(struct stasis_app *app, struct ast_channel *chan)
Send StasisEnd message to the listening app.
Definition: res_stasis.c:1086
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
void control_set_thread(struct stasis_app_control *control, pthread_t threadid)
set the control's thread id
Definition: control.c:196
static void cleanup(void)
Clean up any old apps that we don't need any more.
Definition: res_stasis.c:327
int stasis_app_channel_is_stasis_end_published(struct ast_channel *chan)
Has this channel had a StasisEnd published on it?
Definition: res_stasis.c:1302
void control_move_cleanup(struct stasis_app_control *control)
Free any memory that was allocated for switching applications via /channels/{channelId}/move.
Definition: control.c:1724
void ast_channel_clear_softhangup(struct ast_channel *chan, int flag)
Clear a set of softhangup flags from a channel.
Definition: channel.c:2432
struct ast_json * ast_json_string_create(const char *value)
Construct a JSON string from value.
Definition: json.c:278
enum ast_pbx_result ast_pbx_run_args(struct ast_channel *c, struct ast_pbx_args *args)
Execute the PBX in the current thread.
Definition: pbx.c:4735
int control_next_app_args_size(struct stasis_app_control *control)
Returns the number of arguments to be passed to the application we are moving to. ...
Definition: control.c:1737
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
const char * stasis_app_name(const struct stasis_app *app)
Retrieve an application's name.
#define ast_debug(level,...)
Log a DEBUG message.
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
struct ast_json * ast_json_timeval(const struct timeval tv, const char *zone)
Construct a timeval as JSON.
Definition: json.c:670
int ast_json_array_append(struct ast_json *array, struct ast_json *value)
Append to an array.
Definition: json.c:378
Structure that contains information about a bridge.
Definition: bridge.h:349
void app_send(struct stasis_app *app, struct ast_json *message)
Send a message to an application.
int ast_bridge_depart(struct ast_channel *chan)
Depart a channel from a bridge.
Definition: bridge.c:1906
struct stasis_app_control * control_create(struct ast_channel *channel, struct stasis_app *app)
Create a control object.
Definition: control.c:129
const char * app_name(struct ast_app *app)
Definition: pbx_app.c:463
static void control_unlink(struct stasis_app_control *control)
In addition to running ao2_cleanup(), this function also removes the object from the app_controls con...
Definition: res_stasis.c:785
#define MAX_WAIT_MS
Definition: res_stasis.c:77
void control_set_app(struct stasis_app_control *control, struct stasis_app *app)
Set the application the control object belongs to.
Definition: control.c:1713
void control_silence_stop_now(struct stasis_app_control *control)
Stop playing silence to a channel right now.
Definition: control.c:859
int control_is_done(struct stasis_app_control *control)
Returns true if control_continue() has been called on this control.
Definition: control.c:370
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Definition: channel.c:3162
struct ast_channel_snapshot * ast_channel_snapshot_get_latest(const char *uniqueid)
Obtain the latest ast_channel_snapshot from the Stasis Message Bus API cache. This is an ao2 object...
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:407
int app_unsubscribe_bridge(struct stasis_app *app, struct ast_bridge *bridge)
Cancel the bridge subscription for an application.
struct ao2_container * apps_registry
Stasis application container.
Definition: res_stasis.c:100
Data structure associated with a single frame of data.
Abstract JSON element (object, array, string, int, ...).
struct ast_json * ast_channel_snapshot_to_json(const struct ast_channel_snapshot *snapshot, const struct stasis_message_sanitizer *sanitize)
Build a JSON object from a ast_channel_snapshot.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
int control_prestart_dispatch_all(struct stasis_app_control *control, struct ast_channel *chan)
Dispatch all queued prestart commands.
Definition: control.c:1557
void control_wait(struct stasis_app_control *control)
Blocks until control's command queue has a command available.
Definition: control.c:1537
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
int stasis_app_send_command ( struct stasis_app_control control,
stasis_app_command_cb  command,
void *  data,
command_data_destructor_fn  data_destructor 
)

Invokes a command on a control's channel.

Since
12 This function dispatches the command to be executed in the context of stasis_app_exec(), so this command will block waiting for the results of the command.
Parameters
controlControl object for the channel to send the command to.
commandCommand function to execute.
dataOptional data to pass along with the control function.
data_destructorOptional function which will be called on the data in either the event of command completion or failure to schedule or complete the command
Returns
zero on success.
error code otherwise.

Definition at line 920 of file control.c.

Referenced by ast_ari_bridges_set_video_source(), and stasis_app_control_answer().

922 {
923  return app_send_command_on_condition(control, command_fn, data, data_destructor, NULL);
924 }
int stasis_app_send_command_async ( struct stasis_app_control control,
stasis_app_command_cb  command,
void *  data,
command_data_destructor_fn  data_destructor 
)

Asynchronous version of stasis_app_send_command().

Since
12 This function enqueues a command for execution, but returns immediately without waiting for the response.
Parameters
controlControl object for the channel to send the command to.
commandCommand function to execute.
dataOptional data to pass along with the control function.
data_destructorOptional function which will be called on the data in either the event of command completion or failure to schedule or complete the command
Returns
0 on success.
Non-zero on error.

Definition at line 926 of file control.c.

References ao2_ref, and stasis_app_control::is_done.

Referenced by bridge_timeout(), dial_bridge_after_cb(), stasis_app_control_add_role(), stasis_app_control_clear_roles(), stasis_app_control_continue(), stasis_app_control_dial(), stasis_app_control_dtmf(), stasis_app_control_hold(), stasis_app_control_moh_start(), stasis_app_control_moh_stop(), stasis_app_control_move(), stasis_app_control_mute(), stasis_app_control_play_uri(), stasis_app_control_record(), stasis_app_control_redirect(), stasis_app_control_ring(), stasis_app_control_ring_stop(), stasis_app_control_set_channel_var(), stasis_app_control_silence_start(), stasis_app_control_silence_stop(), stasis_app_control_unhold(), and stasis_app_control_unmute().

929 {
930  struct stasis_app_command *command;
931 
932  if (control == NULL || control->is_done) {
933  /* If exec_command fails, it calls the data_destructor. In order to
934  * provide consistent behavior, we'll also call the data_destructor
935  * on this error path. This way, callers never have to call the
936  * data_destructor themselves.
937  */
938  if (data_destructor) {
939  data_destructor(data);
940  }
941  return -1;
942  }
943 
944  command = exec_command(control, command_fn, data, data_destructor);
945  if (!command) {
946  return -1;
947  }
948  ao2_ref(command, -1);
949 
950  return 0;
951 }
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
unsigned int is_done
Definition: control.c:108