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