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