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