Asterisk - The Open Source Telephony Project  21.4.1
res_musiconhold.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2010, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.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 Routines implementing music on hold
22  *
23  * \author Mark Spencer <markster@digium.com>
24  */
25 
26 /*! \li \ref res_musiconhold.c uses the configuration file \ref musiconhold.conf
27  * \addtogroup configuration_file Configuration Files
28  */
29 
30 /*!
31  * \page musiconhold.conf musiconhold.conf
32  * \verbinclude musiconhold.conf.sample
33  */
34 
35 /*** MODULEINFO
36  <conflict>win32</conflict>
37  <support_level>core</support_level>
38  ***/
39 
40 #include "asterisk.h"
41 
42 #include <ctype.h>
43 #include <signal.h>
44 #include <sys/time.h>
45 #include <signal.h>
46 #include <netinet/in.h>
47 #include <sys/stat.h>
48 #include <dirent.h>
49 
50 #ifdef SOLARIS
51 #include <thread.h>
52 #endif
53 
54 #include "asterisk/lock.h"
55 #include "asterisk/file.h"
56 #include "asterisk/channel.h"
57 #include "asterisk/pbx.h"
58 #include "asterisk/app.h"
59 #include "asterisk/module.h"
60 #include "asterisk/translate.h"
61 #include "asterisk/say.h"
62 #include "asterisk/musiconhold.h"
63 #include "asterisk/config.h"
64 #include "asterisk/utils.h"
65 #include "asterisk/cli.h"
66 #include "asterisk/stringfields.h"
67 #include "asterisk/linkedlists.h"
68 #include "asterisk/stasis.h"
69 #include "asterisk/stasis_channels.h"
70 #include "asterisk/paths.h"
71 #include "asterisk/astobj2.h"
72 #include "asterisk/timing.h"
73 #include "asterisk/time.h"
74 #include "asterisk/poll-compat.h"
75 
76 #define INITIAL_NUM_FILES 8
77 #define HANDLE_REF 1
78 #define DONT_UNREF 0
79 
80 /*** DOCUMENTATION
81  <application name="MusicOnHold" language="en_US">
82  <synopsis>
83  Play Music On Hold indefinitely.
84  </synopsis>
85  <syntax>
86  <parameter name="class" required="true" />
87  <parameter name="duration" />
88  </syntax>
89  <description>
90  <para>Plays hold music specified by class. If omitted, the default music
91  source for the channel will be used. Change the default class with
92  Set(CHANNEL(musicclass)=...). If duration is given, hold music will be played
93  specified number of seconds. If duration is omitted, music plays indefinitely.
94  Returns <literal>0</literal> when done, <literal>-1</literal> on hangup.</para>
95  <para>This application does not automatically answer and should be preceeded by
96  an application such as Answer() or Progress().</para>
97  </description>
98  </application>
99  <application name="StartMusicOnHold" language="en_US">
100  <synopsis>
101  Play Music On Hold.
102  </synopsis>
103  <syntax>
104  <parameter name="class" required="true" />
105  </syntax>
106  <description>
107  <para>Starts playing music on hold, uses default music class for channel.
108  Starts playing music specified by class. If omitted, the default music
109  source for the channel will be used. Always returns <literal>0</literal>.</para>
110  </description>
111  </application>
112  <application name="StopMusicOnHold" language="en_US">
113  <synopsis>
114  Stop playing Music On Hold.
115  </synopsis>
116  <syntax />
117  <description>
118  <para>Stops playing music on hold.</para>
119  </description>
120  </application>
121  ***/
122 
123 static const char play_moh[] = "MusicOnHold";
124 static const char start_moh[] = "StartMusicOnHold";
125 static const char stop_moh[] = "StopMusicOnHold";
126 
127 static int respawn_time = 20;
128 
130  /*! Holds a reference to the MOH class. */
131  struct mohclass *class;
132  struct ast_format *origwfmt;
133  struct ast_format *mohwfmt;
134  int announcement;
135  int samples;
136  int sample_queue;
137  int pos;
138  int save_pos;
139  int save_total;
140  char name[MAX_MUSICCLASS];
141  char save_pos_filename[PATH_MAX];
142 };
143 
144 #define MOH_QUIET (1 << 0)
145 #define MOH_SINGLE (1 << 1)
146 #define MOH_CUSTOM (1 << 2)
147 #define MOH_RANDOMIZE (1 << 3)
148 #define MOH_SORTALPHA (1 << 4)
149 #define MOH_RANDSTART (MOH_RANDOMIZE | MOH_SORTALPHA) /*!< Sorted but start at random position */
150 #define MOH_SORTMODE (3 << 3)
151 
152 #define MOH_CACHERTCLASSES (1 << 5) /*!< Should we use a separate instance of MOH for each user or not */
153 #define MOH_ANNOUNCEMENT (1 << 6) /*!< Do we play announcement files between songs on this channel? */
154 #define MOH_PREFERCHANNELCLASS (1 << 7) /*!< Should queue moh override channel moh */
155 
156 #define MOH_LOOPLAST (1 << 8) /*!< Whether to loop the last file in the music class when we reach the end, rather than starting over */
157 
158 /* Custom astobj2 flag */
159 #define MOH_NOTDELETED (1 << 30) /*!< Find only records that aren't deleted? */
160 #define MOH_REALTIME (1 << 31) /*!< Find only records that are realtime */
161 
162 static struct ast_flags global_flags[1] = {{0}}; /*!< global MOH_ flags */
163 
164 enum kill_methods {
165  KILL_METHOD_PROCESS_GROUP = 0,
166  KILL_METHOD_PROCESS
167 };
168 
169 struct mohclass {
170  char name[MAX_MUSICCLASS];
171  char dir[256];
172  char args[256];
173  char announcement[256];
174  char mode[80];
175  char digit;
176  /*! An immutable vector of filenames in "files" mode */
178  unsigned int flags;
179  /*! The format from the MOH source, not applicable to "files" mode */
181  /*! The pid of the external application delivering MOH */
182  int pid;
183  time_t start;
184  pthread_t thread;
185  /*! Millisecond delay between kill attempts */
186  size_t kill_delay;
187  /*! Kill method */
188  enum kill_methods kill_method;
189  /*! Source of audio */
190  int srcfd;
191  /*! Generic timer */
192  struct ast_timer *timer;
193  /*! Created on the fly, from RT engine */
194  unsigned int realtime:1;
195  unsigned int delete:1;
196  AST_LIST_HEAD_NOLOCK(, mohdata) members;
198  /*!< Play the moh if the channel answered */
199  int answeredonly;
200 };
201 
202 struct mohdata {
203  int pipe[2];
204  struct ast_format *origwfmt;
205  struct mohclass *parent;
206  struct ast_frame f;
207  AST_LIST_ENTRY(mohdata) list;
208 };
209 
210 static struct ao2_container *mohclasses;
211 
212 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
213 #define MPG_123 "/usr/bin/mpg123"
214 #define MAX_MP3S 256
215 
216 static void moh_parse_options(struct ast_variable *var, struct mohclass *mohclass);
217 static int reload(void);
218 
219 #define mohclass_ref(class,string) (ao2_t_ref((class), +1, (string)), class)
220 
221 #ifndef AST_DEVMODE
222 #define mohclass_unref(class,string) ({ ao2_t_ref((class), -1, (string)); (struct mohclass *) NULL; })
223 #else
224 #define mohclass_unref(class,string) _mohclass_unref(class, string, __FILE__,__LINE__,__PRETTY_FUNCTION__)
225 static struct mohclass *_mohclass_unref(struct mohclass *class, const char *tag, const char *file, int line, const char *funcname)
226 {
227  struct mohclass *dup = ao2_callback(mohclasses, OBJ_POINTER, ao2_match_by_addr, class);
228 
229  if (dup) {
230  if (__ao2_ref(dup, -1, tag, file, line, funcname) == 2) {
231  ast_log(LOG_WARNING, "Attempt to unref mohclass %p (%s) when only 1 ref remained, and class is still in a container! (at %s:%d (%s))\n",
232  class, class->name, file, line, funcname);
233  } else {
234  ao2_ref(class, -1);
235  }
236  } else {
237  __ao2_ref(class, -1, tag, file, line, funcname);
238  }
239  return NULL;
240 }
241 #endif
242 
243 static void moh_post_start(struct ast_channel *chan, const char *moh_class_name)
244 {
245  struct stasis_message *message;
246  struct ast_json *json_object;
247 
248  ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n",
249  moh_class_name, ast_channel_name(chan));
250 
251  json_object = ast_json_pack("{s: s}", "class", moh_class_name);
252  if (!json_object) {
253  return;
254  }
255 
256  message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan),
257  ast_channel_moh_start_type(), json_object);
258  if (message) {
259  /* A channel snapshot must have been in the cache. */
260  ast_assert(((struct ast_channel_blob *) stasis_message_data(message))->snapshot != NULL);
261 
262  stasis_publish(ast_channel_topic(chan), message);
263  }
264  ao2_cleanup(message);
265  ast_json_unref(json_object);
266 }
267 
268 static void moh_post_stop(struct ast_channel *chan)
269 {
270  struct stasis_message *message;
271 
272  ast_verb(3, "Stopped music on hold on %s\n", ast_channel_name(chan));
273 
274  message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan),
275  ast_channel_moh_stop_type(), NULL);
276  if (message) {
277  /* A channel snapshot must have been in the cache. */
278  ast_assert(((struct ast_channel_blob *) stasis_message_data(message))->snapshot != NULL);
279 
280  stasis_publish(ast_channel_topic(chan), message);
281  }
282  ao2_cleanup(message);
283 }
284 
285 static void moh_files_release(struct ast_channel *chan, void *data)
286 {
287  struct moh_files_state *state;
288 
289  if (!chan || !ast_channel_music_state(chan)) {
290  return;
291  }
292 
293  state = ast_channel_music_state(chan);
294 
295  if (ast_channel_stream(chan)) {
296  ast_closestream(ast_channel_stream(chan));
297  ast_channel_stream_set(chan, NULL);
298  }
299 
300  moh_post_stop(chan);
301 
302  ao2_ref(state->mohwfmt, -1);
303  state->mohwfmt = NULL; /* make sure to clear this format before restoring the original format */
304  if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
305  ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", ast_channel_name(chan),
306  ast_format_get_name(state->origwfmt));
307  }
308  ao2_cleanup(state->origwfmt);
309  state->origwfmt = NULL;
310 
311  state->save_pos = state->pos;
312  state->announcement = 0;
313 
314  state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
315 }
316 
317 static int ast_moh_files_next(struct ast_channel *chan)
318 {
319  struct moh_files_state *state = ast_channel_music_state(chan);
320  struct ast_vector_string *files;
321  int tries;
322  size_t file_count;
323 
324  /* Discontinue a stream if it is running already */
325  if (ast_channel_stream(chan)) {
326  ast_closestream(ast_channel_stream(chan));
327  ast_channel_stream_set(chan, NULL);
328  }
329 
330  if (ast_test_flag(state->class, MOH_ANNOUNCEMENT) && state->announcement == 0) {
331  state->announcement = 1;
332  if (ast_openstream_full(chan, state->class->announcement, ast_channel_language(chan), 1)) {
333  ast_debug(1, "%s Opened announcement '%s'\n", ast_channel_name(chan), state->class->announcement);
334  return 0;
335  }
336  } else {
337  state->announcement = 0;
338  }
339 
340  ao2_lock(state->class);
341  files = ao2_bump(state->class->files);
342  ao2_unlock(state->class);
343 
344  file_count = AST_VECTOR_SIZE(files);
345  if (!file_count) {
346  ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
347  ao2_ref(files, -1);
348  return -1;
349  }
350 
351  if (state->pos == 0 && ast_strlen_zero(state->save_pos_filename)) {
352  /* First time so lets play the file. */
353  state->save_pos = -1;
354  } else if (state->save_pos >= 0 && state->save_pos < file_count && !strcmp(AST_VECTOR_GET(files, state->save_pos), state->save_pos_filename)) {
355  /* If a specific file has been saved confirm it still exists and that it is still valid */
356  state->pos = state->save_pos;
357  state->save_pos = -1;
358  } else if (ast_test_flag(state->class, MOH_SORTMODE) == MOH_RANDOMIZE) {
359  /* Get a random file and ensure we can open it */
360  for (tries = 0; tries < 20; tries++) {
361  state->pos = ast_random() % file_count;
362  if (ast_fileexists(AST_VECTOR_GET(files, state->pos), NULL, NULL) > 0) {
363  break;
364  }
365  }
366  state->save_pos = -1;
367  state->samples = 0;
368  } else {
369  /* This is easy, just increment our position and make sure we don't exceed the total file count */
370  state->pos++;
371  if (ast_test_flag(state->class, MOH_LOOPLAST)) {
372  state->pos = MIN(file_count - 1, state->pos);
373  } else {
374  state->pos %= file_count;
375  }
376  state->save_pos = -1;
377  state->samples = 0;
378  }
379 
380  for (tries = 0; tries < file_count; ++tries) {
381  if (ast_openstream_full(chan, AST_VECTOR_GET(files, state->pos), ast_channel_language(chan), 1)) {
382  break;
383  }
384 
385  ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", AST_VECTOR_GET(files, state->pos), strerror(errno));
386  state->pos++;
387  state->pos %= file_count;
388  }
389 
390  if (tries == file_count) {
391  ao2_ref(files, -1);
392  return -1;
393  }
394 
395  /* Record the pointer to the filename for position resuming later */
396  ast_copy_string(state->save_pos_filename, AST_VECTOR_GET(files, state->pos), sizeof(state->save_pos_filename));
397 
398  ast_debug(1, "%s Opened file %d '%s'\n", ast_channel_name(chan), state->pos, state->save_pos_filename);
399 
400  if (state->samples) {
401  size_t loc;
402  /* seek *SHOULD* be good since it's from a known location */
403  ast_seekstream(ast_channel_stream(chan), state->samples, SEEK_SET);
404  /* if the seek failed then recover because if there is not a valid read,
405  * moh_files_generate will return -1 and MOH will stop */
406  loc = ast_tellstream(ast_channel_stream(chan));
407  if (state->samples > loc && loc) {
408  /* seek one sample from the end for one guaranteed valid read */
409  ast_seekstream(ast_channel_stream(chan), 1, SEEK_END);
410  }
411  }
412 
413  ao2_ref(files, -1);
414  return 0;
415 }
416 
417 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
418 {
419  struct ast_frame *f;
420 
421  f = ast_readframe(ast_channel_stream(chan));
422  if (!f) {
423  /* Either there was no file stream setup or we reached EOF. */
424  if (!ast_moh_files_next(chan)) {
425  /*
426  * Either we resetup the previously saved file stream position
427  * or we started a new file stream.
428  */
429  f = ast_readframe(ast_channel_stream(chan));
430  if (!f) {
431  /*
432  * We can get here if we were very unlucky because the
433  * resetup file stream was saved at EOF when MOH was
434  * previously stopped.
435  */
436  if (!ast_moh_files_next(chan)) {
437  f = ast_readframe(ast_channel_stream(chan));
438  }
439  }
440  }
441  }
442 
443  return f;
444 }
445 
446 static void moh_files_write_format_change(struct ast_channel *chan, void *data)
447 {
448  struct moh_files_state *state = ast_channel_music_state(chan);
449 
450  /* In order to prevent a recursive call to this function as a result
451  * of setting the moh write format back on the channel. Clear
452  * the moh write format before setting the write format on the channel.*/
453  if (state->origwfmt) {
454  struct ast_format *tmp;
455 
456  tmp = ao2_bump(ast_channel_writeformat(chan));
457  ao2_replace(state->origwfmt, NULL);
458  if (state->mohwfmt) {
459  ast_set_write_format(chan, state->mohwfmt);
460  }
461  state->origwfmt = tmp;
462  }
463 }
464 
465 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
466 {
467  struct moh_files_state *state;
468  struct ast_frame *f = NULL;
469  int res = 0, sample_queue = 0;
470 
471  ast_channel_lock(chan);
472  state = ast_channel_music_state(chan);
473  state->sample_queue += samples;
474  /* save the sample queue value for un-locked access */
475  sample_queue = state->sample_queue;
476  ast_channel_unlock(chan);
477 
478  while (sample_queue > 0) {
479  ast_channel_lock(chan);
480  f = moh_files_readframe(chan);
481  if (!f) {
482  ast_channel_unlock(chan);
483  return -1;
484  }
485 
486  /* Only track our offset within the current file if we are not in the
487  * the middle of an announcement */
488  if (!state->announcement) {
489  state->samples += f->samples;
490  }
491 
492  state->sample_queue -= f->samples;
493  if (ast_format_cmp(f->subclass.format, state->mohwfmt) == AST_FORMAT_CMP_NOT_EQUAL) {
494  ao2_replace(state->mohwfmt, f->subclass.format);
495  }
496 
497  /* We need to be sure that we unlock
498  * the channel prior to calling
499  * ast_write, but after our references to state
500  * as it refers to chan->music_state. Update
501  * sample_queue for our loop
502  * Otherwise, the recursive locking that occurs
503  * can cause deadlocks when using indirect
504  * channels, like local channels
505  */
506  sample_queue = state->sample_queue;
507  ast_channel_unlock(chan);
508 
509  res = ast_write(chan, f);
510  ast_frfree(f);
511  if (res < 0) {
512  ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
513  return -1;
514  }
515  }
516  return res;
517 }
518 
519 static void *moh_files_alloc(struct ast_channel *chan, void *params)
520 {
521  struct moh_files_state *state;
522  struct mohclass *class = params;
523  size_t file_count;
524 
525  state = ast_channel_music_state(chan);
526  if (!state && (state = ast_calloc(1, sizeof(*state)))) {
527  ast_channel_music_state_set(chan, state);
529  } else {
530  if (!state) {
531  return NULL;
532  }
533  if (state->class) {
534  mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
535  ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
536  }
537  }
538 
539  ao2_lock(class);
540  file_count = AST_VECTOR_SIZE(class->files);
541  ao2_unlock(class);
542 
543  /* Resume MOH from where we left off last time or start from scratch? */
544  if (state->save_total != file_count || strcmp(state->name, class->name) != 0) {
545  /* Start MOH from scratch. */
546  ao2_cleanup(state->origwfmt);
547  ao2_cleanup(state->mohwfmt);
548  memset(state, 0, sizeof(*state));
549  if (ast_test_flag(class, MOH_RANDOMIZE) && file_count) {
550  state->pos = ast_random() % file_count;
551  }
552  }
553 
554  state->class = mohclass_ref(class, "Reffing music class for channel");
555  /* it's possible state is not a new allocation, don't leak old refs */
556  ao2_replace(state->origwfmt, ast_channel_writeformat(chan));
557  ao2_replace(state->mohwfmt, ast_channel_writeformat(chan));
558  /* For comparison on restart of MOH (see above) */
559  ast_copy_string(state->name, class->name, sizeof(state->name));
560  state->save_total = file_count;
561 
562  moh_post_start(chan, class->name);
563 
564  return state;
565 }
566 
567 static int moh_digit_match(void *obj, void *arg, int flags)
568 {
569  char *digit = arg;
570  struct mohclass *class = obj;
571 
572  return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
573 }
574 
575 /*! \note This function should be called with the mohclasses list locked */
576 static struct mohclass *get_mohbydigit(char digit)
577 {
578  return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
579 }
580 
581 static void moh_handle_digit(struct ast_channel *chan, char digit)
582 {
583  struct mohclass *class;
584  const char *classname = NULL;
585 
586  if ((class = get_mohbydigit(digit))) {
587  classname = ast_strdupa(class->name);
588  class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
589  ast_channel_musicclass_set(chan, classname);
590  ast_moh_stop(chan);
591  ast_moh_start(chan, classname, NULL);
592  }
593 }
594 
595 static struct ast_generator moh_file_stream = {
596  .alloc = moh_files_alloc,
597  .release = moh_files_release,
598  .generate = moh_files_generator,
599  .digit = moh_handle_digit,
600  .write_format_change = moh_files_write_format_change,
601 };
602 
603 static int spawn_mp3(struct mohclass *class)
604 {
605  int fds[2];
606  int files = 0;
607  char fns[MAX_MP3S][80];
608  char *argv[MAX_MP3S + 50];
609  char xargs[256];
610  char *argptr;
611  int argc = 0;
612  DIR *dir = NULL;
613  struct dirent *de;
614 
615 
616  if (!strcasecmp(class->dir, "nodir")) {
617  files = 1;
618  } else {
619  dir = opendir(class->dir);
620  if (!dir && strncasecmp(class->dir, "http://", 7)) {
621  ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
622  return -1;
623  }
624  }
625 
626  if (!ast_test_flag(class, MOH_CUSTOM)) {
627  argv[argc++] = "mpg123";
628  argv[argc++] = "-q";
629  argv[argc++] = "-s";
630  argv[argc++] = "--mono";
631  argv[argc++] = "-r";
632  argv[argc++] = "8000";
633 
634  if (!ast_test_flag(class, MOH_SINGLE)) {
635  argv[argc++] = "-b";
636  argv[argc++] = "2048";
637  }
638 
639  argv[argc++] = "-f";
640 
641  if (ast_test_flag(class, MOH_QUIET))
642  argv[argc++] = "4096";
643  else
644  argv[argc++] = "8192";
645 
646  /* Look for extra arguments and add them to the list */
647  ast_copy_string(xargs, class->args, sizeof(xargs));
648  argptr = xargs;
649  while (!ast_strlen_zero(argptr)) {
650  argv[argc++] = argptr;
651  strsep(&argptr, ",");
652  }
653  } else {
654  /* Format arguments for argv vector */
655  ast_copy_string(xargs, class->args, sizeof(xargs));
656  argptr = xargs;
657  while (!ast_strlen_zero(argptr)) {
658  argv[argc++] = argptr;
659  strsep(&argptr, " ");
660  }
661  }
662 
663  if (!strncasecmp(class->dir, "http://", 7)) {
664  ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
665  argv[argc++] = fns[files];
666  files++;
667  } else if (dir) {
668  while ((de = readdir(dir)) && (files < MAX_MP3S)) {
669  if ((strlen(de->d_name) > 3) &&
670  ((ast_test_flag(class, MOH_CUSTOM) &&
671  (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
672  !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
673  !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
674  ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
675  argv[argc++] = fns[files];
676  files++;
677  }
678  }
679  }
680  argv[argc] = NULL;
681  if (dir) {
682  closedir(dir);
683  }
684  if (pipe(fds)) {
685  ast_log(LOG_WARNING, "Pipe failed\n");
686  return -1;
687  }
688  if (!files) {
689  ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
690  close(fds[0]);
691  close(fds[1]);
692  return -1;
693  }
694  if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
695  sleep(respawn_time - (time(NULL) - class->start));
696  }
697 
698  time(&class->start);
699  class->pid = ast_safe_fork(0);
700  if (class->pid < 0) {
701  close(fds[0]);
702  close(fds[1]);
703  ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
704  return -1;
705  }
706  if (!class->pid) {
707  if (ast_opt_high_priority)
708  ast_set_priority(0);
709 
710  close(fds[0]);
711  /* Stdout goes to pipe */
712  dup2(fds[1], STDOUT_FILENO);
713 
714  /* Close everything else */
715  ast_close_fds_above_n(STDERR_FILENO);
716 
717  /* Child */
718  if (strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
719  ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
720  _exit(1);
721  }
722  setpgid(0, getpid());
723  if (ast_test_flag(class, MOH_CUSTOM)) {
724  execv(argv[0], argv);
725  } else {
726  /* Default install is /usr/local/bin */
727  execv(LOCAL_MPG_123, argv);
728  /* Many places have it in /usr/bin */
729  execv(MPG_123, argv);
730  /* Check PATH as a last-ditch effort */
731  execvp("mpg123", argv);
732  }
733  /* Can't use logger, since log FDs are closed */
734  fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
735  close(fds[1]);
736  _exit(1);
737  } else {
738  /* Parent */
739  close(fds[1]);
740  }
741  return fds[0];
742 }
743 
744 static int killer(pid_t pid, int signum, enum kill_methods kill_method)
745 {
746  switch (kill_method) {
747  case KILL_METHOD_PROCESS_GROUP:
748  return killpg(pid, signum);
749  case KILL_METHOD_PROCESS:
750  return kill(pid, signum);
751  }
752 
753  return -1;
754 }
755 
756 static void killpid(int pid, size_t delay, enum kill_methods kill_method)
757 {
758  if (killer(pid, SIGHUP, kill_method) < 0) {
759  if (errno == ESRCH) {
760  return;
761  }
762  ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process '%d'?!!: %s\n", pid, strerror(errno));
763  } else {
764  ast_debug(1, "Sent HUP to pid %d%s\n", pid,
765  kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
766  }
767  usleep(delay);
768  if (killer(pid, SIGTERM, kill_method) < 0) {
769  if (errno == ESRCH) {
770  return;
771  }
772  ast_log(LOG_WARNING, "Unable to terminate MOH process '%d'?!!: %s\n", pid, strerror(errno));
773  } else {
774  ast_debug(1, "Sent TERM to pid %d%s\n", pid,
775  kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
776  }
777  usleep(delay);
778  if (killer(pid, SIGKILL, kill_method) < 0) {
779  if (errno == ESRCH) {
780  return;
781  }
782  ast_log(LOG_WARNING, "Unable to kill MOH process '%d'?!!: %s\n", pid, strerror(errno));
783  } else {
784  ast_debug(1, "Sent KILL to pid %d%s\n", pid,
785  kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
786  }
787 }
788 
789 static void *monmp3thread(void *data)
790 {
791 #define MOH_MS_INTERVAL 100
792 
793  struct mohclass *class = data;
794  struct mohdata *moh;
795  short sbuf[8192];
796  int res = 0, res2;
797  int len;
798  struct timeval deadline, tv_tmp;
799 
800  deadline.tv_sec = 0;
801  deadline.tv_usec = 0;
802  for(;/* ever */;) {
803  pthread_testcancel();
804  /* Spawn mp3 player if it's not there */
805  if (class->srcfd < 0) {
806  if ((class->srcfd = spawn_mp3(class)) < 0) {
807  ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
808  /* Try again later */
809  sleep(500);
810  continue;
811  }
812  }
813  if (class->timer) {
814  struct pollfd pfd = { .fd = ast_timer_fd(class->timer), .events = POLLIN | POLLPRI, };
815 
816 #ifdef SOLARIS
817  thr_yield();
818 #endif
819  /* Pause some amount of time */
820  if (ast_poll(&pfd, 1, -1) > 0) {
821  if (ast_timer_ack(class->timer, 1) < 0) {
822  ast_log(LOG_ERROR, "Failed to acknowledge timer for mp3player\n");
823  return NULL;
824  }
825  /* 25 samples per second => 40ms framerate => 320 samples */
826  res = 320; /* 320/40 = 8 samples/ms */
827  } else {
828  ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno));
829  res = 0;
830  }
831  pthread_testcancel();
832  } else {
833  long delta;
834  /* Reliable sleep */
835  tv_tmp = ast_tvnow();
836  if (ast_tvzero(deadline))
837  deadline = tv_tmp;
838  delta = ast_tvdiff_ms(tv_tmp, deadline);
839  if (delta < MOH_MS_INTERVAL) { /* too early */
840  deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
841  usleep(1000 * (MOH_MS_INTERVAL - delta));
842  pthread_testcancel();
843  } else {
844  ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
845  deadline = tv_tmp;
846  }
847  /* 10 samples per second (MOH_MS_INTERVAL) => 100ms framerate => 800 samples */
848  res = 8 * MOH_MS_INTERVAL; /* 800/100 = 8 samples/ms */
849  }
850  /* For non-8000Hz formats, we need to alter the resolution */
851  res = res * ast_format_get_sample_rate(class->format) / 8000;
852 
853  if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
854  continue;
855  /* Read mp3 audio */
856  len = ast_format_determine_length(class->format, res);
857 
858  if ((res2 = read(class->srcfd, sbuf, len)) != len) {
859  if (!res2) {
860  close(class->srcfd);
861  class->srcfd = -1;
862  pthread_testcancel();
863  if (class->pid > 1) {
864  killpid(class->pid, class->kill_delay, class->kill_method);
865  class->pid = 0;
866  }
867  } else {
868  ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
869  }
870  continue;
871  }
872 
873  pthread_testcancel();
874 
875  ao2_lock(class);
876  AST_LIST_TRAVERSE(&class->members, moh, list) {
877  /* Write data */
878  if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
879  ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
880  }
881  }
882  ao2_unlock(class);
883  }
884  return NULL;
885 }
886 
887 static int play_moh_exec(struct ast_channel *chan, const char *data)
888 {
889  char *parse;
890  char *class;
891  int timeout = -1;
892  int res;
894  AST_APP_ARG(class);
895  AST_APP_ARG(duration);
896  );
897 
898  parse = ast_strdupa(data);
899 
900  AST_STANDARD_APP_ARGS(args, parse);
901 
902  if (!ast_strlen_zero(args.duration)) {
903  if (sscanf(args.duration, "%30d", &timeout) == 1) {
904  timeout *= 1000;
905  } else {
906  ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
907  }
908  }
909 
910  class = S_OR(args.class, NULL);
911  if (ast_moh_start(chan, class, NULL)) {
912  ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
913  return 0;
914  }
915 
916  if (timeout > 0)
917  res = ast_safe_sleep(chan, timeout);
918  else {
919  while (!(res = ast_safe_sleep(chan, 10000)));
920  }
921 
922  ast_moh_stop(chan);
923 
924  return res;
925 }
926 
927 static int start_moh_exec(struct ast_channel *chan, const char *data)
928 {
929  char *parse;
930  char *class;
932  AST_APP_ARG(class);
933  );
934 
935  parse = ast_strdupa(data);
936 
937  AST_STANDARD_APP_ARGS(args, parse);
938 
939  class = S_OR(args.class, NULL);
940  if (ast_moh_start(chan, class, NULL))
941  ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, ast_channel_name(chan));
942 
943  return 0;
944 }
945 
946 static int stop_moh_exec(struct ast_channel *chan, const char *data)
947 {
948  ast_moh_stop(chan);
949 
950  return 0;
951 }
952 
953 #define get_mohbyname(a,b,c) _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
954 
955 static struct mohclass *_get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
956 {
957  struct mohclass *moh = NULL;
958  struct mohclass tmp_class = {
959  .flags = 0,
960  };
961 
962  ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
963 
964  moh = __ao2_find(mohclasses, &tmp_class, flags,
965  "get_mohbyname", file, lineno, funcname);
966 
967  if (!moh && warn) {
968  ast_log(LOG_WARNING, "Music on Hold class '%s' not found in memory. Verify your configuration.\n", name);
969  }
970 
971  return moh;
972 }
973 
974 static struct mohdata *mohalloc(struct mohclass *cl)
975 {
976  struct mohdata *moh;
977 
978  if (!(moh = ast_calloc(1, sizeof(*moh))))
979  return NULL;
980 
981  if (ast_pipe_nonblock(moh->pipe)) {
982  ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
983  ast_free(moh);
984  return NULL;
985  }
986 
987  moh->f.frametype = AST_FRAME_VOICE;
988  moh->f.subclass.format = cl->format;
989  moh->f.offset = AST_FRIENDLY_OFFSET;
990 
991  moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
992 
993  ao2_lock(cl);
994  AST_LIST_INSERT_HEAD(&cl->members, moh, list);
995  ao2_unlock(cl);
996 
997  return moh;
998 }
999 
1000 static void moh_release(struct ast_channel *chan, void *data)
1001 {
1002  struct mohdata *moh = data;
1003  struct mohclass *class = moh->parent;
1004  struct ast_format *oldwfmt;
1005 
1006  ao2_lock(class);
1007  AST_LIST_REMOVE(&moh->parent->members, moh, list);
1008  ao2_unlock(class);
1009 
1010  close(moh->pipe[0]);
1011  close(moh->pipe[1]);
1012 
1013  oldwfmt = moh->origwfmt;
1014 
1015  moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
1016 
1017  ast_free(moh);
1018 
1019  if (chan) {
1020  struct moh_files_state *state;
1021 
1022  state = ast_channel_music_state(chan);
1023  if (state && state->class) {
1024  state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
1025  }
1026  if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
1027  ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
1028  ast_channel_name(chan), ast_format_get_name(oldwfmt));
1029  }
1030 
1031  moh_post_stop(chan);
1032  }
1033 
1034  ao2_cleanup(oldwfmt);
1035 }
1036 
1037 static void *moh_alloc(struct ast_channel *chan, void *params)
1038 {
1039  struct mohdata *res;
1040  struct mohclass *class = params;
1041  struct moh_files_state *state;
1042 
1043  /* Initiating music_state for current channel. Channel should know name of moh class */
1044  state = ast_channel_music_state(chan);
1045  if (!state && (state = ast_calloc(1, sizeof(*state)))) {
1046  ast_channel_music_state_set(chan, state);
1048  } else {
1049  if (!state) {
1050  return NULL;
1051  }
1052  if (state->class) {
1053  mohclass_unref(state->class, "Uh Oh. Restarting MOH with an active class");
1054  ast_log(LOG_WARNING, "Uh Oh. Restarting MOH with an active class\n");
1055  }
1056  ao2_cleanup(state->origwfmt);
1057  ao2_cleanup(state->mohwfmt);
1058  memset(state, 0, sizeof(*state));
1059  }
1060 
1061  if ((res = mohalloc(class))) {
1062  res->origwfmt = ao2_bump(ast_channel_writeformat(chan));
1063  if (ast_set_write_format(chan, class->format)) {
1064  ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", ast_channel_name(chan),
1066  moh_release(NULL, res);
1067  res = NULL;
1068  } else {
1069  state->class = mohclass_ref(class, "Placing reference into state container");
1070  moh_post_start(chan, class->name);
1071  }
1072  }
1073  return res;
1074 }
1075 
1076 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
1077 {
1078  struct mohdata *moh = data;
1079  short buf[1280 + AST_FRIENDLY_OFFSET / 2];
1080  int res;
1081 
1082  len = ast_format_determine_length(moh->parent->format, samples);
1083 
1084  if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
1085  ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, ast_channel_name(chan));
1086  len = sizeof(buf) - AST_FRIENDLY_OFFSET;
1087  }
1088  res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
1089  if (res <= 0)
1090  return 0;
1091 
1092  moh->f.datalen = res;
1093  moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
1094  moh->f.samples = ast_codec_samples_count(&moh->f);
1095 
1096  if (ast_write(chan, &moh->f) < 0) {
1097  ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
1098  return -1;
1099  }
1100 
1101  return 0;
1102 }
1103 
1104 static struct ast_generator mohgen = {
1105  .alloc = moh_alloc,
1106  .release = moh_release,
1107  .generate = moh_generate,
1108  .digit = moh_handle_digit,
1109 };
1110 
1111 static void moh_file_vector_destructor(void *obj)
1112 {
1113  struct ast_vector_string *files = obj;
1114  AST_VECTOR_RESET(files, ast_free);
1115  AST_VECTOR_FREE(files);
1116 }
1117 
1118 static struct ast_vector_string *moh_file_vector_alloc(int initial_capacity)
1119 {
1120  struct ast_vector_string *files = ao2_alloc_options(
1121  sizeof(struct ast_vector_string),
1122  moh_file_vector_destructor,
1124  if (files) {
1125  AST_VECTOR_INIT(files, initial_capacity);
1126  }
1127  return files;
1128 }
1129 
1130 static void moh_parse_options(struct ast_variable *var, struct mohclass *mohclass)
1131 {
1132  struct ast_vector_string *playlist_entries = NULL;
1133 
1134  for (; var; var = var->next) {
1135  if (!strcasecmp(var->name, "name")) {
1136  ast_copy_string(mohclass->name, var->value, sizeof(mohclass->name));
1137  } else if (!strcasecmp(var->name, "mode")) {
1138  ast_copy_string(mohclass->mode, var->value, sizeof(mohclass->mode));
1139  } else if (!strcasecmp(var->name, "entry")) {
1140  if (ast_begins_with(var->value, "/") || strstr(var->value, "://")) {
1141  char *dup;
1142 
1143  if (!playlist_entries) {
1144  playlist_entries = moh_file_vector_alloc(16);
1145  if (!playlist_entries) {
1146  continue;
1147  }
1148  }
1149 
1150  dup = ast_strdup(var->value);
1151  if (!dup) {
1152  continue;
1153  }
1154 
1155  if (ast_begins_with(dup, "/")) {
1156  char *last_pos_dot = strrchr(dup, '.');
1157  char *last_pos_slash = strrchr(dup, '/');
1158  if (last_pos_dot && last_pos_dot > last_pos_slash) {
1159  ast_log(LOG_WARNING, "The playlist entry '%s' may include an extension, which could prevent it from playing.\n",
1160  dup);
1161  }
1162  }
1163 
1164  AST_VECTOR_APPEND(playlist_entries, dup);
1165  } else {
1166  ast_log(LOG_ERROR, "Playlist entries must be a URL or an absolute path, '%s' provided.\n", var->value);
1167  }
1168  } else if (!strcasecmp(var->name, "directory")) {
1169  ast_copy_string(mohclass->dir, var->value, sizeof(mohclass->dir));
1170  } else if (!strcasecmp(var->name, "application")) {
1171  ast_copy_string(mohclass->args, var->value, sizeof(mohclass->args));
1172  } else if (!strcasecmp(var->name, "announcement")) {
1173  ast_copy_string(mohclass->announcement, var->value, sizeof(mohclass->announcement));
1174  ast_set_flag(mohclass, MOH_ANNOUNCEMENT);
1175  } else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value))) {
1176  mohclass->digit = *var->value;
1177  } else if (!strcasecmp(var->name, "random")) {
1178  static int deprecation_warning = 0;
1179  if (!deprecation_warning) {
1180  ast_log(LOG_WARNING, "Music on hold 'random' setting is deprecated in 14. Please use 'sort=random' instead.\n");
1181  deprecation_warning = 1;
1182  }
1183  ast_set2_flag(mohclass, ast_true(var->value), MOH_RANDOMIZE);
1184  } else if (!strcasecmp(var->name, "sort")) {
1185  if (!strcasecmp(var->value, "random")) {
1186  ast_set_flag(mohclass, MOH_RANDOMIZE);
1187  } else if (!strcasecmp(var->value, "alpha")) {
1188  ast_set_flag(mohclass, MOH_SORTALPHA);
1189  } else if (!strcasecmp(var->value, "randstart")) {
1190  ast_set_flag(mohclass, MOH_RANDSTART);
1191  }
1192  } else if (!strcasecmp(var->name, "loop_last")) {
1193  if (ast_true(var->value)) {
1194  ast_set_flag(mohclass, MOH_LOOPLAST);
1195  } else {
1196  ast_clear_flag(mohclass, MOH_LOOPLAST);
1197  }
1198  } else if (!strcasecmp(var->name, "format") && !ast_strlen_zero(var->value)) {
1199  ao2_cleanup(mohclass->format);
1200  mohclass->format = ast_format_cache_get(var->value);
1201  if (!mohclass->format) {
1202  ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1203  mohclass->format = ao2_bump(ast_format_slin);
1204  }
1205  } else if (!strcasecmp(var->name, "kill_escalation_delay")) {
1206  if (sscanf(var->value, "%zu", &mohclass->kill_delay) == 1) {
1207  mohclass->kill_delay *= 1000;
1208  } else {
1209  ast_log(LOG_WARNING, "kill_escalation_delay '%s' is invalid. Setting to 100ms\n", var->value);
1210  mohclass->kill_delay = 100000;
1211  }
1212  } else if (!strcasecmp(var->name, "kill_method")) {
1213  if (!strcasecmp(var->value, "process")) {
1214  mohclass->kill_method = KILL_METHOD_PROCESS;
1215  } else if (!strcasecmp(var->value, "process_group")) {
1216  mohclass->kill_method = KILL_METHOD_PROCESS_GROUP;
1217  } else {
1218  ast_log(LOG_WARNING, "kill_method '%s' is invalid. Setting to 'process_group'\n", var->value);
1219  mohclass->kill_method = KILL_METHOD_PROCESS_GROUP;
1220  }
1221  } else if (!strcasecmp(var->name, "answeredonly")) {
1222  mohclass->answeredonly = ast_true(var->value) ? 1: 0;
1223  }
1224  }
1225 
1226  if (playlist_entries) {
1227  /* If we aren't in playlist mode, drop any list we may have already built */
1228  if (strcasecmp(mohclass->mode, "playlist")) {
1229  ast_log(LOG_NOTICE, "Ignoring playlist entries because we are in '%s' mode.\n",
1230  mohclass->mode);
1231  ao2_ref(playlist_entries, -1);
1232  return;
1233  }
1234 
1235  AST_VECTOR_COMPACT(playlist_entries);
1236 
1237  /* We don't need to lock here because we are the thread that
1238  * created this mohclass and we haven't published it yet */
1239  ao2_ref(mohclass->files, -1);
1240  mohclass->files = playlist_entries;
1241  }
1242 }
1243 
1244 static int on_moh_file(const char *directory, const char *filename, void *obj)
1245 {
1246  struct ast_vector_string *files = obj;
1247  char *full_path;
1248  char *extension;
1249 
1250  /* Skip files that starts with a dot */
1251  if (*filename == '.') {
1252  ast_debug(4, "Skipping '%s/%s' because it starts with a dot\n",
1253  directory, filename);
1254  return 0;
1255  }
1256 
1257  /* We can't do anything with files that don't have an extension,
1258  * so check that first and punt if we can't find something */
1259  extension = strrchr(filename, '.');
1260  if (!extension) {
1261  ast_debug(4, "Skipping '%s/%s' because it doesn't have an extension\n",
1262  directory, filename);
1263  return 0;
1264  }
1265 
1266  /* The extension needs at least two characters (after the .) to be useful */
1267  if (strlen(extension) < 3) {
1268  ast_debug(4, "Skipping '%s/%s' because it doesn't have at least a two "
1269  "character extension\n", directory, filename);
1270  return 0;
1271  }
1272 
1273  /* Build the full path (excluding the extension) */
1274  if (ast_asprintf(&full_path, "%s/%.*s",
1275  directory,
1276  (int) (extension - filename), filename) < 0) {
1277  /* If we don't have enough memory to build this path, there is no
1278  * point in continuing */
1279  return 1;
1280  }
1281 
1282  /* If the file is present in multiple formats, ensure we only put it
1283  * into the list once. Pretty sure this is O(n^2). */
1284  if (AST_VECTOR_GET_CMP(files, &full_path[0], !strcmp)) {
1285  ast_free(full_path);
1286  return 0;
1287  }
1288 
1289  if (AST_VECTOR_APPEND(files, full_path)) {
1290  /* AST_VECTOR_APPEND() can only fail on allocation failure, so
1291  * we stop iterating */
1292  ast_free(full_path);
1293  return 1;
1294  }
1295 
1296  return 0;
1297 }
1298 
1299 static int moh_filename_strcasecmp(const void *a, const void *b)
1300 {
1301  const char **s1 = (const char **) a;
1302  const char **s2 = (const char **) b;
1303  return strcasecmp(*s1, *s2);
1304 }
1305 
1306 static int moh_scan_files(struct mohclass *class) {
1307 
1308  char dir_path[PATH_MAX - sizeof(class->dir)];
1309  struct ast_vector_string *files;
1310 
1311  if (class->dir[0] != '/') {
1312  snprintf(dir_path, sizeof(dir_path), "%s/%s", ast_config_AST_DATA_DIR, class->dir);
1313  } else {
1314  ast_copy_string(dir_path, class->dir, sizeof(dir_path));
1315  }
1316 
1317  ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
1318 
1319  /* 16 seems like a reasonable default */
1320  files = moh_file_vector_alloc(16);
1321  if (!files) {
1322  return -1;
1323  }
1324 
1325  if (ast_file_read_dir(dir_path, on_moh_file, files)) {
1326  ao2_ref(files, -1);
1327  return -1;
1328  }
1329 
1330  if (ast_test_flag(class, MOH_SORTALPHA)) {
1331  AST_VECTOR_SORT(files, moh_filename_strcasecmp);
1332  }
1333 
1334  AST_VECTOR_COMPACT(files);
1335 
1336  ao2_lock(class);
1337  ao2_ref(class->files, -1);
1338  class->files = files;
1339  ao2_unlock(class);
1340 
1341  return AST_VECTOR_SIZE(files);
1342 }
1343 
1344 static int init_files_class(struct mohclass *class)
1345 {
1346  int res;
1347 
1348  res = moh_scan_files(class);
1349 
1350  if (res < 0) {
1351  return -1;
1352  }
1353 
1354  if (!res) {
1355  ast_verb(3, "Files not found in %s for moh class:%s\n",
1356  class->dir, class->name);
1357  return -1;
1358  }
1359 
1360  return 0;
1361 }
1362 
1363 static void moh_rescan_files(void) {
1364  struct ao2_iterator i;
1365  struct mohclass *c;
1366 
1367  i = ao2_iterator_init(mohclasses, 0);
1368 
1369  while ((c = ao2_iterator_next(&i))) {
1370  if (!strcasecmp(c->mode, "files")) {
1371  moh_scan_files(c);
1372  }
1373  ao2_ref(c, -1);
1374  }
1375 
1377 }
1378 
1379 static int moh_diff(struct mohclass *old, struct mohclass *new)
1380 {
1381  if (!old || !new) {
1382  return -1;
1383  }
1384 
1385  if (strcmp(old->dir, new->dir)) {
1386  return -1;
1387  } else if (strcmp(old->mode, new->mode)) {
1388  return -1;
1389  } else if (strcmp(old->args, new->args)) {
1390  return -1;
1391  } else if (old->flags != new->flags) {
1392  return -1;
1393  }
1394 
1395  return 0;
1396 }
1397 
1398 static int init_app_class(struct mohclass *class)
1399 {
1400  if (!strcasecmp(class->mode, "custom")) {
1401  ast_set_flag(class, MOH_CUSTOM);
1402  } else if (!strcasecmp(class->mode, "mp3nb")) {
1403  ast_set_flag(class, MOH_SINGLE);
1404  } else if (!strcasecmp(class->mode, "quietmp3nb")) {
1405  ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
1406  } else if (!strcasecmp(class->mode, "quietmp3")) {
1407  ast_set_flag(class, MOH_QUIET);
1408  }
1409 
1410  class->srcfd = -1;
1411 
1412  if (!(class->timer = ast_timer_open())) {
1413  ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1414  return -1;
1415  }
1416  if (class->timer && ast_timer_set_rate(class->timer, 25)) {
1417  ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1418  ast_timer_close(class->timer);
1419  class->timer = NULL;
1420  }
1421 
1422  if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
1423  ast_log(LOG_WARNING, "Unable to create moh thread...\n");
1424  if (class->timer) {
1425  ast_timer_close(class->timer);
1426  class->timer = NULL;
1427  }
1428  return -1;
1429  }
1430 
1431  return 0;
1432 }
1433 
1434 /*!
1435  * \note This function owns the reference it gets to moh if unref is true
1436  */
1437 #define moh_register(moh, reload, unref) _moh_register(moh, reload, unref, __FILE__, __LINE__, __PRETTY_FUNCTION__)
1438 static int _moh_register(struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
1439 {
1440  struct mohclass *mohclass = NULL;
1441 
1442  mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
1443 
1444  if (mohclass && !moh_diff(mohclass, moh)) {
1445  ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
1446  mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1447  if (unref) {
1448  moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
1449  }
1450  return -1;
1451  } else if (mohclass) {
1452  /* Found a class, but it's different from the one being registered */
1453  mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1454  }
1455 
1456  time(&moh->start);
1457  moh->start -= respawn_time;
1458 
1459  if (!strcasecmp(moh->mode, "files")) {
1460  if (init_files_class(moh)) {
1461  if (unref) {
1462  moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
1463  }
1464  return -1;
1465  }
1466  } else if (!strcasecmp(moh->mode, "playlist")) {
1467  size_t file_count;
1468 
1469  ao2_lock(moh);
1470  file_count = AST_VECTOR_SIZE(moh->files);
1471  ao2_unlock(moh);
1472 
1473  if (!file_count) {
1474  if (unref) {
1475  moh = mohclass_unref(moh, "unreffing potential new moh class (no playlist entries)");
1476  }
1477  return -1;
1478  }
1479  } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") ||
1480  !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
1481  !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
1482  if (init_app_class(moh)) {
1483  if (unref) {
1484  moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
1485  }
1486  return -1;
1487  }
1488  } else {
1489  ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
1490  if (unref) {
1491  moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
1492  }
1493  return -1;
1494  }
1495 
1496  ao2_t_link(mohclasses, moh, "Adding class to container");
1497 
1498  if (unref) {
1499  moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
1500  }
1501 
1502  return 0;
1503 }
1504 
1505 #define moh_unregister(a) _moh_unregister(a,__FILE__,__LINE__,__PRETTY_FUNCTION__)
1506 static int _moh_unregister(struct mohclass *moh, const char *file, int line, const char *funcname)
1507 {
1508  ao2_t_unlink(mohclasses, moh, "Removing class from container");
1509  return 0;
1510 }
1511 
1512 static void local_ast_moh_cleanup(struct ast_channel *chan)
1513 {
1514  struct moh_files_state *state = ast_channel_music_state(chan);
1515 
1516  if (state) {
1517  ast_channel_music_state_set(chan, NULL);
1518  if (state->class) {
1519  /* This should never happen. We likely just leaked some resource. */
1520  state->class =
1521  mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
1522  ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
1523  }
1524  ao2_cleanup(state->origwfmt);
1525  ao2_cleanup(state->mohwfmt);
1526  ast_free(state);
1527  /* Only held a module reference if we had a music state */
1529  }
1530 }
1531 
1532 /*! \brief Support routing for 'moh unregister class' CLI
1533  * This is in charge of generating all strings that match a prefix in the
1534  * given position. As many functions of this kind, each invokation has
1535  * O(state) time complexity so be careful in using it.
1536  */
1537 static char *complete_mohclass_realtime(const char *line, const char *word, int pos, int state)
1538 {
1539  int which=0;
1540  struct mohclass *cur;
1541  char *c = NULL;
1542  int wordlen = strlen(word);
1543  struct ao2_iterator i;
1544 
1545  if (pos != 3) {
1546  return NULL;
1547  }
1548 
1549  i = ao2_iterator_init(mohclasses, 0);
1550  while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1551  if (cur->realtime && !strncasecmp(cur->name, word, wordlen) && ++which > state) {
1552  c = ast_strdup(cur->name);
1553  mohclass_unref(cur, "drop ref in iterator loop break");
1554  break;
1555  }
1556  mohclass_unref(cur, "drop ref in iterator loop");
1557  }
1559 
1560  return c;
1561 }
1562 
1563 static char *handle_cli_moh_unregister_class(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1564 {
1565  struct mohclass *cur;
1566  int len;
1567  int found = 0;
1568  struct ao2_iterator i;
1569 
1570  switch (cmd) {
1571  case CLI_INIT:
1572  e->command = "moh unregister class";
1573  e->usage =
1574  "Usage: moh unregister class <class>\n"
1575  " Unregisters a realtime moh class.\n";
1576  return NULL;
1577  case CLI_GENERATE:
1578  return complete_mohclass_realtime(a->line, a->word, a->pos, a->n);
1579  }
1580 
1581  if (a->argc != 4)
1582  return CLI_SHOWUSAGE;
1583 
1584  len = strlen(a->argv[3]);
1585 
1586  i = ao2_iterator_init(mohclasses, 0);
1587  while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1588  if (cur->realtime && len == strlen(cur->name) && !strncasecmp(cur->name, a->argv[3], len)) {
1589  found = 1;
1590  break;
1591  }
1592  mohclass_unref(cur, "drop ref in iterator loop");
1593  }
1595 
1596  if (found) {
1597  moh_unregister(cur);
1598  mohclass_unref(cur, "drop ref after unregister");
1599  } else {
1600  ast_cli(a->fd, "No such realtime moh class '%s'\n", a->argv[3]);
1601  }
1602 
1603  return CLI_SUCCESS;
1604 }
1605 
1606 
1607 
1608 static void moh_class_destructor(void *obj);
1609 
1610 #define moh_class_malloc() _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
1611 
1612 static struct mohclass *_moh_class_malloc(const char *file, int line, const char *funcname)
1613 {
1614  struct mohclass *class;
1615 
1616  class = __ao2_alloc(sizeof(*class), moh_class_destructor, AO2_ALLOC_OPT_LOCK_MUTEX,
1617  "Allocating new moh class", file, line, funcname);
1618  if (class) {
1619  class->format = ao2_bump(ast_format_slin);
1620  class->srcfd = -1;
1621  class->kill_delay = 100000;
1622 
1623  /* We create an empty one by default */
1624  class->files = moh_file_vector_alloc(0);
1625  if (!class->files) {
1626  ao2_ref(class, -1);
1627  return NULL;
1628  }
1629  }
1630 
1631  return class;
1632 }
1633 
1634 static struct ast_variable *load_realtime_musiconhold(const char *name)
1635 {
1636  struct ast_variable *var = ast_load_realtime("musiconhold", "name", name, SENTINEL);
1637 
1638  if (var) {
1639  const char *mode = ast_variable_find_in_list(var, "mode");
1640  if (ast_strings_equal(mode, "playlist")) {
1641  struct ast_config *entries = ast_load_realtime_multientry("musiconhold_entry", "position >=", "0", "name", name, SENTINEL);
1642  char *category = NULL;
1643  size_t entry_count = 0;
1644 
1645  /* entries is NULL if there are no results */
1646  if (entries) {
1647  while ((category = ast_category_browse(entries, category))) {
1648  const char *entry = ast_variable_retrieve(entries, category, "entry");
1649 
1650  if (entry) {
1651  struct ast_variable *dup = ast_variable_new("entry", entry, "");
1652  if (dup) {
1653  entry_count++;
1654  ast_variable_list_append(&var, dup);
1655  }
1656  }
1657  }
1658  ast_config_destroy(entries);
1659  }
1660 
1661  if (entry_count == 0) {
1662  /* Behave as though this class doesn't exist */
1663  ast_variables_destroy(var);
1664  var = NULL;
1665  }
1666  }
1667  }
1668 
1669  if (!var) {
1670  ast_log(LOG_WARNING,
1671  "Music on Hold class '%s' not found in memory/database. "
1672  "Verify your configuration.\n",
1673  name);
1674  }
1675  return var;
1676 }
1677 
1678 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
1679 {
1680  struct mohclass *mohclass = NULL;
1681  struct moh_files_state *state = ast_channel_music_state(chan);
1682  struct ast_variable *var = NULL;
1683  int res = 0;
1684  int i;
1685  int realtime_possible = ast_check_realtime("musiconhold");
1686  int warn_if_not_in_memory = !realtime_possible;
1687  const char *classes[] = {NULL, NULL, interpclass, "default"};
1688 
1689  if (ast_test_flag(global_flags, MOH_PREFERCHANNELCLASS)) {
1690  classes[0] = ast_channel_musicclass(chan);
1691  classes[1] = mclass;
1692  } else {
1693  classes[0] = mclass;
1694  classes[1] = ast_channel_musicclass(chan);
1695  }
1696 
1697  /* The following is the order of preference for which class to use:
1698  * 1) The channels explicitly set musicclass, which should *only* be
1699  * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1700  * Unless preferchannelclass in musiconhold.conf is false
1701  * 2) The mclass argument. If a channel is calling ast_moh_start() as the
1702  * result of receiving a HOLD control frame, this should be the
1703  * payload that came with the frame.
1704  * 3) The channels explicitly set musicclass, which should *only* be
1705  * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1706  * 4) The interpclass argument. This would be from the mohinterpret
1707  * option from channel drivers. This is the same as the old musicclass
1708  * option.
1709  * 5) The default class.
1710  */
1711 
1712  for (i = 0; i < ARRAY_LEN(classes); ++i) {
1713  if (!ast_strlen_zero(classes[i])) {
1714  mohclass = get_mohbyname(classes[i], warn_if_not_in_memory, 0);
1715  if (!mohclass && realtime_possible) {
1716  var = load_realtime_musiconhold(classes[i]);
1717  }
1718  if (mohclass || var) {
1719  break;
1720  }
1721  }
1722  }
1723 
1724  /* If no moh class found in memory, then check RT. Note that the logic used
1725  * above guarantees that if var is non-NULL, then mohclass must be NULL.
1726  */
1727  if (var) {
1728  if ((mohclass = moh_class_malloc())) {
1729  mohclass->realtime = 1;
1730 
1731  moh_parse_options(var, mohclass);
1732  ast_variables_destroy(var);
1733 
1734  if (ast_strlen_zero(mohclass->dir)) {
1735  if (!strcasecmp(mohclass->mode, "custom") || !strcasecmp(mohclass->mode, "playlist")) {
1736  strcpy(mohclass->dir, "nodir");
1737  } else {
1738  ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
1739  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
1740  return -1;
1741  }
1742  }
1743  if (ast_strlen_zero(mohclass->mode)) {
1744  ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
1745  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
1746  return -1;
1747  }
1748  if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
1749  ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
1750  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
1751  return -1;
1752  }
1753 
1754  if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
1755  /* CACHERTCLASSES enabled, let's add this class to default tree */
1756  if (state && state->class) {
1757  /* Class already exist for this channel */
1758  ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1759  }
1760  /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
1761  * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
1762  * be that the destructor would be called when the generator on the channel is deactivated. The container then
1763  * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
1764  * invalid memory.
1765  */
1766  if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
1767  mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
1768  return -1;
1769  }
1770  } else {
1771  /* We don't register RT moh class, so let's init it manually */
1772 
1773  time(&mohclass->start);
1774  mohclass->start -= respawn_time;
1775 
1776  if (!strcasecmp(mohclass->mode, "files")) {
1777  /*
1778  * XXX moh_scan_files returns -1 if it is unable to open the
1779  * configured directory or there is a memory allocation
1780  * failure. Otherwise it returns the number of files for this music
1781  * class. This check is only checking if the number of files is zero
1782  * and it ignores the -1 case. To avoid a behavior change we keep this
1783  * as-is, but we should address what the 'correct' behavior should be.
1784  */
1785  if (!moh_scan_files(mohclass)) {
1786  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1787  return -1;
1788  }
1789  if (strchr(mohclass->args, 'r')) {
1790  static int deprecation_warning = 0;
1791  if (!deprecation_warning) {
1792  ast_log(LOG_WARNING, "Music on hold 'application=r' setting is deprecated in 14. Please use 'sort=random' instead.\n");
1793  deprecation_warning = 1;
1794  }
1795  ast_set_flag(mohclass, MOH_RANDOMIZE);
1796  }
1797  } else if (!strcasecmp(mohclass->mode, "playlist")) {
1798  size_t file_count;
1799 
1800  ao2_lock(mohclass);
1801  file_count = AST_VECTOR_SIZE(mohclass->files);
1802  ao2_unlock(mohclass);
1803 
1804  if (!file_count) {
1805  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no playlist entries)");
1806  return -1;
1807  }
1808  } else if (!strcasecmp(mohclass->mode, "mp3") || !strcasecmp(mohclass->mode, "mp3nb") || !strcasecmp(mohclass->mode, "quietmp3") || !strcasecmp(mohclass->mode, "quietmp3nb") || !strcasecmp(mohclass->mode, "httpmp3") || !strcasecmp(mohclass->mode, "custom")) {
1809 
1810  if (!strcasecmp(mohclass->mode, "custom"))
1811  ast_set_flag(mohclass, MOH_CUSTOM);
1812  else if (!strcasecmp(mohclass->mode, "mp3nb"))
1813  ast_set_flag(mohclass, MOH_SINGLE);
1814  else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1815  ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
1816  else if (!strcasecmp(mohclass->mode, "quietmp3"))
1817  ast_set_flag(mohclass, MOH_QUIET);
1818 
1819  mohclass->srcfd = -1;
1820  if (!(mohclass->timer = ast_timer_open())) {
1821  ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1822  }
1823  if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) {
1824  ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1825  ast_timer_close(mohclass->timer);
1826  mohclass->timer = NULL;
1827  }
1828 
1829  /* Let's check if this channel already had a moh class before */
1830  if (state && state->class) {
1831  /* Class already exist for this channel */
1832  ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1833  if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1834  /* we found RT class with the same name, seems like we should continue playing existing one */
1835  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
1836  mohclass = mohclass_ref(state->class, "using existing class from state");
1837  }
1838  } else {
1839  if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
1840  ast_log(LOG_WARNING, "Unable to create moh...\n");
1841  if (mohclass->timer) {
1842  ast_timer_close(mohclass->timer);
1843  mohclass->timer = NULL;
1844  }
1845  mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
1846  return -1;
1847  }
1848  }
1849  } else {
1850  ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
1851  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
1852  return -1;
1853  }
1854  }
1855  } else {
1856  ast_variables_destroy(var);
1857  var = NULL;
1858  }
1859  }
1860 
1861  if (!mohclass) {
1862  return -1;
1863  }
1864 
1865  if (mohclass->answeredonly && (ast_channel_state(chan) != AST_STATE_UP)) {
1866  ast_verb(3, "The channel '%s' is not answered yet. Ignore the moh request.\n", ast_channel_name(chan));
1867  return -1;
1868  }
1869 
1870  /* If we are using a cached realtime class with files, re-scan the files */
1871  if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
1872  /*
1873  * XXX moh_scan_files returns -1 if it is unable to open the configured directory
1874  * or there is a memory allocation failure. Otherwise it returns the number of
1875  * files for this music class. This check is only checking if the number of files
1876  * is zero and it ignores the -1 case. To avoid a behavior change we keep this
1877  * as-is, but we should address what the 'correct' behavior should be.
1878  */
1879  if (!moh_scan_files(mohclass)) {
1880  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1881  return -1;
1882  }
1883  }
1884 
1885  if (!state || !state->class || strcmp(mohclass->name, state->class->name)) {
1886  size_t file_count;
1887 
1888  ao2_lock(mohclass);
1889  file_count = AST_VECTOR_SIZE(mohclass->files);
1890  ao2_unlock(mohclass);
1891 
1892  if (file_count) {
1893  res = ast_activate_generator(chan, &moh_file_stream, mohclass);
1894  } else {
1895  res = ast_activate_generator(chan, &mohgen, mohclass);
1896  }
1897  }
1898  if (!res) {
1899  ast_channel_lock(chan);
1900  ast_channel_latest_musicclass_set(chan, mohclass->name);
1901  ast_set_flag(ast_channel_flags(chan), AST_FLAG_MOH);
1902  ast_channel_unlock(chan);
1903  }
1904 
1905  mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
1906 
1907  return res;
1908 }
1909 
1910 static void local_ast_moh_stop(struct ast_channel *chan)
1911 {
1913 
1914  ast_channel_lock(chan);
1915  ast_clear_flag(ast_channel_flags(chan), AST_FLAG_MOH);
1916  if (ast_channel_music_state(chan)) {
1917  if (ast_channel_stream(chan)) {
1918  ast_closestream(ast_channel_stream(chan));
1919  ast_channel_stream_set(chan, NULL);
1920  }
1921  }
1922  ast_channel_unlock(chan);
1923 }
1924 
1925 static void moh_class_destructor(void *obj)
1926 {
1927  struct mohclass *class = obj;
1928  struct mohdata *member;
1929  pthread_t tid = 0;
1930 
1931  ast_debug(1, "Destroying MOH class '%s'\n", class->name);
1932 
1933  ao2_lock(class);
1934  while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
1935  ast_free(member);
1936  }
1937  ao2_cleanup(class->files);
1938  ao2_unlock(class);
1939 
1940  /* Kill the thread first, so it cannot restart the child process while the
1941  * class is being destroyed */
1942  if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
1943  tid = class->thread;
1944  class->thread = AST_PTHREADT_NULL;
1945  pthread_cancel(tid);
1946  /* We'll collect the exit status later, after we ensure all the readers
1947  * are dead. */
1948  }
1949 
1950  if (class->pid > 1) {
1951  char buff[8192];
1952  int bytes, tbytes = 0, stime = 0;
1953 
1954  ast_debug(1, "killing %d!\n", class->pid);
1955 
1956  stime = time(NULL) + 2;
1957  killpid(class->pid, class->kill_delay, class->kill_method);
1958 
1959  while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
1960  (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
1961  tbytes = tbytes + bytes;
1962  }
1963 
1964  ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n",
1965  class->pid, tbytes);
1966 
1967  class->pid = 0;
1968  close(class->srcfd);
1969  class->srcfd = -1;
1970  }
1971 
1972  if (class->timer) {
1973  ast_timer_close(class->timer);
1974  class->timer = NULL;
1975  }
1976 
1977  ao2_cleanup(class->format);
1978 
1979  /* Finally, collect the exit status of the monitor thread */
1980  if (tid > 0) {
1981  pthread_join(tid, NULL);
1982  }
1983 
1984 }
1985 
1986 static int moh_class_mark(void *obj, void *arg, int flags)
1987 {
1988  struct mohclass *class = obj;
1989 
1990  if ( ((flags & MOH_REALTIME) && class->realtime) || !(flags & MOH_REALTIME) ) {
1991  class->delete = 1;
1992  }
1993 
1994  return 0;
1995 }
1996 
1997 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
1998 {
1999  struct mohclass *class = obj;
2000 
2001  return class->delete ? CMP_MATCH : 0;
2002 }
2003 
2004 static int load_moh_classes(int reload)
2005 {
2006  struct ast_config *cfg;
2007  struct ast_variable *var;
2008  struct mohclass *class;
2009  char *cat;
2010  int numclasses = 0;
2011  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
2012 
2013  cfg = ast_config_load("musiconhold.conf", config_flags);
2014 
2015  if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
2016  if (ast_check_realtime("musiconhold") && reload) {
2017  ao2_t_callback(mohclasses, OBJ_NODATA | MOH_REALTIME, moh_class_mark, NULL, "Mark realtime classes for deletion");
2018  ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
2019  }
2020  moh_rescan_files();
2021  return 0;
2022  }
2023 
2024  if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
2025  if (ast_check_realtime("musiconhold") && reload) {
2026  ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
2027  ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
2028  }
2029  return 0;
2030  }
2031 
2032  if (reload) {
2033  ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
2034  }
2035 
2036  ast_clear_flag(global_flags, AST_FLAGS_ALL);
2037  ast_set2_flag(global_flags, 1, MOH_PREFERCHANNELCLASS);
2038 
2039  cat = ast_category_browse(cfg, NULL);
2040  for (; cat; cat = ast_category_browse(cfg, cat)) {
2041  /* Setup common options from [general] section */
2042  if (!strcasecmp(cat, "general")) {
2043  for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
2044  if (!strcasecmp(var->name, "cachertclasses")) {
2045  ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
2046  } else if (!strcasecmp(var->name, "preferchannelclass")) {
2047  ast_set2_flag(global_flags, ast_true(var->value), MOH_PREFERCHANNELCLASS);
2048  } else {
2049  ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
2050  }
2051  }
2052  continue;
2053  }
2054 
2055  if (!(class = moh_class_malloc())) {
2056  break;
2057  }
2058 
2059  moh_parse_options(ast_variable_browse(cfg, cat), class);
2060  /* For compatibility with the past, we overwrite any name=name
2061  * with the context [name]. */
2062  ast_copy_string(class->name, cat, sizeof(class->name));
2063 
2064  if (ast_strlen_zero(class->dir)) {
2065  if (!strcasecmp(class->mode, "custom") || !strcasecmp(class->mode, "playlist")) {
2066  strcpy(class->dir, "nodir");
2067  } else {
2068  ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
2069  class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
2070  continue;
2071  }
2072  }
2073  if (ast_strlen_zero(class->mode)) {
2074  ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
2075  class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
2076  continue;
2077  }
2078  if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
2079  ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
2080  class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
2081  continue;
2082  }
2083 
2084  /* Don't leak a class when it's already registered */
2085  if (!moh_register(class, reload, HANDLE_REF)) {
2086  numclasses++;
2087  }
2088  }
2089 
2090  ast_config_destroy(cfg);
2091 
2092  ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
2093  moh_classes_delete_marked, NULL, "Purge marked classes");
2094 
2095  return numclasses;
2096 }
2097 
2098 static void ast_moh_destroy(void)
2099 {
2100  ast_verb(2, "Destroying musiconhold processes\n");
2101  if (mohclasses) {
2102  ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
2103  ao2_ref(mohclasses, -1);
2104  mohclasses = NULL;
2105  }
2106 }
2107 
2108 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2109 {
2110  switch (cmd) {
2111  case CLI_INIT:
2112  e->command = "moh reload";
2113  e->usage =
2114  "Usage: moh reload\n"
2115  " Reloads the MusicOnHold module.\n"
2116  " Alias for 'module reload res_musiconhold.so'\n";
2117  return NULL;
2118  case CLI_GENERATE:
2119  return NULL;
2120  }
2121 
2122  if (a->argc != e->args)
2123  return CLI_SHOWUSAGE;
2124 
2125  /* The module loader will prevent concurrent reloads from occurring, so we delegate */
2126  ast_module_reload("res_musiconhold");
2127 
2128  return CLI_SUCCESS;
2129 }
2130 
2131 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2132 {
2133  struct mohclass *class;
2134  struct ao2_iterator i;
2135 
2136  switch (cmd) {
2137  case CLI_INIT:
2138  e->command = "moh show files";
2139  e->usage =
2140  "Usage: moh show files\n"
2141  " Lists all loaded file-based MusicOnHold classes and their\n"
2142  " files.\n";
2143  return NULL;
2144  case CLI_GENERATE:
2145  return NULL;
2146  }
2147 
2148  if (a->argc != e->args)
2149  return CLI_SHOWUSAGE;
2150 
2151  i = ao2_iterator_init(mohclasses, 0);
2152  for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
2153  struct ast_vector_string *files;
2154 
2155  ao2_lock(class);
2156  files = ao2_bump(class->files);
2157  ao2_unlock(class);
2158 
2159  if (AST_VECTOR_SIZE(files)) {
2160  int x;
2161  ast_cli(a->fd, "Class: %s\n", class->name);
2162  for (x = 0; x < AST_VECTOR_SIZE(files); x++) {
2163  ast_cli(a->fd, "\tFile: %s\n", AST_VECTOR_GET(files, x));
2164  }
2165  }
2166 
2167  ao2_ref(files, -1);
2168  }
2170 
2171  return CLI_SUCCESS;
2172 }
2173 
2174 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2175 {
2176  struct mohclass *class;
2177  struct ao2_iterator i;
2178 
2179  switch (cmd) {
2180  case CLI_INIT:
2181  e->command = "moh show classes";
2182  e->usage =
2183  "Usage: moh show classes\n"
2184  " Lists all MusicOnHold classes.\n";
2185  return NULL;
2186  case CLI_GENERATE:
2187  return NULL;
2188  }
2189 
2190  if (a->argc != e->args)
2191  return CLI_SHOWUSAGE;
2192 
2193  i = ao2_iterator_init(mohclasses, 0);
2194  for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
2195  ast_cli(a->fd, "Class: %s\n", class->name);
2196  ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
2197  ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
2198  if (ast_test_flag(class, MOH_ANNOUNCEMENT)) {
2199  ast_cli(a->fd, "\tAnnouncement: %s\n", S_OR(class->announcement, "<none>"));
2200  }
2201  if (ast_test_flag(class, MOH_CUSTOM)) {
2202  ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
2203  ast_cli(a->fd, "\tKill Escalation Delay: %zu ms\n", class->kill_delay / 1000);
2204  ast_cli(a->fd, "\tKill Method: %s\n",
2205  class->kill_method == KILL_METHOD_PROCESS ? "process" : "process_group");
2206  }
2207  if (strcasecmp(class->mode, "files")) {
2208  ast_cli(a->fd, "\tFormat: %s\n", ast_format_get_name(class->format));
2209  }
2210  }
2212 
2213  return CLI_SUCCESS;
2214 }
2215 
2216 static struct ast_cli_entry cli_moh[] = {
2217  AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"),
2218  AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
2219  AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes"),
2220  AST_CLI_DEFINE(handle_cli_moh_unregister_class, "Unregister realtime MusicOnHold class")
2221 };
2222 
2223 static int moh_class_hash(const void *obj, const int flags)
2224 {
2225  const struct mohclass *class = obj;
2226 
2227  return ast_str_case_hash(class->name);
2228 }
2229 
2230 static int moh_class_cmp(void *obj, void *arg, int flags)
2231 {
2232  struct mohclass *class = obj, *class2 = arg;
2233 
2234  return strcasecmp(class->name, class2->name) ? 0 :
2235  (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
2236  CMP_MATCH | CMP_STOP;
2237 }
2238 
2239 /*!
2240  * \brief Load the module
2241  *
2242  * Module loading including tests for configuration or dependencies.
2243  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
2244  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
2245  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
2246  * configuration file or other non-critical problem return
2247  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
2248  */
2249 static int load_module(void)
2250 {
2251  int res;
2252 
2253  mohclasses = ao2_t_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, 53,
2254  moh_class_hash, NULL, moh_class_cmp, "Moh class container");
2255  if (!mohclasses) {
2256  return AST_MODULE_LOAD_DECLINE;
2257  }
2258 
2259  if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) { /* No music classes configured, so skip it */
2260  ast_log(LOG_WARNING, "No music on hold classes configured, "
2261  "disabling music on hold.\n");
2262  } else {
2263  ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
2264  local_ast_moh_cleanup);
2265  }
2266 
2267  res = ast_register_application_xml(play_moh, play_moh_exec);
2268  ast_register_atexit(ast_moh_destroy);
2269  ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
2270  if (!res)
2271  res = ast_register_application_xml(start_moh, start_moh_exec);
2272  if (!res)
2273  res = ast_register_application_xml(stop_moh, stop_moh_exec);
2274 
2275  return AST_MODULE_LOAD_SUCCESS;
2276 }
2277 
2278 static int reload(void)
2279 {
2280  if (load_moh_classes(1)) {
2281  ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
2282  local_ast_moh_cleanup);
2283  }
2284 
2285  return AST_MODULE_LOAD_SUCCESS;
2286 }
2287 
2288 static int moh_class_inuse(void *obj, void *arg, int flags)
2289 {
2290  struct mohclass *class = obj;
2291 
2292  return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
2293 }
2294 
2295 static int unload_module(void)
2296 {
2297  int res = 0;
2298  struct mohclass *class = NULL;
2299 
2300  /* XXX This check shouldn't be required if module ref counting was being used
2301  * properly ... */
2302  if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
2303  class = mohclass_unref(class, "unref of class from module unload callback");
2304  res = -1;
2305  }
2306 
2307  if (res < 0) {
2308  ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
2309  return res;
2310  }
2311 
2312  ast_uninstall_music_functions();
2313 
2314  ast_moh_destroy();
2315  res = ast_unregister_application(play_moh);
2316  res |= ast_unregister_application(start_moh);
2317  res |= ast_unregister_application(stop_moh);
2318  ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
2319  ast_unregister_atexit(ast_moh_destroy);
2320 
2321  return res;
2322 }
2323 
2324 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Music On Hold Resource",
2325  .support_level = AST_MODULE_SUPPORT_CORE,
2326  .load = load_module,
2327  .unload = unload_module,
2328  .reload = reload,
2329  .load_pri = AST_MODPRI_CHANNEL_DEPEND,
2330 );
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
struct ast_variable * next
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
Definition: channel.c:1574
Main Channel structure associated with a channel.
Music on hold handling.
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
#define MOH_NOTDELETED
const char * name
Definition: format.c:45
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
void * __ao2_find(struct ao2_container *c, const void *arg, enum search_flags flags, const char *tag, const char *file, int line, const char *func)
#define ast_pipe_nonblock(filedes)
Create a non-blocking pipe.
Definition: utils.h:1090
int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
Definition: channel.c:2951
Support for translation of data formats. translate.c.
#define MAX_MUSICCLASS
Definition: channel.h:173
#define moh_register(moh, reload, unref)
Time-related functions and macros.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
#define OBJ_POINTER
Definition: astobj2.h:1150
struct stasis_message_type * ast_channel_moh_start_type(void)
Message type for starting music on hold on a channel.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
enum ast_module_reload_result ast_module_reload(const char *name)
Reload asterisk modules.
Definition: loader.c:1721
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
descriptor for a cli entry.
Definition: cli.h:171
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
Definition: astobj2.h:1693
struct mohclass * class
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
Definition: main/config.c:3530
Structure for variables, used for configurations and for channel variables.
void ast_close_fds_above_n(int n)
Common routine for child processes, to close all fds prior to exec(2)
Definition: main/app.c:3202
pthread_t thread
Definition: app_sla.c:329
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Definition: time.h:117
void ast_timer_close(struct ast_timer *handle)
Close an opened timing handle.
Definition: timing.c:154
ast_channel_state
ast_channel states
Definition: channelstate.h:35
struct ast_timer * ast_timer_open(void)
Open a timer.
Definition: timing.c:122
Definition of a media format.
Definition: format.c:43
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
#define AST_VECTOR_COMPACT(vec)
Resize a vector so that its capacity is the same as its size.
Definition: vector.h:638
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
struct ast_filestream * ast_openstream_full(struct ast_channel *chan, const char *filename, const char *preflang, int asis)
Opens stream for use in seeking, playing.
Definition: file.c:795
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:450
const char * ast_format_get_name(const struct ast_format *format)
Get the name associated with a format.
Definition: format.c:334
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:107
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
#define MOH_RANDSTART
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
Definition: linkedlists.h:856
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7776
struct ao2_container * c
Definition: astobj2.h:1823
#define AST_VECTOR_SORT(vec, cmp)
Sort a vector in-place.
Definition: vector.h:396
struct ast_frame_subclass subclass
#define ast_format_cache_get(name)
Retrieve a named format from the cache.
Definition: format_cache.h:278
Utility functions.
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
Blob of data associated with a channel.
int args
This gets set in ast_cli_register()
Definition: cli.h:185
off_t ast_tellstream(struct ast_filestream *fs)
Tell where we are in a stream.
Definition: file.c:1085
unsigned int realtime
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
enum ast_format_cmp_res ast_format_cmp(const struct ast_format *format1, const struct ast_format *format2)
Compare two formats.
Definition: format.c:201
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
struct ast_module * self
Definition: module.h:356
General Asterisk PBX channel definitions.
Asterisk file paths, configured in asterisk.conf.
#define AST_FRIENDLY_OFFSET
Offset into a frame's data buffer.
void ast_unregister_atexit(void(*func)(void))
Unregister a function registered with ast_register_atexit().
Definition: asterisk.c:1064
#define MOH_CACHERTCLASSES
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
structure to hold extensions
int ast_register_atexit(void(*func)(void))
Register a function to be executed before Asterisk exits.
Definition: clicompat.c:13
struct timeval ast_samp2tv(unsigned int _nsamp, unsigned int _rate)
Returns a timeval corresponding to the duration of n samples at rate r. Useful to convert samples to ...
Definition: time.h:282
unsigned int ast_codec_samples_count(struct ast_frame *frame)
Get the number of samples contained within a frame.
Definition: codec.c:379
const char * ast_variable_find_in_list(const struct ast_variable *list, const char *variable)
Gets the value of a variable from a variable list by name.
Definition: main/config.c:919
A set of macros to manage forward-linked lists.
#define ast_debug(level,...)
Log a DEBUG message.
int ast_set_write_format(struct ast_channel *chan, struct ast_format *format)
Sets write format on channel chan.
Definition: channel.c:5803
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define MOH_PREFERCHANNELCLASS
struct ast_vector_string * files
Core PBX routines and definitions.
int ast_set_priority(int)
We set ourselves to a high priority, that we might pre-empt everything else. If your PBX has heavy ac...
Definition: asterisk.c:1841
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
#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:225
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3622
int ast_timer_ack(const struct ast_timer *handle, unsigned int quantity)
Acknowledge a timer event.
Definition: timing.c:171
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: utils.c:2199
unsigned int ast_format_determine_length(const struct ast_format *format, unsigned int samples)
Get the length (in milliseconds) for the format with a given number of samples.
Definition: format.c:384
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
struct ast_frame * ast_readframe(struct ast_filestream *s)
Read a frame from a filestream.
Definition: file.c:936
#define MOH_ANNOUNCEMENT
int ast_safe_fork(int stop_reaper)
Common routine to safely fork without a chance of a signal handler firing badly in the child...
Definition: main/app.c:3207
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition: extconf.c:2282
int ast_strings_equal(const char *str1, const char *str2)
Compare strings for equality checking for NULL.
Definition: strings.c:238
const ast_string_field name
int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
Turn on music on hold on a given channel.
Definition: channel.c:7766
#define ast_module_ref(mod)
Hold a reference to the module.
Definition: module.h:457
int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence)
Seeks into stream.
Definition: file.c:1075
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition: stasis.c:1511
union ast_frame::@224 data
char * command
Definition: cli.h:186
int ao2_match_by_addr(void *obj, void *arg, int flags)
A common ao2_callback is one that matches by address.
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define AST_VECTOR_RESET(vec, cleanup)
Reset vector.
Definition: vector.h:625
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1111
struct ast_format * format
enum kill_methods kill_method
int ast_write(struct ast_channel *chan, struct ast_frame *frame)
Write a frame to a channel This function writes the given frame to the indicated channel.
Definition: channel.c:5144
#define MOH_REALTIME
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
int ast_timer_fd(const struct ast_timer *handle)
Get a poll()-able file descriptor for a timer.
Definition: timing.c:161
int ast_timer_set_rate(const struct ast_timer *handle, unsigned int rate)
Set the timing tick rate.
Definition: timing.c:166
#define AST_VECTOR_GET_CMP(vec, value, cmp)
Get an element from a vector that matches the given comparison.
Definition: vector.h:731
struct ast_timer * timer
Structure used to handle boolean flags.
Definition: utils.h:199
static struct ast_flags global_flags[1]
struct stasis_message * ast_channel_blob_create_from_cache(const char *uniqueid, struct stasis_message_type *type, struct ast_json *blob)
Create a ast_channel_blob message, pulling channel state from the cache.
const char * usage
Definition: cli.h:177
#define ast_module_unref(mod)
Release a reference to the module.
Definition: module.h:483
void ast_deactivate_generator(struct ast_channel *chan)
Definition: channel.c:2893
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:680
#define ao2_replace(dst, src)
Replace one object reference with another cleaning up the original.
Definition: astobj2.h:501
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
Standard Command Line Interface.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:80
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1129
size_t kill_delay
unsigned int ast_format_get_sample_rate(const struct ast_format *format)
Get the sample rate of a media format.
Definition: format.c:379
struct mohclass::@446 list
Data structure associated with a single frame of data.
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
Definition: strings.h:97
Abstract JSON element (object, array, string, int, ...).
Definition: search.h:40
#define MOH_LOOPLAST
enum ast_frame_type frametype
Generic container type.
static char * complete_mohclass_realtime(const char *line, const char *word, int pos, int state)
Support routing for 'moh unregister class' CLI This is in charge of generating all strings that match...
String vector definitions.
Definition: vector.h:55
struct stasis_message_type * ast_channel_moh_stop_type(void)
Message type for stopping music on hold on a channel.
struct ast_format * format
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
Say numbers and dates (maybe words one day too)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
Asterisk module definitions.
static struct mohclass * get_mohbydigit(char digit)
#define ast_file_read_dir(dir_name, on_file, obj)
Iterate over each file in a given directory.
Definition: file.h:203
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
static int load_module(void)
Load the module.
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609
Timing source management.
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
static force_inline int attribute_pure ast_str_case_hash(const char *str)
Compute a hash value on a case-insensitive string.
Definition: strings.h:1303
#define AST_APP_ARG(name)
Define an application argument.