Wed Jan 8 2020 09:49:42

Asterisk developer's documentation


autoservice.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2008, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  * Russell Bryant <russell@digium.com>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19 
20 /*! \file
21  *
22  * \brief Automatic channel service routines
23  *
24  * \author Mark Spencer <markster@digium.com>
25  * \author Russell Bryant <russell@digium.com>
26  */
27 
28 /*** MODULEINFO
29  <support_level>core</support_level>
30  ***/
31 
32 #include "asterisk.h"
33 
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 415463 $")
35 
36 #include <sys/time.h>
37 #include <signal.h>
38 
39 #include "asterisk/_private.h" /* prototype for ast_autoservice_init() */
40 
41 #include "asterisk/pbx.h"
42 #include "asterisk/frame.h"
43 #include "asterisk/sched.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/file.h"
46 #include "asterisk/translate.h"
47 #include "asterisk/manager.h"
48 #include "asterisk/chanvars.h"
49 #include "asterisk/linkedlists.h"
50 #include "asterisk/indications.h"
51 #include "asterisk/lock.h"
52 #include "asterisk/utils.h"
53 
54 #define MAX_AUTOMONS 1500
55 
56 struct asent {
57  struct ast_channel *chan;
58  /*! This gets incremented each time autoservice gets started on the same
59  * channel. It will ensure that it doesn't actually get stopped until
60  * it gets stopped for the last time. */
61  unsigned int use_count;
62  unsigned int orig_end_dtmf_flag:1;
63  unsigned int ignore_frame_types;
64  /*! Frames go on at the head of deferred_frames, so we have the frames
65  * from newest to oldest. As we put them at the head of the readq, we'll
66  * end up with them in the right order for the channel's readq. */
69 };
70 
73 
74 static pthread_t asthread = AST_PTHREADT_NULL;
75 static volatile int asexit = 0;
76 
77 static int as_chan_list_state;
78 
79 static void *autoservice_run(void *ign)
80 {
81  struct ast_frame hangup_frame = {
83  .subclass.integer = AST_CONTROL_HANGUP,
84  };
85 
86  while (!asexit) {
87  struct ast_channel *mons[MAX_AUTOMONS];
88  struct asent *ents[MAX_AUTOMONS];
89  struct ast_channel *chan;
90  struct asent *as;
91  int i, x = 0, ms = 50;
92  struct ast_frame *f = NULL;
93  struct ast_frame *defer_frame = NULL;
94 
95  AST_LIST_LOCK(&aslist);
96 
97  /* At this point, we know that no channels that have been removed are going
98  * to get used again. */
99  as_chan_list_state++;
100 
101  if (AST_LIST_EMPTY(&aslist)) {
102  ast_cond_wait(&as_cond, &aslist.lock);
103  }
104 
105  AST_LIST_TRAVERSE(&aslist, as, list) {
106  if (!ast_check_hangup(as->chan)) {
107  if (x < MAX_AUTOMONS) {
108  ents[x] = as;
109  mons[x++] = as->chan;
110  } else {
111  ast_log(LOG_WARNING, "Exceeded maximum number of automatic monitoring events. Fix autoservice.c\n");
112  }
113  }
114  }
115 
116  AST_LIST_UNLOCK(&aslist);
117 
118  if (!x) {
119  /* If we don't sleep, this becomes a busy loop, which causes
120  * problems when Asterisk runs at a different priority than other
121  * user processes. As long as we check for new channels at least
122  * once every 10ms, we should be fine. */
123  usleep(10000);
124  continue;
125  }
126 
127  chan = ast_waitfor_n(mons, x, &ms);
128  if (!chan) {
129  continue;
130  }
131 
132  f = ast_read(chan);
133 
134  if (!f) {
135  /* No frame means the channel has been hung up.
136  * A hangup frame needs to be queued here as ast_waitfor() may
137  * never return again for the condition to be detected outside
138  * of autoservice. So, we'll leave a HANGUP queued up so the
139  * thread in charge of this channel will know. */
140 
141  defer_frame = &hangup_frame;
142  } else if (ast_is_deferrable_frame(f)) {
143  defer_frame = f;
144  }
145 
146  if (defer_frame) {
147  for (i = 0; i < x; i++) {
148  struct ast_frame *dup_f;
149 
150  if (mons[i] != chan) {
151  continue;
152  }
153 
154  if (defer_frame != f) {
155  if ((dup_f = ast_frdup(defer_frame))) {
156  AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list);
157  }
158  } else {
159  if ((dup_f = ast_frisolate(defer_frame))) {
160  if (dup_f != defer_frame) {
161  ast_frfree(defer_frame);
162  }
163  AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list);
164  }
165  }
166 
167  break;
168  }
169  } else if (f) {
170  ast_frfree(f);
171  }
172  }
173 
175 
176  return NULL;
177 }
178 
180 {
181  int res = 0;
182  struct asent *as;
183 
185  AST_LIST_TRAVERSE(&aslist, as, list) {
186  if (as->chan == chan) {
187  as->use_count++;
188  break;
189  }
190  }
192 
193  if (as) {
194  /* Entry exists, autoservice is already handling this channel */
195  return 0;
196  }
197 
198  if (!(as = ast_calloc(1, sizeof(*as))))
199  return -1;
200 
201  /* New entry created */
202  as->chan = chan;
203  as->use_count = 1;
204 
205  ast_channel_lock(chan);
207  if (!as->orig_end_dtmf_flag)
209  ast_channel_unlock(chan);
210 
212 
215  }
216 
218 
219  if (asthread == AST_PTHREADT_NULL) { /* need start the thread */
221  ast_log(LOG_WARNING, "Unable to create autoservice thread :(\n");
222  /* There will only be a single member in the list at this point,
223  the one we just added. */
224  AST_LIST_REMOVE(&aslist, as, list);
225  free(as);
227  res = -1;
228  } else {
229  pthread_kill(asthread, SIGURG);
230  }
231  }
232 
234 
235  return res;
236 }
237 
239 {
240  int res = -1;
241  struct asent *as, *removed = NULL;
242  struct ast_frame *f;
243  int chan_list_state;
244 
246 
247  /* Save the autoservice channel list state. We _must_ verify that the channel
248  * list has been rebuilt before we return. Because, after we return, the channel
249  * could get destroyed and we don't want our poor autoservice thread to step on
250  * it after its gone! */
251  chan_list_state = as_chan_list_state;
252 
253  /* Find the entry, but do not free it because it still can be in the
254  autoservice thread array */
256  if (as->chan == chan) {
257  as->use_count--;
258  if (as->use_count < 1) {
260  removed = as;
261  }
262  break;
263  }
264  }
266 
267  if (removed && asthread != AST_PTHREADT_NULL) {
268  pthread_kill(asthread, SIGURG);
269  }
270 
272 
273  if (!removed) {
274  return 0;
275  }
276 
277  /* Wait while autoservice thread rebuilds its list. */
278  while (chan_list_state == as_chan_list_state) {
279  usleep(1000);
280  }
281 
282  /* Now autoservice thread should have no references to our entry
283  and we can safely destroy it */
284 
285  if (!chan->_softhangup) {
286  res = 0;
287  }
288 
289  if (!as->orig_end_dtmf_flag) {
291  }
292 
293  ast_channel_lock(chan);
294  while ((f = AST_LIST_REMOVE_HEAD(&as->deferred_frames, frame_list))) {
295  if (!((1 << f->frametype) & as->ignore_frame_types)) {
296  ast_queue_frame_head(chan, f);
297  }
298  ast_frfree(f);
299  }
300  ast_channel_unlock(chan);
301 
302  free(as);
303 
304  return res;
305 }
306 
308 {
309  struct asent *as;
310  int res = -1;
311 
313  AST_LIST_TRAVERSE(&aslist, as, list) {
314  if (as->chan == chan) {
315  res = 0;
316  as->ignore_frame_types |= (1 << ftype);
317  break;
318  }
319  }
321  return res;
322 }
323 
324 static void autoservice_shutdown(void)
325 {
326  pthread_t th = asthread;
327  asexit = 1;
328  if (th != AST_PTHREADT_NULL) {
330  pthread_kill(th, SIGURG);
331  pthread_join(th, NULL);
332  }
333 }
334 
336 {
338  ast_cond_init(&as_cond, NULL);
339 }
static volatile int asexit
Definition: autoservice.c:75
struct ast_channel * ast_waitfor_n(struct ast_channel **chan, int n, int *ms)
Waits for input on a group of channels Wait for input on an array of channels for a given # of millis...
Definition: channel.c:3534
struct asent::@226 deferred_frames
Tone Indication Support.
#define ast_channel_lock(chan)
Definition: channel.h:2466
Main Channel structure associated with a channel.
Definition: channel.h:742
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:179
void ast_autoservice_init(void)
Definition: autoservice.c:335
#define ast_test_flag(p, flag)
Definition: utils.h:63
Support for translation of data formats. translate.c.
Channel Variables.
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define LOG_WARNING
Definition: logger.h:144
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
#define MAX_AUTOMONS
Definition: autoservice.c:54
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4383
#define ast_cond_wait(cond, mutex)
Definition: lock.h:171
#define ast_cond_init(cond, attr)
Definition: lock.h:167
static void autoservice_shutdown(void)
Definition: autoservice.c:324
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449
struct ast_frame * ast_frisolate(struct ast_frame *fr)
Makes a frame independent of any static storage.
Definition: frame.c:391
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
static void * autoservice_run(void *ign)
Definition: autoservice.c:79
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
Definition: linkedlists.h:841
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:600
#define ast_cond_signal(cond)
Definition: lock.h:169
Utility functions.
static ast_cond_t as_cond
Definition: autoservice.c:72
pthread_cond_t ast_cond_t
Definition: lock.h:144
#define ast_pthread_create_background(a, b, c, d)
Definition: utils.h:426
static int as_chan_list_state
Definition: autoservice.c:77
General Asterisk PBX channel definitions.
int ast_register_cleanup(void(*func)(void))
Register a function to be executed before Asterisk gracefully exits.
Definition: asterisk.c:1003
struct ast_channel * chan
Definition: autoservice.c:57
#define AST_PTHREADT_NULL
Definition: lock.h:65
Asterisk internal frame definitions.
Scheduler Routines (derived from cheops)
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:554
A set of macros to manage forward-linked lists.
struct asent::@227 list
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:818
Core PBX routines and definitions.
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:238
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:806
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:290
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
Definition: linkedlists.h:224
#define free(a)
Definition: astmm.h:94
unsigned int orig_end_dtmf_flag
Definition: autoservice.c:62
unsigned int use_count
Definition: autoservice.c:61
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
int _softhangup
Definition: channel.h:832
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:696
#define ast_channel_unlock(chan)
Definition: channel.h:2467
static pthread_t asthread
Definition: autoservice.c:74
static struct ast_format f[]
Definition: format_g726.c:181
Prototypes for public functions only of internal interest,.
int ast_autoservice_ignore(struct ast_channel *chan, enum ast_frame_type ftype)
Ignore certain frame types.
Definition: autoservice.c:307
#define ast_clear_flag(p, flag)
Definition: utils.h:77
ast_frame_type
Frame types.
Definition: frame.h:101
#define ast_calloc(a, b)
Definition: astmm.h:82
int ast_is_deferrable_frame(const struct ast_frame *frame)
Should we keep this frame for later?
Definition: channel.c:1818
Data structure associated with a single frame of data.
Definition: frame.h:142
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
enum ast_frame_type frametype
Definition: frame.h:144
unsigned int ignore_frame_types
Definition: autoservice.c:63
#define ast_frfree(fr)
Definition: frame.h:583
int ast_queue_frame_head(struct ast_channel *chan, struct ast_frame *f)
Queue one or more frames to the head of a channel&#39;s frame queue.
Definition: channel.c:1563
struct ast_frame * ast_frdup(const struct ast_frame *fr)
Copies a frame.
Definition: frame.c:474
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180