Asterisk - The Open Source Telephony Project GIT-master-7e7a603
app_signal.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2022, Naveen Albert
5 *
6 * Naveen Albert <asterisk@phreaknet.org>
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 Channel signaling applications
22 *
23 * \author Naveen Albert <asterisk@phreaknet.org>
24 *
25 * \ingroup applications
26 */
27
28/*** MODULEINFO
29 <support_level>extended</support_level>
30 ***/
31
32#include "asterisk.h"
33
34#include "asterisk/file.h"
35#include "asterisk/channel.h"
36#include "asterisk/pbx.h"
37#include "asterisk/module.h"
38#include "asterisk/app.h"
39#include "asterisk/module.h"
40
41/*** DOCUMENTATION
42 <application name="Signal" language="en_US">
43 <synopsis>
44 Sends a signal to any waiting channels.
45 </synopsis>
46 <syntax>
47 <parameter name="signalname" required="true">
48 <para>Name of signal to send.</para>
49 </parameter>
50 <parameter name="payload" required="false">
51 <para>Payload data to deliver.</para>
52 </parameter>
53 </syntax>
54 <description>
55 <para>Sends a named signal to any channels that may be
56 waiting for one. Acts as a producer in a simple
57 message queue.</para>
58 <variablelist>
59 <variable name="SIGNALSTATUS">
60 <value name="SUCCESS">
61 Signal was successfully sent to at least
62 one listener for processing.
63 </value>
64 <value name="FAILURE">
65 Signal could not be sent or nobody
66 was listening for this signal.
67 </value>
68 </variable>
69 </variablelist>
70 <example title="Send a signal named workdone">
71 same => n,Signal(workdone,Work has completed)
72 </example>
73 </description>
74 <see-also>
75 <ref type="application">WaitForSignal</ref>
76 </see-also>
77 </application>
78 <application name="WaitForSignal" language="en_US">
79 <synopsis>
80 Waits for a named signal on a channel.
81 </synopsis>
82 <syntax>
83 <parameter name="signalname" required="true">
84 <para>Name of signal to send.</para>
85 </parameter>
86 <parameter name="signaltimeout" required="false">
87 <para>Maximum time, in seconds, to wait for signal.</para>
88 </parameter>
89 </syntax>
90 <description>
91 <para>Waits for <replaceable>signaltimeout</replaceable> seconds on the current
92 channel to receive a signal with name <replaceable>signalname</replaceable>.
93 Acts as a consumer in a simple message queue.</para>
94 <para>Result of signal wait will be stored in the following variables:</para>
95 <variablelist>
96 <variable name="WAITFORSIGNALSTATUS">
97 <value name="SIGNALED">
98 Signal was received.
99 </value>
100 <value name="TIMEOUT">
101 Timed out waiting for signal.
102 </value>
103 <value name="HANGUP">
104 Channel hung up before signal was received.
105 </value>
106 </variable>
107 <variable name="WAITFORSIGNALPAYLOAD">
108 <para>Data payload attached to signal, if it exists</para>
109 </variable>
110 </variablelist>
111 <example title="Wait for the workdone signal, indefinitely, and print out payload">
112 same => n,WaitForSignal(workdone)
113 same => n,NoOp(Received: ${WAITFORSIGNALPAYLOAD})
114 </example>
115 </description>
116 <see-also>
117 <ref type="application">Signal</ref>
118 </see-also>
119 </application>
120 ***/
121
122static const char * const app = "Signal";
123static const char * const app2 = "WaitForSignal";
124
130 unsigned int signaled:1;
131 char *payload;
132 AST_LIST_ENTRY(signalitem) entry; /*!< Next Signal item */
133};
134
136
137static struct signalitem *alloc_signal(const char *sname)
138{
139 struct signalitem *s;
140
141 if (!(s = ast_calloc(1, sizeof(*s)))) {
142 return NULL;
143 }
144
145 ast_mutex_init(&s->lock);
146 ast_copy_string(s->name, sname, sizeof(s->name));
147
148 s->sig_alert_pipe[0] = -1;
149 s->sig_alert_pipe[1] = -1;
150 s->watchers = 0;
151 s->payload = NULL;
153
154 return s;
155}
156
157static int dealloc_signal(struct signalitem *s)
158{
159 if (s->watchers) { /* somebody is still using us... refuse to go away */
160 ast_debug(1, "Signal '%s' is still being used by %d listener(s)\n", s->name, s->watchers);
161 return -1;
162 }
165 if (s->payload) {
166 ast_free(s->payload);
167 s->payload = NULL;
168 }
169 ast_free(s);
170 s = NULL;
171 return 0;
172}
173
174static int remove_signal(char *sname)
175{
176 int res = -1;
177 struct signalitem *s;
178
180 if (!strcmp(s->name, sname)) {
182 res = dealloc_signal(s);
183 ast_debug(1, "Removed signal '%s'\n", sname);
184 }
185 }
187
188 return res;
189}
190
191static struct signalitem *get_signal(char *sname, int addnew)
192{
193 struct signalitem *s = NULL;
196 if (!strcasecmp(s->name, sname)) {
197 ast_debug(1, "Using existing signal item '%s'\n", sname);
198 break;
199 }
200 }
201 if (!s) {
202 if (addnew) { /* signal doesn't exist, so create it */
203 s = alloc_signal(sname);
204 /* Totally fail if we fail to find/create an entry */
205 if (s) {
206 ast_debug(1, "Created new signal item '%s'\n", sname);
208 } else {
209 ast_log(LOG_WARNING, "Failed to create signal item for '%s'\n", sname);
210 }
211 } else {
212 ast_debug(1, "Signal '%s' doesn't exist, and not creating it\n", sname);
213 }
214 }
216 return s;
217}
218
219static int wait_for_signal_or_hangup(struct ast_channel *chan, char *signame, int timeout)
220{
221 struct signalitem *s = NULL;
222 int ms, remaining_time, res = 1, goaway = 0;
223 struct timeval start;
224 struct ast_frame *frame = NULL;
225
226 remaining_time = timeout;
227 start = ast_tvnow();
228
229 s = get_signal(signame, 1);
230
231 ast_mutex_lock(&s->lock);
232 s->watchers = s->watchers + 1; /* we unlock, because a) other people need to use this and */
233 ast_mutex_unlock(&s->lock); /* b) the signal will be available to us as long as watchers > 0 */
234
235 while (timeout == 0 || remaining_time > 0) {
236 int ofd, exception;
237
238 ms = 1000;
239 errno = 0;
240 if (ast_waitfor_nandfds(&chan, 1, &s->sig_alert_pipe[0], 1, &exception, &ofd, &ms)) { /* channel won */
241 if (!(frame = ast_read(chan))) { /* channel hung up */
242 ast_debug(1, "Channel '%s' did not return a frame; probably hung up.\n", ast_channel_name(chan));
243 res = -1;
244 break;
245 } else {
246 ast_frfree(frame); /* handle frames */
247 }
248 } else if (ofd == s->sig_alert_pipe[0]) { /* fd won */
250 ast_debug(1, "Alert pipe has data for us\n");
251 res = 0;
252 break;
253 } else {
254 ast_debug(1, "Alert pipe does not have data for us\n");
255 }
256 } else { /* nobody won */
257 if (ms && (ofd < 0)) {
258 if (!((errno == 0) || (errno == EINTR))) {
259 ast_log(LOG_WARNING, "Something bad happened while channel '%s' was polling.\n", ast_channel_name(chan));
260 break;
261 }
262 } /* else, nothing happened */
263 }
264 if (timeout) {
265 remaining_time = ast_remaining_ms(start, timeout);
266 }
267 }
268
269 /* WRLOCK the list so that if we're going to destroy the signal now, nobody else can grab it before that happens. */
271 ast_mutex_lock(&s->lock);
272 if (s->payload) {
273 pbx_builtin_setvar_helper(chan, "WAITFORSIGNALPAYLOAD", s->payload);
274 }
275 s->watchers = s->watchers - 1;
276 if (s->watchers) { /* folks are still waiting for this, pass it on... */
277 int save_errno = errno;
279 ast_log(LOG_WARNING, "%s: write() failed: %s\n", __FUNCTION__, strerror(errno));
280 }
281 errno = save_errno;
282 } else { /* nobody else is waiting for this */
283 goaway = 1; /* we were the last guy using this, so mark signal item for destruction */
284 }
286
287 if (goaway) {
288 /* remove_signal calls ast_mutex_destroy, so don't call it with the mutex itself locked. */
289 remove_signal(signame);
290 }
292
293 return res;
294}
295
296static int send_signal(char *signame, char *payload)
297{
298 struct signalitem *s;
299 int save_errno = errno;
300 int res = 0;
301
302 s = get_signal(signame, 0); /* if signal doesn't exist already, no point in creating it, because nobody could be waiting for it! */
303
304 if (!s) {
305 return -1; /* this signal didn't exist, so we can't send a signal for it */
306 }
307
308 /* at this point, we know someone is listening, since signals are destroyed when watchers gets down to 0 */
309 ast_mutex_lock(&s->lock);
310 s->signaled = 1;
311 if (payload && *payload) {
312 int len = strlen(payload);
313 if (s->payload) {
314 ast_free(s->payload); /* if there was already a payload, replace it */
315 s->payload = NULL;
316 }
317 s->payload = ast_malloc(len + 1);
318 if (!s->payload) {
319 ast_log(LOG_WARNING, "Failed to allocate signal payload '%s'\n", payload);
320 } else {
322 }
323 }
325 ast_log(LOG_WARNING, "%s: write() failed: %s\n", __FUNCTION__, strerror(errno));
326 s->signaled = 0; /* okay, so we didn't send a signal after all... */
327 res = -1;
328 }
329 errno = save_errno;
330 ast_debug(1, "Sent '%s' signal to %d listeners\n", signame, s->watchers);
332
333 return res;
334}
335
336static int waitsignal_exec(struct ast_channel *chan, const char *data)
337{
338 char *argcopy;
339 int r = 0, timeoutms = 0;
340 double timeout = 0;
341
343 AST_APP_ARG(signame);
344 AST_APP_ARG(sigtimeout);
345 );
346
347 if (ast_strlen_zero(data)) {
348 ast_log(LOG_WARNING, "Signal() requires arguments\n");
349 return -1;
350 }
351
352 argcopy = ast_strdupa(data);
353 AST_STANDARD_APP_ARGS(args, argcopy);
354
355 if (ast_strlen_zero(args.signame)) {
356 ast_log(LOG_WARNING, "Missing signal name\n");
357 return -1;
358 }
359 if (strlen(args.signame) >= AST_MAX_CONTEXT) {
360 ast_log(LOG_WARNING, "Signal name '%s' is too long\n", args.signame);
361 return -1;
362 }
363 if (!ast_strlen_zero(args.sigtimeout)) {
364 if (sscanf(args.sigtimeout, "%30lg", &timeout) != 1 || timeout < 0) {
365 ast_log(LOG_WARNING, "Invalid timeout provided: %s. Defaulting to no timeout.\n", args.sigtimeout);
366 } else {
367 timeoutms = timeout * 1000; /* sec to msec */
368 }
369 }
370
371 if (timeout > 0) {
372 ast_debug(1, "Waiting for signal '%s' for %d ms\n", args.signame, timeoutms);
373 } else {
374 ast_debug(1, "Waiting for signal '%s', indefinitely\n", args.signame);
375 }
376
377 r = wait_for_signal_or_hangup(chan, args.signame, timeoutms);
378
379 if (r == 1) {
380 ast_verb(3, "Channel '%s' timed out, waiting for signal '%s'\n", ast_channel_name(chan), args.signame);
381 pbx_builtin_setvar_helper(chan, "WAITFORSIGNALSTATUS", "TIMEOUT");
382 } else if (!r) {
383 ast_verb(3, "Received signal '%s' on channel '%s'\n", args.signame, ast_channel_name(chan));
384 pbx_builtin_setvar_helper(chan, "WAITFORSIGNALSTATUS", "SIGNALED");
385 } else {
386 pbx_builtin_setvar_helper(chan, "WAITFORSIGNALSTATUS", "HANGUP");
387 ast_verb(3, "Channel '%s' hung up\n", ast_channel_name(chan));
388 return -1;
389 }
390
391 return 0;
392}
393
394static int signal_exec(struct ast_channel *chan, const char *data)
395{
396 char *argcopy;
398 AST_APP_ARG(signame);
400 );
401
402 if (ast_strlen_zero(data)) {
403 ast_log(LOG_WARNING, "Signal() requires arguments\n");
404 return -1;
405 }
406
407 argcopy = ast_strdupa(data);
408 AST_STANDARD_APP_ARGS(args, argcopy);
409
410 if (ast_strlen_zero(args.signame)) {
411 ast_log(LOG_WARNING, "Missing signal name\n");
412 return -1;
413 }
414 if (strlen(args.signame) >= AST_MAX_CONTEXT) {
415 ast_log(LOG_WARNING, "Signal name '%s' is too long\n", args.signame);
416 return -1;
417 }
418
419 if (send_signal(args.signame, args.payload)) {
420 pbx_builtin_setvar_helper(chan, "SIGNALSTATUS", "FAILURE");
421 } else {
422 pbx_builtin_setvar_helper(chan, "SIGNALSTATUS", "SUCCESS");
423 }
424
425 return 0;
426}
427
428static int unload_module(void)
429{
430 struct signalitem *s;
431 int res = 0;
432
433 /* To avoid a locking nightmare, and for logistical reasons, this module
434 * will refuse to unload if watchers > 0. That way we know a signal's
435 * pipe won't disappear while it's being used. */
436
438 /* Don't just use AST_RWLIST_REMOVE_HEAD, because if dealloc_signal fails, it should stay in the list. */
440 int mres = dealloc_signal(s);
441 res |= mres;
442 if (!mres) {
444 }
445 }
448
449 /* One or more signals still has watchers. */
450 if (res) {
451 ast_log(LOG_WARNING, "One or more signals is currently in use. Unload failed.\n");
452 return res;
453 }
454
457
458 return res;
459}
460
461static int load_module(void)
462{
463 int res;
464
467
468 return res;
469}
470
471AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Channel Signaling Applications");
void ast_alertpipe_close(int alert_pipe[2])
Close an alert pipe.
Definition: alertpipe.c:79
ssize_t ast_alertpipe_write(int alert_pipe[2])
Write an event to an alert pipe.
Definition: alertpipe.c:120
int ast_alertpipe_init(int alert_pipe[2])
Initialize an alert pipe.
Definition: alertpipe.c:38
@ AST_ALERT_READ_SUCCESS
Definition: alertpipe.h:25
ast_alert_status_t ast_alertpipe_read(int alert_pipe[2])
Read an event from an alert pipe.
Definition: alertpipe.c:102
static int wait_for_signal_or_hangup(struct ast_channel *chan, char *signame, int timeout)
Definition: app_signal.c:219
static int signal_exec(struct ast_channel *chan, const char *data)
Definition: app_signal.c:394
static int waitsignal_exec(struct ast_channel *chan, const char *data)
Definition: app_signal.c:336
static struct signalitem * alloc_signal(const char *sname)
Definition: app_signal.c:137
static const char *const app
Definition: app_signal.c:122
static const char *const app2
Definition: app_signal.c:123
static int dealloc_signal(struct signalitem *s)
Definition: app_signal.c:157
static int send_signal(char *signame, char *payload)
Definition: app_signal.c:296
static struct signalitem * get_signal(char *sname, int addnew)
Definition: app_signal.c:191
static int remove_signal(char *sname)
Definition: app_signal.c:174
static int load_module(void)
Definition: app_signal.c:461
static int unload_module(void)
Definition: app_signal.c:428
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Channel Signaling Applications")
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
struct ast_channel * ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds, int *exception, int *outfd, int *ms)
Waits for activity on a group of channels.
Definition: channel.c:2988
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4257
#define AST_MAX_CONTEXT
Definition: channel.h:135
Generic File Format Support. Should be included by clients of the file handling routines....
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application 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_frfree(fr)
#define ast_debug(level,...)
Log a DEBUG message.
#define ast_verb(level,...)
#define LOG_WARNING
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:52
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:151
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized.
Definition: linkedlists.h:333
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
#define AST_RWLIST_INSERT_HEAD
Definition: linkedlists.h:718
#define ast_mutex_init(pmutex)
Definition: lock.h:186
#define ast_mutex_unlock(a)
Definition: lock.h:190
#define ast_mutex_destroy(a)
Definition: lock.h:188
#define ast_mutex_lock(a)
Definition: lock.h:189
int errno
Asterisk module definitions.
#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
Core PBX routines and definitions.
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.
#define NULL
Definition: resample.c:96
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
Main Channel structure associated with a channel.
Data structure associated with a single frame of data.
Structure for mutex and tracking information.
Definition: lock.h:135
Definition: search.h:40
unsigned int signaled
Definition: app_signal.c:130
char * payload
Definition: app_signal.c:131
struct signalitem::@62 entry
int sig_alert_pipe[2]
Definition: app_signal.c:128
int watchers
Definition: app_signal.c:129
char name[AST_MAX_CONTEXT]
Definition: app_signal.c:127
ast_mutex_t lock
Definition: app_signal.c:126
const char * args
int ast_remaining_ms(struct timeval start, int max_ms)
Calculate remaining milliseconds given a starting timestamp and upper bound.
Definition: utils.c:2281
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159