On reload, re-read the files in the specified moh directory (closes issue #10536)
[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>zaptel</use>
31  ***/
32
33 #include "asterisk.h"
34
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36
37 #include <stdlib.h>
38 #include <errno.h>
39 #include <ctype.h>
40 #include <unistd.h>
41 #include <string.h>
42 #include <signal.h>
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <sys/time.h>
46 #include <sys/signal.h>
47 #include <netinet/in.h>
48 #include <sys/stat.h>
49 #include <dirent.h>
50 #include <unistd.h>
51 #include <sys/ioctl.h>
52 #ifdef SOLARIS
53 #include <thread.h>
54 #endif
55
56 #include "asterisk/zapata.h"
57
58 #include "asterisk/lock.h"
59 #include "asterisk/file.h"
60 #include "asterisk/logger.h"
61 #include "asterisk/channel.h"
62 #include "asterisk/pbx.h"
63 #include "asterisk/options.h"
64 #include "asterisk/module.h"
65 #include "asterisk/translate.h"
66 #include "asterisk/say.h"
67 #include "asterisk/musiconhold.h"
68 #include "asterisk/config.h"
69 #include "asterisk/utils.h"
70 #include "asterisk/cli.h"
71 #include "asterisk/stringfields.h"
72 #include "asterisk/linkedlists.h"
73
74 #define INITIAL_NUM_FILES   8
75
76 static char *play_moh = "MusicOnHold";
77 static char *wait_moh = "WaitMusicOnHold";
78 static char *set_moh = "SetMusicOnHold";
79 static char *start_moh = "StartMusicOnHold";
80 static char *stop_moh = "StopMusicOnHold";
81
82 static char *play_moh_syn = "Play Music On Hold indefinitely";
83 static char *wait_moh_syn = "Wait, playing Music On Hold";
84 static char *set_moh_syn = "Set default Music On Hold class";
85 static char *start_moh_syn = "Play Music On Hold";
86 static char *stop_moh_syn = "Stop Playing Music On Hold";
87
88 static char *play_moh_desc = "MusicOnHold(class): "
89 "Plays hold music specified by class.  If omitted, the default\n"
90 "music source for the channel will be used. Set the default \n"
91 "class with the SetMusicOnHold() application.\n"
92 "Returns -1 on hangup.\n"
93 "Never returns otherwise.\n";
94
95 static char *wait_moh_desc = "WaitMusicOnHold(delay): "
96 "Plays hold music specified number of seconds.  Returns 0 when\n"
97 "done, or -1 on hangup.  If no hold music is available, the delay will\n"
98 "still occur with no sound.\n";
99
100 static char *set_moh_desc = "SetMusicOnHold(class): "
101 "Sets the default class for music on hold for a given channel.  When\n"
102 "music on hold is activated, this class will be used to select which\n"
103 "music is played.\n";
104
105 static char *start_moh_desc = "StartMusicOnHold(class): "
106 "Starts playing music on hold, uses default music class for channel.\n"
107 "Starts playing music specified by class.  If omitted, the default\n"
108 "music source for the channel will be used.  Always returns 0.\n";
109
110 static char *stop_moh_desc = "StopMusicOnHold: "
111 "Stops playing music on hold.\n";
112
113 static int respawn_time = 20;
114
115 struct moh_files_state {
116         struct mohclass *class;
117         int origwfmt;
118         int samples;
119         int sample_queue;
120         int pos;
121         int save_pos;
122 };
123
124 #define MOH_QUIET               (1 << 0)
125 #define MOH_SINGLE              (1 << 1)
126 #define MOH_CUSTOM              (1 << 2)
127 #define MOH_RANDOMIZE           (1 << 3)
128 #define MOH_SORTALPHA           (1 << 4)
129
130 struct mohclass {
131         char name[MAX_MUSICCLASS];
132         char dir[256];
133         char args[256];
134         char mode[80];
135         char digit;
136         /*! A dynamically sized array to hold the list of filenames in "files" mode */
137         char **filearray;
138         /*! The current size of the filearray */
139         int allowed_files;
140         /*! The current number of files loaded into the filearray */
141         int total_files;
142         unsigned int flags;
143         /*! The format from the MOH source, not applicable to "files" mode */
144         int format;
145         /*! The pid of the external application delivering MOH */
146         int pid;
147         time_t start;
148         pthread_t thread;
149         /*! Source of audio */
150         int srcfd;
151         /*! FD for timing source */
152         int pseudofd;
153         /*! Number of users */
154         int inuse;
155         unsigned int delete:1;
156         AST_LIST_HEAD_NOLOCK(, mohdata) members;
157         AST_LIST_ENTRY(mohclass) list;
158 };
159
160 struct mohdata {
161         int pipe[2];
162         int origwfmt;
163         struct mohclass *parent;
164         struct ast_frame f;
165         AST_LIST_ENTRY(mohdata) list;
166 };
167
168 AST_RWLIST_HEAD_STATIC(mohclasses, mohclass);
169
170 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
171 #define MPG_123 "/usr/bin/mpg123"
172 #define MAX_MP3S 256
173
174 static int ast_moh_destroy_one(struct mohclass *moh);
175 static int reload(void);
176
177 static void ast_moh_free_class(struct mohclass **mohclass) 
178 {
179         struct mohdata *member;
180         struct mohclass *class = *mohclass;
181         int i;
182         
183         while ((member = AST_LIST_REMOVE_HEAD(&class->members, list)))
184                 ast_free(member);
185         
186         if (class->thread) {
187                 pthread_cancel(class->thread);
188                 class->thread = 0;
189         }
190
191         if (class->filearray) {
192                 for (i = 0; i < class->total_files; i++)
193                         ast_free(class->filearray[i]);
194                 ast_free(class->filearray);
195         }
196
197         ast_free(class);
198         *mohclass = NULL;
199 }
200
201
202 static void moh_files_release(struct ast_channel *chan, void *data)
203 {
204         struct moh_files_state *state = chan->music_state;
205
206         if (chan && state) {
207                 if (chan->stream) {
208                         ast_closestream(chan->stream);
209                         chan->stream = NULL;
210                 }
211                 ast_verb(3, "Stopped music on hold on %s\n", chan->name);
212
213                 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
214                         ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
215                 }
216                 state->save_pos = state->pos;
217         }
218         if (ast_atomic_dec_and_test(&state->class->inuse) && state->class->delete)
219                 ast_moh_destroy_one(state->class);
220 }
221
222
223 static int ast_moh_files_next(struct ast_channel *chan) 
224 {
225         struct moh_files_state *state = chan->music_state;
226         int tries;
227
228         /* Discontinue a stream if it is running already */
229         if (chan->stream) {
230                 ast_closestream(chan->stream);
231                 chan->stream = NULL;
232         }
233
234         /* If a specific file has been saved, use it */
235         if (state->save_pos >= 0) {
236                 state->pos = state->save_pos;
237                 state->save_pos = -1;
238         } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
239                 /* Get a random file and ensure we can open it */
240                 for (tries = 0; tries < 20; tries++) {
241                         state->pos = rand() % state->class->total_files;
242                         if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
243                                 break;
244                 }
245                 state->samples = 0;
246         } else {
247                 /* This is easy, just increment our position and make sure we don't exceed the total file count */
248                 state->pos++;
249                 state->pos %= state->class->total_files;
250                 state->samples = 0;
251         }
252
253         if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
254                 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
255                 state->pos++;
256                 state->pos %= state->class->total_files;
257                 return -1;
258         }
259
260         ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
261
262         if (state->samples)
263                 ast_seekstream(chan->stream, state->samples, SEEK_SET);
264
265         return 0;
266 }
267
268
269 static struct ast_frame *moh_files_readframe(struct ast_channel *chan) 
270 {
271         struct ast_frame *f = NULL;
272         
273         if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
274                 if (!ast_moh_files_next(chan))
275                         f = ast_readframe(chan->stream);
276         }
277
278         return f;
279 }
280
281 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
282 {
283         struct moh_files_state *state = chan->music_state;
284         struct ast_frame *f = NULL;
285         int res = 0;
286
287         state->sample_queue += samples;
288
289         while (state->sample_queue > 0) {
290                 if ((f = moh_files_readframe(chan))) {
291                         state->samples += f->samples;
292                         res = ast_write(chan, f);
293                         state->sample_queue -= f->samples;
294                         ast_frfree(f);
295                         if (res < 0) {
296                                 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
297                                 return -1;
298                         }
299                 } else
300                         return -1;      
301         }
302         return res;
303 }
304
305
306 static void *moh_files_alloc(struct ast_channel *chan, void *params)
307 {
308         struct moh_files_state *state;
309         struct mohclass *class = params;
310
311         if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
312                 chan->music_state = state;
313                 state->class = class;
314                 state->save_pos = -1;
315         } else 
316                 state = chan->music_state;
317
318         if (state) {
319                 if (state->class != class) {
320                         /* initialize */
321                         memset(state, 0, sizeof(*state));
322                         state->class = class;
323                         if (ast_test_flag(state->class, MOH_RANDOMIZE))
324                                 state->pos = ast_random() % class->total_files;
325                 }
326
327                 state->origwfmt = chan->writeformat;
328
329                 ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
330         }
331         
332         return chan->music_state;
333 }
334
335 /*! \note This function should be called with the mohclasses list locked */
336 static struct mohclass *get_mohbydigit(char digit)
337 {
338         struct mohclass *moh = NULL;
339
340         AST_RWLIST_TRAVERSE(&mohclasses, moh, list) {
341                 if (digit == moh->digit)
342                         break;
343         }
344
345         return moh;
346 }
347
348 static void moh_handle_digit(struct ast_channel *chan, char digit)
349 {
350         struct mohclass *moh;
351         const char *classname = NULL;
352
353         AST_RWLIST_RDLOCK(&mohclasses);
354         if ((moh = get_mohbydigit(digit)))
355                 classname = ast_strdupa(moh->name);
356         AST_RWLIST_UNLOCK(&mohclasses);
357
358         if (!moh)
359                 return;
360
361         ast_moh_stop(chan);
362         ast_moh_start(chan, classname, NULL);
363 }
364
365 static struct ast_generator moh_file_stream = 
366 {
367         alloc: moh_files_alloc,
368         release: moh_files_release,
369         generate: moh_files_generator,
370         digit: moh_handle_digit,
371 };
372
373 static int spawn_mp3(struct mohclass *class)
374 {
375         int fds[2];
376         int files = 0;
377         char fns[MAX_MP3S][80];
378         char *argv[MAX_MP3S + 50];
379         char xargs[256];
380         char *argptr;
381         int argc = 0;
382         DIR *dir = NULL;
383         struct dirent *de;
384         sigset_t signal_set, old_set;
385
386         
387         if (!strcasecmp(class->dir, "nodir")) {
388                 files = 1;
389         } else {
390                 dir = opendir(class->dir);
391                 if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) {
392                         ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
393                         return -1;
394                 }
395         }
396
397         if (!ast_test_flag(class, MOH_CUSTOM)) {
398                 argv[argc++] = "mpg123";
399                 argv[argc++] = "-q";
400                 argv[argc++] = "-s";
401                 argv[argc++] = "--mono";
402                 argv[argc++] = "-r";
403                 argv[argc++] = "8000";
404                 
405                 if (!ast_test_flag(class, MOH_SINGLE)) {
406                         argv[argc++] = "-b";
407                         argv[argc++] = "2048";
408                 }
409                 
410                 argv[argc++] = "-f";
411                 
412                 if (ast_test_flag(class, MOH_QUIET))
413                         argv[argc++] = "4096";
414                 else
415                         argv[argc++] = "8192";
416                 
417                 /* Look for extra arguments and add them to the list */
418                 ast_copy_string(xargs, class->args, sizeof(xargs));
419                 argptr = xargs;
420                 while (!ast_strlen_zero(argptr)) {
421                         argv[argc++] = argptr;
422                         strsep(&argptr, ",");
423                 }
424         } else  {
425                 /* Format arguments for argv vector */
426                 ast_copy_string(xargs, class->args, sizeof(xargs));
427                 argptr = xargs;
428                 while (!ast_strlen_zero(argptr)) {
429                         argv[argc++] = argptr;
430                         strsep(&argptr, " ");
431                 }
432         }
433
434
435         if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) {
436                 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
437                 argv[argc++] = fns[files];
438                 files++;
439         } else if (dir) {
440                 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
441                         if ((strlen(de->d_name) > 3) && 
442                             ((ast_test_flag(class, MOH_CUSTOM) && 
443                               (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 
444                                !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
445                              !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
446                                 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
447                                 argv[argc++] = fns[files];
448                                 files++;
449                         }
450                 }
451         }
452         argv[argc] = NULL;
453         if (dir) {
454                 closedir(dir);
455         }
456         if (pipe(fds)) {        
457                 ast_log(LOG_WARNING, "Pipe failed\n");
458                 return -1;
459         }
460         if (!files) {
461                 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
462                 close(fds[0]);
463                 close(fds[1]);
464                 return -1;
465         }
466         if (time(NULL) - class->start < respawn_time) {
467                 sleep(respawn_time - (time(NULL) - class->start));
468         }
469
470         /* Block signals during the fork() */
471         sigfillset(&signal_set);
472         pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
473
474         time(&class->start);
475         class->pid = fork();
476         if (class->pid < 0) {
477                 close(fds[0]);
478                 close(fds[1]);
479                 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
480                 return -1;
481         }
482         if (!class->pid) {
483                 int x;
484
485                 if (ast_opt_high_priority)
486                         ast_set_priority(0);
487
488                 /* Reset ignored signals back to default */
489                 signal(SIGPIPE, SIG_DFL);
490                 pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
491
492                 close(fds[0]);
493                 /* Stdout goes to pipe */
494                 dup2(fds[1], STDOUT_FILENO);
495                 /* Close unused file descriptors */
496                 for (x=3;x<8192;x++) {
497                         if (-1 != fcntl(x, F_GETFL)) {
498                                 close(x);
499                         }
500                 }
501                 /* Child */
502                 chdir(class->dir);
503                 if (ast_test_flag(class, MOH_CUSTOM)) {
504                         execv(argv[0], argv);
505                 } else {
506                         /* Default install is /usr/local/bin */
507                         execv(LOCAL_MPG_123, argv);
508                         /* Many places have it in /usr/bin */
509                         execv(MPG_123, argv);
510                         /* Check PATH as a last-ditch effort */
511                         execvp("mpg123", argv);
512                 }
513                 ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
514                 close(fds[1]);
515                 _exit(1);
516         } else {
517                 /* Parent */
518                 pthread_sigmask(SIG_SETMASK, &old_set, NULL);
519                 close(fds[1]);
520         }
521         return fds[0];
522 }
523
524 static void *monmp3thread(void *data)
525 {
526 #define MOH_MS_INTERVAL         100
527
528         struct mohclass *class = data;
529         struct mohdata *moh;
530         char buf[8192];
531         short sbuf[8192];
532         int res, res2;
533         int len;
534         struct timeval tv, tv_tmp;
535
536         tv.tv_sec = 0;
537         tv.tv_usec = 0;
538         for(;/* ever */;) {
539                 pthread_testcancel();
540                 /* Spawn mp3 player if it's not there */
541                 if (class->srcfd < 0) {
542                         if ((class->srcfd = spawn_mp3(class)) < 0) {
543                                 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
544                                 /* Try again later */
545                                 sleep(500);
546                                 pthread_testcancel();
547                         }
548                 }
549                 if (class->pseudofd > -1) {
550 #ifdef SOLARIS
551                         thr_yield();
552 #endif
553                         /* Pause some amount of time */
554                         res = read(class->pseudofd, buf, sizeof(buf));
555                         pthread_testcancel();
556                 } else {
557                         long delta;
558                         /* Reliable sleep */
559                         tv_tmp = ast_tvnow();
560                         if (ast_tvzero(tv))
561                                 tv = tv_tmp;
562                         delta = ast_tvdiff_ms(tv_tmp, tv);
563                         if (delta < MOH_MS_INTERVAL) {  /* too early */
564                                 tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
565                                 usleep(1000 * (MOH_MS_INTERVAL - delta));
566                                 pthread_testcancel();
567                         } else {
568                                 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
569                                 tv = tv_tmp;
570                         }
571                         res = 8 * MOH_MS_INTERVAL;      /* 8 samples per millisecond */
572                 }
573                 if (AST_LIST_EMPTY(&class->members))
574                         continue;
575                 /* Read mp3 audio */
576                 len = ast_codec_get_len(class->format, res);
577                 
578                 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
579                         if (!res2) {
580                                 close(class->srcfd);
581                                 class->srcfd = -1;
582                                 pthread_testcancel();
583                                 if (class->pid > 1) {
584                                         kill(class->pid, SIGHUP);
585                                         usleep(100000);
586                                         kill(class->pid, SIGTERM);
587                                         usleep(100000);
588                                         kill(class->pid, SIGKILL);
589                                         class->pid = 0;
590                                 }
591                         } else {
592                                 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
593                         }
594                         continue;
595                 }
596                 pthread_testcancel();
597                 AST_RWLIST_RDLOCK(&mohclasses);
598                 AST_RWLIST_TRAVERSE(&class->members, moh, list) {
599                         /* Write data */
600                         if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
601                                 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
602                         }
603                 }
604                 AST_RWLIST_UNLOCK(&mohclasses);
605         }
606         return NULL;
607 }
608
609 static int play_moh_exec(struct ast_channel *chan, void *data)
610 {
611         if (ast_moh_start(chan, data, NULL)) {
612                 ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
613                 return 0;
614         }
615         while (!ast_safe_sleep(chan, 10000));
616         ast_moh_stop(chan);
617         return -1;
618 }
619
620 static int wait_moh_exec(struct ast_channel *chan, void *data)
621 {
622         int res;
623         if (!data || !atoi(data)) {
624                 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
625                 return -1;
626         }
627         if (ast_moh_start(chan, NULL, NULL)) {
628                 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
629                 return 0;
630         }
631         res = ast_safe_sleep(chan, atoi(data) * 1000);
632         ast_moh_stop(chan);
633         return res;
634 }
635
636 static int set_moh_exec(struct ast_channel *chan, void *data)
637 {
638         if (ast_strlen_zero(data)) {
639                 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
640                 return -1;
641         }
642         ast_string_field_set(chan, musicclass, data);
643         return 0;
644 }
645
646 static int start_moh_exec(struct ast_channel *chan, void *data)
647 {
648         char *class = NULL;
649         if (data && strlen(data))
650                 class = data;
651         if (ast_moh_start(chan, class, NULL)) 
652                 ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
653
654         return 0;
655 }
656
657 static int stop_moh_exec(struct ast_channel *chan, void *data)
658 {
659         ast_moh_stop(chan);
660
661         return 0;
662 }
663
664 /*! \note This function should be called with the mohclasses list locked */
665 static struct mohclass *get_mohbyname(const char *name, int warn)
666 {
667         struct mohclass *moh = NULL;
668
669         AST_RWLIST_TRAVERSE(&mohclasses, moh, list) {
670                 if (!strcasecmp(name, moh->name))
671                         break;
672         }
673
674         if (!moh && warn)
675                 ast_log(LOG_WARNING, "Music on Hold class '%s' not found\n", name);
676
677         return moh;
678 }
679
680 static struct mohdata *mohalloc(struct mohclass *cl)
681 {
682         struct mohdata *moh;
683         long flags;     
684         
685         if (!(moh = ast_calloc(1, sizeof(*moh))))
686                 return NULL;
687         
688         if (pipe(moh->pipe)) {
689                 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
690                 ast_free(moh);
691                 return NULL;
692         }
693
694         /* Make entirely non-blocking */
695         flags = fcntl(moh->pipe[0], F_GETFL);
696         fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
697         flags = fcntl(moh->pipe[1], F_GETFL);
698         fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
699
700         moh->f.frametype = AST_FRAME_VOICE;
701         moh->f.subclass = cl->format;
702         moh->f.offset = AST_FRIENDLY_OFFSET;
703
704         moh->parent = cl;
705
706         AST_RWLIST_WRLOCK(&mohclasses);
707         AST_LIST_INSERT_HEAD(&cl->members, moh, list);
708         AST_RWLIST_UNLOCK(&mohclasses);
709         
710         return moh;
711 }
712
713 static void moh_release(struct ast_channel *chan, void *data)
714 {
715         struct mohdata *moh = data;
716         int oldwfmt;
717
718         AST_RWLIST_WRLOCK(&mohclasses);
719         AST_RWLIST_REMOVE(&moh->parent->members, moh, list);    
720         AST_RWLIST_UNLOCK(&mohclasses);
721         
722         close(moh->pipe[0]);
723         close(moh->pipe[1]);
724         oldwfmt = moh->origwfmt;
725         if (moh->parent->delete && ast_atomic_dec_and_test(&moh->parent->inuse))
726                 ast_moh_destroy_one(moh->parent);
727         ast_free(moh);
728         if (chan) {
729                 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) 
730                         ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
731                 ast_verb(3, "Stopped music on hold on %s\n", chan->name);
732         }
733 }
734
735 static void *moh_alloc(struct ast_channel *chan, void *params)
736 {
737         struct mohdata *res;
738         struct mohclass *class = params;
739
740         if ((res = mohalloc(class))) {
741                 res->origwfmt = chan->writeformat;
742                 if (ast_set_write_format(chan, class->format)) {
743                         ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
744                         moh_release(NULL, res);
745                         res = NULL;
746                 }
747                 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
748         }
749         return res;
750 }
751
752 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
753 {
754         struct mohdata *moh = data;
755         short buf[1280 + AST_FRIENDLY_OFFSET / 2];
756         int res;
757
758         if (!moh->parent->pid)
759                 return -1;
760
761         len = ast_codec_get_len(moh->parent->format, samples);
762
763         if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
764                 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
765                 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
766         }
767         res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
768         if (res <= 0)
769                 return 0;
770
771         moh->f.datalen = res;
772         moh->f.data = buf + AST_FRIENDLY_OFFSET / 2;
773         moh->f.samples = ast_codec_get_samples(&moh->f);
774
775         if (ast_write(chan, &moh->f) < 0) {
776                 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
777                 return -1;
778         }
779
780         return 0;
781 }
782
783 static struct ast_generator mohgen = 
784 {
785         alloc: moh_alloc,
786         release: moh_release,
787         generate: moh_generate,
788         digit: moh_handle_digit
789 };
790
791 static int moh_add_file(struct mohclass *class, const char *filepath)
792 {
793         if (!class->allowed_files) {
794                 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
795                         return -1;
796                 class->allowed_files = INITIAL_NUM_FILES;
797         } else if (class->total_files == class->allowed_files) {
798                 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
799                         class->allowed_files = 0;
800                         class->total_files = 0;
801                         return -1;
802                 }
803                 class->allowed_files *= 2;
804         }
805
806         if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
807                 return -1;
808
809         class->total_files++;
810
811         return 0;
812 }
813
814 static int moh_sort_compare(const void *i1, const void *i2)
815 {
816         char *s1, *s2;
817
818         s1 = ((char **)i1)[0];
819         s2 = ((char **)i2)[0];
820
821         return strcasecmp(s1, s2);
822 }
823
824 static int moh_scan_files(struct mohclass *class) {
825
826         DIR *files_DIR;
827         struct dirent *files_dirent;
828         char path[PATH_MAX];
829         char filepath[PATH_MAX];
830         char *ext;
831         struct stat statbuf;
832         int dirnamelen;
833         int i;
834         
835         files_DIR = opendir(class->dir);
836         if (!files_DIR) {
837                 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir);
838                 return -1;
839         }
840
841         for (i = 0; i < class->total_files; i++)
842                 ast_free(class->filearray[i]);
843
844         class->total_files = 0;
845         dirnamelen = strlen(class->dir) + 2;
846         getcwd(path, sizeof(path));
847         chdir(class->dir);
848         while ((files_dirent = readdir(files_DIR))) {
849                 /* The file name must be at least long enough to have the file type extension */
850                 if ((strlen(files_dirent->d_name) < 4))
851                         continue;
852
853                 /* Skip files that starts with a dot */
854                 if (files_dirent->d_name[0] == '.')
855                         continue;
856
857                 /* Skip files without extensions... they are not audio */
858                 if (!strchr(files_dirent->d_name, '.'))
859                         continue;
860
861                 snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
862
863                 if (stat(filepath, &statbuf))
864                         continue;
865
866                 if (!S_ISREG(statbuf.st_mode))
867                         continue;
868
869                 if ((ext = strrchr(filepath, '.')))
870                         *ext = '\0';
871
872                 /* if the file is present in multiple formats, ensure we only put it into the list once */
873                 for (i = 0; i < class->total_files; i++)
874                         if (!strcmp(filepath, class->filearray[i]))
875                                 break;
876
877                 if (i == class->total_files) {
878                         if (moh_add_file(class, filepath))
879                                 break;
880                 }
881         }
882
883         closedir(files_DIR);
884         chdir(path);
885         if (ast_test_flag(class, MOH_SORTALPHA))
886                 qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
887         return class->total_files;
888 }
889
890 static int moh_diff(struct mohclass *old, struct mohclass *new)
891 {
892         if (!old || !new)
893                 return -1;
894
895         if (strcmp(old->dir, new->dir))
896                 return -1;
897         else if (strcmp(old->mode, new->mode))
898                 return -1;
899         else if (strcmp(old->args, new->args))
900                 return -1;
901         else if (old->flags != new->flags)
902                 return -1;
903
904         return 0;
905 }
906
907 static int moh_register(struct mohclass *moh, int reload)
908 {
909 #ifdef HAVE_ZAPTEL
910         int x;
911 #endif
912         struct mohclass *mohclass = NULL;
913
914         AST_RWLIST_WRLOCK(&mohclasses);
915         if ((mohclass = get_mohbyname(moh->name, 0)) && !moh_diff(mohclass, moh)) {
916                 mohclass->delete = 0;
917                 if (reload) {
918                         ast_debug(1, "Music on Hold class '%s' left alone from initial load.\n", moh->name);
919                 } else {
920                         ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
921                 }
922                 ast_free(moh);  
923                 AST_RWLIST_UNLOCK(&mohclasses);
924                 return -1;
925         }
926         AST_RWLIST_UNLOCK(&mohclasses);
927
928         time(&moh->start);
929         moh->start -= respawn_time;
930         
931         if (!strcasecmp(moh->mode, "files")) {
932                 if (!moh_scan_files(moh)) {
933                         ast_moh_free_class(&moh);
934                         return -1;
935                 }
936                 if (strchr(moh->args, 'r'))
937                         ast_set_flag(moh, MOH_RANDOMIZE);
938         } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
939
940                 if (!strcasecmp(moh->mode, "custom"))
941                         ast_set_flag(moh, MOH_CUSTOM);
942                 else if (!strcasecmp(moh->mode, "mp3nb"))
943                         ast_set_flag(moh, MOH_SINGLE);
944                 else if (!strcasecmp(moh->mode, "quietmp3nb"))
945                         ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
946                 else if (!strcasecmp(moh->mode, "quietmp3"))
947                         ast_set_flag(moh, MOH_QUIET);
948                 
949                 moh->srcfd = -1;
950 #ifdef HAVE_ZAPTEL
951                 /* Open /dev/zap/pseudo for timing...  Is
952                    there a better, yet reliable way to do this? */
953                 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
954                 if (moh->pseudofd < 0) {
955                         ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
956                 } else {
957                         x = 320;
958                         ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
959                 }
960 #else
961                 moh->pseudofd = -1;
962 #endif
963                 if (ast_pthread_create_background(&moh->thread, NULL, monmp3thread, moh)) {
964                         ast_log(LOG_WARNING, "Unable to create moh...\n");
965                         if (moh->pseudofd > -1)
966                                 close(moh->pseudofd);
967                         ast_moh_free_class(&moh);
968                         return -1;
969                 }
970         } else {
971                 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
972                 ast_moh_free_class(&moh);
973                 return -1;
974         }
975
976         AST_RWLIST_WRLOCK(&mohclasses);
977         AST_RWLIST_INSERT_HEAD(&mohclasses, moh, list);
978         AST_RWLIST_UNLOCK(&mohclasses);
979         
980         return 0;
981 }
982
983 static void local_ast_moh_cleanup(struct ast_channel *chan)
984 {
985         if (chan->music_state) {
986                 ast_free(chan->music_state);
987                 chan->music_state = NULL;
988         }
989 }
990
991 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
992 {
993         struct mohclass *mohclass = NULL;
994
995         /* The following is the order of preference for which class to use:
996          * 1) The channels explicitly set musicclass, which should *only* be
997          *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
998          * 2) The mclass argument. If a channel is calling ast_moh_start() as the
999          *    result of receiving a HOLD control frame, this should be the
1000          *    payload that came with the frame.
1001          * 3) The interpclass argument. This would be from the mohinterpret
1002          *    option from channel drivers. This is the same as the old musicclass
1003          *    option.
1004          * 4) The default class.
1005          */
1006         AST_RWLIST_RDLOCK(&mohclasses);
1007         if (!ast_strlen_zero(chan->musicclass))
1008                 mohclass = get_mohbyname(chan->musicclass, 1);
1009         if (!mohclass && !ast_strlen_zero(mclass))
1010                 mohclass = get_mohbyname(mclass, 1);
1011         if (!mohclass && !ast_strlen_zero(interpclass))
1012                 mohclass = get_mohbyname(interpclass, 1);
1013         if (!mohclass)  
1014                 mohclass = get_mohbyname("default", 1);
1015         if (mohclass)
1016                 ast_atomic_fetchadd_int(&mohclass->inuse, +1);
1017         AST_RWLIST_UNLOCK(&mohclasses);
1018
1019         if (!mohclass)
1020                 return -1;
1021
1022         ast_set_flag(chan, AST_FLAG_MOH);
1023         if (mohclass->total_files) {
1024                 return ast_activate_generator(chan, &moh_file_stream, mohclass);
1025         } else
1026                 return ast_activate_generator(chan, &mohgen, mohclass);
1027 }
1028
1029 static void local_ast_moh_stop(struct ast_channel *chan)
1030 {
1031         ast_clear_flag(chan, AST_FLAG_MOH);
1032         ast_deactivate_generator(chan);
1033
1034         if (chan->music_state) {
1035                 if (chan->stream) {
1036                         ast_closestream(chan->stream);
1037                         chan->stream = NULL;
1038                 }
1039         }
1040 }
1041
1042 static struct mohclass *moh_class_malloc(void)
1043 {
1044         struct mohclass *class;
1045
1046         if ((class = ast_calloc(1, sizeof(*class))))
1047                 class->format = AST_FORMAT_SLINEAR;
1048
1049         return class;
1050 }
1051
1052 static int load_moh_classes(int reload)
1053 {
1054         struct ast_config *cfg;
1055         struct ast_variable *var;
1056         struct mohclass *class; 
1057         char *cat;
1058         int numclasses = 0;
1059         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1060
1061         cfg = ast_config_load("musiconhold.conf", config_flags);
1062
1063         if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
1064                 return 0;
1065
1066         if (reload) {
1067                 AST_RWLIST_WRLOCK(&mohclasses);
1068                 AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1069                         class->delete = 1;
1070                 }
1071                 AST_RWLIST_UNLOCK(&mohclasses);
1072         }
1073
1074         cat = ast_category_browse(cfg, NULL);
1075         for (; cat; cat = ast_category_browse(cfg, cat)) {
1076                 /* These names were deprecated in 1.4 and should not be used until after the next major release. */
1077                 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {                       
1078                         if (!(class = moh_class_malloc()))
1079                                 break;
1080
1081                         ast_copy_string(class->name, cat, sizeof(class->name)); 
1082                         var = ast_variable_browse(cfg, cat);
1083                         while (var) {
1084                                 if (!strcasecmp(var->name, "mode"))
1085                                         ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
1086                                 else if (!strcasecmp(var->name, "directory"))
1087                                         ast_copy_string(class->dir, var->value, sizeof(class->dir));
1088                                 else if (!strcasecmp(var->name, "application"))
1089                                         ast_copy_string(class->args, var->value, sizeof(class->args));
1090                                 else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
1091                                         class->digit = *var->value;
1092                                 else if (!strcasecmp(var->name, "random"))
1093                                         ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
1094                                 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
1095                                         ast_set_flag(class, MOH_RANDOMIZE);
1096                                 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 
1097                                         ast_set_flag(class, MOH_SORTALPHA);
1098                                 else if (!strcasecmp(var->name, "format")) {
1099                                         class->format = ast_getformatbyname(var->value);
1100                                         if (!class->format) {
1101                                                 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1102                                                 class->format = AST_FORMAT_SLINEAR;
1103                                         }
1104                                 }
1105                                 var = var->next;
1106                         }
1107
1108                         if (ast_strlen_zero(class->dir)) {
1109                                 if (!strcasecmp(class->mode, "custom")) {
1110                                         strcpy(class->dir, "nodir");
1111                                 } else {
1112                                         ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1113                                         ast_free(class);
1114                                         continue;
1115                                 }
1116                         }
1117                         if (ast_strlen_zero(class->mode)) {
1118                                 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1119                                 ast_free(class);
1120                                 continue;
1121                         }
1122                         if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1123                                 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1124                                 ast_free(class);
1125                                 continue;
1126                         }
1127
1128                         /* Don't leak a class when it's already registered */
1129                         moh_register(class, reload);
1130
1131                         numclasses++;
1132                 }
1133         }
1134
1135         ast_config_destroy(cfg);
1136
1137         return numclasses;
1138 }
1139
1140 static int ast_moh_destroy_one(struct mohclass *moh)
1141 {
1142         char buff[8192];
1143         int bytes, tbytes = 0, stime = 0, pid = 0;
1144
1145         if (moh) {
1146                 if (moh->pid > 1) {
1147                         ast_debug(1, "killing %d!\n", moh->pid);
1148                         stime = time(NULL) + 2;
1149                         pid = moh->pid;
1150                         moh->pid = 0;
1151                         /* Back when this was just mpg123, SIGKILL was fine.  Now we need
1152                          * to give the process a reason and time enough to kill off its
1153                          * children. */
1154                         kill(pid, SIGHUP);
1155                         usleep(100000);
1156                         kill(pid, SIGTERM);
1157                         usleep(100000);
1158                         kill(pid, SIGKILL);
1159                         while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
1160                                 tbytes = tbytes + bytes;
1161                         ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1162                         close(moh->srcfd);
1163                 }
1164                 ast_moh_free_class(&moh);
1165         }
1166
1167         return 0;
1168 }
1169
1170 static void ast_moh_destroy(void)
1171 {
1172         struct mohclass *moh;
1173
1174         ast_verb(2, "Destroying musiconhold processes\n");
1175
1176         AST_RWLIST_WRLOCK(&mohclasses);
1177         while ((moh = AST_RWLIST_REMOVE_HEAD(&mohclasses, list))) {
1178                 ast_moh_destroy_one(moh);
1179         }
1180         AST_RWLIST_UNLOCK(&mohclasses);
1181 }
1182
1183 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1184 {
1185         switch (cmd) {
1186         case CLI_INIT:
1187                 e->command = "moh reload";
1188                 e->usage =
1189                         "Usage: moh reload\n"
1190                         "       Reloads the MusicOnHold module.\n"
1191                         "       Alias for 'module reload res_musiconhold.so'\n";
1192                 return NULL;
1193         case CLI_GENERATE:
1194                 return NULL;
1195         }
1196
1197         if (a->argc != e->args)
1198                 return CLI_SHOWUSAGE;
1199
1200         reload();
1201
1202         return CLI_SUCCESS;
1203 }
1204
1205 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1206 {
1207         int i;
1208         struct mohclass *class;
1209
1210         switch (cmd) {
1211         case CLI_INIT:
1212                 e->command = "moh show files";
1213                 e->usage =
1214                         "Usage: moh show files\n"
1215                         "       Lists all loaded file-based MusicOnHold classes and their\n"
1216                         "       files.\n";
1217                 return NULL;
1218         case CLI_GENERATE:
1219                 return NULL;
1220         }
1221
1222         if (a->argc != e->args)
1223                 return CLI_SHOWUSAGE;
1224
1225         AST_RWLIST_RDLOCK(&mohclasses);
1226         AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1227                 if (!class->total_files)
1228                         continue;
1229
1230                 ast_cli(a->fd, "Class: %s\n", class->name);
1231                 for (i = 0; i < class->total_files; i++)
1232                         ast_cli(a->fd, "\tFile: %s\n", class->filearray[i]);
1233         }
1234         AST_RWLIST_UNLOCK(&mohclasses);
1235
1236         return CLI_SUCCESS;
1237 }
1238
1239 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1240 {
1241         struct mohclass *class;
1242
1243         switch (cmd) {
1244         case CLI_INIT:
1245                 e->command = "moh show classes";
1246                 e->usage =
1247                         "Usage: moh show classes\n"
1248                         "       Lists all MusicOnHold classes.\n";
1249                 return NULL;
1250         case CLI_GENERATE:
1251                 return NULL;
1252         }
1253
1254         if (a->argc != e->args)
1255                 return CLI_SHOWUSAGE;
1256
1257         AST_RWLIST_RDLOCK(&mohclasses);
1258         AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1259                 ast_cli(a->fd, "Class: %s\n", class->name);
1260                 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1261                 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1262                 ast_cli(a->fd, "\tUse Count: %d\n", class->inuse);
1263                 if (class->digit)
1264                         ast_cli(a->fd, "\tDigit: %c\n", class->digit);
1265                 if (ast_test_flag(class, MOH_CUSTOM))
1266                         ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1267                 if (strcasecmp(class->mode, "files"))
1268                         ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
1269         }
1270         AST_RWLIST_UNLOCK(&mohclasses);
1271
1272         return CLI_SUCCESS;
1273 }
1274
1275 static struct ast_cli_entry cli_moh[] = {
1276         NEW_CLI(handle_cli_moh_reload,       "Reload MusicOnHold"),
1277         NEW_CLI(handle_cli_moh_show_classes, "List MusicOnHold classes"),
1278         NEW_CLI(handle_cli_moh_show_files,   "List MusicOnHold file-based classes")
1279 };
1280
1281 static int init_classes(int reload) 
1282 {
1283         struct mohclass *moh;
1284     
1285         if (!load_moh_classes(reload))          /* Load classes from config */
1286                 return 0;                       /* Return if nothing is found */
1287
1288         AST_RWLIST_WRLOCK(&mohclasses);
1289         AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mohclasses, moh, list) {
1290                 if (reload && moh->delete) {
1291                         AST_RWLIST_REMOVE_CURRENT(&mohclasses, list);
1292                         if (!moh->inuse)
1293                                 ast_moh_destroy_one(moh);
1294                 } else if (moh->total_files)
1295                         moh_scan_files(moh);
1296         }
1297         AST_RWLIST_TRAVERSE_SAFE_END
1298         AST_RWLIST_UNLOCK(&mohclasses);
1299
1300         return 1;
1301 }
1302
1303 static int load_module(void)
1304 {
1305         int res;
1306
1307         res = ast_register_application(play_moh, play_moh_exec, play_moh_syn, play_moh_desc);
1308         ast_register_atexit(ast_moh_destroy);
1309         ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
1310         if (!res)
1311                 res = ast_register_application(wait_moh, wait_moh_exec, wait_moh_syn, wait_moh_desc);
1312         if (!res)
1313                 res = ast_register_application(set_moh, set_moh_exec, set_moh_syn, set_moh_desc);
1314         if (!res)
1315                 res = ast_register_application(start_moh, start_moh_exec, start_moh_syn, start_moh_desc);
1316         if (!res)
1317                 res = ast_register_application(stop_moh, stop_moh_exec, stop_moh_syn, stop_moh_desc);
1318
1319         if (!init_classes(0)) {         /* No music classes configured, so skip it */
1320                 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.\n");
1321         } else {
1322                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1323         }
1324
1325         return 0;
1326 }
1327
1328 static int reload(void)
1329 {
1330         if (init_classes(1))
1331                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1332
1333         return 0;
1334 }
1335
1336 static int unload_module(void)
1337 {
1338         return -1;
1339 }
1340
1341 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Music On Hold Resource",
1342                 .load = load_module,
1343                 .unload = unload_module,
1344                 .reload = reload,
1345                );