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