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