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