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