Asterisk - The Open Source Telephony Project  GIT-master-e8cda4b
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, "/") || strstr(var->value, "://")) {
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 a URL or an 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_ref(mohclass->files, -1);
1213  mohclass->files = playlist_entries;
1214  }
1215 }
1216 
1217 static int on_moh_file(const char *directory, const char *filename, void *obj)
1218 {
1219  struct ast_vector_string *files = obj;
1220  char *full_path;
1221  char *extension;
1222 
1223  /* Skip files that starts with a dot */
1224  if (*filename == '.') {
1225  ast_debug(4, "Skipping '%s/%s' because it starts with a dot\n",
1226  directory, filename);
1227  return 0;
1228  }
1229 
1230  /* We can't do anything with files that don't have an extension,
1231  * so check that first and punt if we can't find something */
1232  extension = strrchr(filename, '.');
1233  if (!extension) {
1234  ast_debug(4, "Skipping '%s/%s' because it doesn't have an extension\n",
1235  directory, filename);
1236  return 0;
1237  }
1238 
1239  /* The extension needs at least two characters (after the .) to be useful */
1240  if (strlen(extension) < 3) {
1241  ast_debug(4, "Skipping '%s/%s' because it doesn't have at least a two "
1242  "character extension\n", directory, filename);
1243  return 0;
1244  }
1245 
1246  /* Build the full path (excluding the extension) */
1247  if (ast_asprintf(&full_path, "%s/%.*s",
1248  directory,
1249  (int) (extension - filename), filename) < 0) {
1250  /* If we don't have enough memory to build this path, there is no
1251  * point in continuing */
1252  return 1;
1253  }
1254 
1255  /* If the file is present in multiple formats, ensure we only put it
1256  * into the list once. Pretty sure this is O(n^2). */
1257  if (AST_VECTOR_GET_CMP(files, &full_path[0], !strcmp)) {
1258  ast_free(full_path);
1259  return 0;
1260  }
1261 
1262  if (AST_VECTOR_APPEND(files, full_path)) {
1263  /* AST_VECTOR_APPEND() can only fail on allocation failure, so
1264  * we stop iterating */
1265  ast_free(full_path);
1266  return 1;
1267  }
1268 
1269  return 0;
1270 }
1271 
1272 static int moh_filename_strcasecmp(const void *a, const void *b)
1273 {
1274  const char **s1 = (const char **) a;
1275  const char **s2 = (const char **) b;
1276  return strcasecmp(*s1, *s2);
1277 }
1278 
1279 static int moh_scan_files(struct mohclass *class) {
1280 
1281  char dir_path[PATH_MAX - sizeof(class->dir)];
1282  struct ast_vector_string *files;
1283 
1284  if (class->dir[0] != '/') {
1285  snprintf(dir_path, sizeof(dir_path), "%s/%s", ast_config_AST_DATA_DIR, class->dir);
1286  } else {
1287  ast_copy_string(dir_path, class->dir, sizeof(dir_path));
1288  }
1289 
1290  ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
1291 
1292  /* 16 seems like a reasonable default */
1293  files = moh_file_vector_alloc(16);
1294  if (!files) {
1295  return -1;
1296  }
1297 
1298  if (ast_file_read_dir(dir_path, on_moh_file, files)) {
1299  ao2_ref(files, -1);
1300  return -1;
1301  }
1302 
1303  if (ast_test_flag(class, MOH_SORTALPHA)) {
1305  }
1306 
1307  AST_VECTOR_COMPACT(files);
1308 
1309  ao2_lock(class);
1310  ao2_ref(class->files, -1);
1311  class->files = files;
1312  ao2_unlock(class);
1313 
1314  return AST_VECTOR_SIZE(files);
1315 }
1316 
1317 static int init_files_class(struct mohclass *class)
1318 {
1319  int res;
1320 
1321  res = moh_scan_files(class);
1322 
1323  if (res < 0) {
1324  return -1;
1325  }
1326 
1327  if (!res) {
1328  ast_verb(3, "Files not found in %s for moh class:%s\n",
1329  class->dir, class->name);
1330  return -1;
1331  }
1332 
1333  return 0;
1334 }
1335 
1336 static void moh_rescan_files(void) {
1337  struct ao2_iterator i;
1338  struct mohclass *c;
1339 
1340  i = ao2_iterator_init(mohclasses, 0);
1341 
1342  while ((c = ao2_iterator_next(&i))) {
1343  if (!strcasecmp(c->mode, "files")) {
1344  moh_scan_files(c);
1345  }
1346  ao2_ref(c, -1);
1347  }
1348 
1350 }
1351 
1352 static int moh_diff(struct mohclass *old, struct mohclass *new)
1353 {
1354  if (!old || !new) {
1355  return -1;
1356  }
1357 
1358  if (strcmp(old->dir, new->dir)) {
1359  return -1;
1360  } else if (strcmp(old->mode, new->mode)) {
1361  return -1;
1362  } else if (strcmp(old->args, new->args)) {
1363  return -1;
1364  } else if (old->flags != new->flags) {
1365  return -1;
1366  }
1367 
1368  return 0;
1369 }
1370 
1371 static int init_app_class(struct mohclass *class)
1372 {
1373  if (!strcasecmp(class->mode, "custom")) {
1374  ast_set_flag(class, MOH_CUSTOM);
1375  } else if (!strcasecmp(class->mode, "mp3nb")) {
1376  ast_set_flag(class, MOH_SINGLE);
1377  } else if (!strcasecmp(class->mode, "quietmp3nb")) {
1378  ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
1379  } else if (!strcasecmp(class->mode, "quietmp3")) {
1380  ast_set_flag(class, MOH_QUIET);
1381  }
1382 
1383  class->srcfd = -1;
1384 
1385  if (!(class->timer = ast_timer_open())) {
1386  ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1387  return -1;
1388  }
1389  if (class->timer && ast_timer_set_rate(class->timer, 25)) {
1390  ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1392  class->timer = NULL;
1393  }
1394 
1396  ast_log(LOG_WARNING, "Unable to create moh thread...\n");
1397  if (class->timer) {
1399  class->timer = NULL;
1400  }
1401  return -1;
1402  }
1403 
1404  return 0;
1405 }
1406 
1407 /*!
1408  * \note This function owns the reference it gets to moh if unref is true
1409  */
1410 #define moh_register(moh, reload, unref) _moh_register(moh, reload, unref, __FILE__, __LINE__, __PRETTY_FUNCTION__)
1411 static int _moh_register(struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
1412 {
1413  struct mohclass *mohclass = NULL;
1414 
1415  mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname);
1416 
1417  if (mohclass && !moh_diff(mohclass, moh)) {
1418  ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
1419  mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1420  if (unref) {
1421  moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
1422  }
1423  return -1;
1424  } else if (mohclass) {
1425  /* Found a class, but it's different from the one being registered */
1426  mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1427  }
1428 
1429  time(&moh->start);
1430  moh->start -= respawn_time;
1431 
1432  if (!strcasecmp(moh->mode, "files")) {
1433  if (init_files_class(moh)) {
1434  if (unref) {
1435  moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
1436  }
1437  return -1;
1438  }
1439  } else if (!strcasecmp(moh->mode, "playlist")) {
1440  size_t file_count;
1441 
1442  ao2_lock(moh);
1443  file_count = AST_VECTOR_SIZE(moh->files);
1444  ao2_unlock(moh);
1445 
1446  if (!file_count) {
1447  if (unref) {
1448  moh = mohclass_unref(moh, "unreffing potential new moh class (no playlist entries)");
1449  }
1450  return -1;
1451  }
1452  } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") ||
1453  !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
1454  !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
1455  if (init_app_class(moh)) {
1456  if (unref) {
1457  moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
1458  }
1459  return -1;
1460  }
1461  } else {
1462  ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
1463  if (unref) {
1464  moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
1465  }
1466  return -1;
1467  }
1468 
1469  ao2_t_link(mohclasses, moh, "Adding class to container");
1470 
1471  if (unref) {
1472  moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
1473  }
1474 
1475  return 0;
1476 }
1477 
1478 #define moh_unregister(a) _moh_unregister(a,__FILE__,__LINE__,__PRETTY_FUNCTION__)
1479 static int _moh_unregister(struct mohclass *moh, const char *file, int line, const char *funcname)
1480 {
1481  ao2_t_unlink(mohclasses, moh, "Removing class from container");
1482  return 0;
1483 }
1484 
1485 static void local_ast_moh_cleanup(struct ast_channel *chan)
1486 {
1488 
1489  if (state) {
1491  if (state->class) {
1492  /* This should never happen. We likely just leaked some resource. */
1493  state->class =
1494  mohclass_unref(state->class, "Uh Oh. Cleaning up MOH with an active class");
1495  ast_log(LOG_WARNING, "Uh Oh. Cleaning up MOH with an active class\n");
1496  }
1497  ao2_cleanup(state->origwfmt);
1498  ao2_cleanup(state->mohwfmt);
1499  ast_free(state);
1500  /* Only held a module reference if we had a music state */
1502  }
1503 }
1504 
1505 /*! \brief Support routing for 'moh unregister class' CLI
1506  * This is in charge of generating all strings that match a prefix in the
1507  * given position. As many functions of this kind, each invokation has
1508  * O(state) time complexity so be careful in using it.
1509  */
1510 static char *complete_mohclass_realtime(const char *line, const char *word, int pos, int state)
1511 {
1512  int which=0;
1513  struct mohclass *cur;
1514  char *c = NULL;
1515  int wordlen = strlen(word);
1516  struct ao2_iterator i;
1517 
1518  if (pos != 3) {
1519  return NULL;
1520  }
1521 
1522  i = ao2_iterator_init(mohclasses, 0);
1523  while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1524  if (cur->realtime && !strncasecmp(cur->name, word, wordlen) && ++which > state) {
1525  c = ast_strdup(cur->name);
1526  mohclass_unref(cur, "drop ref in iterator loop break");
1527  break;
1528  }
1529  mohclass_unref(cur, "drop ref in iterator loop");
1530  }
1532 
1533  return c;
1534 }
1535 
1536 static char *handle_cli_moh_unregister_class(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1537 {
1538  struct mohclass *cur;
1539  int len;
1540  int found = 0;
1541  struct ao2_iterator i;
1542 
1543  switch (cmd) {
1544  case CLI_INIT:
1545  e->command = "moh unregister class";
1546  e->usage =
1547  "Usage: moh unregister class <class>\n"
1548  " Unregisters a realtime moh class.\n";
1549  return NULL;
1550  case CLI_GENERATE:
1551  return complete_mohclass_realtime(a->line, a->word, a->pos, a->n);
1552  }
1553 
1554  if (a->argc != 4)
1555  return CLI_SHOWUSAGE;
1556 
1557  len = strlen(a->argv[3]);
1558 
1559  i = ao2_iterator_init(mohclasses, 0);
1560  while ((cur = ao2_t_iterator_next(&i, "iterate thru mohclasses"))) {
1561  if (cur->realtime && len == strlen(cur->name) && !strncasecmp(cur->name, a->argv[3], len)) {
1562  found = 1;
1563  break;
1564  }
1565  mohclass_unref(cur, "drop ref in iterator loop");
1566  }
1568 
1569  if (found) {
1570  moh_unregister(cur);
1571  mohclass_unref(cur, "drop ref after unregister");
1572  } else {
1573  ast_cli(a->fd, "No such realtime moh class '%s'\n", a->argv[3]);
1574  }
1575 
1576  return CLI_SUCCESS;
1577 }
1578 
1579 
1580 
1581 static void moh_class_destructor(void *obj);
1582 
1583 #define moh_class_malloc() _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
1584 
1585 static struct mohclass *_moh_class_malloc(const char *file, int line, const char *funcname)
1586 {
1587  struct mohclass *class;
1588 
1590  "Allocating new moh class", file, line, funcname);
1591  if (class) {
1592  class->format = ao2_bump(ast_format_slin);
1593  class->srcfd = -1;
1594  class->kill_delay = 100000;
1595 
1596  /* We create an empty one by default */
1597  class->files = moh_file_vector_alloc(0);
1598  if (!class->files) {
1599  ao2_ref(class, -1);
1600  return NULL;
1601  }
1602  }
1603 
1604  return class;
1605 }
1606 
1607 static struct ast_variable *load_realtime_musiconhold(const char *name)
1608 {
1609  struct ast_variable *var = ast_load_realtime("musiconhold", "name", name, SENTINEL);
1610 
1611  if (var) {
1612  const char *mode = ast_variable_find_in_list(var, "mode");
1613  if (ast_strings_equal(mode, "playlist")) {
1614  struct ast_config *entries = ast_load_realtime_multientry("musiconhold_entry", "position >=", "0", "name", name, SENTINEL);
1615  char *category = NULL;
1616  size_t entry_count = 0;
1617 
1618  /* entries is NULL if there are no results */
1619  if (entries) {
1620  while ((category = ast_category_browse(entries, category))) {
1621  const char *entry = ast_variable_retrieve(entries, category, "entry");
1622 
1623  if (entry) {
1624  struct ast_variable *dup = ast_variable_new("entry", entry, "");
1625  if (dup) {
1626  entry_count++;
1627  ast_variable_list_append(&var, dup);
1628  }
1629  }
1630  }
1631  ast_config_destroy(entries);
1632  }
1633 
1634  if (entry_count == 0) {
1635  /* Behave as though this class doesn't exist */
1636  ast_variables_destroy(var);
1637  var = NULL;
1638  }
1639  }
1640  }
1641 
1642  if (!var) {
1644  "Music on Hold class '%s' not found in memory/database. "
1645  "Verify your configuration.\n",
1646  name);
1647  }
1648  return var;
1649 }
1650 
1651 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
1652 {
1653  struct mohclass *mohclass = NULL;
1655  struct ast_variable *var = NULL;
1656  int res = 0;
1657  int i;
1658  int realtime_possible = ast_check_realtime("musiconhold");
1659  int warn_if_not_in_memory = !realtime_possible;
1660  const char *classes[] = {NULL, NULL, interpclass, "default"};
1661 
1662  if (ast_test_flag(global_flags, MOH_PREFERCHANNELCLASS)) {
1663  classes[0] = ast_channel_musicclass(chan);
1664  classes[1] = mclass;
1665  } else {
1666  classes[0] = mclass;
1667  classes[1] = ast_channel_musicclass(chan);
1668  }
1669 
1670  /* The following is the order of preference for which class to use:
1671  * 1) The channels explicitly set musicclass, which should *only* be
1672  * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1673  * Unless preferchannelclass in musiconhold.conf is false
1674  * 2) The mclass argument. If a channel is calling ast_moh_start() as the
1675  * result of receiving a HOLD control frame, this should be the
1676  * payload that came with the frame.
1677  * 3) The channels explicitly set musicclass, which should *only* be
1678  * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1679  * 4) The interpclass argument. This would be from the mohinterpret
1680  * option from channel drivers. This is the same as the old musicclass
1681  * option.
1682  * 5) The default class.
1683  */
1684 
1685  for (i = 0; i < ARRAY_LEN(classes); ++i) {
1686  if (!ast_strlen_zero(classes[i])) {
1687  mohclass = get_mohbyname(classes[i], warn_if_not_in_memory, 0);
1688  if (!mohclass && realtime_possible) {
1689  var = load_realtime_musiconhold(classes[i]);
1690  }
1691  if (mohclass || var) {
1692  break;
1693  }
1694  }
1695  }
1696 
1697  /* If no moh class found in memory, then check RT. Note that the logic used
1698  * above guarantees that if var is non-NULL, then mohclass must be NULL.
1699  */
1700  if (var) {
1701  if ((mohclass = moh_class_malloc())) {
1702  mohclass->realtime = 1;
1703 
1704  moh_parse_options(var, mohclass);
1705  ast_variables_destroy(var);
1706 
1707  if (ast_strlen_zero(mohclass->dir)) {
1708  if (!strcasecmp(mohclass->mode, "custom") || !strcasecmp(mohclass->mode, "playlist")) {
1709  strcpy(mohclass->dir, "nodir");
1710  } else {
1711  ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
1712  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
1713  return -1;
1714  }
1715  }
1716  if (ast_strlen_zero(mohclass->mode)) {
1717  ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
1718  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
1719  return -1;
1720  }
1721  if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
1722  ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
1723  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
1724  return -1;
1725  }
1726 
1727  if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
1728  /* CACHERTCLASSES enabled, let's add this class to default tree */
1729  if (state && state->class) {
1730  /* Class already exist for this channel */
1731  ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1732  }
1733  /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
1734  * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
1735  * be that the destructor would be called when the generator on the channel is deactivated. The container then
1736  * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
1737  * invalid memory.
1738  */
1739  if (moh_register(mohclass, 0, DONT_UNREF) == -1) {
1740  mohclass = mohclass_unref(mohclass, "unreffing mohclass failed to register");
1741  return -1;
1742  }
1743  } else {
1744  /* We don't register RT moh class, so let's init it manualy */
1745 
1746  time(&mohclass->start);
1747  mohclass->start -= respawn_time;
1748 
1749  if (!strcasecmp(mohclass->mode, "files")) {
1750  /*
1751  * XXX moh_scan_files returns -1 if it is unable to open the
1752  * configured directory or there is a memory allocation
1753  * failure. Otherwise it returns the number of files for this music
1754  * class. This check is only checking if the number of files is zero
1755  * and it ignores the -1 case. To avoid a behavior change we keep this
1756  * as-is, but we should address what the 'correct' behavior should be.
1757  */
1758  if (!moh_scan_files(mohclass)) {
1759  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1760  return -1;
1761  }
1762  if (strchr(mohclass->args, 'r')) {
1763  static int deprecation_warning = 0;
1764  if (!deprecation_warning) {
1765  ast_log(LOG_WARNING, "Music on hold 'application=r' setting is deprecated in 14. Please use 'sort=random' instead.\n");
1766  deprecation_warning = 1;
1767  }
1768  ast_set_flag(mohclass, MOH_RANDOMIZE);
1769  }
1770  } else if (!strcasecmp(mohclass->mode, "playlist")) {
1771  size_t file_count;
1772 
1773  ao2_lock(mohclass);
1774  file_count = AST_VECTOR_SIZE(mohclass->files);
1775  ao2_unlock(mohclass);
1776 
1777  if (!file_count) {
1778  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no playlist entries)");
1779  return -1;
1780  }
1781  } 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")) {
1782 
1783  if (!strcasecmp(mohclass->mode, "custom"))
1784  ast_set_flag(mohclass, MOH_CUSTOM);
1785  else if (!strcasecmp(mohclass->mode, "mp3nb"))
1786  ast_set_flag(mohclass, MOH_SINGLE);
1787  else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1788  ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
1789  else if (!strcasecmp(mohclass->mode, "quietmp3"))
1790  ast_set_flag(mohclass, MOH_QUIET);
1791 
1792  mohclass->srcfd = -1;
1793  if (!(mohclass->timer = ast_timer_open())) {
1794  ast_log(LOG_WARNING, "Unable to create timer: %s\n", strerror(errno));
1795  }
1796  if (mohclass->timer && ast_timer_set_rate(mohclass->timer, 25)) {
1797  ast_log(LOG_WARNING, "Unable to set 40ms frame rate: %s\n", strerror(errno));
1798  ast_timer_close(mohclass->timer);
1799  mohclass->timer = NULL;
1800  }
1801 
1802  /* Let's check if this channel already had a moh class before */
1803  if (state && state->class) {
1804  /* Class already exist for this channel */
1805  ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1806  if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1807  /* we found RT class with the same name, seems like we should continue playing existing one */
1808  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
1809  mohclass = mohclass_ref(state->class, "using existing class from state");
1810  }
1811  } else {
1812  if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
1813  ast_log(LOG_WARNING, "Unable to create moh...\n");
1814  if (mohclass->timer) {
1815  ast_timer_close(mohclass->timer);
1816  mohclass->timer = NULL;
1817  }
1818  mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
1819  return -1;
1820  }
1821  }
1822  } else {
1823  ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
1824  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
1825  return -1;
1826  }
1827  }
1828  } else {
1829  ast_variables_destroy(var);
1830  var = NULL;
1831  }
1832  }
1833 
1834  if (!mohclass) {
1835  return -1;
1836  }
1837 
1838  /* If we are using a cached realtime class with files, re-scan the files */
1839  if (!var && ast_test_flag(global_flags, MOH_CACHERTCLASSES) && mohclass->realtime && !strcasecmp(mohclass->mode, "files")) {
1840  /*
1841  * XXX moh_scan_files returns -1 if it is unable to open the configured directory
1842  * or there is a memory allocation failure. Otherwise it returns the number of
1843  * files for this music class. This check is only checking if the number of files
1844  * is zero and it ignores the -1 case. To avoid a behavior change we keep this
1845  * as-is, but we should address what the 'correct' behavior should be.
1846  */
1847  if (!moh_scan_files(mohclass)) {
1848  mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1849  return -1;
1850  }
1851  }
1852 
1853  if (!state || !state->class || strcmp(mohclass->name, state->class->name)) {
1854  size_t file_count;
1855 
1856  ao2_lock(mohclass);
1857  file_count = AST_VECTOR_SIZE(mohclass->files);
1858  ao2_unlock(mohclass);
1859 
1860  if (file_count) {
1861  res = ast_activate_generator(chan, &moh_file_stream, mohclass);
1862  } else {
1863  res = ast_activate_generator(chan, &mohgen, mohclass);
1864  }
1865  }
1866  if (!res) {
1867  ast_channel_lock(chan);
1868  ast_channel_latest_musicclass_set(chan, mohclass->name);
1870  ast_channel_unlock(chan);
1871  }
1872 
1873  mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
1874 
1875  return res;
1876 }
1877 
1878 static void local_ast_moh_stop(struct ast_channel *chan)
1879 {
1881 
1882  ast_channel_lock(chan);
1884  if (ast_channel_music_state(chan)) {
1885  if (ast_channel_stream(chan)) {
1888  }
1889  }
1890  ast_channel_unlock(chan);
1891 }
1892 
1893 static void moh_class_destructor(void *obj)
1894 {
1895  struct mohclass *class = obj;
1896  struct mohdata *member;
1897  pthread_t tid = 0;
1898 
1899  ast_debug(1, "Destroying MOH class '%s'\n", class->name);
1900 
1901  ao2_lock(class);
1902  while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
1903  ast_free(member);
1904  }
1906  ao2_unlock(class);
1907 
1908  /* Kill the thread first, so it cannot restart the child process while the
1909  * class is being destroyed */
1910  if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
1911  tid = class->thread;
1912  class->thread = AST_PTHREADT_NULL;
1913  pthread_cancel(tid);
1914  /* We'll collect the exit status later, after we ensure all the readers
1915  * are dead. */
1916  }
1917 
1918  if (class->pid > 1) {
1919  char buff[8192];
1920  int bytes, tbytes = 0, stime = 0;
1921 
1922  ast_debug(1, "killing %d!\n", class->pid);
1923 
1924  stime = time(NULL) + 2;
1926 
1927  while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
1928  (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
1929  tbytes = tbytes + bytes;
1930  }
1931 
1932  ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n",
1933  class->pid, tbytes);
1934 
1935  class->pid = 0;
1936  close(class->srcfd);
1937  class->srcfd = -1;
1938  }
1939 
1940  if (class->timer) {
1942  class->timer = NULL;
1943  }
1944 
1946 
1947  /* Finally, collect the exit status of the monitor thread */
1948  if (tid > 0) {
1949  pthread_join(tid, NULL);
1950  }
1951 
1952 }
1953 
1954 static int moh_class_mark(void *obj, void *arg, int flags)
1955 {
1956  struct mohclass *class = obj;
1957 
1958  if ( ((flags & MOH_REALTIME) && class->realtime) || !(flags & MOH_REALTIME) ) {
1959  class->delete = 1;
1960  }
1961 
1962  return 0;
1963 }
1964 
1965 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
1966 {
1967  struct mohclass *class = obj;
1968 
1969  return class->delete ? CMP_MATCH : 0;
1970 }
1971 
1972 static int load_moh_classes(int reload)
1973 {
1974  struct ast_config *cfg;
1975  struct ast_variable *var;
1976  struct mohclass *class;
1977  char *cat;
1978  int numclasses = 0;
1979  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1980 
1981  cfg = ast_config_load("musiconhold.conf", config_flags);
1982 
1983  if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
1984  if (ast_check_realtime("musiconhold") && reload) {
1985  ao2_t_callback(mohclasses, OBJ_NODATA | MOH_REALTIME, moh_class_mark, NULL, "Mark realtime classes for deletion");
1986  ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
1987  }
1988  moh_rescan_files();
1989  return 0;
1990  }
1991 
1993  if (ast_check_realtime("musiconhold") && reload) {
1994  ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
1995  ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
1996  }
1997  return 0;
1998  }
1999 
2000  if (reload) {
2001  ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
2002  }
2003 
2004  ast_clear_flag(global_flags, AST_FLAGS_ALL);
2005  ast_set2_flag(global_flags, 1, MOH_PREFERCHANNELCLASS);
2006 
2007  cat = ast_category_browse(cfg, NULL);
2008  for (; cat; cat = ast_category_browse(cfg, cat)) {
2009  /* Setup common options from [general] section */
2010  if (!strcasecmp(cat, "general")) {
2011  for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
2012  if (!strcasecmp(var->name, "cachertclasses")) {
2013  ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
2014  } else if (!strcasecmp(var->name, "preferchannelclass")) {
2015  ast_set2_flag(global_flags, ast_true(var->value), MOH_PREFERCHANNELCLASS);
2016  } else {
2017  ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
2018  }
2019  }
2020  continue;
2021  }
2022 
2023  if (!(class = moh_class_malloc())) {
2024  break;
2025  }
2026 
2027  moh_parse_options(ast_variable_browse(cfg, cat), class);
2028  /* For compatibility with the past, we overwrite any name=name
2029  * with the context [name]. */
2030  ast_copy_string(class->name, cat, sizeof(class->name));
2031 
2032  if (ast_strlen_zero(class->dir)) {
2033  if (!strcasecmp(class->mode, "custom") || !strcasecmp(class->mode, "playlist")) {
2034  strcpy(class->dir, "nodir");
2035  } else {
2036  ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
2037  class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
2038  continue;
2039  }
2040  }
2041  if (ast_strlen_zero(class->mode)) {
2042  ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
2043  class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
2044  continue;
2045  }
2046  if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
2047  ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
2048  class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
2049  continue;
2050  }
2051 
2052  /* Don't leak a class when it's already registered */
2053  if (!moh_register(class, reload, HANDLE_REF)) {
2054  numclasses++;
2055  }
2056  }
2057 
2058  ast_config_destroy(cfg);
2059 
2061  moh_classes_delete_marked, NULL, "Purge marked classes");
2062 
2063  return numclasses;
2064 }
2065 
2066 static void ast_moh_destroy(void)
2067 {
2068  ast_verb(2, "Destroying musiconhold processes\n");
2069  if (mohclasses) {
2070  ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
2071  ao2_ref(mohclasses, -1);
2072  mohclasses = NULL;
2073  }
2074 }
2075 
2076 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2077 {
2078  switch (cmd) {
2079  case CLI_INIT:
2080  e->command = "moh reload";
2081  e->usage =
2082  "Usage: moh reload\n"
2083  " Reloads the MusicOnHold module.\n"
2084  " Alias for 'module reload res_musiconhold.so'\n";
2085  return NULL;
2086  case CLI_GENERATE:
2087  return NULL;
2088  }
2089 
2090  if (a->argc != e->args)
2091  return CLI_SHOWUSAGE;
2092 
2093  /* The module loader will prevent concurrent reloads from occurring, so we delegate */
2094  ast_module_reload("res_musiconhold");
2095 
2096  return CLI_SUCCESS;
2097 }
2098 
2099 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2100 {
2101  struct mohclass *class;
2102  struct ao2_iterator i;
2103 
2104  switch (cmd) {
2105  case CLI_INIT:
2106  e->command = "moh show files";
2107  e->usage =
2108  "Usage: moh show files\n"
2109  " Lists all loaded file-based MusicOnHold classes and their\n"
2110  " files.\n";
2111  return NULL;
2112  case CLI_GENERATE:
2113  return NULL;
2114  }
2115 
2116  if (a->argc != e->args)
2117  return CLI_SHOWUSAGE;
2118 
2119  i = ao2_iterator_init(mohclasses, 0);
2120  for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
2121  struct ast_vector_string *files;
2122 
2123  ao2_lock(class);
2124  files = ao2_bump(class->files);
2125  ao2_unlock(class);
2126 
2127  if (AST_VECTOR_SIZE(files)) {
2128  int x;
2129  ast_cli(a->fd, "Class: %s\n", class->name);
2130  for (x = 0; x < AST_VECTOR_SIZE(files); x++) {
2131  ast_cli(a->fd, "\tFile: %s\n", AST_VECTOR_GET(files, x));
2132  }
2133  }
2134 
2135  ao2_ref(files, -1);
2136  }
2138 
2139  return CLI_SUCCESS;
2140 }
2141 
2142 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2143 {
2144  struct mohclass *class;
2145  struct ao2_iterator i;
2146 
2147  switch (cmd) {
2148  case CLI_INIT:
2149  e->command = "moh show classes";
2150  e->usage =
2151  "Usage: moh show classes\n"
2152  " Lists all MusicOnHold classes.\n";
2153  return NULL;
2154  case CLI_GENERATE:
2155  return NULL;
2156  }
2157 
2158  if (a->argc != e->args)
2159  return CLI_SHOWUSAGE;
2160 
2161  i = ao2_iterator_init(mohclasses, 0);
2162  for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
2163  ast_cli(a->fd, "Class: %s\n", class->name);
2164  ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
2165  ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
2166  if (ast_test_flag(class, MOH_ANNOUNCEMENT)) {
2167  ast_cli(a->fd, "\tAnnouncement: %s\n", S_OR(class->announcement, "<none>"));
2168  }
2169  if (ast_test_flag(class, MOH_CUSTOM)) {
2170  ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
2171  ast_cli(a->fd, "\tKill Escalation Delay: %zu ms\n", class->kill_delay / 1000);
2172  ast_cli(a->fd, "\tKill Method: %s\n",
2173  class->kill_method == KILL_METHOD_PROCESS ? "process" : "process_group");
2174  }
2175  if (strcasecmp(class->mode, "files")) {
2176  ast_cli(a->fd, "\tFormat: %s\n", ast_format_get_name(class->format));
2177  }
2178  }
2180 
2181  return CLI_SUCCESS;
2182 }
2183 
2184 static struct ast_cli_entry cli_moh[] = {
2185  AST_CLI_DEFINE(handle_cli_moh_reload, "Reload MusicOnHold"),
2186  AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
2187  AST_CLI_DEFINE(handle_cli_moh_show_files, "List MusicOnHold file-based classes"),
2188  AST_CLI_DEFINE(handle_cli_moh_unregister_class, "Unregister realtime MusicOnHold class")
2189 };
2190 
2191 static int moh_class_hash(const void *obj, const int flags)
2192 {
2193  const struct mohclass *class = obj;
2194 
2195  return ast_str_case_hash(class->name);
2196 }
2197 
2198 static int moh_class_cmp(void *obj, void *arg, int flags)
2199 {
2200  struct mohclass *class = obj, *class2 = arg;
2201 
2202  return strcasecmp(class->name, class2->name) ? 0 :
2203  (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
2204  CMP_MATCH | CMP_STOP;
2205 }
2206 
2207 /*!
2208  * \brief Load the module
2209  *
2210  * Module loading including tests for configuration or dependencies.
2211  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
2212  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
2213  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
2214  * configuration file or other non-critical problem return
2215  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
2216  */
2217 static int load_module(void)
2218 {
2219  int res;
2220 
2222  moh_class_hash, NULL, moh_class_cmp, "Moh class container");
2223  if (!mohclasses) {
2224  return AST_MODULE_LOAD_DECLINE;
2225  }
2226 
2227  if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) { /* No music classes configured, so skip it */
2228  ast_log(LOG_WARNING, "No music on hold classes configured, "
2229  "disabling music on hold.\n");
2230  } else {
2233  }
2234 
2237  ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
2238  if (!res)
2240  if (!res)
2242 
2243  return AST_MODULE_LOAD_SUCCESS;
2244 }
2245 
2246 static int reload(void)
2247 {
2248  if (load_moh_classes(1)) {
2251  }
2252 
2253  return AST_MODULE_LOAD_SUCCESS;
2254 }
2255 
2256 static int moh_class_inuse(void *obj, void *arg, int flags)
2257 {
2258  struct mohclass *class = obj;
2259 
2260  return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
2261 }
2262 
2263 static int unload_module(void)
2264 {
2265  int res = 0;
2266  struct mohclass *class = NULL;
2267 
2268  /* XXX This check shouldn't be required if module ref counting was being used
2269  * properly ... */
2270  if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
2271  class = mohclass_unref(class, "unref of class from module unload callback");
2272  res = -1;
2273  }
2274 
2275  if (res < 0) {
2276  ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
2277  return res;
2278  }
2279 
2281 
2282  ast_moh_destroy();
2286  ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
2288 
2289  return res;
2290 }
2291 
2292 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Music On Hold Resource",
2293  .support_level = AST_MODULE_SUPPORT_CORE,
2294  .load = load_module,
2295  .unload = unload_module,
2296  .reload = reload,
2297  .load_pri = AST_MODPRI_CHANNEL_DEPEND,
2298 );
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:1570
enum sip_cc_notify_state state
Definition: chan_sip.c:960
char digit
#define ast_channel_lock(chan)
Definition: channel.h:2913
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:1060
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:2956
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:1562
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:710
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:7793
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:7776
#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:281
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:567
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:2064
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:5807
#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:158
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:1951
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:7783
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:2914
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:1511
#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:7767
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:5106
#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:2898
#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:1519
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