Merged revisions 260345 via svnmerge from
[asterisk/asterisk.git] / res / res_musiconhold.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, 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  * \arg See also \ref Config_moh
24  * 
25  * \author Mark Spencer <markster@digium.com>
26  */
27
28 /*** MODULEINFO
29         <conflict>win32</conflict>
30         <use>dahdi</use>
31  ***/
32
33 #include "asterisk.h"
34
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36
37 #include <ctype.h>
38 #include <signal.h>
39 #include <sys/time.h>
40 #include <sys/signal.h>
41 #include <netinet/in.h>
42 #include <sys/stat.h>
43 #include <dirent.h>
44 #include <sys/ioctl.h>
45
46 #ifdef SOLARIS
47 #include <thread.h>
48 #endif
49
50 #ifdef HAVE_DAHDI
51 #include <dahdi/user.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/manager.h"
69 #include "asterisk/paths.h"
70 #include "asterisk/astobj2.h"
71
72 #define INITIAL_NUM_FILES   8
73 #define HANDLE_REF      1
74 #define DONT_UNREF      0
75
76 /*** DOCUMENTATION
77         <application name="MusicOnHold" language="en_US">
78                 <synopsis>
79                         Play Music On Hold indefinitely.
80                 </synopsis>
81                 <syntax>
82                         <parameter name="class" required="true" />
83                         <parameter name="duration" />
84                 </syntax>
85                 <description>
86                         <para>Plays hold music specified by class. If omitted, the default music
87                         source for the channel will be used. Change the default class with
88                         Set(CHANNEL(musicclass)=...). If duration is given, hold music will be played
89                         specified number of seconds. If duration is ommited, music plays indefinitely.
90                         Returns <literal>0</literal> when done, <literal>-1</literal> on hangup.</para>
91                 </description>
92         </application>
93         <application name="WaitMusicOnHold" language="en_US">
94                 <synopsis>
95                         Wait, playing Music On Hold.
96                 </synopsis>
97                 <syntax>
98                         <parameter name="delay" required="true" />
99                 </syntax>
100                 <description>
101                         <para> !!! DEPRECATED. Use MusicOnHold instead !!!</para>
102                         <para>Plays hold music specified number of seconds. Returns <literal>0</literal> when done,
103                         or <literal>-1</literal> on hangup. If no hold music is available, the delay will still occur
104                         with no sound.</para>
105                         <para> !!! DEPRECATED. Use MusicOnHold instead !!!</para>
106                 </description>
107         </application>
108         <application name="SetMusicOnHold" language="en_US">
109                 <synopsis>
110                         Set default Music On Hold class.
111                 </synopsis>
112                 <syntax>
113                         <parameter name="class" required="yes" />
114                 </syntax>
115                 <description>
116                         <para>!!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!</para>
117                         <para>Sets the default class for music on hold for a given channel.
118                         When music on hold is activated, this class will be used to select which
119                         music is played.</para>
120                         <para>!!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!</para>
121                 </description>
122         </application>
123         <application name="StartMusicOnHold" language="en_US">
124                 <synopsis>
125                         Play Music On Hold.
126                 </synopsis>
127                 <syntax>
128                         <parameter name="class" required="true" />
129                 </syntax>
130                 <description>
131                         <para>Starts playing music on hold, uses default music class for channel.
132                         Starts playing music specified by class. If omitted, the default music
133                         source for the channel will be used. Always returns <literal>0</literal>.</para>
134                 </description>
135         </application>
136         <application name="StopMusicOnHold" language="en_US">
137                 <synopsis>
138                         Stop playing Music On Hold.
139                 </synopsis>
140                 <syntax />
141                 <description>
142                         <para>Stops playing music on hold.</para>
143                 </description>
144         </application>
145  ***/
146
147 static const char play_moh[] = "MusicOnHold";
148 static const char wait_moh[] = "WaitMusicOnHold";
149 static const char set_moh[] = "SetMusicOnHold";
150 static const char start_moh[] = "StartMusicOnHold";
151 static const char stop_moh[] = "StopMusicOnHold";
152
153 static int respawn_time = 20;
154
155 struct moh_files_state {
156         struct mohclass *class;
157         char name[MAX_MUSICCLASS];
158         format_t origwfmt;
159         int samples;
160         int sample_queue;
161         int pos;
162         int save_pos;
163         int save_total;
164         char *save_pos_filename;
165 };
166
167 #define MOH_QUIET               (1 << 0)
168 #define MOH_SINGLE              (1 << 1)
169 #define MOH_CUSTOM              (1 << 2)
170 #define MOH_RANDOMIZE           (1 << 3)
171 #define MOH_SORTALPHA           (1 << 4)
172
173 #define MOH_CACHERTCLASSES      (1 << 5)        /*!< Should we use a separate instance of MOH for each user or not */
174
175 /* Custom astobj2 flag */
176 #define MOH_NOTDELETED          (1 << 30)       /*!< Find only records that aren't deleted? */
177
178 static struct ast_flags global_flags[1] = {{0}};        /*!< global MOH_ flags */
179
180 struct mohclass {
181         char name[MAX_MUSICCLASS];
182         char dir[256];
183         char args[256];
184         char mode[80];
185         char digit;
186         /*! A dynamically sized array to hold the list of filenames in "files" mode */
187         char **filearray;
188         /*! The current size of the filearray */
189         int allowed_files;
190         /*! The current number of files loaded into the filearray */
191         int total_files;
192         unsigned int flags;
193         /*! The format from the MOH source, not applicable to "files" mode */
194         format_t format;
195         /*! The pid of the external application delivering MOH */
196         int pid;
197         time_t start;
198         pthread_t thread;
199         /*! Source of audio */
200         int srcfd;
201         /*! FD for timing source */
202         int pseudofd;
203         /*! Created on the fly, from RT engine */
204         int realtime;
205         unsigned int delete:1;
206         AST_LIST_HEAD_NOLOCK(, mohdata) members;
207         AST_LIST_ENTRY(mohclass) list;
208 };
209
210 struct mohdata {
211         int pipe[2];
212         format_t origwfmt;
213         struct mohclass *parent;
214         struct ast_frame f;
215         AST_LIST_ENTRY(mohdata) list;
216 };
217
218 static struct ao2_container *mohclasses;
219
220 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
221 #define MPG_123 "/usr/bin/mpg123"
222 #define MAX_MP3S 256
223
224 static int reload(void);
225
226 #define mohclass_ref(class,string)   (ao2_t_ref((class), +1, (string)), class)
227
228 #ifndef REF_DEBUG
229 #define mohclass_unref(class,string) (ao2_t_ref((class), -1, (string)), (struct mohclass *) NULL)
230 #else
231 #define mohclass_unref(class,string) _mohclass_unref(class, string, __FILE__,__LINE__,__PRETTY_FUNCTION__)
232 static struct mohclass *_mohclass_unref(struct mohclass *class, const char *tag, const char *file, int line, const char *funcname)
233 {
234         struct mohclass *dup;
235         if ((dup = ao2_find(mohclasses, class, OBJ_POINTER))) {
236                 if (_ao2_ref_debug(dup, -1, (char *) tag, (char *) file, line, funcname) == 2) {
237                         FILE *ref = fopen("/tmp/refs", "a");
238                         if (ref) {
239                                 fprintf(ref, "%p =1   %s:%d:%s (%s) BAD ATTEMPT!\n", class, file, line, funcname, tag);
240                                 fclose(ref);
241                         }
242                         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",
243                                 class, class->name, file, line, funcname);
244                 } else {
245                         ao2_ref(class, -1);
246                 }
247         } else {
248                 ao2_t_ref(class, -1, (char *) tag);
249         }
250         return NULL;
251 }
252 #endif
253
254 static void moh_files_release(struct ast_channel *chan, void *data)
255 {
256         struct moh_files_state *state;
257
258         if (!chan || !chan->music_state) {
259                 return;
260         }
261
262         state = chan->music_state;
263
264         if (chan->stream) {
265                 ast_closestream(chan->stream);
266                 chan->stream = NULL;
267         }
268         
269         if (option_verbose > 2) {
270                 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
271         }
272
273         if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
274                 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", chan->name, ast_getformatname(state->origwfmt));
275         }
276
277         state->save_pos = state->pos;
278
279         state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
280 }
281
282 static int ast_moh_files_next(struct ast_channel *chan) 
283 {
284         struct moh_files_state *state = chan->music_state;
285         int tries;
286
287         /* Discontinue a stream if it is running already */
288         if (chan->stream) {
289                 ast_closestream(chan->stream);
290                 chan->stream = NULL;
291         }
292
293         if (!state->class->total_files) {
294                 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
295                 return -1;
296         }
297
298         /* If a specific file has been saved confirm it still exists and that it is still valid */
299         if (state->save_pos >= 0 && state->save_pos < state->class->total_files && state->class->filearray[state->save_pos] == state->save_pos_filename) {
300                 state->pos = state->save_pos;
301                 state->save_pos = -1;
302         } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
303                 /* Get a random file and ensure we can open it */
304                 for (tries = 0; tries < 20; tries++) {
305                         state->pos = ast_random() % state->class->total_files;
306                         if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
307                                 break;
308                 }
309                 state->save_pos = -1;
310                 state->samples = 0;
311         } else {
312                 /* This is easy, just increment our position and make sure we don't exceed the total file count */
313                 state->pos++;
314                 state->pos %= state->class->total_files;
315                 state->save_pos = -1;
316                 state->samples = 0;
317         }
318
319         if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
320                 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
321                 state->pos++;
322                 state->pos %= state->class->total_files;
323                 return -1;
324         }
325
326         /* Record the pointer to the filename for position resuming later */
327         state->save_pos_filename = state->class->filearray[state->pos];
328
329         ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
330
331         if (state->samples)
332                 ast_seekstream(chan->stream, state->samples, SEEK_SET);
333
334         return 0;
335 }
336
337 static struct ast_frame *moh_files_readframe(struct ast_channel *chan) 
338 {
339         struct ast_frame *f = NULL;
340         
341         if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
342                 if (!ast_moh_files_next(chan))
343                         f = ast_readframe(chan->stream);
344         }
345
346         return f;
347 }
348
349 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
350 {
351         struct moh_files_state *state = chan->music_state;
352         struct ast_frame *f = NULL;
353         int res = 0;
354
355         state->sample_queue += samples;
356
357         while (state->sample_queue > 0) {
358                 ast_channel_lock(chan);
359                 if ((f = moh_files_readframe(chan))) {
360                         /* We need to be sure that we unlock
361                          * the channel prior to calling
362                          * ast_write. Otherwise, the recursive locking
363                          * that occurs can cause deadlocks when using
364                          * indirect channels, like local channels
365                          */
366                         ast_channel_unlock(chan);
367                         state->samples += f->samples;
368                         state->sample_queue -= f->samples;
369                         res = ast_write(chan, f);
370                         ast_frfree(f);
371                         if (res < 0) {
372                                 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
373                                 return -1;
374                         }
375                 } else {
376                         ast_channel_unlock(chan);
377                         return -1;      
378                 }
379         }
380         return res;
381 }
382
383 static void *moh_files_alloc(struct ast_channel *chan, void *params)
384 {
385         struct moh_files_state *state;
386         struct mohclass *class = params;
387
388         if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
389                 chan->music_state = state;
390                 ast_module_ref(ast_module_info->self);
391         } else {
392                 state = chan->music_state;
393         }
394
395         if (!state) {
396                 return NULL;
397         }
398
399         /* LOGIC: Comparing an unrefcounted pointer is a really bad idea, because
400          * malloc may allocate a different class to the same memory block.  This
401          * might only happen when two reloads are generated in a short period of
402          * time, but it's still important to protect against.
403          * PROG: Compare the quick operation first, to save CPU. */
404         if (state->save_total != class->total_files || strcmp(state->name, class->name) != 0) {
405                 memset(state, 0, sizeof(*state));
406                 if (ast_test_flag(class, MOH_RANDOMIZE) && class->total_files) {
407                         state->pos = ast_random() % class->total_files;
408                 }
409         }
410
411         state->class = mohclass_ref(class, "Reffing music class for channel");
412         state->origwfmt = chan->writeformat;
413         /* For comparison on restart of MOH (see above) */
414         ast_copy_string(state->name, class->name, sizeof(state->name));
415         state->save_total = class->total_files;
416
417         ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
418         
419         return chan->music_state;
420 }
421
422 static int moh_digit_match(void *obj, void *arg, int flags)
423 {
424         char *digit = arg;
425         struct mohclass *class = obj;
426
427         return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
428 }
429
430 /*! \note This function should be called with the mohclasses list locked */
431 static struct mohclass *get_mohbydigit(char digit)
432 {
433         return ao2_t_callback(mohclasses, 0, moh_digit_match, &digit, "digit callback");
434 }
435
436 static void moh_handle_digit(struct ast_channel *chan, char digit)
437 {
438         struct mohclass *class;
439         const char *classname = NULL;
440
441         if ((class = get_mohbydigit(digit))) {
442                 classname = ast_strdupa(class->name);
443                 class = mohclass_unref(class, "Unreffing ao2_find from finding by digit");
444                 ast_string_field_set(chan,musicclass,classname);
445                 ast_moh_stop(chan);
446                 ast_moh_start(chan, classname, NULL);
447         }
448 }
449
450 static struct ast_generator moh_file_stream = 
451 {
452         .alloc    = moh_files_alloc,
453         .release  = moh_files_release,
454         .generate = moh_files_generator,
455         .digit    = moh_handle_digit,
456 };
457
458 static int spawn_mp3(struct mohclass *class)
459 {
460         int fds[2];
461         int files = 0;
462         char fns[MAX_MP3S][80];
463         char *argv[MAX_MP3S + 50];
464         char xargs[256];
465         char *argptr;
466         int argc = 0;
467         DIR *dir = NULL;
468         struct dirent *de;
469
470         
471         if (!strcasecmp(class->dir, "nodir")) {
472                 files = 1;
473         } else {
474                 dir = opendir(class->dir);
475                 if (!dir && strncasecmp(class->dir, "http://", 7)) {
476                         ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
477                         return -1;
478                 }
479         }
480
481         if (!ast_test_flag(class, MOH_CUSTOM)) {
482                 argv[argc++] = "mpg123";
483                 argv[argc++] = "-q";
484                 argv[argc++] = "-s";
485                 argv[argc++] = "--mono";
486                 argv[argc++] = "-r";
487                 argv[argc++] = "8000";
488                 
489                 if (!ast_test_flag(class, MOH_SINGLE)) {
490                         argv[argc++] = "-b";
491                         argv[argc++] = "2048";
492                 }
493                 
494                 argv[argc++] = "-f";
495                 
496                 if (ast_test_flag(class, MOH_QUIET))
497                         argv[argc++] = "4096";
498                 else
499                         argv[argc++] = "8192";
500                 
501                 /* Look for extra arguments and add them to the list */
502                 ast_copy_string(xargs, class->args, sizeof(xargs));
503                 argptr = xargs;
504                 while (!ast_strlen_zero(argptr)) {
505                         argv[argc++] = argptr;
506                         strsep(&argptr, ",");
507                 }
508         } else  {
509                 /* Format arguments for argv vector */
510                 ast_copy_string(xargs, class->args, sizeof(xargs));
511                 argptr = xargs;
512                 while (!ast_strlen_zero(argptr)) {
513                         argv[argc++] = argptr;
514                         strsep(&argptr, " ");
515                 }
516         }
517
518         if (!strncasecmp(class->dir, "http://", 7)) {
519                 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
520                 argv[argc++] = fns[files];
521                 files++;
522         } else if (dir) {
523                 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
524                         if ((strlen(de->d_name) > 3) && 
525                             ((ast_test_flag(class, MOH_CUSTOM) && 
526                               (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 
527                                !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
528                              !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
529                                 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
530                                 argv[argc++] = fns[files];
531                                 files++;
532                         }
533                 }
534         }
535         argv[argc] = NULL;
536         if (dir) {
537                 closedir(dir);
538         }
539         if (pipe(fds)) {        
540                 ast_log(LOG_WARNING, "Pipe failed\n");
541                 return -1;
542         }
543         if (!files) {
544                 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
545                 close(fds[0]);
546                 close(fds[1]);
547                 return -1;
548         }
549         if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
550                 sleep(respawn_time - (time(NULL) - class->start));
551         }
552
553         time(&class->start);
554         class->pid = ast_safe_fork(0);
555         if (class->pid < 0) {
556                 close(fds[0]);
557                 close(fds[1]);
558                 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
559                 return -1;
560         }
561         if (!class->pid) {
562                 if (ast_opt_high_priority)
563                         ast_set_priority(0);
564
565                 close(fds[0]);
566                 /* Stdout goes to pipe */
567                 dup2(fds[1], STDOUT_FILENO);
568
569                 /* Close everything else */
570                 ast_close_fds_above_n(STDERR_FILENO);
571
572                 /* Child */
573                 if (strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
574                         ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
575                         _exit(1);
576                 }
577                 setpgid(0, getpid());
578                 if (ast_test_flag(class, MOH_CUSTOM)) {
579                         execv(argv[0], argv);
580                 } else {
581                         /* Default install is /usr/local/bin */
582                         execv(LOCAL_MPG_123, argv);
583                         /* Many places have it in /usr/bin */
584                         execv(MPG_123, argv);
585                         /* Check PATH as a last-ditch effort */
586                         execvp("mpg123", argv);
587                 }
588                 /* Can't use logger, since log FDs are closed */
589                 fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
590                 close(fds[1]);
591                 _exit(1);
592         } else {
593                 /* Parent */
594                 close(fds[1]);
595         }
596         return fds[0];
597 }
598
599 static void *monmp3thread(void *data)
600 {
601 #define MOH_MS_INTERVAL         100
602
603         struct mohclass *class = data;
604         struct mohdata *moh;
605         char buf[8192];
606         short sbuf[8192];
607         int res, res2;
608         int len;
609         struct timeval deadline, tv_tmp;
610
611         deadline.tv_sec = 0;
612         deadline.tv_usec = 0;
613         for(;/* ever */;) {
614                 pthread_testcancel();
615                 /* Spawn mp3 player if it's not there */
616                 if (class->srcfd < 0) {
617                         if ((class->srcfd = spawn_mp3(class)) < 0) {
618                                 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
619                                 /* Try again later */
620                                 sleep(500);
621                                 pthread_testcancel();
622                         }
623                 }
624                 if (class->pseudofd > -1) {
625 #ifdef SOLARIS
626                         thr_yield();
627 #endif
628                         /* Pause some amount of time */
629                         res = read(class->pseudofd, buf, sizeof(buf));
630                         pthread_testcancel();
631                 } else {
632                         long delta;
633                         /* Reliable sleep */
634                         tv_tmp = ast_tvnow();
635                         if (ast_tvzero(deadline))
636                                 deadline = tv_tmp;
637                         delta = ast_tvdiff_ms(tv_tmp, deadline);
638                         if (delta < MOH_MS_INTERVAL) {  /* too early */
639                                 deadline = ast_tvadd(deadline, ast_samp2tv(MOH_MS_INTERVAL, 1000));     /* next deadline */
640                                 usleep(1000 * (MOH_MS_INTERVAL - delta));
641                                 pthread_testcancel();
642                         } else {
643                                 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
644                                 deadline = tv_tmp;
645                         }
646                         res = 8 * MOH_MS_INTERVAL;      /* 8 samples per millisecond */
647                 }
648                 if ((strncasecmp(class->dir, "http://", 7) && strcasecmp(class->dir, "nodir")) && AST_LIST_EMPTY(&class->members))
649                         continue;
650                 /* Read mp3 audio */
651                 len = ast_codec_get_len(class->format, res);
652
653                 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
654                         if (!res2) {
655                                 close(class->srcfd);
656                                 class->srcfd = -1;
657                                 pthread_testcancel();
658                                 if (class->pid > 1) {
659                                         do {
660                                                 if (killpg(class->pid, SIGHUP) < 0) {
661                                                         if (errno == ESRCH) {
662                                                                 break;
663                                                         }
664                                                         ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
665                                                 }
666                                                 usleep(100000);
667                                                 if (killpg(class->pid, SIGTERM) < 0) {
668                                                         if (errno == ESRCH) {
669                                                                 break;
670                                                         }
671                                                         ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
672                                                 }
673                                                 usleep(100000);
674                                                 if (killpg(class->pid, SIGKILL) < 0) {
675                                                         if (errno == ESRCH) {
676                                                                 break;
677                                                         }
678                                                         ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
679                                                 }
680                                         } while (0);
681                                         class->pid = 0;
682                                 }
683                         } else {
684                                 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
685                         }
686                         continue;
687                 }
688
689                 pthread_testcancel();
690
691                 ao2_lock(class);
692                 AST_LIST_TRAVERSE(&class->members, moh, list) {
693                         /* Write data */
694                         if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
695                                 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
696                         }
697                 }
698                 ao2_unlock(class);
699         }
700         return NULL;
701 }
702
703 static int play_moh_exec(struct ast_channel *chan, const char *data)
704 {
705         char *parse;
706         char *class;
707         int timeout = -1;
708         int res;
709         AST_DECLARE_APP_ARGS(args,
710                 AST_APP_ARG(class);
711                 AST_APP_ARG(duration);
712         );
713
714         parse = ast_strdupa(data);
715
716         AST_STANDARD_APP_ARGS(args, parse);
717
718         if (!ast_strlen_zero(args.duration)) {
719                 if (sscanf(args.duration, "%30d", &timeout) == 1) {
720                         timeout *= 1000;
721                 } else {
722                         ast_log(LOG_WARNING, "Invalid MusicOnHold duration '%s'. Will wait indefinitely.\n", args.duration);
723                 }
724         }
725
726         class = S_OR(args.class, NULL);
727         if (ast_moh_start(chan, class, NULL)) {
728                 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
729                 return 0;
730         }
731
732         if (timeout > 0)
733                 res = ast_safe_sleep(chan, timeout);
734         else {
735                 while (!(res = ast_safe_sleep(chan, 10000)));
736         }
737
738         ast_moh_stop(chan);
739
740         return res;
741 }
742
743 static int wait_moh_exec(struct ast_channel *chan, const char *data)
744 {
745         static int deprecation_warning = 0;
746         int res;
747
748         if (!deprecation_warning) {
749                 deprecation_warning = 1;
750                 ast_log(LOG_WARNING, "WaitMusicOnHold application is deprecated and will be removed. Use MusicOnHold with duration parameter instead\n");
751         }
752
753         if (!data || !atoi(data)) {
754                 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
755                 return -1;
756         }
757         if (ast_moh_start(chan, NULL, NULL)) {
758                 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
759                 return 0;
760         }
761         res = ast_safe_sleep(chan, atoi(data) * 1000);
762         ast_moh_stop(chan);
763         return res;
764 }
765
766 static int set_moh_exec(struct ast_channel *chan, const char *data)
767 {
768         static int deprecation_warning = 0;
769
770         if (!deprecation_warning) {
771                 deprecation_warning = 1;
772                 ast_log(LOG_WARNING, "SetMusicOnHold application is deprecated and will be removed. Use Set(CHANNEL(musicclass)=...) instead\n");
773         }
774
775         if (ast_strlen_zero(data)) {
776                 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
777                 return -1;
778         }
779         ast_string_field_set(chan, musicclass, data);
780         return 0;
781 }
782
783 static int start_moh_exec(struct ast_channel *chan, const char *data)
784 {
785         char *parse;
786         char *class;
787         AST_DECLARE_APP_ARGS(args,
788                 AST_APP_ARG(class);
789         );
790
791         parse = ast_strdupa(data);
792
793         AST_STANDARD_APP_ARGS(args, parse);
794
795         class = S_OR(args.class, NULL);
796         if (ast_moh_start(chan, class, NULL)) 
797                 ast_log(LOG_WARNING, "Unable to start music on hold class '%s' on channel %s\n", class, chan->name);
798
799         return 0;
800 }
801
802 static int stop_moh_exec(struct ast_channel *chan, const char *data)
803 {
804         ast_moh_stop(chan);
805
806         return 0;
807 }
808
809 #define get_mohbyname(a,b,c)    _get_mohbyname(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
810
811 static struct mohclass *_get_mohbyname(const char *name, int warn, int flags, const char *file, int lineno, const char *funcname)
812 {
813         struct mohclass *moh = NULL;
814         struct mohclass tmp_class = {
815                 .flags = 0,
816         };
817
818         ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
819
820 #ifdef REF_DEBUG
821         moh = __ao2_find_debug(mohclasses, &tmp_class, flags, "get_mohbyname", (char *) file, lineno, funcname);
822 #else
823         moh = __ao2_find(mohclasses, &tmp_class, flags);
824 #endif
825
826         if (!moh && warn) {
827                 ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
828         }
829
830         return moh;
831 }
832
833 static struct mohdata *mohalloc(struct mohclass *cl)
834 {
835         struct mohdata *moh;
836         long flags;     
837         
838         if (!(moh = ast_calloc(1, sizeof(*moh))))
839                 return NULL;
840         
841         if (pipe(moh->pipe)) {
842                 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
843                 ast_free(moh);
844                 return NULL;
845         }
846
847         /* Make entirely non-blocking */
848         flags = fcntl(moh->pipe[0], F_GETFL);
849         fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
850         flags = fcntl(moh->pipe[1], F_GETFL);
851         fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
852
853         moh->f.frametype = AST_FRAME_VOICE;
854         moh->f.subclass.codec = cl->format;
855         moh->f.offset = AST_FRIENDLY_OFFSET;
856
857         moh->parent = mohclass_ref(cl, "Reffing music class for mohdata parent");
858
859         ao2_lock(cl);
860         AST_LIST_INSERT_HEAD(&cl->members, moh, list);
861         ao2_unlock(cl);
862         
863         return moh;
864 }
865
866 static void moh_release(struct ast_channel *chan, void *data)
867 {
868         struct mohdata *moh = data;
869         struct mohclass *class = moh->parent;
870         format_t oldwfmt;
871
872         ao2_lock(class);
873         AST_LIST_REMOVE(&moh->parent->members, moh, list);      
874         ao2_unlock(class);
875         
876         close(moh->pipe[0]);
877         close(moh->pipe[1]);
878
879         oldwfmt = moh->origwfmt;
880
881         moh->parent = class = mohclass_unref(class, "unreffing moh->parent upon deactivation of generator");
882
883         ast_free(moh);
884
885         if (chan) {
886                 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
887                         ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
888                                         chan->name, ast_getformatname(oldwfmt));
889                 }
890
891                 ast_verb(3, "Stopped music on hold on %s\n", chan->name);
892         }
893 }
894
895 static void *moh_alloc(struct ast_channel *chan, void *params)
896 {
897         struct mohdata *res;
898         struct mohclass *class = params;
899         struct moh_files_state *state;
900
901         /* Initiating music_state for current channel. Channel should know name of moh class */
902         if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
903                 chan->music_state = state;
904                 state->class = mohclass_ref(class, "Copying reference into state container");
905                 ast_module_ref(ast_module_info->self);
906         } else
907                 state = chan->music_state;
908         if (state && state->class != class) {
909                 memset(state, 0, sizeof(*state));
910                 state->class = class;
911         }
912
913         if ((res = mohalloc(class))) {
914                 res->origwfmt = chan->writeformat;
915                 if (ast_set_write_format(chan, class->format)) {
916                         ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
917                         moh_release(NULL, res);
918                         res = NULL;
919                 }
920                 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
921         }
922         return res;
923 }
924
925 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
926 {
927         struct mohdata *moh = data;
928         short buf[1280 + AST_FRIENDLY_OFFSET / 2];
929         int res;
930
931         len = ast_codec_get_len(moh->parent->format, samples);
932
933         if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
934                 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
935                 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
936         }
937         res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
938         if (res <= 0)
939                 return 0;
940
941         moh->f.datalen = res;
942         moh->f.data.ptr = buf + AST_FRIENDLY_OFFSET / 2;
943         moh->f.samples = ast_codec_get_samples(&moh->f);
944
945         if (ast_write(chan, &moh->f) < 0) {
946                 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
947                 return -1;
948         }
949
950         return 0;
951 }
952
953 static struct ast_generator mohgen = {
954         .alloc    = moh_alloc,
955         .release  = moh_release,
956         .generate = moh_generate,
957         .digit    = moh_handle_digit,
958 };
959
960 static int moh_add_file(struct mohclass *class, const char *filepath)
961 {
962         if (!class->allowed_files) {
963                 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
964                         return -1;
965                 class->allowed_files = INITIAL_NUM_FILES;
966         } else if (class->total_files == class->allowed_files) {
967                 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
968                         class->allowed_files = 0;
969                         class->total_files = 0;
970                         return -1;
971                 }
972                 class->allowed_files *= 2;
973         }
974
975         if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
976                 return -1;
977
978         class->total_files++;
979
980         return 0;
981 }
982
983 static int moh_sort_compare(const void *i1, const void *i2)
984 {
985         char *s1, *s2;
986
987         s1 = ((char **)i1)[0];
988         s2 = ((char **)i2)[0];
989
990         return strcasecmp(s1, s2);
991 }
992
993 static int moh_scan_files(struct mohclass *class) {
994
995         DIR *files_DIR;
996         struct dirent *files_dirent;
997         char dir_path[PATH_MAX];
998         char path[PATH_MAX];
999         char filepath[PATH_MAX];
1000         char *ext;
1001         struct stat statbuf;
1002         int dirnamelen;
1003         int i;
1004
1005         if (class->dir[0] != '/') {
1006                 ast_copy_string(dir_path, ast_config_AST_VAR_DIR, sizeof(dir_path));
1007                 strncat(dir_path, "/", sizeof(dir_path) - 1);
1008                 strncat(dir_path, class->dir, sizeof(dir_path) - 1);
1009         } else {
1010                 ast_copy_string(dir_path, class->dir, sizeof(dir_path));
1011         }
1012         ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
1013         files_DIR = opendir(dir_path);
1014         if (!files_DIR) {
1015                 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
1016                 return -1;
1017         }
1018
1019         for (i = 0; i < class->total_files; i++)
1020                 ast_free(class->filearray[i]);
1021
1022         class->total_files = 0;
1023         dirnamelen = strlen(dir_path) + 2;
1024         if (!getcwd(path, sizeof(path))) {
1025                 ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
1026                 return -1;
1027         }
1028         if (chdir(dir_path) < 0) {
1029                 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
1030                 return -1;
1031         }
1032         while ((files_dirent = readdir(files_DIR))) {
1033                 /* The file name must be at least long enough to have the file type extension */
1034                 if ((strlen(files_dirent->d_name) < 4))
1035                         continue;
1036
1037                 /* Skip files that starts with a dot */
1038                 if (files_dirent->d_name[0] == '.')
1039                         continue;
1040
1041                 /* Skip files without extensions... they are not audio */
1042                 if (!strchr(files_dirent->d_name, '.'))
1043                         continue;
1044
1045                 snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);
1046
1047                 if (stat(filepath, &statbuf))
1048                         continue;
1049
1050                 if (!S_ISREG(statbuf.st_mode))
1051                         continue;
1052
1053                 if ((ext = strrchr(filepath, '.')))
1054                         *ext = '\0';
1055
1056                 /* if the file is present in multiple formats, ensure we only put it into the list once */
1057                 for (i = 0; i < class->total_files; i++)
1058                         if (!strcmp(filepath, class->filearray[i]))
1059                                 break;
1060
1061                 if (i == class->total_files) {
1062                         if (moh_add_file(class, filepath))
1063                                 break;
1064                 }
1065         }
1066
1067         closedir(files_DIR);
1068         if (chdir(path) < 0) {
1069                 ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
1070                 return -1;
1071         }
1072         if (ast_test_flag(class, MOH_SORTALPHA))
1073                 qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
1074         return class->total_files;
1075 }
1076
1077 static int init_files_class(struct mohclass *class)
1078 {
1079         int res;
1080
1081         res = moh_scan_files(class);
1082
1083         if (res < 0) {
1084                 return -1;
1085         }
1086
1087         if (!res) {
1088                 if (option_verbose > 2) {
1089                         ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
1090                                         class->dir, class->name);
1091                 }
1092                 return -1;
1093         }
1094
1095         if (strchr(class->args, 'r')) {
1096                 ast_set_flag(class, MOH_RANDOMIZE);
1097         }
1098
1099         return 0;
1100 }
1101
1102
1103 static int moh_diff(struct mohclass *old, struct mohclass *new)
1104 {
1105         if (!old || !new) {
1106                 return -1;
1107         }
1108
1109         if (strcmp(old->dir, new->dir)) {
1110                 return -1;
1111         } else if (strcmp(old->mode, new->mode)) {
1112                 return -1;
1113         } else if (strcmp(old->args, new->args)) {
1114                 return -1;
1115         } else if (old->flags != new->flags) {
1116                 return -1;
1117         }
1118
1119         return 0;
1120 }
1121
1122 static int init_app_class(struct mohclass *class)
1123 {
1124 #ifdef HAVE_DAHDI
1125         int x;
1126 #endif
1127
1128         if (!strcasecmp(class->mode, "custom")) {
1129                 ast_set_flag(class, MOH_CUSTOM);
1130         } else if (!strcasecmp(class->mode, "mp3nb")) {
1131                 ast_set_flag(class, MOH_SINGLE);
1132         } else if (!strcasecmp(class->mode, "quietmp3nb")) {
1133                 ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
1134         } else if (!strcasecmp(class->mode, "quietmp3")) {
1135                 ast_set_flag(class, MOH_QUIET);
1136         }
1137                 
1138         class->srcfd = -1;
1139         class->pseudofd = -1;
1140
1141 #ifdef HAVE_DAHDI
1142         /* Open /dev/zap/pseudo for timing...  Is
1143            there a better, yet reliable way to do this? */
1144         class->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY);
1145         if (class->pseudofd < 0) {
1146                 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
1147         } else {
1148                 x = 320;
1149                 ioctl(class->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
1150         }
1151 #endif
1152
1153         if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
1154                 ast_log(LOG_WARNING, "Unable to create moh thread...\n");
1155                 if (class->pseudofd > -1) {
1156                         close(class->pseudofd);
1157                         class->pseudofd = -1;
1158                 }
1159                 return -1;
1160         }
1161
1162         return 0;
1163 }
1164
1165 /*!
1166  * \note This function owns the reference it gets to moh if unref is true
1167  */
1168 #define moh_register(a,b,c)     _moh_register(a,b,c,__FILE__,__LINE__,__PRETTY_FUNCTION__)
1169 static int _moh_register(struct mohclass *moh, int reload, int unref, const char *file, int line, const char *funcname)
1170 {
1171         struct mohclass *mohclass = NULL;
1172
1173         if ((mohclass = _get_mohbyname(moh->name, 0, MOH_NOTDELETED, file, line, funcname)) && !moh_diff(mohclass, moh)) {
1174                 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
1175                 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1176                 if (unref) {
1177                         moh = mohclass_unref(moh, "unreffing potential new moh class (it is a duplicate)");
1178                 }
1179                 return -1;
1180         } else if (mohclass) {
1181                 /* Found a class, but it's different from the one being registered */
1182                 mohclass = mohclass_unref(mohclass, "unreffing mohclass we just found by name");
1183         }
1184
1185         time(&moh->start);
1186         moh->start -= respawn_time;
1187         
1188         if (!strcasecmp(moh->mode, "files")) {
1189                 if (init_files_class(moh)) {
1190                         if (unref) {
1191                                 moh = mohclass_unref(moh, "unreffing potential new moh class (init_files_class failed)");
1192                         }
1193                         return -1;
1194                 }
1195         } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
1196                         !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || 
1197                         !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
1198                 if (init_app_class(moh)) {
1199                         if (unref) {
1200                                 moh = mohclass_unref(moh, "unreffing potential new moh class (init_app_class_failed)");
1201                         }
1202                         return -1;
1203                 }
1204         } else {
1205                 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
1206                 if (unref) {
1207                         moh = mohclass_unref(moh, "unreffing potential new moh class (unknown mode)");
1208                 }
1209                 return -1;
1210         }
1211
1212         ao2_t_link(mohclasses, moh, "Adding class to container");
1213
1214         if (unref) {
1215                 moh = mohclass_unref(moh, "Unreffing new moh class because we just added it to the container");
1216         }
1217         
1218         return 0;
1219 }
1220
1221 static void local_ast_moh_cleanup(struct ast_channel *chan)
1222 {
1223         struct moh_files_state *state = chan->music_state;
1224
1225         if (state) {
1226                 if (state->class) {
1227                         state->class = mohclass_unref(state->class, "Channel MOH state destruction");
1228                 }
1229                 ast_free(chan->music_state);
1230                 chan->music_state = NULL;
1231                 /* Only held a module reference if we had a music state */
1232                 ast_module_unref(ast_module_info->self);
1233         }
1234 }
1235
1236 static void moh_class_destructor(void *obj);
1237
1238 #define moh_class_malloc()      _moh_class_malloc(__FILE__,__LINE__,__PRETTY_FUNCTION__)
1239
1240 static struct mohclass *_moh_class_malloc(const char *file, int line, const char *funcname)
1241 {
1242         struct mohclass *class;
1243
1244         if ((class =
1245 #ifdef REF_DEBUG
1246                         __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 1)
1247 #elif defined(__AST_DEBUG_MALLOC)
1248                         __ao2_alloc_debug(sizeof(*class), moh_class_destructor, "Allocating new moh class", file, line, funcname, 0)
1249 #else
1250                         ao2_alloc(sizeof(*class), moh_class_destructor)
1251 #endif
1252                 )) {
1253                 class->format = AST_FORMAT_SLINEAR;
1254         }
1255
1256         return class;
1257 }
1258
1259 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
1260 {
1261         struct mohclass *mohclass = NULL;
1262         struct moh_files_state *state = chan->music_state;
1263         struct ast_variable *var = NULL;
1264         int res;
1265         int realtime_possible = ast_check_realtime("musiconhold");
1266
1267         /* The following is the order of preference for which class to use:
1268          * 1) The channels explicitly set musicclass, which should *only* be
1269          *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1270          * 2) The mclass argument. If a channel is calling ast_moh_start() as the
1271          *    result of receiving a HOLD control frame, this should be the
1272          *    payload that came with the frame.
1273          * 3) The interpclass argument. This would be from the mohinterpret
1274          *    option from channel drivers. This is the same as the old musicclass
1275          *    option.
1276          * 4) The default class.
1277          */
1278         if (!ast_strlen_zero(chan->musicclass)) {
1279                 mohclass = get_mohbyname(chan->musicclass, 1, 0);
1280                 if (!mohclass && realtime_possible) {
1281                         var = ast_load_realtime("musiconhold", "name", chan->musicclass, SENTINEL);
1282                 }
1283         }
1284         if (!mohclass && !var && !ast_strlen_zero(mclass)) {
1285                 mohclass = get_mohbyname(mclass, 1, 0);
1286                 if (!mohclass && realtime_possible) {
1287                         var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL);
1288                 }
1289         }
1290         if (!mohclass && !var && !ast_strlen_zero(interpclass)) {
1291                 mohclass = get_mohbyname(interpclass, 1, 0);
1292                 if (!mohclass && realtime_possible) {
1293                         var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL);
1294                 }
1295         }
1296
1297         if (!mohclass && !var) {
1298                 mohclass = get_mohbyname("default", 1, 0);
1299                 if (!mohclass && realtime_possible) {
1300                         var = ast_load_realtime("musiconhold", "name", "default", SENTINEL);
1301                 }
1302         }
1303
1304         /* If no moh class found in memory, then check RT. Note that the logic used
1305          * above guarantees that if var is non-NULL, then mohclass must be NULL.
1306          */
1307         if (var) {
1308                 struct ast_variable *tmp = NULL;
1309
1310                 if ((mohclass = moh_class_malloc())) {
1311                         mohclass->realtime = 1;
1312                         for (tmp = var; tmp; tmp = tmp->next) {
1313                                 if (!strcasecmp(tmp->name, "name"))
1314                                         ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
1315                                 else if (!strcasecmp(tmp->name, "mode"))
1316                                         ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode)); 
1317                                 else if (!strcasecmp(tmp->name, "directory"))
1318                                         ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
1319                                 else if (!strcasecmp(tmp->name, "application"))
1320                                         ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
1321                                 else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
1322                                         mohclass->digit = *tmp->value;
1323                                 else if (!strcasecmp(tmp->name, "random"))
1324                                         ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
1325                                 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
1326                                         ast_set_flag(mohclass, MOH_RANDOMIZE);
1327                                 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha")) 
1328                                         ast_set_flag(mohclass, MOH_SORTALPHA);
1329                                 else if (!strcasecmp(tmp->name, "format")) {
1330                                         mohclass->format = ast_getformatbyname(tmp->value);
1331                                         if (!mohclass->format) {
1332                                                 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
1333                                                 mohclass->format = AST_FORMAT_SLINEAR;
1334                                         }
1335                                 }
1336                         }
1337                         ast_variables_destroy(var);
1338                         if (ast_strlen_zero(mohclass->dir)) {
1339                                 if (!strcasecmp(mohclass->mode, "custom")) {
1340                                         strcpy(mohclass->dir, "nodir");
1341                                 } else {
1342                                         ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
1343                                         mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no directory specified)");
1344                                         return -1;
1345                                 }
1346                         }
1347                         if (ast_strlen_zero(mohclass->mode)) {
1348                                 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
1349                                 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no mode specified)");
1350                                 return -1;
1351                         }
1352                         if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
1353                                 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
1354                                 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (no app specified for custom mode");
1355                                 return -1;
1356                         }
1357
1358                         if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
1359                                 /* CACHERTCLASSES enabled, let's add this class to default tree */
1360                                 if (state && state->class) {
1361                                         /* Class already exist for this channel */
1362                                         ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1363                                         if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1364                                                 /* we found RT class with the same name, seems like we should continue playing existing one */
1365                                                 /* XXX This code is impossible to reach */
1366                                                 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has a class)");
1367                                                 mohclass = state->class;
1368                                         }
1369                                 }
1370                                 /* We don't want moh_register to unref the mohclass because we do it at the end of this function as well.
1371                                  * If we allowed moh_register to unref the mohclass,too, then the count would be off by one. The result would
1372                                  * be that the destructor would be called when the generator on the channel is deactivated. The container then
1373                                  * has a pointer to a freed mohclass, so any operations involving the mohclass container would result in reading
1374                                  * invalid memory.
1375                                  */
1376                                 moh_register(mohclass, 0, DONT_UNREF);
1377                         } else {
1378                                 /* We don't register RT moh class, so let's init it manualy */
1379
1380                                 time(&mohclass->start);
1381                                 mohclass->start -= respawn_time;
1382         
1383                                 if (!strcasecmp(mohclass->mode, "files")) {
1384                                         if (!moh_scan_files(mohclass)) {
1385                                                 mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (moh_scan_files failed)");
1386                                                 return -1;
1387                                         }
1388                                         if (strchr(mohclass->args, 'r'))
1389                                                 ast_set_flag(mohclass, MOH_RANDOMIZE);
1390                                 } 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")) {
1391
1392                                         if (!strcasecmp(mohclass->mode, "custom"))
1393                                                 ast_set_flag(mohclass, MOH_CUSTOM);
1394                                         else if (!strcasecmp(mohclass->mode, "mp3nb"))
1395                                                 ast_set_flag(mohclass, MOH_SINGLE);
1396                                         else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1397                                                 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
1398                                         else if (!strcasecmp(mohclass->mode, "quietmp3"))
1399                                                 ast_set_flag(mohclass, MOH_QUIET);
1400                         
1401                                         mohclass->srcfd = -1;
1402 #ifdef HAVE_DAHDI
1403                                         /* Open /dev/dahdi/pseudo for timing...  Is
1404                                            there a better, yet reliable way to do this? */
1405                                         mohclass->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY);
1406                                         if (mohclass->pseudofd < 0) {
1407                                                 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
1408                                         } else {
1409                                                 int x = 320;
1410                                                 ioctl(mohclass->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
1411                                         }
1412 #else
1413                                         mohclass->pseudofd = -1;
1414 #endif
1415                                         /* Let's check if this channel already had a moh class before */
1416                                         if (state && state->class) {
1417                                                 /* Class already exist for this channel */
1418                                                 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1419                                                 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1420                                                         /* we found RT class with the same name, seems like we should continue playing existing one */
1421                                                         mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (channel already has one)");
1422                                                         mohclass = state->class;
1423                                                 }
1424                                         } else {
1425                                                 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
1426                                                         ast_log(LOG_WARNING, "Unable to create moh...\n");
1427                                                         if (mohclass->pseudofd > -1) {
1428                                                                 close(mohclass->pseudofd);
1429                                                                 mohclass->pseudofd = -1;
1430                                                         }
1431                                                         mohclass = mohclass_unref(mohclass, "Unreffing potential mohclass (failed to create background thread)");
1432                                                         return -1;
1433                                                 }
1434                                         }
1435                                 } else {
1436                                         ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
1437                                         mohclass = mohclass_unref(mohclass, "unreffing potential mohclass (unknown mode)");
1438                                         return -1;
1439                                 }
1440                         }
1441                 } else {
1442                         ast_variables_destroy(var);
1443                 }
1444         }
1445
1446         if (!mohclass) {
1447                 return -1;
1448         }
1449
1450         ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
1451                 "State: Start\r\n"
1452                 "Channel: %s\r\n"
1453                 "UniqueID: %s\r\n"
1454                 "Class: %s\r\n",
1455                 chan->name, chan->uniqueid,
1456                 mohclass->name);
1457
1458         ast_set_flag(chan, AST_FLAG_MOH);
1459
1460         if (mohclass->total_files) {
1461                 res = ast_activate_generator(chan, &moh_file_stream, mohclass);
1462         } else {
1463                 res = ast_activate_generator(chan, &mohgen, mohclass);
1464         }
1465
1466         mohclass = mohclass_unref(mohclass, "unreffing local reference to mohclass in local_ast_moh_start");
1467
1468         return res;
1469 }
1470
1471 static void local_ast_moh_stop(struct ast_channel *chan)
1472 {
1473         ast_clear_flag(chan, AST_FLAG_MOH);
1474         ast_deactivate_generator(chan);
1475
1476         ast_channel_lock(chan);
1477         if (chan->music_state) {
1478                 if (chan->stream) {
1479                         ast_closestream(chan->stream);
1480                         chan->stream = NULL;
1481                 }
1482         }
1483
1484         ast_manager_event(chan, EVENT_FLAG_CALL, "MusicOnHold",
1485                 "State: Stop\r\n"
1486                 "Channel: %s\r\n"
1487                 "UniqueID: %s\r\n",
1488                 chan->name, chan->uniqueid);
1489         ast_channel_unlock(chan);
1490 }
1491
1492 static void moh_class_destructor(void *obj)
1493 {
1494         struct mohclass *class = obj;
1495         struct mohdata *member;
1496         pthread_t tid = 0;
1497
1498         ast_debug(1, "Destroying MOH class '%s'\n", class->name);
1499
1500         /* Kill the thread first, so it cannot restart the child process while the
1501          * class is being destroyed */
1502         if (class->thread != AST_PTHREADT_NULL && class->thread != 0) {
1503                 tid = class->thread;
1504                 class->thread = AST_PTHREADT_NULL;
1505                 pthread_cancel(tid);
1506                 /* We'll collect the exit status later, after we ensure all the readers
1507                  * are dead. */
1508         }
1509
1510         if (class->pid > 1) {
1511                 char buff[8192];
1512                 int bytes, tbytes = 0, stime = 0, pid = 0;
1513
1514                 ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
1515
1516                 stime = time(NULL) + 2;
1517                 pid = class->pid;
1518                 class->pid = 0;
1519
1520                 /* Back when this was just mpg123, SIGKILL was fine.  Now we need
1521                  * to give the process a reason and time enough to kill off its
1522                  * children. */
1523                 do {
1524                         if (killpg(pid, SIGHUP) < 0) {
1525                                 ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
1526                         }
1527                         usleep(100000);
1528                         if (killpg(pid, SIGTERM) < 0) {
1529                                 if (errno == ESRCH) {
1530                                         break;
1531                                 }
1532                                 ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
1533                         }
1534                         usleep(100000);
1535                         if (killpg(pid, SIGKILL) < 0) {
1536                                 if (errno == ESRCH) {
1537                                         break;
1538                                 }
1539                                 ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
1540                         }
1541                 } while (0);
1542
1543                 while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
1544                                 (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
1545                         tbytes = tbytes + bytes;
1546                 }
1547
1548                 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1549
1550                 close(class->srcfd);
1551         }
1552
1553         while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
1554                 free(member);
1555         }
1556
1557         if (class->filearray) {
1558                 int i;
1559                 for (i = 0; i < class->total_files; i++) {
1560                         free(class->filearray[i]);
1561                 }
1562                 free(class->filearray);
1563                 class->filearray = NULL;
1564         }
1565
1566         /* Finally, collect the exit status of the monitor thread */
1567         if (tid > 0) {
1568                 pthread_join(tid, NULL);
1569         }
1570 }
1571
1572 static int moh_class_mark(void *obj, void *arg, int flags)
1573 {
1574         struct mohclass *class = obj;
1575
1576         class->delete = 1;
1577
1578         return 0;
1579 }
1580
1581 static int moh_classes_delete_marked(void *obj, void *arg, int flags)
1582 {
1583         struct mohclass *class = obj;
1584
1585         return class->delete ? CMP_MATCH : 0;
1586 }
1587
1588 static int load_moh_classes(int reload)
1589 {
1590         struct ast_config *cfg;
1591         struct ast_variable *var;
1592         struct mohclass *class; 
1593         char *cat;
1594         int numclasses = 0;
1595         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1596
1597         cfg = ast_config_load("musiconhold.conf", config_flags);
1598
1599         if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
1600                 return 0;
1601         }
1602
1603         if (reload) {
1604                 ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
1605         }
1606         
1607         ast_clear_flag(global_flags, AST_FLAGS_ALL);
1608
1609         cat = ast_category_browse(cfg, NULL);
1610         for (; cat; cat = ast_category_browse(cfg, cat)) {
1611                 /* Setup common options from [general] section */
1612                 if (!strcasecmp(cat, "general")) {
1613                         for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
1614                                 if (!strcasecmp(var->name, "cachertclasses")) {
1615                                         ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
1616                                 } else {
1617                                         ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
1618                                 }
1619                         }
1620                 }
1621                 /* These names were deprecated in 1.4 and should not be used until after the next major release. */
1622                 if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || 
1623                                 !strcasecmp(cat, "general")) {
1624                         continue;
1625                 }
1626
1627                 if (!(class = moh_class_malloc())) {
1628                         break;
1629                 }
1630
1631                 ast_copy_string(class->name, cat, sizeof(class->name)); 
1632                 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
1633                         if (!strcasecmp(var->name, "mode"))
1634                                 ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
1635                         else if (!strcasecmp(var->name, "directory"))
1636                                 ast_copy_string(class->dir, var->value, sizeof(class->dir));
1637                         else if (!strcasecmp(var->name, "application"))
1638                                 ast_copy_string(class->args, var->value, sizeof(class->args));
1639                         else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
1640                                 class->digit = *var->value;
1641                         else if (!strcasecmp(var->name, "random"))
1642                                 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
1643                         else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
1644                                 ast_set_flag(class, MOH_RANDOMIZE);
1645                         else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 
1646                                 ast_set_flag(class, MOH_SORTALPHA);
1647                         else if (!strcasecmp(var->name, "format")) {
1648                                 class->format = ast_getformatbyname(var->value);
1649                                 if (!class->format) {
1650                                         ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1651                                         class->format = AST_FORMAT_SLINEAR;
1652                                 }
1653                         }
1654                 }
1655
1656                 if (ast_strlen_zero(class->dir)) {
1657                         if (!strcasecmp(class->mode, "custom")) {
1658                                 strcpy(class->dir, "nodir");
1659                         } else {
1660                                 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1661                                 class = mohclass_unref(class, "unreffing potential mohclass (no directory)");
1662                                 continue;
1663                         }
1664                 }
1665                 if (ast_strlen_zero(class->mode)) {
1666                         ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1667                         class = mohclass_unref(class, "unreffing potential mohclass (no mode)");
1668                         continue;
1669                 }
1670                 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1671                         ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1672                         class = mohclass_unref(class, "unreffing potential mohclass (no app for custom mode)");
1673                         continue;
1674                 }
1675
1676                 /* Don't leak a class when it's already registered */
1677                 if (!moh_register(class, reload, HANDLE_REF)) {
1678                         numclasses++;
1679                 }
1680         }
1681
1682         ast_config_destroy(cfg);
1683
1684         ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
1685                         moh_classes_delete_marked, NULL, "Purge marked classes");
1686
1687         return numclasses;
1688 }
1689
1690 static void ast_moh_destroy(void)
1691 {
1692         ast_verb(2, "Destroying musiconhold processes\n");
1693         ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "Destroy callback");
1694 }
1695
1696 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1697 {
1698         switch (cmd) {
1699         case CLI_INIT:
1700                 e->command = "moh reload";
1701                 e->usage =
1702                         "Usage: moh reload\n"
1703                         "       Reloads the MusicOnHold module.\n"
1704                         "       Alias for 'module reload res_musiconhold.so'\n";
1705                 return NULL;
1706         case CLI_GENERATE:
1707                 return NULL;
1708         }
1709
1710         if (a->argc != e->args)
1711                 return CLI_SHOWUSAGE;
1712
1713         reload();
1714
1715         return CLI_SUCCESS;
1716 }
1717
1718 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1719 {
1720         struct mohclass *class;
1721         struct ao2_iterator i;
1722
1723         switch (cmd) {
1724         case CLI_INIT:
1725                 e->command = "moh show files";
1726                 e->usage =
1727                         "Usage: moh show files\n"
1728                         "       Lists all loaded file-based MusicOnHold classes and their\n"
1729                         "       files.\n";
1730                 return NULL;
1731         case CLI_GENERATE:
1732                 return NULL;
1733         }
1734
1735         if (a->argc != e->args)
1736                 return CLI_SHOWUSAGE;
1737
1738         i = ao2_iterator_init(mohclasses, 0);
1739         for (; (class = ao2_t_iterator_next(&i, "Show files iterator")); mohclass_unref(class, "Unref iterator in moh show files")) {
1740                 int x;
1741
1742                 if (!class->total_files) {
1743                         continue;
1744                 }
1745
1746                 ast_cli(a->fd, "Class: %s\n", class->name);
1747                 for (x = 0; x < class->total_files; x++) {
1748                         ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
1749                 }
1750         }
1751         ao2_iterator_destroy(&i);
1752
1753         return CLI_SUCCESS;
1754 }
1755
1756 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1757 {
1758         struct mohclass *class;
1759         struct ao2_iterator i;
1760
1761         switch (cmd) {
1762         case CLI_INIT:
1763                 e->command = "moh show classes";
1764                 e->usage =
1765                         "Usage: moh show classes\n"
1766                         "       Lists all MusicOnHold classes.\n";
1767                 return NULL;
1768         case CLI_GENERATE:
1769                 return NULL;
1770         }
1771
1772         if (a->argc != e->args)
1773                 return CLI_SHOWUSAGE;
1774
1775         i = ao2_iterator_init(mohclasses, 0);
1776         for (; (class = ao2_t_iterator_next(&i, "Show classes iterator")); mohclass_unref(class, "Unref iterator in moh show classes")) {
1777                 ast_cli(a->fd, "Class: %s\n", class->name);
1778                 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1779                 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1780                 if (ast_test_flag(class, MOH_CUSTOM)) {
1781                         ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1782                 }
1783                 if (strcasecmp(class->mode, "files")) {
1784                         ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
1785                 }
1786         }
1787         ao2_iterator_destroy(&i);
1788
1789         return CLI_SUCCESS;
1790 }
1791
1792 static struct ast_cli_entry cli_moh[] = {
1793         AST_CLI_DEFINE(handle_cli_moh_reload,       "Reload MusicOnHold"),
1794         AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
1795         AST_CLI_DEFINE(handle_cli_moh_show_files,   "List MusicOnHold file-based classes")
1796 };
1797
1798 static int moh_class_hash(const void *obj, const int flags)
1799 {
1800         const struct mohclass *class = obj;
1801
1802         return ast_str_case_hash(class->name);
1803 }
1804
1805 static int moh_class_cmp(void *obj, void *arg, int flags)
1806 {
1807         struct mohclass *class = obj, *class2 = arg;
1808
1809         return strcasecmp(class->name, class2->name) ? 0 :
1810                 (flags & MOH_NOTDELETED) && (class->delete || class2->delete) ? 0 :
1811                 CMP_MATCH | CMP_STOP;
1812 }
1813
1814 static int load_module(void)
1815 {
1816         int res;
1817
1818         if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
1819                 return AST_MODULE_LOAD_DECLINE;
1820         }
1821
1822         if (!load_moh_classes(0)) {     /* No music classes configured, so skip it */
1823                 ast_log(LOG_WARNING, "No music on hold classes configured, "
1824                                 "disabling music on hold.\n");
1825         } else {
1826                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
1827                                 local_ast_moh_cleanup);
1828         }
1829
1830         res = ast_register_application_xml(play_moh, play_moh_exec);
1831         ast_register_atexit(ast_moh_destroy);
1832         ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
1833         if (!res)
1834                 res = ast_register_application_xml(wait_moh, wait_moh_exec);
1835         if (!res)
1836                 res = ast_register_application_xml(set_moh, set_moh_exec);
1837         if (!res)
1838                 res = ast_register_application_xml(start_moh, start_moh_exec);
1839         if (!res)
1840                 res = ast_register_application_xml(stop_moh, stop_moh_exec);
1841
1842         return AST_MODULE_LOAD_SUCCESS;
1843 }
1844
1845 static int reload(void)
1846 {
1847         if (load_moh_classes(1)) {
1848                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
1849                                 local_ast_moh_cleanup);
1850         }
1851
1852         return AST_MODULE_LOAD_SUCCESS;
1853 }
1854
1855 static int moh_class_inuse(void *obj, void *arg, int flags)
1856 {
1857         struct mohclass *class = obj;
1858
1859         return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
1860 }
1861
1862 static int unload_module(void)
1863 {
1864         int res = 0;
1865         struct mohclass *class = NULL;
1866
1867         /* XXX This check shouldn't be required if module ref counting was being used
1868          * properly ... */
1869         if ((class = ao2_t_callback(mohclasses, 0, moh_class_inuse, NULL, "Module unload callback"))) {
1870                 class = mohclass_unref(class, "unref of class from module unload callback");
1871                 res = -1;
1872         }
1873
1874         if (res < 0) {
1875                 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
1876                 return res;
1877         }
1878
1879         ast_uninstall_music_functions();
1880
1881         ast_moh_destroy();
1882         res = ast_unregister_application(play_moh);
1883         res |= ast_unregister_application(wait_moh);
1884         res |= ast_unregister_application(set_moh);
1885         res |= ast_unregister_application(start_moh);
1886         res |= ast_unregister_application(stop_moh);
1887         ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
1888         ast_unregister_atexit(ast_moh_destroy);
1889
1890         return res;
1891 }
1892
1893 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Music On Hold Resource",
1894         .load = load_module,
1895         .unload = unload_module,
1896         .reload = reload,
1897 );