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