Merged revisions 90101 via svnmerge from
[asterisk/asterisk.git] / res / res_musiconhold.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Routines implementing music on hold
22  *
23  * \arg See also \ref Config_moh
24  * 
25  * \author Mark Spencer <markster@digium.com>
26  */
27
28 /*** MODULEINFO
29         <conflict>win32</conflict>
30         <use>zaptel</use>
31  ***/
32
33 #include "asterisk.h"
34
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36
37 #include <ctype.h>
38 #include <signal.h>
39 #include <sys/time.h>
40 #include <sys/signal.h>
41 #include <netinet/in.h>
42 #include <sys/stat.h>
43 #include <dirent.h>
44 #include <sys/ioctl.h>
45 #ifdef SOLARIS
46 #include <thread.h>
47 #endif
48
49 #include "asterisk/zapata.h"
50
51 #include "asterisk/lock.h"
52 #include "asterisk/file.h"
53 #include "asterisk/channel.h"
54 #include "asterisk/pbx.h"
55 #include "asterisk/module.h"
56 #include "asterisk/translate.h"
57 #include "asterisk/say.h"
58 #include "asterisk/musiconhold.h"
59 #include "asterisk/config.h"
60 #include "asterisk/utils.h"
61 #include "asterisk/cli.h"
62 #include "asterisk/stringfields.h"
63 #include "asterisk/linkedlists.h"
64
65 #define INITIAL_NUM_FILES   8
66
67 static char *play_moh = "MusicOnHold";
68 static char *wait_moh = "WaitMusicOnHold";
69 static char *set_moh = "SetMusicOnHold";
70 static char *start_moh = "StartMusicOnHold";
71 static char *stop_moh = "StopMusicOnHold";
72
73 static char *play_moh_syn = "Play Music On Hold indefinitely";
74 static char *wait_moh_syn = "Wait, playing Music On Hold";
75 static char *set_moh_syn = "Set default Music On Hold class";
76 static char *start_moh_syn = "Play Music On Hold";
77 static char *stop_moh_syn = "Stop Playing Music On Hold";
78
79 static char *play_moh_desc = "  MusicOnHold(class):\n"
80 "Plays hold music specified by class.  If omitted, the default\n"
81 "music source for the channel will be used. Set the default \n"
82 "class with the SetMusicOnHold() application.\n"
83 "Returns -1 on hangup.\n"
84 "Never returns otherwise.\n";
85
86 static char *wait_moh_desc = "  WaitMusicOnHold(delay):\n"
87 "Plays hold music specified number of seconds.  Returns 0 when\n"
88 "done, or -1 on hangup.  If no hold music is available, the delay will\n"
89 "still occur with no sound.\n";
90
91 static char *set_moh_desc = "  SetMusicOnHold(class):\n"
92 "Sets the default class for music on hold for a given channel.  When\n"
93 "music on hold is activated, this class will be used to select which\n"
94 "music is played.\n";
95
96 static char *start_moh_desc = "  StartMusicOnHold(class):\n"
97 "Starts playing music on hold, uses default music class for channel.\n"
98 "Starts playing music specified by class.  If omitted, the default\n"
99 "music source for the channel will be used.  Always returns 0.\n";
100
101 static char *stop_moh_desc = "  StopMusicOnHold(): "
102 "Stops playing music on hold.\n";
103
104 static int respawn_time = 20;
105
106 struct moh_files_state {
107         struct mohclass *class;
108         int origwfmt;
109         int samples;
110         int sample_queue;
111         int pos;
112         int save_pos;
113 };
114
115 #define MOH_QUIET               (1 << 0)
116 #define MOH_SINGLE              (1 << 1)
117 #define MOH_CUSTOM              (1 << 2)
118 #define MOH_RANDOMIZE           (1 << 3)
119 #define MOH_SORTALPHA           (1 << 4)
120
121 #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                         ast_variables_destroy(var);
1112                         if (ast_strlen_zero(mohclass->dir)) {
1113                                 if (!strcasecmp(mohclass->mode, "custom")) {
1114                                         strcpy(mohclass->dir, "nodir");
1115                                 } else {
1116                                         ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
1117                                         ast_free(mohclass);
1118                                         return -1;
1119                                 }
1120                         }
1121                         if (ast_strlen_zero(mohclass->mode)) {
1122                                 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
1123                                 ast_free(mohclass);
1124                                 return -1;
1125                         }
1126                         if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
1127                                 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
1128                                 ast_free(mohclass);
1129                                 return -1;
1130                         }
1131
1132                         if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
1133                                 /* CACHERTCLASSES enabled, let's add this class to default tree */
1134                                 if (state && state->class) {
1135                                         /* Class already exist for this channel */
1136                                         ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1137                                         if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1138                                                 /* we found RT class with the same name, seems like we should continue playing existing one */
1139                                                 ast_moh_free_class(&mohclass);
1140                                                 mohclass = state->class;
1141                                         }
1142                                 }
1143                                 moh_register(mohclass, 0);
1144                         } else {
1145
1146                                 /* We don't register RT moh class, so let's init it manualy */
1147
1148                                 time(&mohclass->start);
1149                                 mohclass->start -= respawn_time;
1150         
1151                                 if (!strcasecmp(mohclass->mode, "files")) {
1152                                         if (!moh_scan_files(mohclass)) {
1153                                                 ast_moh_free_class(&mohclass);
1154                                                 return -1;
1155                                         }
1156                                         if (strchr(mohclass->args, 'r'))
1157                                                 ast_set_flag(mohclass, MOH_RANDOMIZE);
1158                                 } 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")) {
1159
1160                                         if (!strcasecmp(mohclass->mode, "custom"))
1161                                                 ast_set_flag(mohclass, MOH_CUSTOM);
1162                                         else if (!strcasecmp(mohclass->mode, "mp3nb"))
1163                                                 ast_set_flag(mohclass, MOH_SINGLE);
1164                                         else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1165                                                 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
1166                                         else if (!strcasecmp(mohclass->mode, "quietmp3"))
1167                                                 ast_set_flag(mohclass, MOH_QUIET);
1168                         
1169                                         mohclass->srcfd = -1;
1170 #ifdef HAVE_ZAPTEL
1171                                         /* Open /dev/zap/pseudo for timing...  Is
1172                                            there a better, yet reliable way to do this? */
1173                                         mohclass->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
1174                                         if (mohclass->pseudofd < 0) {
1175                                                 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
1176                                         } else {
1177                                                 x = 320;
1178                                                 ioctl(mohclass->pseudofd, ZT_SET_BLOCKSIZE, &x);
1179                                         }
1180 #else
1181                                         mohclass->pseudofd = -1;
1182 #endif
1183                                         /* Let's check if this channel already had a moh class before */
1184                                         if (state && state->class) {
1185                                                 /* Class already exist for this channel */
1186                                                 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1187                                                 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1188                                                         /* we found RT class with the same name, seems like we should continue playing existing one */
1189                                                         ast_moh_free_class(&mohclass);
1190                                                         mohclass = state->class;
1191         
1192                                                 }
1193                                         } else {
1194                                                 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
1195                                                         ast_log(LOG_WARNING, "Unable to create moh...\n");
1196                                                         if (mohclass->pseudofd > -1)
1197                                                                 close(mohclass->pseudofd);
1198                                                         ast_moh_free_class(&mohclass);
1199                                                         return -1;
1200                                                 }
1201                                         }
1202                                 } else {
1203                                         ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
1204                                         ast_moh_free_class(&mohclass);
1205                                         return -1;
1206                                 }
1207
1208                         }
1209
1210                 } else if (var)
1211                         ast_variables_destroy(var);
1212         }
1213
1214         
1215
1216         /* Requested MOH class not found, check for 'default' class in musiconhold.conf  */
1217         if (!mohclass) {
1218                 AST_RWLIST_RDLOCK(&mohclasses);
1219                 mohclass = get_mohbyname("default", 1);
1220                 if (mohclass)
1221                         ast_atomic_fetchadd_int(&mohclass->inuse, +1);
1222                 AST_RWLIST_UNLOCK(&mohclasses);
1223         } else {
1224                 AST_RWLIST_RDLOCK(&mohclasses);
1225                 ast_atomic_fetchadd_int(&mohclass->inuse, +1);
1226                 AST_RWLIST_UNLOCK(&mohclasses);
1227         }
1228
1229         if (!mohclass)
1230                 return -1;
1231
1232         ast_set_flag(chan, AST_FLAG_MOH);
1233         if (mohclass->total_files) {
1234                 return ast_activate_generator(chan, &moh_file_stream, mohclass);
1235         } else
1236                 return ast_activate_generator(chan, &mohgen, mohclass);
1237 }
1238
1239 static void local_ast_moh_stop(struct ast_channel *chan)
1240 {
1241         struct moh_files_state *state = chan->music_state;
1242         ast_clear_flag(chan, AST_FLAG_MOH);
1243         ast_deactivate_generator(chan);
1244
1245         if (state) {
1246                 if (chan->stream) {
1247                         ast_closestream(chan->stream);
1248                         chan->stream = NULL;
1249                 }
1250         }
1251 }
1252
1253 static int load_moh_classes(int reload)
1254 {
1255         struct ast_config *cfg;
1256         struct ast_variable *var;
1257         struct mohclass *class; 
1258         char *cat;
1259         int numclasses = 0;
1260         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1261
1262         cfg = ast_config_load("musiconhold.conf", config_flags);
1263
1264         if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
1265                 return 0;
1266
1267         if (reload) {
1268                 AST_RWLIST_WRLOCK(&mohclasses);
1269                 AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1270                         class->delete = 1;
1271                 }
1272                 AST_RWLIST_UNLOCK(&mohclasses);
1273         }
1274         
1275         ast_clear_flag(global_flags, AST_FLAGS_ALL);
1276
1277         cat = ast_category_browse(cfg, NULL);
1278         for (; cat; cat = ast_category_browse(cfg, cat)) {
1279                 /* Setup common options from [general] section */
1280                 if (!strcasecmp(cat, "general")) {
1281                         var = ast_variable_browse(cfg, cat);
1282                         while (var) {
1283                                 if (!strcasecmp(var->name, "cachertclasses")) {
1284                                         ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
1285                                 } else {
1286                                         ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
1287                                 }
1288                                 var = var->next;
1289                         }
1290                 }
1291                 /* These names were deprecated in 1.4 and should not be used until after the next major release. */
1292                 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files") && strcasecmp(cat, "general")) {
1293                         if (!(class = moh_class_malloc()))
1294                                 break;
1295
1296                         ast_copy_string(class->name, cat, sizeof(class->name)); 
1297                         var = ast_variable_browse(cfg, cat);
1298                         while (var) {
1299                                 if (!strcasecmp(var->name, "mode"))
1300                                         ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
1301                                 else if (!strcasecmp(var->name, "directory"))
1302                                         ast_copy_string(class->dir, var->value, sizeof(class->dir));
1303                                 else if (!strcasecmp(var->name, "application"))
1304                                         ast_copy_string(class->args, var->value, sizeof(class->args));
1305                                 else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
1306                                         class->digit = *var->value;
1307                                 else if (!strcasecmp(var->name, "random"))
1308                                         ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
1309                                 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
1310                                         ast_set_flag(class, MOH_RANDOMIZE);
1311                                 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 
1312                                         ast_set_flag(class, MOH_SORTALPHA);
1313                                 else if (!strcasecmp(var->name, "format")) {
1314                                         class->format = ast_getformatbyname(var->value);
1315                                         if (!class->format) {
1316                                                 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1317                                                 class->format = AST_FORMAT_SLINEAR;
1318                                         }
1319                                 }
1320                                 var = var->next;
1321                         }
1322
1323                         if (ast_strlen_zero(class->dir)) {
1324                                 if (!strcasecmp(class->mode, "custom")) {
1325                                         strcpy(class->dir, "nodir");
1326                                 } else {
1327                                         ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1328                                         ast_free(class);
1329                                         continue;
1330                                 }
1331                         }
1332                         if (ast_strlen_zero(class->mode)) {
1333                                 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1334                                 ast_free(class);
1335                                 continue;
1336                         }
1337                         if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1338                                 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1339                                 ast_free(class);
1340                                 continue;
1341                         }
1342
1343                         /* Don't leak a class when it's already registered */
1344                         moh_register(class, reload);
1345
1346                         numclasses++;
1347                 }
1348         }
1349
1350         ast_config_destroy(cfg);
1351
1352         return numclasses;
1353 }
1354
1355 static int ast_moh_destroy_one(struct mohclass *moh)
1356 {
1357         char buff[8192];
1358         int bytes, tbytes = 0, stime = 0, pid = 0;
1359
1360         if (moh) {
1361                 if (moh->pid > 1) {
1362                         ast_debug(1, "killing %d!\n", moh->pid);
1363                         stime = time(NULL) + 2;
1364                         pid = moh->pid;
1365                         moh->pid = 0;
1366                         /* Back when this was just mpg123, SIGKILL was fine.  Now we need
1367                          * to give the process a reason and time enough to kill off its
1368                          * children. */
1369                         kill(pid, SIGHUP);
1370                         usleep(100000);
1371                         kill(pid, SIGTERM);
1372                         usleep(100000);
1373                         kill(pid, SIGKILL);
1374                         while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
1375                                 tbytes = tbytes + bytes;
1376                         ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1377                         close(moh->srcfd);
1378                 }
1379                 ast_moh_free_class(&moh);
1380         }
1381
1382         return 0;
1383 }
1384
1385 static void ast_moh_destroy(void)
1386 {
1387         struct mohclass *moh;
1388
1389         ast_verb(2, "Destroying musiconhold processes\n");
1390
1391         AST_RWLIST_WRLOCK(&mohclasses);
1392         while ((moh = AST_RWLIST_REMOVE_HEAD(&mohclasses, list))) {
1393                 ast_moh_destroy_one(moh);
1394         }
1395         AST_RWLIST_UNLOCK(&mohclasses);
1396 }
1397
1398 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1399 {
1400         switch (cmd) {
1401         case CLI_INIT:
1402                 e->command = "moh reload";
1403                 e->usage =
1404                         "Usage: moh reload\n"
1405                         "       Reloads the MusicOnHold module.\n"
1406                         "       Alias for 'module reload res_musiconhold.so'\n";
1407                 return NULL;
1408         case CLI_GENERATE:
1409                 return NULL;
1410         }
1411
1412         if (a->argc != e->args)
1413                 return CLI_SHOWUSAGE;
1414
1415         reload();
1416
1417         return CLI_SUCCESS;
1418 }
1419
1420 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1421 {
1422         int i;
1423         struct mohclass *class;
1424
1425         switch (cmd) {
1426         case CLI_INIT:
1427                 e->command = "moh show files";
1428                 e->usage =
1429                         "Usage: moh show files\n"
1430                         "       Lists all loaded file-based MusicOnHold classes and their\n"
1431                         "       files.\n";
1432                 return NULL;
1433         case CLI_GENERATE:
1434                 return NULL;
1435         }
1436
1437         if (a->argc != e->args)
1438                 return CLI_SHOWUSAGE;
1439
1440         AST_RWLIST_RDLOCK(&mohclasses);
1441         AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1442                 if (!class->total_files)
1443                         continue;
1444
1445                 ast_cli(a->fd, "Class: %s\n", class->name);
1446                 for (i = 0; i < class->total_files; i++)
1447                         ast_cli(a->fd, "\tFile: %s\n", class->filearray[i]);
1448         }
1449         AST_RWLIST_UNLOCK(&mohclasses);
1450
1451         return CLI_SUCCESS;
1452 }
1453
1454 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1455 {
1456         struct mohclass *class;
1457
1458         switch (cmd) {
1459         case CLI_INIT:
1460                 e->command = "moh show classes";
1461                 e->usage =
1462                         "Usage: moh show classes\n"
1463                         "       Lists all MusicOnHold classes.\n";
1464                 return NULL;
1465         case CLI_GENERATE:
1466                 return NULL;
1467         }
1468
1469         if (a->argc != e->args)
1470                 return CLI_SHOWUSAGE;
1471
1472         AST_RWLIST_RDLOCK(&mohclasses);
1473         AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1474                 ast_cli(a->fd, "Class: %s\n", class->name);
1475                 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1476                 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1477                 ast_cli(a->fd, "\tUse Count: %d\n", class->inuse);
1478                 if (class->digit)
1479                         ast_cli(a->fd, "\tDigit: %c\n", class->digit);
1480                 if (ast_test_flag(class, MOH_CUSTOM))
1481                         ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1482                 if (strcasecmp(class->mode, "files"))
1483                         ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
1484         }
1485         AST_RWLIST_UNLOCK(&mohclasses);
1486
1487         return CLI_SUCCESS;
1488 }
1489
1490 static struct ast_cli_entry cli_moh[] = {
1491         AST_CLI_DEFINE(handle_cli_moh_reload,       "Reload MusicOnHold"),
1492         AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
1493         AST_CLI_DEFINE(handle_cli_moh_show_files,   "List MusicOnHold file-based classes")
1494 };
1495
1496 static int init_classes(int reload) 
1497 {
1498         struct mohclass *moh;
1499     
1500         if (!load_moh_classes(reload))          /* Load classes from config */
1501                 return 0;                       /* Return if nothing is found */
1502
1503         AST_RWLIST_WRLOCK(&mohclasses);
1504         AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mohclasses, moh, list) {
1505                 if (reload && moh->delete) {
1506                         AST_RWLIST_REMOVE_CURRENT(list);
1507                         if (!moh->inuse)
1508                                 ast_moh_destroy_one(moh);
1509                 } else if (moh->total_files) {
1510                         if (moh_scan_files(moh) <= 0) {
1511                                 ast_log(LOG_WARNING, "No files found for class '%s'\n", moh->name);
1512                                 moh->delete = 1;
1513                                 AST_LIST_REMOVE_CURRENT(list);
1514                                 if (!moh->inuse)
1515                                         ast_moh_destroy_one(moh);
1516                         }
1517                 }
1518         }
1519         AST_RWLIST_TRAVERSE_SAFE_END
1520         AST_RWLIST_UNLOCK(&mohclasses);
1521
1522         return 1;
1523 }
1524
1525 static int load_module(void)
1526 {
1527         int res;
1528
1529         res = ast_register_application(play_moh, play_moh_exec, play_moh_syn, play_moh_desc);
1530         ast_register_atexit(ast_moh_destroy);
1531         ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
1532         if (!res)
1533                 res = ast_register_application(wait_moh, wait_moh_exec, wait_moh_syn, wait_moh_desc);
1534         if (!res)
1535                 res = ast_register_application(set_moh, set_moh_exec, set_moh_syn, set_moh_desc);
1536         if (!res)
1537                 res = ast_register_application(start_moh, start_moh_exec, start_moh_syn, start_moh_desc);
1538         if (!res)
1539                 res = ast_register_application(stop_moh, stop_moh_exec, stop_moh_syn, stop_moh_desc);
1540
1541         if (!init_classes(0)) {         /* No music classes configured, so skip it */
1542                 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.\n");
1543         } else {
1544                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1545         }
1546
1547         return AST_MODULE_LOAD_SUCCESS;
1548 }
1549
1550 static int reload(void)
1551 {
1552         if (init_classes(1))
1553                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1554
1555         return 0;
1556 }
1557
1558 static int unload_module(void)
1559 {
1560         return -1;
1561 }
1562
1563 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Music On Hold Resource",
1564                 .load = load_module,
1565                 .unload = unload_module,
1566                 .reload = reload,
1567                );