<p>N A has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/19870">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">app_signal: Add signaling applications<br><br>Adds the Signal and WaitForSignal<br>applications, which can be used for inter-channel<br>signaling in the dialplan.<br><br>Signal supports sending a signal to other channels<br>listening for a signal of the same name, with an<br>optional data payload. The signal is received by<br>all channels waiting for that named signal.<br><br>ASTERISK-29810 #close<br><br>Change-Id: Ic34439de3d60f8609357666a465c354d81f5fef3<br>---<br>A apps/app_signal.c<br>A doc/CHANGES-staging/app_signal.txt<br>2 files changed, 496 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/70/19870/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/apps/app_signal.c b/apps/app_signal.c</span><br><span>new file mode 100644</span><br><span>index 0000000..a61bc61</span><br><span>--- /dev/null</span><br><span>+++ b/apps/app_signal.c</span><br><span>@@ -0,0 +1,471 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2022, Naveen Albert</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Naveen Albert <asterisk@phreaknet.org></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Channel signaling applications</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \author Naveen Albert <asterisk@phreaknet.org></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \ingroup applications</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+       <support_level>extended</support_level></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/file.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/channel.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/pbx.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/app.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** DOCUMENTATION</span><br><span style="color: hsl(120, 100%, 40%);">+        <application name="Signal" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+                <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                      Sends a signal to any waiting channels.</span><br><span style="color: hsl(120, 100%, 40%);">+               </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+             <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+                        <parameter name="signalname" required="true"></span><br><span style="color: hsl(120, 100%, 40%);">+                               <para>Name of signal to send.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                      </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+                    <parameter name="payload" required="false"></span><br><span style="color: hsl(120, 100%, 40%);">+                         <para>Payload data to deliver.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                     </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+            </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+               <description></span><br><span style="color: hsl(120, 100%, 40%);">+                   <para>Sends a named signal to any channels that may be</span><br><span style="color: hsl(120, 100%, 40%);">+                  waiting for one. Acts as a producer in a simple</span><br><span style="color: hsl(120, 100%, 40%);">+                       message queue.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                   <variablelist></span><br><span style="color: hsl(120, 100%, 40%);">+                          <variable name="SIGNALSTATUS"></span><br><span style="color: hsl(120, 100%, 40%);">+                                        <value name="SUCCESS"></span><br><span style="color: hsl(120, 100%, 40%);">+                                                Signal was successfully sent to at least</span><br><span style="color: hsl(120, 100%, 40%);">+                                              one listener for processing.</span><br><span style="color: hsl(120, 100%, 40%);">+                                  </value></span><br><span style="color: hsl(120, 100%, 40%);">+                                        <value name="FAILURE"></span><br><span style="color: hsl(120, 100%, 40%);">+                                                Signal could not be sent or nobody</span><br><span style="color: hsl(120, 100%, 40%);">+                                            was listening for this signal.</span><br><span style="color: hsl(120, 100%, 40%);">+                                        </value></span><br><span style="color: hsl(120, 100%, 40%);">+                                </variable></span><br><span style="color: hsl(120, 100%, 40%);">+                     </variablelist></span><br><span style="color: hsl(120, 100%, 40%);">+                 <example title="Send a signal named workdone"></span><br><span style="color: hsl(120, 100%, 40%);">+                        same => n,Signal(workdone,Work has completed)</span><br><span style="color: hsl(120, 100%, 40%);">+                      </example></span><br><span style="color: hsl(120, 100%, 40%);">+              </description></span><br><span style="color: hsl(120, 100%, 40%);">+          <see-also></span><br><span style="color: hsl(120, 100%, 40%);">+                      <ref type="application">WaitForSignal</ref></span><br><span style="color: hsl(120, 100%, 40%);">+             </see-also></span><br><span style="color: hsl(120, 100%, 40%);">+     </application></span><br><span style="color: hsl(120, 100%, 40%);">+  <application name="WaitForSignal" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+         <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                      Waits for a named signal on a channel.</span><br><span style="color: hsl(120, 100%, 40%);">+                </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+             <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+                        <parameter name="signalname" required="true"></span><br><span style="color: hsl(120, 100%, 40%);">+                               <para>Name of signal to send.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                      </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+                    <parameter name="signaltimeout" required="false"></span><br><span style="color: hsl(120, 100%, 40%);">+                           <para>Maximum time, in seconds, to wait for signal.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                        </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+            </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+               <description></span><br><span style="color: hsl(120, 100%, 40%);">+                   <para>Waits for <replaceable>signaltimeout</replaceable> seconds on the current</span><br><span style="color: hsl(120, 100%, 40%);">+                     channel to receive a signal with name <replaceable>signalname</replaceable>.</span><br><span style="color: hsl(120, 100%, 40%);">+                      Acts as a consumer in a simple message queue.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                    <para>Result of signal wait will be stored in the following variables:</para></span><br><span style="color: hsl(120, 100%, 40%);">+                     <variablelist></span><br><span style="color: hsl(120, 100%, 40%);">+                          <variable name="WAITFORSIGNALSTATUS"></span><br><span style="color: hsl(120, 100%, 40%);">+                                 <value name="SIGNALED"></span><br><span style="color: hsl(120, 100%, 40%);">+                                               Signal was received.</span><br><span style="color: hsl(120, 100%, 40%);">+                                  </value></span><br><span style="color: hsl(120, 100%, 40%);">+                                        <value name="TIMEOUT"></span><br><span style="color: hsl(120, 100%, 40%);">+                                                Timed out waiting for signal.</span><br><span style="color: hsl(120, 100%, 40%);">+                                 </value></span><br><span style="color: hsl(120, 100%, 40%);">+                                        <value name="HANGUP"></span><br><span style="color: hsl(120, 100%, 40%);">+                                         Channel hung up before signal was received.</span><br><span style="color: hsl(120, 100%, 40%);">+                                   </value></span><br><span style="color: hsl(120, 100%, 40%);">+                                </variable></span><br><span style="color: hsl(120, 100%, 40%);">+                             <variable name="WAITFORSIGNALPAYLOAD"></span><br><span style="color: hsl(120, 100%, 40%);">+                                        <para>Data payload attached to signal, if it exists</para></span><br><span style="color: hsl(120, 100%, 40%);">+                                </variable></span><br><span style="color: hsl(120, 100%, 40%);">+                     </variablelist></span><br><span style="color: hsl(120, 100%, 40%);">+                 <example title="Wait for the workdone signal, indefinitely, and print out payload"></span><br><span style="color: hsl(120, 100%, 40%);">+                   same => n,WaitForSignal(workdone)</span><br><span style="color: hsl(120, 100%, 40%);">+                  same => n,NoOp(Received: ${WAITFORSIGNALPAYLOAD})</span><br><span style="color: hsl(120, 100%, 40%);">+                  </example></span><br><span style="color: hsl(120, 100%, 40%);">+              </description></span><br><span style="color: hsl(120, 100%, 40%);">+          <see-also></span><br><span style="color: hsl(120, 100%, 40%);">+                      <ref type="application">Signal</ref></span><br><span style="color: hsl(120, 100%, 40%);">+            </see-also></span><br><span style="color: hsl(120, 100%, 40%);">+     </application></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const char * const app = "Signal";</span><br><span style="color: hsl(120, 100%, 40%);">+static const char * const app2 = "WaitForSignal";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct signalitem {</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_mutex_t lock;</span><br><span style="color: hsl(120, 100%, 40%);">+     char name[AST_MAX_CONTEXT];</span><br><span style="color: hsl(120, 100%, 40%);">+   int sig_alert_pipe[2];</span><br><span style="color: hsl(120, 100%, 40%);">+        int watchers;</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int signaled:1;</span><br><span style="color: hsl(120, 100%, 40%);">+      char *payload;</span><br><span style="color: hsl(120, 100%, 40%);">+        AST_LIST_ENTRY(signalitem) entry;               /*!< Next Signal item */</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static AST_RWLIST_HEAD_STATIC(signals, signalitem);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct signalitem *alloc_signal(const char *sname)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct signalitem *s;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!(s = ast_calloc(1, sizeof(*s)))) {</span><br><span style="color: hsl(120, 100%, 40%);">+               return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_mutex_init(&s->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_copy_string(s->name, sname, sizeof(s->name));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     s->sig_alert_pipe[0] = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ s->sig_alert_pipe[1] = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ s->watchers = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+   s->payload = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_alertpipe_init(s->sig_alert_pipe);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return s;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int dealloc_signal(struct signalitem *s)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    if (s->watchers) { /* somebody is still using us... refuse to go away */</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_debug(1, "Signal '%s' is still being used by %d listener(s)\n", s->name, s->watchers);</span><br><span style="color: hsl(120, 100%, 40%);">+            return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_alertpipe_close(s->sig_alert_pipe);</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_mutex_destroy(&s->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (s->payload) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_free(s->payload);</span><br><span style="color: hsl(120, 100%, 40%);">+              s->payload = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_free(s);</span><br><span style="color: hsl(120, 100%, 40%);">+  s = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int remove_signal(char *sname)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      int res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct signalitem *s;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       AST_LIST_TRAVERSE_SAFE_BEGIN(&signals, s, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (!strcmp(s->name, sname)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     AST_LIST_REMOVE_CURRENT(entry);</span><br><span style="color: hsl(120, 100%, 40%);">+                       res = dealloc_signal(s);</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_debug(1, "Removed signal '%s'\n", sname);</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     AST_LIST_TRAVERSE_SAFE_END;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct signalitem *get_signal(char *sname, int addnew)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct signalitem *s = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  AST_RWLIST_WRLOCK(&signals);</span><br><span style="color: hsl(120, 100%, 40%);">+      AST_LIST_TRAVERSE(&signals, s, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!strcasecmp(s->name, sname)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 ast_debug(1, "Using existing signal item '%s'\n", sname);</span><br><span style="color: hsl(120, 100%, 40%);">+                   break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!s) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (addnew) { /* signal doesn't exist, so create it */</span><br><span style="color: hsl(120, 100%, 40%);">+                    s = alloc_signal(sname);</span><br><span style="color: hsl(120, 100%, 40%);">+                      /* Totally fail if we fail to find/create an entry */</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (s) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_debug(1, "Created new signal item '%s'\n", sname);</span><br><span style="color: hsl(120, 100%, 40%);">+                              AST_RWLIST_INSERT_HEAD(&signals, s, entry);</span><br><span style="color: hsl(120, 100%, 40%);">+                       } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_log(LOG_WARNING, "Failed to create signal item for '%s'\n", sname);</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_debug(1, "Signal '%s' doesn't exist, and not creating it\n", sname);</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     AST_RWLIST_UNLOCK(&signals);</span><br><span style="color: hsl(120, 100%, 40%);">+      return s;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int wait_for_signal_or_hangup(struct ast_channel *chan, char *signame, int timeout)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct signalitem *s = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  int ms, remaining_time, res = 1, goaway = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct timeval start;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_frame *frame = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     remaining_time = timeout;</span><br><span style="color: hsl(120, 100%, 40%);">+     start = ast_tvnow();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        s = get_signal(signame, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_lock(&s->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+      s->watchers = s->watchers + 1; /* we unlock, because a) other people need to use this and */</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_mutex_unlock(&s->lock); /* b) the signal will be available to us as long as watchers > 0 */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   while (timeout == 0 || remaining_time > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+               int ofd, exception;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         ms = 1000;</span><br><span style="color: hsl(120, 100%, 40%);">+            errno = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+            if (ast_waitfor_nandfds(&chan, 1, &s->sig_alert_pipe[0], 1, &exception, &ofd, &ms)) { /* channel won */</span><br><span style="color: hsl(120, 100%, 40%);">+                        if (!(frame = ast_read(chan))) { /* channel hung up */</span><br><span style="color: hsl(120, 100%, 40%);">+                                ast_debug(1, "Channel '%s' did not return a frame; probably hung up.\n", ast_channel_name(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+                           res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+                             break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_frfree(frame); /* handle frames */</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else if (ofd == s->sig_alert_pipe[0]) { /* fd won */</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (ast_alertpipe_read(s->sig_alert_pipe) == AST_ALERT_READ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             ast_debug(1, "Alert pipe has data for us\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                               res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                              break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_debug(1, "Alert pipe does not have data for us\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else { /* nobody won */</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (ms && (ofd < 0)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             if (!((errno == 0) || (errno == EINTR))) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                    ast_log(LOG_WARNING, "Something bad happened while channel '%s' was polling.\n", ast_channel_name(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+                                   break;</span><br><span style="color: hsl(120, 100%, 40%);">+                                }</span><br><span style="color: hsl(120, 100%, 40%);">+                     } /* else, nothing happened */</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+             if (timeout) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        remaining_time = ast_remaining_ms(start, timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* WRLOCK the list so that if we're going to destroy the signal now, nobody else can grab it before that happens. */</span><br><span style="color: hsl(120, 100%, 40%);">+      AST_RWLIST_WRLOCK(&signals);</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_mutex_lock(&s->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (s->payload) {</span><br><span style="color: hsl(120, 100%, 40%);">+          pbx_builtin_setvar_helper(chan, "WAITFORSIGNALPAYLOAD", s->payload);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     s->watchers = s->watchers - 1;</span><br><span style="color: hsl(120, 100%, 40%);">+  if (s->watchers) { /* folks are still waiting for this, pass it on... */</span><br><span style="color: hsl(120, 100%, 40%);">+           int save_errno = errno;</span><br><span style="color: hsl(120, 100%, 40%);">+               if (ast_alertpipe_write(s->sig_alert_pipe)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_log(LOG_WARNING, "%s: write() failed: %s\n", __FUNCTION__, strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+             errno = save_errno;</span><br><span style="color: hsl(120, 100%, 40%);">+   } else { /* nobody else is waiting for this */</span><br><span style="color: hsl(120, 100%, 40%);">+                goaway = 1; /* we were the last guy using this, so mark signal item for destruction */</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_mutex_unlock(&s->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (goaway) {</span><br><span style="color: hsl(120, 100%, 40%);">+         /* remove_signal calls ast_mutex_destroy, so don't call it with the mutex itself locked. */</span><br><span style="color: hsl(120, 100%, 40%);">+               remove_signal(signame);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     AST_RWLIST_UNLOCK(&signals);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int send_signal(char *signame, char *payload)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct signalitem *s;</span><br><span style="color: hsl(120, 100%, 40%);">+ int save_errno = errno;</span><br><span style="color: hsl(120, 100%, 40%);">+       int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        s = get_signal(signame, 0); /* if signal doesn't exist already, no point in creating it, because nobody could be waiting for it! */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!s) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return -1; /* this signal didn't exist, so we can't send a signal for it */</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* at this point, we know someone is listening, since signals are destroyed when watchers gets down to 0 */</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_mutex_lock(&s->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+      s->signaled = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+   if (payload && *payload) {</span><br><span style="color: hsl(120, 100%, 40%);">+            int len = strlen(payload);</span><br><span style="color: hsl(120, 100%, 40%);">+            if (s->payload) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  ast_free(s->payload); /* if there was already a payload, replace it */</span><br><span style="color: hsl(120, 100%, 40%);">+                     s->payload = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+             s->payload = ast_malloc(len + 1);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (!s->payload) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 ast_log(LOG_WARNING, "Failed to allocate signal payload '%s'\n", payload);</span><br><span style="color: hsl(120, 100%, 40%);">+          } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_copy_string(s->payload, payload, len + 1);</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (ast_alertpipe_write(s->sig_alert_pipe)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_WARNING, "%s: write() failed: %s\n", __FUNCTION__, strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+            s->signaled = 0; /* okay, so we didn't send a signal after all... */</span><br><span style="color: hsl(120, 100%, 40%);">+           res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     errno = save_errno;</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_debug(1, "Sent '%s' signal to %d listeners\n", signame, s->watchers);</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_mutex_unlock(&s->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int waitsignal_exec(struct ast_channel *chan, const char *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   char *argcopy;</span><br><span style="color: hsl(120, 100%, 40%);">+        int r = 0, timeoutms = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     double timeout = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_DECLARE_APP_ARGS(args,</span><br><span style="color: hsl(120, 100%, 40%);">+            AST_APP_ARG(signame);</span><br><span style="color: hsl(120, 100%, 40%);">+         AST_APP_ARG(sigtimeout);</span><br><span style="color: hsl(120, 100%, 40%);">+      );</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (ast_strlen_zero(data)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_WARNING, "Signal() requires arguments\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   argcopy = ast_strdupa(data);</span><br><span style="color: hsl(120, 100%, 40%);">+  AST_STANDARD_APP_ARGS(args, argcopy);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (ast_strlen_zero(args.signame)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_WARNING, "Missing signal name\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (strlen(args.signame) >= AST_MAX_CONTEXT) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_log(LOG_WARNING, "Signal name '%s' is too long\n", args.signame);</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!ast_strlen_zero(args.sigtimeout)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (sscanf(args.sigtimeout, "%30lg", &timeout) != 1 || timeout < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_log(LOG_WARNING, "Invalid timeout provided: %s. Defaulting to no timeout.\n", args.sigtimeout);</span><br><span style="color: hsl(120, 100%, 40%);">+         } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      timeoutms = timeout * 1000; /* sec to msec */</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (timeout > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_debug(1, "Waiting for signal '%s' for %d ms\n", args.signame, timeoutms);</span><br><span style="color: hsl(120, 100%, 40%);">+       } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_debug(1, "Waiting for signal '%s', indefinitely\n", args.signame);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   r = wait_for_signal_or_hangup(chan, args.signame, timeoutms);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (r == 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_verb(3, "Channel '%s' timed out, waiting for signal '%s'\n", ast_channel_name(chan), args.signame);</span><br><span style="color: hsl(120, 100%, 40%);">+             pbx_builtin_setvar_helper(chan, "WAITFORSIGNALSTATUS", "TIMEOUT");</span><br><span style="color: hsl(120, 100%, 40%);">+        } else if (!r) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_verb(3, "Received signal '%s' on channel '%s'\n", args.signame, ast_channel_name(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+                pbx_builtin_setvar_helper(chan, "WAITFORSIGNALSTATUS", "SIGNALED");</span><br><span style="color: hsl(120, 100%, 40%);">+       } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              pbx_builtin_setvar_helper(chan, "WAITFORSIGNALSTATUS", "HANGUP");</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_verb(3, "Channel '%s' hung up\n", ast_channel_name(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int signal_exec(struct ast_channel *chan, const char *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ char *argcopy;</span><br><span style="color: hsl(120, 100%, 40%);">+        AST_DECLARE_APP_ARGS(args,</span><br><span style="color: hsl(120, 100%, 40%);">+            AST_APP_ARG(signame);</span><br><span style="color: hsl(120, 100%, 40%);">+         AST_APP_ARG(payload);</span><br><span style="color: hsl(120, 100%, 40%);">+ );</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (ast_strlen_zero(data)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_WARNING, "Signal() requires arguments\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   argcopy = ast_strdupa(data);</span><br><span style="color: hsl(120, 100%, 40%);">+  AST_STANDARD_APP_ARGS(args, argcopy);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (ast_strlen_zero(args.signame)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_WARNING, "Missing signal name\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (strlen(args.signame) >= AST_MAX_CONTEXT) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_log(LOG_WARNING, "Signal name '%s' is too long\n", args.signame);</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (send_signal(args.signame, args.payload)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                pbx_builtin_setvar_helper(chan, "SIGNALSTATUS", "FAILURE");</span><br><span style="color: hsl(120, 100%, 40%);">+       } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              pbx_builtin_setvar_helper(chan, "SIGNALSTATUS", "SUCCESS");</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int unload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct signalitem *s;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* To avoid a locking nightmare, and for logistical reasons, this module</span><br><span style="color: hsl(120, 100%, 40%);">+       * will refuse to unload if watchers > 0. That way we know a signal's</span><br><span style="color: hsl(120, 100%, 40%);">+   * pipe won't disappear while it's being used. */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   AST_RWLIST_WRLOCK(&signals);</span><br><span style="color: hsl(120, 100%, 40%);">+      /* Don't just use AST_RWLIST_REMOVE_HEAD, because if dealloc_signal fails, it should stay in the list. */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_TRAVERSE_SAFE_BEGIN(&signals, s, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+                int mres = dealloc_signal(s);</span><br><span style="color: hsl(120, 100%, 40%);">+         res |= mres;</span><br><span style="color: hsl(120, 100%, 40%);">+          if (!mres) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  AST_LIST_REMOVE_CURRENT(entry);</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     AST_LIST_TRAVERSE_SAFE_END;</span><br><span style="color: hsl(120, 100%, 40%);">+   AST_RWLIST_UNLOCK(&signals);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /* One or more signals still has watchers. */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_log(LOG_WARNING, "One or more signals is currently in use. Unload failed.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          return res;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   res |= ast_unregister_application(app);</span><br><span style="color: hsl(120, 100%, 40%);">+       res |= ast_unregister_application(app2);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     int res;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    res = ast_register_application_xml(app, signal_exec);</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_register_application_xml(app2, waitsignal_exec);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Channel Signaling Applications");</span><br><span>diff --git a/doc/CHANGES-staging/app_signal.txt b/doc/CHANGES-staging/app_signal.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..b3b108d</span><br><span>--- /dev/null</span><br><span>+++ b/doc/CHANGES-staging/app_signal.txt</span><br><span>@@ -0,0 +1,5 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: app_signal</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Adds Signal and WaitForSignal applications</span><br><span style="color: hsl(120, 100%, 40%);">+which can be used for signaling or as a</span><br><span style="color: hsl(120, 100%, 40%);">+simple message queue in the dialplan.</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/19870">change 19870</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/19870"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 18 </div>
<div style="display:none"> Gerrit-Change-Id: Ic34439de3d60f8609357666a465c354d81f5fef3 </div>
<div style="display:none"> Gerrit-Change-Number: 19870 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: N A <asterisk@phreaknet.org> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>