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