Merge "asterisk.c: When astcanary dies on linux, reset priority on all threads."
[asterisk/asterisk.git] / main / pbx_hangup_handler.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2016, CFWare, LLC
5  *
6  * Corey Farrell <git@cfware.com>
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 PBX Hangup Handler management routines.
22  *
23  * \author Corey Farrell <git@cfware.com>
24  */
25
26 /*** MODULEINFO
27         <support_level>core</support_level>
28  ***/
29
30 #include "asterisk.h"
31
32 ASTERISK_REGISTER_FILE()
33
34 #include "asterisk/_private.h"
35 #include "asterisk/app.h"
36 #include "asterisk/cli.h"
37 #include "asterisk/linkedlists.h"
38 #include "asterisk/pbx.h"
39 #include "asterisk/stasis_channels.h"
40 #include "asterisk/utils.h"
41
42 /*!
43  * \internal
44  * \brief Publish a hangup handler related message to \ref stasis
45  */
46 static void publish_hangup_handler_message(const char *action, struct ast_channel *chan, const char *handler)
47 {
48         RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
49
50         blob = ast_json_pack("{s: s, s: s}",
51                         "type", action,
52                         "handler", S_OR(handler, ""));
53         if (!blob) {
54                 return;
55         }
56
57         ast_channel_publish_blob(chan, ast_channel_hangup_handler_type(), blob);
58 }
59
60 int ast_pbx_hangup_handler_run(struct ast_channel *chan)
61 {
62         struct ast_hangup_handler_list *handlers;
63         struct ast_hangup_handler *h_handler;
64
65         ast_channel_lock(chan);
66         handlers = ast_channel_hangup_handlers(chan);
67         if (AST_LIST_EMPTY(handlers)) {
68                 ast_channel_unlock(chan);
69                 return 0;
70         }
71
72         /*
73          * Make sure that the channel is marked as hungup since we are
74          * going to run the hangup handlers on it.
75          */
76         ast_softhangup_nolock(chan, AST_SOFTHANGUP_HANGUP_EXEC);
77
78         for (;;) {
79                 handlers = ast_channel_hangup_handlers(chan);
80                 h_handler = AST_LIST_REMOVE_HEAD(handlers, node);
81                 if (!h_handler) {
82                         break;
83                 }
84
85                 publish_hangup_handler_message("run", chan, h_handler->args);
86                 ast_channel_unlock(chan);
87
88                 ast_app_exec_sub(NULL, chan, h_handler->args, 1);
89                 ast_free(h_handler);
90
91                 ast_channel_lock(chan);
92         }
93         ast_channel_unlock(chan);
94         return 1;
95 }
96
97 void ast_pbx_hangup_handler_init(struct ast_channel *chan)
98 {
99         struct ast_hangup_handler_list *handlers;
100
101         handlers = ast_channel_hangup_handlers(chan);
102         AST_LIST_HEAD_INIT_NOLOCK(handlers);
103 }
104
105 void ast_pbx_hangup_handler_destroy(struct ast_channel *chan)
106 {
107         struct ast_hangup_handler_list *handlers;
108         struct ast_hangup_handler *h_handler;
109
110         ast_channel_lock(chan);
111
112         /* Get rid of each of the hangup handlers on the channel */
113         handlers = ast_channel_hangup_handlers(chan);
114         while ((h_handler = AST_LIST_REMOVE_HEAD(handlers, node))) {
115                 ast_free(h_handler);
116         }
117
118         ast_channel_unlock(chan);
119 }
120
121 int ast_pbx_hangup_handler_pop(struct ast_channel *chan)
122 {
123         struct ast_hangup_handler_list *handlers;
124         struct ast_hangup_handler *h_handler;
125
126         ast_channel_lock(chan);
127         handlers = ast_channel_hangup_handlers(chan);
128         h_handler = AST_LIST_REMOVE_HEAD(handlers, node);
129         if (h_handler) {
130                 publish_hangup_handler_message("pop", chan, h_handler->args);
131         }
132         ast_channel_unlock(chan);
133         if (h_handler) {
134                 ast_free(h_handler);
135                 return 1;
136         }
137         return 0;
138 }
139
140 void ast_pbx_hangup_handler_push(struct ast_channel *chan, const char *handler)
141 {
142         struct ast_hangup_handler_list *handlers;
143         struct ast_hangup_handler *h_handler;
144         const char *expanded_handler;
145
146         if (ast_strlen_zero(handler)) {
147                 return;
148         }
149
150         expanded_handler = ast_app_expand_sub_args(chan, handler);
151         if (!expanded_handler) {
152                 return;
153         }
154         h_handler = ast_malloc(sizeof(*h_handler) + 1 + strlen(expanded_handler));
155         if (!h_handler) {
156                 ast_free((char *) expanded_handler);
157                 return;
158         }
159         strcpy(h_handler->args, expanded_handler);/* Safe */
160         ast_free((char *) expanded_handler);
161
162         ast_channel_lock(chan);
163
164         handlers = ast_channel_hangup_handlers(chan);
165         AST_LIST_INSERT_HEAD(handlers, h_handler, node);
166         publish_hangup_handler_message("push", chan, h_handler->args);
167         ast_channel_unlock(chan);
168 }
169
170 #define HANDLER_FORMAT  "%-30s %s\n"
171
172 /*!
173  * \internal
174  * \brief CLI output the hangup handler headers.
175  * \since 11.0
176  *
177  * \param fd CLI file descriptor to use.
178  *
179  * \return Nothing
180  */
181 static void ast_pbx_hangup_handler_headers(int fd)
182 {
183         ast_cli(fd, HANDLER_FORMAT, "Channel", "Handler");
184 }
185
186 /*!
187  * \internal
188  * \brief CLI output the channel hangup handlers.
189  * \since 11.0
190  *
191  * \param fd CLI file descriptor to use.
192  * \param chan Channel to show hangup handlers.
193  *
194  * \return Nothing
195  */
196 static void ast_pbx_hangup_handler_show(int fd, struct ast_channel *chan)
197 {
198         struct ast_hangup_handler_list *handlers;
199         struct ast_hangup_handler *h_handler;
200         int first = 1;
201
202         ast_channel_lock(chan);
203         handlers = ast_channel_hangup_handlers(chan);
204         AST_LIST_TRAVERSE(handlers, h_handler, node) {
205                 ast_cli(fd, HANDLER_FORMAT, first ? ast_channel_name(chan) : "", h_handler->args);
206                 first = 0;
207         }
208         ast_channel_unlock(chan);
209 }
210
211 /*
212  * \brief 'show hanguphandlers <channel>' CLI command implementation function...
213  */
214 static char *handle_show_hangup_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
215 {
216         struct ast_channel *chan;
217
218         switch (cmd) {
219         case CLI_INIT:
220                 e->command = "core show hanguphandlers";
221                 e->usage =
222                         "Usage: core show hanguphandlers <channel>\n"
223                         "       Show hangup handlers of a specified channel.\n";
224                 return NULL;
225         case CLI_GENERATE:
226                 return ast_complete_channels(a->line, a->word, a->pos, a->n, e->args);
227         }
228
229         if (a->argc < 4) {
230                 return CLI_SHOWUSAGE;
231         }
232
233         chan = ast_channel_get_by_name(a->argv[3]);
234         if (!chan) {
235                 ast_cli(a->fd, "Channel does not exist.\n");
236                 return CLI_FAILURE;
237         }
238
239         ast_pbx_hangup_handler_headers(a->fd);
240         ast_pbx_hangup_handler_show(a->fd, chan);
241
242         ast_channel_unref(chan);
243
244         return CLI_SUCCESS;
245 }
246
247 /*
248  * \brief 'show hanguphandlers all' CLI command implementation function...
249  */
250 static char *handle_show_hangup_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
251 {
252         struct ast_channel_iterator *iter;
253         struct ast_channel *chan;
254
255         switch (cmd) {
256         case CLI_INIT:
257                 e->command = "core show hanguphandlers all";
258                 e->usage =
259                         "Usage: core show hanguphandlers all\n"
260                         "       Show hangup handlers for all channels.\n";
261                 return NULL;
262         case CLI_GENERATE:
263                 return ast_complete_channels(a->line, a->word, a->pos, a->n, e->args);
264         }
265
266         if (a->argc < 4) {
267                 return CLI_SHOWUSAGE;
268         }
269
270         iter = ast_channel_iterator_all_new();
271         if (!iter) {
272                 return CLI_FAILURE;
273         }
274
275         ast_pbx_hangup_handler_headers(a->fd);
276         for (; (chan = ast_channel_iterator_next(iter)); ast_channel_unref(chan)) {
277                 ast_pbx_hangup_handler_show(a->fd, chan);
278         }
279         ast_channel_iterator_destroy(iter);
280
281         return CLI_SUCCESS;
282 }
283
284 static struct ast_cli_entry cli[] = {
285         AST_CLI_DEFINE(handle_show_hangup_all, "Show hangup handlers of all channels"),
286         AST_CLI_DEFINE(handle_show_hangup_channel, "Show hangup handlers of a specified channel"),
287 };
288
289 static void unload_pbx_hangup_handler(void)
290 {
291         ast_cli_unregister_multiple(cli, ARRAY_LEN(cli));
292 }
293
294 int load_pbx_hangup_handler(void)
295 {
296         ast_cli_register_multiple(cli, ARRAY_LEN(cli));
297         ast_register_cleanup(unload_pbx_hangup_handler);
298
299         return 0;
300 }