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