Asterisk - The Open Source Telephony Project  18.5.0
func_holdintercept.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2015, Digium, Inc.
5  *
6  * Matt Jordan <[email protected]>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*! \file
20  *
21  * \brief Function that intercepts HOLD frames from channels and raises events
22  *
23  * \author Matt Jordan <[email protected]>
24  *
25  * \ingroup functions
26  */
27 
28 /*** MODULEINFO
29  <support_level>core</support_level>
30  ***/
31 
32 #include "asterisk.h"
33 
34 #include "asterisk/module.h"
35 #include "asterisk/channel.h"
36 #include "asterisk/pbx.h"
37 #include "asterisk/app.h"
38 #include "asterisk/frame.h"
39 #include "asterisk/stasis.h"
41 
42 /*** DOCUMENTATION
43  <function name="HOLD_INTERCEPT" language="en_US">
44  <synopsis>
45  Intercepts hold frames on a channel and raises an event instead of passing the frame on
46  </synopsis>
47  <syntax>
48  <parameter name="action" required="true">
49  <optionlist>
50  <option name="remove">
51  <para>W/O. Removes the hold interception function.</para>
52  </option>
53  <option name="set">
54  <para>W/O. Enable hold interception on the channel. When
55  enabled, the channel will intercept any hold action that
56  is signalled from the device, and instead simply raise an
57  event (AMI/ARI) indicating that the channel wanted to put other
58  parties on hold.</para>
59  </option>
60  </optionlist>
61  </parameter>
62  </syntax>
63  </function>
64 ***/
65 
66 /*! \brief Private data structure used with the function's datastore */
69 };
70 
71 /*! \brief The channel datastore the function uses to store state */
73  .type = "hold_intercept",
74 };
75 
76 /*! \internal \brief Disable hold interception on the channel */
77 static int remove_hold_intercept(struct ast_channel *chan)
78 {
79  struct ast_datastore *datastore = NULL;
80  struct hold_intercept_data *data;
81  SCOPED_CHANNELLOCK(chan_lock, chan);
82 
83  datastore = ast_channel_datastore_find(chan, &hold_intercept_datastore, NULL);
84  if (!datastore) {
85  ast_log(AST_LOG_WARNING, "Cannot remove HOLD_INTERCEPT from %s: HOLD_INTERCEPT not currently enabled\n",
86  ast_channel_name(chan));
87  return -1;
88  }
89  data = datastore->data;
90 
91  if (ast_framehook_detach(chan, data->framehook_id)) {
92  ast_log(AST_LOG_WARNING, "Failed to remove HOLD_INTERCEPT framehook from channel %s\n",
93  ast_channel_name(chan));
94  return -1;
95  }
96 
97  if (ast_channel_datastore_remove(chan, datastore)) {
98  ast_log(AST_LOG_WARNING, "Failed to remove HOLD_INTERCEPT datastore from channel %s\n",
99  ast_channel_name(chan));
100  return -1;
101  }
102  ast_datastore_free(datastore);
103 
104  return 0;
105 }
106 
107 /*! \brief Frame hook that is called to intercept hold/unhold */
108 static struct ast_frame *hold_intercept_framehook(struct ast_channel *chan,
109  struct ast_frame *f, enum ast_framehook_event event, void *data)
110 {
111  int frame_type;
112 
113  if (!f || (event != AST_FRAMEHOOK_EVENT_WRITE)) {
114  return f;
115  }
116 
117  if (f->frametype != AST_FRAME_CONTROL) {
118  return f;
119  }
120 
121  frame_type = f->subclass.integer;
122  if (frame_type != AST_CONTROL_HOLD && frame_type != AST_CONTROL_UNHOLD) {
123  return f;
124  }
125 
126  /* Munch munch */
127  ast_frfree(f);
128  f = &ast_null_frame;
129 
132  NULL);
133 
134  return f;
135 }
136 
137 /*! \brief Callback function which informs upstream if we are consuming a frame of a specific type */
139 {
140  return (type == AST_FRAME_CONTROL ? 1 : 0);
141 }
142 
143 /*! \internal \brief Enable hold interception on the channel */
144 static int set_hold_intercept(struct ast_channel *chan)
145 {
146  struct ast_datastore *datastore;
147  struct hold_intercept_data *data;
148  static struct ast_framehook_interface hold_framehook_interface = {
150  .event_cb = hold_intercept_framehook,
151  .consume_cb = hold_intercept_framehook_consume,
152  .disable_inheritance = 1,
153  };
154  SCOPED_CHANNELLOCK(chan_lock, chan);
155 
156  datastore = ast_channel_datastore_find(chan, &hold_intercept_datastore, NULL);
157  if (datastore) {
158  ast_log(AST_LOG_WARNING, "HOLD_INTERCEPT already set on '%s'\n",
159  ast_channel_name(chan));
160  return 0;
161  }
162 
163  datastore = ast_datastore_alloc(&hold_intercept_datastore, NULL);
164  if (!datastore) {
165  return -1;
166  }
167 
168  data = ast_calloc(1, sizeof(*data));
169  if (!data) {
170  ast_datastore_free(datastore);
171  return -1;
172  }
173 
174  data->framehook_id = ast_framehook_attach(chan, &hold_framehook_interface);
175  if (data->framehook_id < 0) {
176  ast_log(AST_LOG_WARNING, "Failed to attach HOLD_INTERCEPT framehook to '%s'\n",
177  ast_channel_name(chan));
178  ast_datastore_free(datastore);
179  ast_free(data);
180  return -1;
181  }
182  datastore->data = data;
183 
184  ast_channel_datastore_add(chan, datastore);
185 
186  return 0;
187 }
188 
189 /*! \internal \brief HOLD_INTERCEPT write function callback */
190 static int hold_intercept_fn_write(struct ast_channel *chan, const char *function,
191  char *data, const char *value)
192 {
193  int res;
194 
195  if (!chan) {
196  return -1;
197  }
198 
199  if (ast_strlen_zero(data)) {
200  ast_log(AST_LOG_WARNING, "HOLD_INTERCEPT requires an argument\n");
201  return -1;
202  }
203 
204  if (!strcasecmp(data, "set")) {
205  res = set_hold_intercept(chan);
206  } else if (!strcasecmp(data, "remove")) {
207  res = remove_hold_intercept(chan);
208  } else {
209  ast_log(AST_LOG_WARNING, "HOLD_INTERCEPT: unknown option %s\n", data);
210  res = -1;
211  }
212 
213  return res;
214 }
215 
216 /*! \brief Definition of the HOLD_INTERCEPT function */
218  .name = "HOLD_INTERCEPT",
219  .write = hold_intercept_fn_write,
220 };
221 
222 /*! \internal \brief Unload the module */
223 static int unload_module(void)
224 {
225  return ast_custom_function_unregister(&hold_intercept_function);
226 }
227 
228 /*! \internal \brief Load the module */
229 static int load_module(void)
230 {
232 }
233 
234 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Hold interception dialplan function");
const char * name
Definition: pbx.h:119
struct stasis_message_type * ast_channel_hold_type(void)
Message type for when a channel is placed on hold.
const char * type
Definition: datastore.h:32
static const char type[]
Definition: chan_ooh323.c:109
Main Channel structure associated with a channel.
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:567
Asterisk main include file. File version handling, generic pbx functions.
struct stasis_message_type * ast_channel_unhold_type(void)
Message type for when a channel is removed from hold.
static int hold_intercept_framehook_consume(void *data, enum ast_frame_type type)
Callback function which informs upstream if we are consuming a frame of a specific type...
void ast_channel_publish_cached_blob(struct ast_channel *chan, struct stasis_message_type *type, struct ast_json *blob)
Publish a channel blob message using the latest snapshot from the cache.
static struct ast_custom_function hold_intercept_function
Definition of the HOLD_INTERCEPT function.
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
ast_framehook_event
These are the types of events that the framehook&#39;s event callback can receive.
Definition: framehook.h:151
#define AST_LOG_WARNING
Definition: logger.h:279
int ast_framehook_detach(struct ast_channel *chan, int framehook_id)
Detach an framehook from a channel.
Definition: framehook.c:177
Structure for a data store type.
Definition: datastore.h:31
Definition: astman.c:222
static int remove_hold_intercept(struct ast_channel *chan)
Structure for a data store object.
Definition: datastore.h:68
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2404
static int set_hold_intercept(struct ast_channel *chan)
#define NULL
Definition: resample.c:96
int value
Definition: syslog.c:37
static int hold_intercept_fn_write(struct ast_channel *chan, const char *function, char *data, const char *value)
static int load_module(void)
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
struct ast_frame_subclass subclass
#define ast_strlen_zero(foo)
Definition: strings.h:52
int ast_framehook_attach(struct ast_channel *chan, struct ast_framehook_interface *i)
Attach an framehook onto a channel for frame interception.
Definition: framehook.c:132
#define ast_log
Definition: astobj2.c:42
#define SCOPED_CHANNELLOCK(varname, chan)
scoped lock specialization for channels.
Definition: lock.h:617
General Asterisk PBX channel definitions.
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
Asterisk internal frame definitions.
Core PBX routines and definitions.
ast_frame_type
Frame types.
#define AST_FRAMEHOOK_INTERFACE_VERSION
Definition: framehook.h:227
frame_type
Definition: codec_builtin.c:44
static struct ast_frame * hold_intercept_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data)
Frame hook that is called to intercept hold/unhold.
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Private data structure used with the function&#39;s datastore.
static const struct ast_datastore_info hold_intercept_datastore
The channel datastore the function uses to store state.
struct ast_frame ast_null_frame
Definition: main/frame.c:79
void * data
Definition: datastore.h:70
const char * ast_channel_name(const struct ast_channel *chan)
#define ast_frfree(fr)
Data structure associated with a single frame of data.
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:89
union ast_frame::@263 data
enum ast_frame_type frametype
static int unload_module(void)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2390
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2399
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1508