Removing a pointless memset. The memory was just calloc'd, so the
[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                 state->class = class;
752         } else
753                 state = chan->music_state;
754         if (state && state->class != class) {
755                 memset(state, 0, sizeof(*state));
756                 state->class = class;
757         }
758
759         if ((res = mohalloc(class))) {
760                 res->origwfmt = chan->writeformat;
761                 if (ast_set_write_format(chan, class->format)) {
762                         ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
763                         moh_release(NULL, res);
764                         res = NULL;
765                 }
766                 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
767         }
768         return res;
769 }
770
771 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
772 {
773         struct mohdata *moh = data;
774         short buf[1280 + AST_FRIENDLY_OFFSET / 2];
775         int res;
776
777         if (!moh->parent->pid)
778                 return -1;
779
780         len = ast_codec_get_len(moh->parent->format, samples);
781
782         if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
783                 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
784                 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
785         }
786         res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
787         if (res <= 0)
788                 return 0;
789
790         moh->f.datalen = res;
791         moh->f.data = buf + AST_FRIENDLY_OFFSET / 2;
792         moh->f.samples = ast_codec_get_samples(&moh->f);
793
794         if (ast_write(chan, &moh->f) < 0) {
795                 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
796                 return -1;
797         }
798
799         return 0;
800 }
801
802 static struct ast_generator mohgen = 
803 {
804         alloc: moh_alloc,
805         release: moh_release,
806         generate: moh_generate,
807         digit: moh_handle_digit
808 };
809
810 static int moh_add_file(struct mohclass *class, const char *filepath)
811 {
812         if (!class->allowed_files) {
813                 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
814                         return -1;
815                 class->allowed_files = INITIAL_NUM_FILES;
816         } else if (class->total_files == class->allowed_files) {
817                 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
818                         class->allowed_files = 0;
819                         class->total_files = 0;
820                         return -1;
821                 }
822                 class->allowed_files *= 2;
823         }
824
825         if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
826                 return -1;
827
828         class->total_files++;
829
830         return 0;
831 }
832
833 static int moh_sort_compare(const void *i1, const void *i2)
834 {
835         char *s1, *s2;
836
837         s1 = ((char **)i1)[0];
838         s2 = ((char **)i2)[0];
839
840         return strcasecmp(s1, s2);
841 }
842
843 static int moh_scan_files(struct mohclass *class) {
844
845         DIR *files_DIR;
846         struct dirent *files_dirent;
847         char path[PATH_MAX];
848         char filepath[PATH_MAX];
849         char *ext;
850         struct stat statbuf;
851         int dirnamelen;
852         int i;
853
854         files_DIR = opendir(class->dir);
855         if (!files_DIR) {
856                 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir);
857                 return -1;
858         }
859
860         for (i = 0; i < class->total_files; i++)
861                 ast_free(class->filearray[i]);
862
863         class->total_files = 0;
864         dirnamelen = strlen(class->dir) + 2;
865         getcwd(path, sizeof(path));
866         chdir(class->dir);
867         while ((files_dirent = readdir(files_DIR))) {
868                 /* The file name must be at least long enough to have the file type extension */
869                 if ((strlen(files_dirent->d_name) < 4))
870                         continue;
871
872                 /* Skip files that starts with a dot */
873                 if (files_dirent->d_name[0] == '.')
874                         continue;
875
876                 /* Skip files without extensions... they are not audio */
877                 if (!strchr(files_dirent->d_name, '.'))
878                         continue;
879
880                 snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
881
882                 if (stat(filepath, &statbuf))
883                         continue;
884
885                 if (!S_ISREG(statbuf.st_mode))
886                         continue;
887
888                 if ((ext = strrchr(filepath, '.')))
889                         *ext = '\0';
890
891                 /* if the file is present in multiple formats, ensure we only put it into the list once */
892                 for (i = 0; i < class->total_files; i++)
893                         if (!strcmp(filepath, class->filearray[i]))
894                                 break;
895
896                 if (i == class->total_files) {
897                         if (moh_add_file(class, filepath))
898                                 break;
899                 }
900         }
901
902         closedir(files_DIR);
903         chdir(path);
904         if (ast_test_flag(class, MOH_SORTALPHA))
905                 qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
906         return class->total_files;
907 }
908
909 static int moh_diff(struct mohclass *old, struct mohclass *new)
910 {
911         if (!old || !new)
912                 return -1;
913
914         if (strcmp(old->dir, new->dir))
915                 return -1;
916         else if (strcmp(old->mode, new->mode))
917                 return -1;
918         else if (strcmp(old->args, new->args))
919                 return -1;
920         else if (old->flags != new->flags)
921                 return -1;
922
923         return 0;
924 }
925
926 static int moh_register(struct mohclass *moh, int reload)
927 {
928 #ifdef HAVE_ZAPTEL
929         int x;
930 #endif
931         struct mohclass *mohclass = NULL;
932
933         AST_RWLIST_WRLOCK(&mohclasses);
934         if ((mohclass = get_mohbyname(moh->name, 0)) && !moh_diff(mohclass, moh)) {
935                 mohclass->delete = 0;
936                 if (reload) {
937                         ast_debug(1, "Music on Hold class '%s' left alone from initial load.\n", moh->name);
938                 } else {
939                         ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
940                 }
941                 ast_free(moh);  
942                 AST_RWLIST_UNLOCK(&mohclasses);
943                 return -1;
944         }
945         AST_RWLIST_UNLOCK(&mohclasses);
946
947         time(&moh->start);
948         moh->start -= respawn_time;
949         
950         if (!strcasecmp(moh->mode, "files")) {
951                 if (!moh_scan_files(moh)) {
952                         ast_moh_free_class(&moh);
953                         return -1;
954                 }
955                 if (strchr(moh->args, 'r'))
956                         ast_set_flag(moh, MOH_RANDOMIZE);
957         } 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")) {
958
959                 if (!strcasecmp(moh->mode, "custom"))
960                         ast_set_flag(moh, MOH_CUSTOM);
961                 else if (!strcasecmp(moh->mode, "mp3nb"))
962                         ast_set_flag(moh, MOH_SINGLE);
963                 else if (!strcasecmp(moh->mode, "quietmp3nb"))
964                         ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
965                 else if (!strcasecmp(moh->mode, "quietmp3"))
966                         ast_set_flag(moh, MOH_QUIET);
967                 
968                 moh->srcfd = -1;
969 #ifdef HAVE_ZAPTEL
970                 /* Open /dev/zap/pseudo for timing...  Is
971                    there a better, yet reliable way to do this? */
972                 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
973                 if (moh->pseudofd < 0) {
974                         ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
975                 } else {
976                         x = 320;
977                         ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
978                 }
979 #else
980                 moh->pseudofd = -1;
981 #endif
982                 if (ast_pthread_create_background(&moh->thread, NULL, monmp3thread, moh)) {
983                         ast_log(LOG_WARNING, "Unable to create moh...\n");
984                         if (moh->pseudofd > -1)
985                                 close(moh->pseudofd);
986                         ast_moh_free_class(&moh);
987                         return -1;
988                 }
989         } else {
990                 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
991                 ast_moh_free_class(&moh);
992                 return -1;
993         }
994
995         AST_RWLIST_WRLOCK(&mohclasses);
996         AST_RWLIST_INSERT_HEAD(&mohclasses, moh, list);
997         AST_RWLIST_UNLOCK(&mohclasses);
998         
999         return 0;
1000 }
1001
1002 static void local_ast_moh_cleanup(struct ast_channel *chan)
1003 {
1004         struct moh_files_state *state = chan->music_state;
1005
1006         if (state) {
1007                 if (state->class->realtime) {
1008                         if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
1009                                 /* We are cleaning out cached RT class, we should remove it from list, if no one else using it */
1010                                 if (!(state->class->inuse)) {
1011                                         /* Remove this class from list */
1012                                         AST_RWLIST_WRLOCK(&mohclasses);
1013                                         AST_RWLIST_REMOVE(&mohclasses, state->class, list);
1014                                         AST_RWLIST_UNLOCK(&mohclasses);
1015         
1016                                         /* Free some memory */
1017                                         ast_moh_destroy_one(state->class);
1018                                 }
1019                         } else {
1020                                 ast_moh_destroy_one(state->class);
1021                         }
1022                 }
1023                 ast_free(chan->music_state);
1024                 chan->music_state = NULL;
1025         }
1026 }
1027
1028 static struct mohclass *moh_class_malloc(void)
1029 {
1030         struct mohclass *class;
1031
1032         if ((class = ast_calloc(1, sizeof(*class)))) {
1033                 class->format = AST_FORMAT_SLINEAR;
1034                 class->realtime = 0;
1035         }
1036
1037         return class;
1038 }
1039
1040 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
1041 {
1042         struct mohclass *mohclass = NULL;
1043         struct ast_variable *var = NULL;
1044         struct ast_variable *tmp = NULL;
1045         struct moh_files_state *state = chan->music_state;
1046 #ifdef HAVE_ZAPTEL
1047         int x;
1048 #endif
1049
1050         /* The following is the order of preference for which class to use:
1051          * 1) The channels explicitly set musicclass, which should *only* be
1052          *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1053          * 2) The mclass argument. If a channel is calling ast_moh_start() as the
1054          *    result of receiving a HOLD control frame, this should be the
1055          *    payload that came with the frame.
1056          * 3) The interpclass argument. This would be from the mohinterpret
1057          *    option from channel drivers. This is the same as the old musicclass
1058          *    option.
1059          * 4) The default class.
1060          */
1061         
1062         /* First, let's check in memory for static and cached RT classes */
1063         AST_RWLIST_RDLOCK(&mohclasses);
1064         if (!ast_strlen_zero(chan->musicclass))
1065                 mohclass = get_mohbyname(chan->musicclass, 1);
1066         if (!mohclass && !ast_strlen_zero(mclass))
1067                 mohclass = get_mohbyname(mclass, 1);
1068         if (!mohclass && !ast_strlen_zero(interpclass))
1069                 mohclass = get_mohbyname(interpclass, 1);
1070         AST_RWLIST_UNLOCK(&mohclasses);
1071
1072         /* If no moh class found in memory, then check RT */
1073         if (!mohclass && ast_check_realtime("musiconhold")) {
1074                 if (!ast_strlen_zero(chan->musicclass)) {
1075                         var = ast_load_realtime("musiconhold", "name", chan->musicclass, NULL);
1076                 }
1077                 if (!var && !ast_strlen_zero(mclass))
1078                         var = ast_load_realtime("musiconhold", "name", mclass, NULL);
1079                 if (!var && !ast_strlen_zero(interpclass))
1080                         var = ast_load_realtime("musiconhold", "name", interpclass, NULL);
1081                 if (!var)
1082                         var = ast_load_realtime("musiconhold", "name", "default", NULL);
1083                 if (var && (mohclass = moh_class_malloc())) {
1084                         mohclass->realtime = 1;
1085                         for (tmp = var; tmp; tmp = tmp->next) {
1086                                 if (!strcasecmp(tmp->name, "name"))
1087                                         ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
1088                                 else if (!strcasecmp(tmp->name, "mode"))
1089                                         ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode)); 
1090                                 else if (!strcasecmp(tmp->name, "directory"))
1091                                         ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
1092                                 else if (!strcasecmp(tmp->name, "application"))
1093                                         ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
1094                                 else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
1095                                         mohclass->digit = *tmp->value;
1096                                 else if (!strcasecmp(tmp->name, "random"))
1097                                         ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
1098                                 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
1099                                         ast_set_flag(mohclass, MOH_RANDOMIZE);
1100                                 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha")) 
1101                                         ast_set_flag(mohclass, MOH_SORTALPHA);
1102                                 else if (!strcasecmp(tmp->name, "format")) {
1103                                         mohclass->format = ast_getformatbyname(tmp->value);
1104                                         if (!mohclass->format) {
1105                                                 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
1106                                                 mohclass->format = AST_FORMAT_SLINEAR;
1107                                         }
1108                                 }
1109                         }
1110                         ast_variables_destroy(var);
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                 } else if (var)
1210                         ast_variables_destroy(var);
1211         }
1212
1213         
1214
1215         /* Requested MOH class not found, check for 'default' class in musiconhold.conf  */
1216         if (!mohclass) {
1217                 AST_RWLIST_RDLOCK(&mohclasses);
1218                 mohclass = get_mohbyname("default", 1);
1219                 if (mohclass)
1220                         ast_atomic_fetchadd_int(&mohclass->inuse, +1);
1221                 AST_RWLIST_UNLOCK(&mohclasses);
1222         } else {
1223                 AST_RWLIST_RDLOCK(&mohclasses);
1224                 ast_atomic_fetchadd_int(&mohclass->inuse, +1);
1225                 AST_RWLIST_UNLOCK(&mohclasses);
1226         }
1227
1228         if (!mohclass)
1229                 return -1;
1230
1231         ast_set_flag(chan, AST_FLAG_MOH);
1232         if (mohclass->total_files) {
1233                 return ast_activate_generator(chan, &moh_file_stream, mohclass);
1234         } else
1235                 return ast_activate_generator(chan, &mohgen, mohclass);
1236 }
1237
1238 static void local_ast_moh_stop(struct ast_channel *chan)
1239 {
1240         struct moh_files_state *state = chan->music_state;
1241         ast_clear_flag(chan, AST_FLAG_MOH);
1242         ast_deactivate_generator(chan);
1243
1244         if (state) {
1245                 if (chan->stream) {
1246                         ast_closestream(chan->stream);
1247                         chan->stream = NULL;
1248                 }
1249         }
1250 }
1251
1252 static int load_moh_classes(int reload)
1253 {
1254         struct ast_config *cfg;
1255         struct ast_variable *var;
1256         struct mohclass *class; 
1257         char *cat;
1258         int numclasses = 0;
1259         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1260
1261         cfg = ast_config_load("musiconhold.conf", config_flags);
1262
1263         if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
1264                 return 0;
1265
1266         if (reload) {
1267                 AST_RWLIST_WRLOCK(&mohclasses);
1268                 AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1269                         class->delete = 1;
1270                 }
1271                 AST_RWLIST_UNLOCK(&mohclasses);
1272         }
1273         
1274         ast_clear_flag(global_flags, AST_FLAGS_ALL);
1275
1276         cat = ast_category_browse(cfg, NULL);
1277         for (; cat; cat = ast_category_browse(cfg, cat)) {
1278                 /* Setup common options from [general] section */
1279                 if (!strcasecmp(cat, "general")) {
1280                         var = ast_variable_browse(cfg, cat);
1281                         while (var) {
1282                                 if (!strcasecmp(var->name, "cachertclasses")) {
1283                                         ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
1284                                 } else {
1285                                         ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
1286                                 }
1287                                 var = var->next;
1288                         }
1289                 }
1290                 /* These names were deprecated in 1.4 and should not be used until after the next major release. */
1291                 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files") && strcasecmp(cat, "general")) {
1292                         if (!(class = moh_class_malloc()))
1293                                 break;
1294
1295                         ast_copy_string(class->name, cat, sizeof(class->name)); 
1296                         var = ast_variable_browse(cfg, cat);
1297                         while (var) {
1298                                 if (!strcasecmp(var->name, "mode"))
1299                                         ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
1300                                 else if (!strcasecmp(var->name, "directory"))
1301                                         ast_copy_string(class->dir, var->value, sizeof(class->dir));
1302                                 else if (!strcasecmp(var->name, "application"))
1303                                         ast_copy_string(class->args, var->value, sizeof(class->args));
1304                                 else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
1305                                         class->digit = *var->value;
1306                                 else if (!strcasecmp(var->name, "random"))
1307                                         ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
1308                                 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
1309                                         ast_set_flag(class, MOH_RANDOMIZE);
1310                                 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 
1311                                         ast_set_flag(class, MOH_SORTALPHA);
1312                                 else if (!strcasecmp(var->name, "format")) {
1313                                         class->format = ast_getformatbyname(var->value);
1314                                         if (!class->format) {
1315                                                 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1316                                                 class->format = AST_FORMAT_SLINEAR;
1317                                         }
1318                                 }
1319                                 var = var->next;
1320                         }
1321
1322                         if (ast_strlen_zero(class->dir)) {
1323                                 if (!strcasecmp(class->mode, "custom")) {
1324                                         strcpy(class->dir, "nodir");
1325                                 } else {
1326                                         ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1327                                         ast_free(class);
1328                                         continue;
1329                                 }
1330                         }
1331                         if (ast_strlen_zero(class->mode)) {
1332                                 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1333                                 ast_free(class);
1334                                 continue;
1335                         }
1336                         if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1337                                 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1338                                 ast_free(class);
1339                                 continue;
1340                         }
1341
1342                         /* Don't leak a class when it's already registered */
1343                         moh_register(class, reload);
1344
1345                         numclasses++;
1346                 }
1347         }
1348
1349         ast_config_destroy(cfg);
1350
1351         return numclasses;
1352 }
1353
1354 static int ast_moh_destroy_one(struct mohclass *moh)
1355 {
1356         char buff[8192];
1357         int bytes, tbytes = 0, stime = 0, pid = 0;
1358
1359         if (moh) {
1360                 if (moh->pid > 1) {
1361                         ast_debug(1, "killing %d!\n", moh->pid);
1362                         stime = time(NULL) + 2;
1363                         pid = moh->pid;
1364                         moh->pid = 0;
1365                         /* Back when this was just mpg123, SIGKILL was fine.  Now we need
1366                          * to give the process a reason and time enough to kill off its
1367                          * children. */
1368                         kill(pid, SIGHUP);
1369                         usleep(100000);
1370                         kill(pid, SIGTERM);
1371                         usleep(100000);
1372                         kill(pid, SIGKILL);
1373                         while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
1374                                 tbytes = tbytes + bytes;
1375                         ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1376                         close(moh->srcfd);
1377                 }
1378                 ast_moh_free_class(&moh);
1379         }
1380
1381         return 0;
1382 }
1383
1384 static void ast_moh_destroy(void)
1385 {
1386         struct mohclass *moh;
1387
1388         ast_verb(2, "Destroying musiconhold processes\n");
1389
1390         AST_RWLIST_WRLOCK(&mohclasses);
1391         while ((moh = AST_RWLIST_REMOVE_HEAD(&mohclasses, list))) {
1392                 ast_moh_destroy_one(moh);
1393         }
1394         AST_RWLIST_UNLOCK(&mohclasses);
1395 }
1396
1397 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1398 {
1399         switch (cmd) {
1400         case CLI_INIT:
1401                 e->command = "moh reload";
1402                 e->usage =
1403                         "Usage: moh reload\n"
1404                         "       Reloads the MusicOnHold module.\n"
1405                         "       Alias for 'module reload res_musiconhold.so'\n";
1406                 return NULL;
1407         case CLI_GENERATE:
1408                 return NULL;
1409         }
1410
1411         if (a->argc != e->args)
1412                 return CLI_SHOWUSAGE;
1413
1414         reload();
1415
1416         return CLI_SUCCESS;
1417 }
1418
1419 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1420 {
1421         int i;
1422         struct mohclass *class;
1423
1424         switch (cmd) {
1425         case CLI_INIT:
1426                 e->command = "moh show files";
1427                 e->usage =
1428                         "Usage: moh show files\n"
1429                         "       Lists all loaded file-based MusicOnHold classes and their\n"
1430                         "       files.\n";
1431                 return NULL;
1432         case CLI_GENERATE:
1433                 return NULL;
1434         }
1435
1436         if (a->argc != e->args)
1437                 return CLI_SHOWUSAGE;
1438
1439         AST_RWLIST_RDLOCK(&mohclasses);
1440         AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1441                 if (!class->total_files)
1442                         continue;
1443
1444                 ast_cli(a->fd, "Class: %s\n", class->name);
1445                 for (i = 0; i < class->total_files; i++)
1446                         ast_cli(a->fd, "\tFile: %s\n", class->filearray[i]);
1447         }
1448         AST_RWLIST_UNLOCK(&mohclasses);
1449
1450         return CLI_SUCCESS;
1451 }
1452
1453 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1454 {
1455         struct mohclass *class;
1456
1457         switch (cmd) {
1458         case CLI_INIT:
1459                 e->command = "moh show classes";
1460                 e->usage =
1461                         "Usage: moh show classes\n"
1462                         "       Lists all MusicOnHold classes.\n";
1463                 return NULL;
1464         case CLI_GENERATE:
1465                 return NULL;
1466         }
1467
1468         if (a->argc != e->args)
1469                 return CLI_SHOWUSAGE;
1470
1471         AST_RWLIST_RDLOCK(&mohclasses);
1472         AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1473                 ast_cli(a->fd, "Class: %s\n", class->name);
1474                 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1475                 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1476                 ast_cli(a->fd, "\tUse Count: %d\n", class->inuse);
1477                 if (class->digit)
1478                         ast_cli(a->fd, "\tDigit: %c\n", class->digit);
1479                 if (ast_test_flag(class, MOH_CUSTOM))
1480                         ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1481                 if (strcasecmp(class->mode, "files"))
1482                         ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
1483         }
1484         AST_RWLIST_UNLOCK(&mohclasses);
1485
1486         return CLI_SUCCESS;
1487 }
1488
1489 static struct ast_cli_entry cli_moh[] = {
1490         AST_CLI_DEFINE(handle_cli_moh_reload,       "Reload MusicOnHold"),
1491         AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
1492         AST_CLI_DEFINE(handle_cli_moh_show_files,   "List MusicOnHold file-based classes")
1493 };
1494
1495 static int init_classes(int reload) 
1496 {
1497         struct mohclass *moh;
1498     
1499         if (!load_moh_classes(reload))          /* Load classes from config */
1500                 return 0;                       /* Return if nothing is found */
1501
1502         AST_RWLIST_WRLOCK(&mohclasses);
1503         AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mohclasses, moh, list) {
1504                 if (reload && moh->delete) {
1505                         AST_RWLIST_REMOVE_CURRENT(list);
1506                         if (!moh->inuse)
1507                                 ast_moh_destroy_one(moh);
1508                 } else if (moh->total_files) {
1509                         if (moh_scan_files(moh) <= 0) {
1510                                 ast_log(LOG_WARNING, "No files found for class '%s'\n", moh->name);
1511                                 moh->delete = 1;
1512                                 AST_LIST_REMOVE_CURRENT(list);
1513                                 if (!moh->inuse)
1514                                         ast_moh_destroy_one(moh);
1515                         }
1516                 }
1517         }
1518         AST_RWLIST_TRAVERSE_SAFE_END
1519         AST_RWLIST_UNLOCK(&mohclasses);
1520
1521         return 1;
1522 }
1523
1524 static int load_module(void)
1525 {
1526         int res;
1527
1528         res = ast_register_application(play_moh, play_moh_exec, play_moh_syn, play_moh_desc);
1529         ast_register_atexit(ast_moh_destroy);
1530         ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
1531         if (!res)
1532                 res = ast_register_application(wait_moh, wait_moh_exec, wait_moh_syn, wait_moh_desc);
1533         if (!res)
1534                 res = ast_register_application(set_moh, set_moh_exec, set_moh_syn, set_moh_desc);
1535         if (!res)
1536                 res = ast_register_application(start_moh, start_moh_exec, start_moh_syn, start_moh_desc);
1537         if (!res)
1538                 res = ast_register_application(stop_moh, stop_moh_exec, stop_moh_syn, stop_moh_desc);
1539
1540         if (!init_classes(0)) {         /* No music classes configured, so skip it */
1541                 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.\n");
1542         } else {
1543                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1544         }
1545
1546         return AST_MODULE_LOAD_SUCCESS;
1547 }
1548
1549 static int reload(void)
1550 {
1551         if (init_classes(1))
1552                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1553
1554         return 0;
1555 }
1556
1557 static int unload_module(void)
1558 {
1559         return -1;
1560 }
1561
1562 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Music On Hold Resource",
1563                 .load = load_module,
1564                 .unload = unload_module,
1565                 .reload = reload,
1566                );