support relative paths in musiconhold.conf, which makes moh work by default when...
[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>dahdi</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 #if defined(HAVE_DAHDI)
50 #include <dahdi/user.h>
51 #endif
52
53 #include "asterisk/lock.h"
54 #include "asterisk/file.h"
55 #include "asterisk/channel.h"
56 #include "asterisk/pbx.h"
57 #include "asterisk/app.h"
58 #include "asterisk/module.h"
59 #include "asterisk/translate.h"
60 #include "asterisk/say.h"
61 #include "asterisk/musiconhold.h"
62 #include "asterisk/config.h"
63 #include "asterisk/utils.h"
64 #include "asterisk/cli.h"
65 #include "asterisk/stringfields.h"
66 #include "asterisk/linkedlists.h"
67 #include "asterisk/manager.h"
68 #include "asterisk/paths.h"
69
70 #define INITIAL_NUM_FILES   8
71
72 static char *play_moh = "MusicOnHold";
73 static char *wait_moh = "WaitMusicOnHold";
74 static char *set_moh = "SetMusicOnHold";
75 static char *start_moh = "StartMusicOnHold";
76 static char *stop_moh = "StopMusicOnHold";
77
78 static char *play_moh_syn = "Play Music On Hold indefinitely";
79 static char *wait_moh_syn = "Wait, playing Music On Hold";
80 static char *set_moh_syn = "Set default Music On Hold class";
81 static char *start_moh_syn = "Play Music On Hold";
82 static char *stop_moh_syn = "Stop Playing Music On Hold";
83
84 static char *play_moh_desc = "  MusicOnHold(class[,duration]):\n"
85 "Plays hold music specified by class.  If omitted, the default\n"
86 "music source for the channel will be used. Change the default \n"
87 "class with Set(CHANNEL(musicclass)=...).\n"
88 "If duration is given, hold music will be played specified number\n"
89 "of seconds. If duration is ommited, music plays indefinitely.\n"
90 "Returns 0 when done, -1 on hangup.\n";
91
92 static char *wait_moh_desc = "  WaitMusicOnHold(delay):\n"
93 "\n"
94 "  !!! DEPRECATED. Use MusicOnHold instead !!!\n"
95 "\n"
96 "Plays hold music specified number of seconds.  Returns 0 when\n"
97 "done, or -1 on hangup.  If no hold music is available, the delay will\n"
98 "still occur with no sound.\n"
99 "\n"
100 "  !!! DEPRECATED. Use MusicOnHold instead !!!\n";
101
102 static char *set_moh_desc = "  SetMusicOnHold(class):\n"
103 "\n"
104 "  !!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!\n"
105 "\n"
106 "Sets the default class for music on hold for a given channel.  When\n"
107 "music on hold is activated, this class will be used to select which\n"
108 "music is played.\n"
109 "\n"
110 "  !!! DEPRECATED. USe Set(CHANNEL(musicclass)=...) instead !!!\n";
111
112 static char *start_moh_desc = "  StartMusicOnHold(class):\n"
113 "Starts playing music on hold, uses default music class for channel.\n"
114 "Starts playing music specified by class.  If omitted, the default\n"
115 "music source for the channel will be used.  Always returns 0.\n";
116
117 static char *stop_moh_desc = "  StopMusicOnHold(): "
118 "Stops playing music on hold.\n";
119
120 static int respawn_time = 20;
121
122 struct moh_files_state {
123         struct mohclass *class;
124         int origwfmt;
125         int samples;
126         int sample_queue;
127         int pos;
128         int save_pos;
129         char *save_pos_filename;
130 };
131
132 #define MOH_QUIET               (1 << 0)
133 #define MOH_SINGLE              (1 << 1)
134 #define MOH_CUSTOM              (1 << 2)
135 #define MOH_RANDOMIZE           (1 << 3)
136 #define MOH_SORTALPHA           (1 << 4)
137
138 #define MOH_CACHERTCLASSES      (1 << 5)        /*!< Should we use a separate instance of MOH for each user or not */
139
140 static struct ast_flags global_flags[1] = {{0}};        /*!< global MOH_ flags */
141
142 struct mohclass {
143         char name[MAX_MUSICCLASS];
144         char dir[256];
145         char args[256];
146         char mode[80];
147         char digit;
148         /*! A dynamically sized array to hold the list of filenames in "files" mode */
149         char **filearray;
150         /*! The current size of the filearray */
151         int allowed_files;
152         /*! The current number of files loaded into the filearray */
153         int total_files;
154         unsigned int flags;
155         /*! The format from the MOH source, not applicable to "files" mode */
156         int format;
157         /*! The pid of the external application delivering MOH */
158         int pid;
159         time_t start;
160         pthread_t thread;
161         /*! Source of audio */
162         int srcfd;
163         /*! FD for timing source */
164         int pseudofd;
165         /*! Number of users */
166         int inuse;
167         /*! Created on the fly, from RT engine */
168         int realtime;
169         unsigned int delete:1;
170         AST_LIST_HEAD_NOLOCK(, mohdata) members;
171         AST_LIST_ENTRY(mohclass) list;
172 };
173
174 struct mohdata {
175         int pipe[2];
176         int origwfmt;
177         struct mohclass *parent;
178         struct ast_frame f;
179         AST_LIST_ENTRY(mohdata) list;
180 };
181
182 AST_RWLIST_HEAD_STATIC(mohclasses, mohclass);
183
184 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
185 #define MPG_123 "/usr/bin/mpg123"
186 #define MAX_MP3S 256
187
188 static int ast_moh_destroy_one(struct mohclass *moh);
189 static int reload(void);
190
191 static void ast_moh_free_class(struct mohclass **mohclass) 
192 {
193         struct mohdata *member;
194         struct mohclass *class = *mohclass;
195         int i;
196         
197         while ((member = AST_LIST_REMOVE_HEAD(&class->members, list)))
198                 ast_free(member);
199         
200         if (class->thread) {
201                 pthread_cancel(class->thread);
202                 class->thread = 0;
203         }
204
205         if (class->filearray) {
206                 for (i = 0; i < class->total_files; i++)
207                         ast_free(class->filearray[i]);
208                 ast_free(class->filearray);
209         }
210
211         ast_free(class);
212         *mohclass = NULL;
213 }
214
215
216 static void moh_files_release(struct ast_channel *chan, void *data)
217 {
218         struct moh_files_state *state;
219
220         if (chan) {
221                 if ((state = chan->music_state)) {
222                         if (chan->stream) {
223                                 ast_closestream(chan->stream);
224                                 chan->stream = NULL;
225                         }
226                         ast_verb(3, "Stopped music on hold on %s\n", chan->name);
227         
228                         if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
229                                 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
230                         }
231                         state->save_pos = state->pos;
232
233                         if (ast_atomic_dec_and_test(&state->class->inuse) && state->class->delete)
234                                 ast_moh_destroy_one(state->class);
235                 }
236         }
237 }
238
239
240 static int ast_moh_files_next(struct ast_channel *chan) 
241 {
242         struct moh_files_state *state = chan->music_state;
243         int tries;
244
245         /* Discontinue a stream if it is running already */
246         if (chan->stream) {
247                 ast_closestream(chan->stream);
248                 chan->stream = NULL;
249         }
250
251         if (!state->class->total_files) {
252                 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
253                 return -1;
254         }
255
256         /* If a specific file has been saved confirm it still exists and that it is still valid */
257         if (state->save_pos >= 0 && state->save_pos < state->class->total_files && state->class->filearray[state->save_pos] == state->save_pos_filename) {
258                 state->pos = state->save_pos;
259                 state->save_pos = -1;
260         } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
261                 /* Get a random file and ensure we can open it */
262                 for (tries = 0; tries < 20; tries++) {
263                         state->pos = ast_random() % state->class->total_files;
264                         if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
265                                 break;
266                 }
267                 state->save_pos = -1;
268                 state->samples = 0;
269         } else {
270                 /* This is easy, just increment our position and make sure we don't exceed the total file count */
271                 state->pos++;
272                 state->pos %= state->class->total_files;
273                 state->save_pos = -1;
274                 state->samples = 0;
275         }
276
277         if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
278                 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
279                 state->pos++;
280                 state->pos %= state->class->total_files;
281                 return -1;
282         }
283
284         /* Record the pointer to the filename for position resuming later */
285         state->save_pos_filename = state->class->filearray[state->pos];
286
287         ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
288
289         if (state->samples)
290                 ast_seekstream(chan->stream, state->samples, SEEK_SET);
291
292         return 0;
293 }
294
295
296 static struct ast_frame *moh_files_readframe(struct ast_channel *chan) 
297 {
298         struct ast_frame *f = NULL;
299         
300         if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
301                 if (!ast_moh_files_next(chan))
302                         f = ast_readframe(chan->stream);
303         }
304
305         return f;
306 }
307
308 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
309 {
310         struct moh_files_state *state = chan->music_state;
311         struct ast_frame *f = NULL;
312         int res = 0;
313
314         state->sample_queue += samples;
315
316         while (state->sample_queue > 0) {
317                 if ((f = moh_files_readframe(chan))) {
318                         state->samples += f->samples;
319                         state->sample_queue -= f->samples;
320                         res = ast_write(chan, f);
321                         ast_frfree(f);
322                         if (res < 0) {
323                                 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
324                                 return -1;
325                         }
326                 } else
327                         return -1;      
328         }
329         return res;
330 }
331
332
333 static void *moh_files_alloc(struct ast_channel *chan, void *params)
334 {
335         struct moh_files_state *state;
336         struct mohclass *class = params;
337
338         if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
339                 chan->music_state = state;
340                 state->class = class;
341                 state->save_pos = -1;
342         } else 
343                 state = chan->music_state;
344
345         if (state) {
346                 if (state->class != class) {
347                         /* initialize */
348                         memset(state, 0, sizeof(*state));
349                         state->class = class;
350                         if (ast_test_flag(state->class, MOH_RANDOMIZE) && class->total_files)
351                                 state->pos = ast_random() % class->total_files;
352                 }
353
354                 state->origwfmt = chan->writeformat;
355
356                 ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
357         }
358         
359         return chan->music_state;
360 }
361
362 /*! \note This function should be called with the mohclasses list locked */
363 static struct mohclass *get_mohbydigit(char digit)
364 {
365         struct mohclass *moh = NULL;
366
367         AST_RWLIST_TRAVERSE(&mohclasses, moh, list) {
368                 if (digit == moh->digit)
369                         break;
370         }
371
372         return moh;
373 }
374
375 static void moh_handle_digit(struct ast_channel *chan, char digit)
376 {
377         struct mohclass *moh;
378         const char *classname = NULL;
379
380         AST_RWLIST_RDLOCK(&mohclasses);
381         if ((moh = get_mohbydigit(digit)))
382                 classname = ast_strdupa(moh->name);
383         AST_RWLIST_UNLOCK(&mohclasses);
384
385         if (!moh)
386                 return;
387
388         ast_moh_stop(chan);
389         ast_moh_start(chan, classname, NULL);
390 }
391
392 static struct ast_generator moh_file_stream = 
393 {
394         alloc: moh_files_alloc,
395         release: moh_files_release,
396         generate: moh_files_generator,
397         digit: moh_handle_digit,
398 };
399
400 static int spawn_mp3(struct mohclass *class)
401 {
402         int fds[2];
403         int files = 0;
404         char fns[MAX_MP3S][80];
405         char *argv[MAX_MP3S + 50];
406         char xargs[256];
407         char *argptr;
408         int argc = 0;
409         DIR *dir = NULL;
410         struct dirent *de;
411
412         
413         if (!strcasecmp(class->dir, "nodir")) {
414                 files = 1;
415         } else {
416                 dir = opendir(class->dir);
417                 if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) {
418                         ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
419                         return -1;
420                 }
421         }
422
423         if (!ast_test_flag(class, MOH_CUSTOM)) {
424                 argv[argc++] = "mpg123";
425                 argv[argc++] = "-q";
426                 argv[argc++] = "-s";
427                 argv[argc++] = "--mono";
428                 argv[argc++] = "-r";
429                 argv[argc++] = "8000";
430                 
431                 if (!ast_test_flag(class, MOH_SINGLE)) {
432                         argv[argc++] = "-b";
433                         argv[argc++] = "2048";
434                 }
435                 
436                 argv[argc++] = "-f";
437                 
438                 if (ast_test_flag(class, MOH_QUIET))
439                         argv[argc++] = "4096";
440                 else
441                         argv[argc++] = "8192";
442                 
443                 /* Look for extra arguments and add them to the list */
444                 ast_copy_string(xargs, class->args, sizeof(xargs));
445                 argptr = xargs;
446                 while (!ast_strlen_zero(argptr)) {
447                         argv[argc++] = argptr;
448                         strsep(&argptr, ",");
449                 }
450         } else  {
451                 /* Format arguments for argv vector */
452                 ast_copy_string(xargs, class->args, sizeof(xargs));
453                 argptr = xargs;
454                 while (!ast_strlen_zero(argptr)) {
455                         argv[argc++] = argptr;
456                         strsep(&argptr, " ");
457                 }
458         }
459
460
461         if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) {
462                 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
463                 argv[argc++] = fns[files];
464                 files++;
465         } else if (dir) {
466                 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
467                         if ((strlen(de->d_name) > 3) && 
468                             ((ast_test_flag(class, MOH_CUSTOM) && 
469                               (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 
470                                !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
471                              !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
472                                 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
473                                 argv[argc++] = fns[files];
474                                 files++;
475                         }
476                 }
477         }
478         argv[argc] = NULL;
479         if (dir) {
480                 closedir(dir);
481         }
482         if (pipe(fds)) {        
483                 ast_log(LOG_WARNING, "Pipe failed\n");
484                 return -1;
485         }
486         if (!files) {
487                 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
488                 close(fds[0]);
489                 close(fds[1]);
490                 return -1;
491         }
492         if (time(NULL) - class->start < respawn_time) {
493                 sleep(respawn_time - (time(NULL) - class->start));
494         }
495
496         time(&class->start);
497         class->pid = ast_safe_fork(0);
498         if (class->pid < 0) {
499                 close(fds[0]);
500                 close(fds[1]);
501                 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
502                 return -1;
503         }
504         if (!class->pid) {
505                 if (ast_opt_high_priority)
506                         ast_set_priority(0);
507
508                 close(fds[0]);
509                 /* Stdout goes to pipe */
510                 dup2(fds[1], STDOUT_FILENO);
511
512                 /* Close everything else */
513                 ast_close_fds_above_n(STDERR_FILENO);
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                 /* Can't use logger, since log FDs are closed */
528                 fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
529                 close(fds[1]);
530                 _exit(1);
531         } else {
532                 /* Parent */
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 deadline, tv_tmp;
549
550         deadline.tv_sec = 0;
551         deadline.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(deadline))
575                                 deadline = tv_tmp;
576                         delta = ast_tvdiff_ms(tv_tmp, deadline);
577                         if (delta < MOH_MS_INTERVAL) {  /* too early */
578                                 deadline = ast_tvadd(deadline, 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                                 deadline = 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.ptr = 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 dir_path[PATH_MAX];
911         char path[PATH_MAX];
912         char filepath[PATH_MAX];
913         char *ext;
914         struct stat statbuf;
915         int dirnamelen;
916         int i;
917
918         if (class->dir[0] != '/') {
919                 ast_copy_string(dir_path, ast_config_AST_VAR_DIR, sizeof(dir_path));
920                 strncat(dir_path, "/", sizeof(dir_path));
921                 strncat(dir_path, class->dir, sizeof(dir_path));
922         } else {
923                 ast_copy_string(dir_path, class->dir, sizeof(dir_path));
924         }
925         ast_debug(4, "Scanning '%s' for files for class '%s'\n", dir_path, class->name);
926         files_DIR = opendir(dir_path);
927         if (!files_DIR) {
928                 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", dir_path);
929                 return -1;
930         }
931
932         for (i = 0; i < class->total_files; i++)
933                 ast_free(class->filearray[i]);
934
935         class->total_files = 0;
936         dirnamelen = strlen(dir_path) + 2;
937         getcwd(path, sizeof(path));
938         chdir(dir_path);
939         while ((files_dirent = readdir(files_DIR))) {
940                 /* The file name must be at least long enough to have the file type extension */
941                 if ((strlen(files_dirent->d_name) < 4))
942                         continue;
943
944                 /* Skip files that starts with a dot */
945                 if (files_dirent->d_name[0] == '.')
946                         continue;
947
948                 /* Skip files without extensions... they are not audio */
949                 if (!strchr(files_dirent->d_name, '.'))
950                         continue;
951
952                 snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, files_dirent->d_name);
953
954                 if (stat(filepath, &statbuf))
955                         continue;
956
957                 if (!S_ISREG(statbuf.st_mode))
958                         continue;
959
960                 if ((ext = strrchr(filepath, '.')))
961                         *ext = '\0';
962
963                 /* if the file is present in multiple formats, ensure we only put it into the list once */
964                 for (i = 0; i < class->total_files; i++)
965                         if (!strcmp(filepath, class->filearray[i]))
966                                 break;
967
968                 if (i == class->total_files) {
969                         if (moh_add_file(class, filepath))
970                                 break;
971                 }
972         }
973
974         closedir(files_DIR);
975         chdir(path);
976         if (ast_test_flag(class, MOH_SORTALPHA))
977                 qsort(&class->filearray[0], class->total_files, sizeof(char *), moh_sort_compare);
978         return class->total_files;
979 }
980
981 static int moh_diff(struct mohclass *old, struct mohclass *new)
982 {
983         if (!old || !new)
984                 return -1;
985
986         if (strcmp(old->dir, new->dir))
987                 return -1;
988         else if (strcmp(old->mode, new->mode))
989                 return -1;
990         else if (strcmp(old->args, new->args))
991                 return -1;
992         else if (old->flags != new->flags)
993                 return -1;
994
995         return 0;
996 }
997
998 static int moh_register(struct mohclass *moh, int is_reload)
999 {
1000 #ifdef HAVE_DAHDI
1001         int x;
1002 #endif
1003         struct mohclass *mohclass = NULL;
1004         int res = 0;
1005
1006         AST_RWLIST_WRLOCK(&mohclasses);
1007         if ((mohclass = get_mohbyname(moh->name, 0)) && !moh_diff(mohclass, moh)) {
1008                 if (!mohclass->delete) {
1009                         ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
1010                         ast_free(moh);
1011                         AST_RWLIST_UNLOCK(&mohclasses);
1012                         return -1;
1013                 }
1014         }
1015         AST_RWLIST_UNLOCK(&mohclasses);
1016
1017         time(&moh->start);
1018         moh->start -= respawn_time;
1019         
1020         if (!strcasecmp(moh->mode, "files")) {
1021                 res = moh_scan_files(moh);
1022                 if (res <= 0) {
1023                         if (res == 0) {
1024                                 if (option_verbose > 2)
1025                                         ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n", moh->dir, moh->name);
1026                         }
1027                         ast_moh_free_class(&moh);
1028                         return -1;
1029                 }
1030                 if (strchr(moh->args, 'r'))
1031                         ast_set_flag(moh, MOH_RANDOMIZE);
1032         } 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")) {
1033
1034                 if (!strcasecmp(moh->mode, "custom"))
1035                         ast_set_flag(moh, MOH_CUSTOM);
1036                 else if (!strcasecmp(moh->mode, "mp3nb"))
1037                         ast_set_flag(moh, MOH_SINGLE);
1038                 else if (!strcasecmp(moh->mode, "quietmp3nb"))
1039                         ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
1040                 else if (!strcasecmp(moh->mode, "quietmp3"))
1041                         ast_set_flag(moh, MOH_QUIET);
1042                 
1043                 moh->srcfd = -1;
1044 #ifdef HAVE_DAHDI
1045                 /* Open /dev/dahdi/pseudo for timing...  Is
1046                    there a better, yet reliable way to do this? */
1047                 moh->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY);
1048                 if (moh->pseudofd < 0) {
1049                         ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
1050                 } else {
1051                         x = 320;
1052                         ioctl(moh->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
1053                 }
1054 #else
1055                 moh->pseudofd = -1;
1056 #endif
1057                 if (ast_pthread_create_background(&moh->thread, NULL, monmp3thread, moh)) {
1058                         ast_log(LOG_WARNING, "Unable to create moh...\n");
1059                         if (moh->pseudofd > -1)
1060                                 close(moh->pseudofd);
1061                         ast_moh_free_class(&moh);
1062                         return -1;
1063                 }
1064         } else {
1065                 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
1066                 ast_moh_free_class(&moh);
1067                 return -1;
1068         }
1069
1070         AST_RWLIST_WRLOCK(&mohclasses);
1071         AST_RWLIST_INSERT_HEAD(&mohclasses, moh, list);
1072         AST_RWLIST_UNLOCK(&mohclasses);
1073         
1074         return 0;
1075 }
1076
1077 static void local_ast_moh_cleanup(struct ast_channel *chan)
1078 {
1079         struct moh_files_state *state = chan->music_state;
1080
1081         if (state) {
1082                 if (state->class->realtime) {
1083                         if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
1084                                 /* We are cleaning out cached RT class, we should remove it from list, if no one else using it */
1085                                 if (!(state->class->inuse)) {
1086                                         /* Remove this class from list */
1087                                         AST_RWLIST_WRLOCK(&mohclasses);
1088                                         AST_RWLIST_REMOVE(&mohclasses, state->class, list);
1089                                         AST_RWLIST_UNLOCK(&mohclasses);
1090         
1091                                         /* Free some memory */
1092                                         ast_moh_destroy_one(state->class);
1093                                 }
1094                         } else {
1095                                 ast_moh_destroy_one(state->class);
1096                         }
1097                 }
1098                 ast_free(chan->music_state);
1099                 chan->music_state = NULL;
1100         }
1101 }
1102
1103 static struct mohclass *moh_class_malloc(void)
1104 {
1105         struct mohclass *class;
1106
1107         if ((class = ast_calloc(1, sizeof(*class)))) {
1108                 class->format = AST_FORMAT_SLINEAR;
1109                 class->realtime = 0;
1110         }
1111
1112         return class;
1113 }
1114
1115 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
1116 {
1117         struct mohclass *mohclass = NULL;
1118         struct ast_variable *var = NULL;
1119         struct ast_variable *tmp = NULL;
1120         struct moh_files_state *state = chan->music_state;
1121 #ifdef HAVE_DAHDI
1122         int x;
1123 #endif
1124
1125         /* The following is the order of preference for which class to use:
1126          * 1) The channels explicitly set musicclass, which should *only* be
1127          *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
1128          * 2) The mclass argument. If a channel is calling ast_moh_start() as the
1129          *    result of receiving a HOLD control frame, this should be the
1130          *    payload that came with the frame.
1131          * 3) The interpclass argument. This would be from the mohinterpret
1132          *    option from channel drivers. This is the same as the old musicclass
1133          *    option.
1134          * 4) The default class.
1135          */
1136         
1137         /* First, let's check in memory for static and cached RT classes */
1138         AST_RWLIST_RDLOCK(&mohclasses);
1139         if (!ast_strlen_zero(chan->musicclass))
1140                 mohclass = get_mohbyname(chan->musicclass, 1);
1141         if (!mohclass && !ast_strlen_zero(mclass))
1142                 mohclass = get_mohbyname(mclass, 1);
1143         if (!mohclass && !ast_strlen_zero(interpclass))
1144                 mohclass = get_mohbyname(interpclass, 1);
1145         AST_RWLIST_UNLOCK(&mohclasses);
1146
1147         /* If no moh class found in memory, then check RT */
1148         if (!mohclass && ast_check_realtime("musiconhold")) {
1149                 if (!ast_strlen_zero(chan->musicclass)) {
1150                         var = ast_load_realtime("musiconhold", "name", chan->musicclass, SENTINEL);
1151                 }
1152                 if (!var && !ast_strlen_zero(mclass))
1153                         var = ast_load_realtime("musiconhold", "name", mclass, SENTINEL);
1154                 if (!var && !ast_strlen_zero(interpclass))
1155                         var = ast_load_realtime("musiconhold", "name", interpclass, SENTINEL);
1156                 if (!var)
1157                         var = ast_load_realtime("musiconhold", "name", "default", SENTINEL);
1158                 if (var && (mohclass = moh_class_malloc())) {
1159                         mohclass->realtime = 1;
1160                         for (tmp = var; tmp; tmp = tmp->next) {
1161                                 if (!strcasecmp(tmp->name, "name"))
1162                                         ast_copy_string(mohclass->name, tmp->value, sizeof(mohclass->name));
1163                                 else if (!strcasecmp(tmp->name, "mode"))
1164                                         ast_copy_string(mohclass->mode, tmp->value, sizeof(mohclass->mode)); 
1165                                 else if (!strcasecmp(tmp->name, "directory"))
1166                                         ast_copy_string(mohclass->dir, tmp->value, sizeof(mohclass->dir));
1167                                 else if (!strcasecmp(tmp->name, "application"))
1168                                         ast_copy_string(mohclass->args, tmp->value, sizeof(mohclass->args));
1169                                 else if (!strcasecmp(tmp->name, "digit") && (isdigit(*tmp->value) || strchr("*#", *tmp->value)))
1170                                         mohclass->digit = *tmp->value;
1171                                 else if (!strcasecmp(tmp->name, "random"))
1172                                         ast_set2_flag(mohclass, ast_true(tmp->value), MOH_RANDOMIZE);
1173                                 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "random"))
1174                                         ast_set_flag(mohclass, MOH_RANDOMIZE);
1175                                 else if (!strcasecmp(tmp->name, "sort") && !strcasecmp(tmp->value, "alpha")) 
1176                                         ast_set_flag(mohclass, MOH_SORTALPHA);
1177                                 else if (!strcasecmp(tmp->name, "format")) {
1178                                         mohclass->format = ast_getformatbyname(tmp->value);
1179                                         if (!mohclass->format) {
1180                                                 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", tmp->value);
1181                                                 mohclass->format = AST_FORMAT_SLINEAR;
1182                                         }
1183                                 }
1184                         }
1185                         ast_variables_destroy(var);
1186                         if (ast_strlen_zero(mohclass->dir)) {
1187                                 if (!strcasecmp(mohclass->mode, "custom")) {
1188                                         strcpy(mohclass->dir, "nodir");
1189                                 } else {
1190                                         ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
1191                                         ast_free(mohclass);
1192                                         return -1;
1193                                 }
1194                         }
1195                         if (ast_strlen_zero(mohclass->mode)) {
1196                                 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
1197                                 ast_free(mohclass);
1198                                 return -1;
1199                         }
1200                         if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
1201                                 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
1202                                 ast_free(mohclass);
1203                                 return -1;
1204                         }
1205
1206                         if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
1207                                 /* CACHERTCLASSES enabled, let's add this class to default tree */
1208                                 if (state && state->class) {
1209                                         /* Class already exist for this channel */
1210                                         ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1211                                         if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1212                                                 /* we found RT class with the same name, seems like we should continue playing existing one */
1213                                                 ast_moh_free_class(&mohclass);
1214                                                 mohclass = state->class;
1215                                         }
1216                                 }
1217                                 moh_register(mohclass, 0);
1218                         } else {
1219
1220                                 /* We don't register RT moh class, so let's init it manualy */
1221
1222                                 time(&mohclass->start);
1223                                 mohclass->start -= respawn_time;
1224         
1225                                 if (!strcasecmp(mohclass->mode, "files")) {
1226                                         if (!moh_scan_files(mohclass)) {
1227                                                 ast_moh_free_class(&mohclass);
1228                                                 return -1;
1229                                         }
1230                                         if (strchr(mohclass->args, 'r'))
1231                                                 ast_set_flag(mohclass, MOH_RANDOMIZE);
1232                                 } 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")) {
1233
1234                                         if (!strcasecmp(mohclass->mode, "custom"))
1235                                                 ast_set_flag(mohclass, MOH_CUSTOM);
1236                                         else if (!strcasecmp(mohclass->mode, "mp3nb"))
1237                                                 ast_set_flag(mohclass, MOH_SINGLE);
1238                                         else if (!strcasecmp(mohclass->mode, "quietmp3nb"))
1239                                                 ast_set_flag(mohclass, MOH_SINGLE | MOH_QUIET);
1240                                         else if (!strcasecmp(mohclass->mode, "quietmp3"))
1241                                                 ast_set_flag(mohclass, MOH_QUIET);
1242                         
1243                                         mohclass->srcfd = -1;
1244 #ifdef HAVE_DAHDI
1245                                         /* Open /dev/dahdi/pseudo for timing...  Is
1246                                            there a better, yet reliable way to do this? */
1247                                         mohclass->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY);
1248                                         if (mohclass->pseudofd < 0) {
1249                                                 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
1250                                         } else {
1251                                                 x = 320;
1252                                                 ioctl(mohclass->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
1253                                         }
1254 #else
1255                                         mohclass->pseudofd = -1;
1256 #endif
1257                                         /* Let's check if this channel already had a moh class before */
1258                                         if (state && state->class) {
1259                                                 /* Class already exist for this channel */
1260                                                 ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
1261                                                 if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
1262                                                         /* we found RT class with the same name, seems like we should continue playing existing one */
1263                                                         ast_moh_free_class(&mohclass);
1264                                                         mohclass = state->class;
1265         
1266                                                 }
1267                                         } else {
1268                                                 if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
1269                                                         ast_log(LOG_WARNING, "Unable to create moh...\n");
1270                                                         if (mohclass->pseudofd > -1)
1271                                                                 close(mohclass->pseudofd);
1272                                                         ast_moh_free_class(&mohclass);
1273                                                         return -1;
1274                                                 }
1275                                         }
1276                                 } else {
1277                                         ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
1278                                         ast_moh_free_class(&mohclass);
1279                                         return -1;
1280                                 }
1281
1282                         }
1283
1284                 } else if (var)
1285                         ast_variables_destroy(var);
1286         }
1287
1288         
1289
1290         /* Requested MOH class not found, check for 'default' class in musiconhold.conf  */
1291         if (!mohclass) {
1292                 AST_RWLIST_RDLOCK(&mohclasses);
1293                 mohclass = get_mohbyname("default", 1);
1294                 if (mohclass)
1295                         ast_atomic_fetchadd_int(&mohclass->inuse, +1);
1296                 AST_RWLIST_UNLOCK(&mohclasses);
1297         } else {
1298                 AST_RWLIST_RDLOCK(&mohclasses);
1299                 ast_atomic_fetchadd_int(&mohclass->inuse, +1);
1300                 AST_RWLIST_UNLOCK(&mohclasses);
1301         }
1302
1303         if (!mohclass)
1304                 return -1;
1305
1306         manager_event(EVENT_FLAG_CALL, "MusicOnHold",
1307                 "State: Start\r\n"
1308                 "Channel: %s\r\n"
1309                 "UniqueID: %s\r\n",
1310                 chan->name, chan->uniqueid);
1311
1312         ast_set_flag(chan, AST_FLAG_MOH);
1313         if (mohclass->total_files) {
1314                 return ast_activate_generator(chan, &moh_file_stream, mohclass);
1315         } else
1316                 return ast_activate_generator(chan, &mohgen, mohclass);
1317 }
1318
1319 static void local_ast_moh_stop(struct ast_channel *chan)
1320 {
1321         struct moh_files_state *state = chan->music_state;
1322         ast_clear_flag(chan, AST_FLAG_MOH);
1323         ast_deactivate_generator(chan);
1324
1325         if (state) {
1326                 if (chan->stream) {
1327                         ast_closestream(chan->stream);
1328                         chan->stream = NULL;
1329                 }
1330         }
1331
1332         manager_event(EVENT_FLAG_CALL, "MusicOnHold",
1333                 "State: Stop\r\n"
1334                 "Channel: %s\r\n"
1335                 "UniqueID: %s\r\n",
1336                 chan->name, chan->uniqueid);
1337 }
1338
1339 static int load_moh_classes(int is_reload)
1340 {
1341         struct ast_config *cfg;
1342         struct ast_variable *var;
1343         struct mohclass *class; 
1344         char *cat;
1345         int numclasses = 0;
1346         struct ast_flags config_flags = { is_reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1347
1348         cfg = ast_config_load("musiconhold.conf", config_flags);
1349
1350         if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
1351                 return 0;
1352         }
1353
1354         if (is_reload) {
1355                 AST_RWLIST_WRLOCK(&mohclasses);
1356                 AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1357                         class->delete = 1;
1358                 }
1359                 AST_RWLIST_UNLOCK(&mohclasses);
1360         }
1361         
1362         ast_clear_flag(global_flags, AST_FLAGS_ALL);
1363
1364         cat = ast_category_browse(cfg, NULL);
1365         for (; cat; cat = ast_category_browse(cfg, cat)) {
1366                 /* Setup common options from [general] section */
1367                 if (!strcasecmp(cat, "general")) {
1368                         var = ast_variable_browse(cfg, cat);
1369                         while (var) {
1370                                 if (!strcasecmp(var->name, "cachertclasses")) {
1371                                         ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
1372                                 } else {
1373                                         ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
1374                                 }
1375                                 var = var->next;
1376                         }
1377                 }
1378                 /* These names were deprecated in 1.4 and should not be used until after the next major release. */
1379                 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files") && strcasecmp(cat, "general")) {
1380                         if (!(class = moh_class_malloc()))
1381                                 break;
1382
1383                         ast_copy_string(class->name, cat, sizeof(class->name)); 
1384                         var = ast_variable_browse(cfg, cat);
1385                         while (var) {
1386                                 if (!strcasecmp(var->name, "mode"))
1387                                         ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
1388                                 else if (!strcasecmp(var->name, "directory"))
1389                                         ast_copy_string(class->dir, var->value, sizeof(class->dir));
1390                                 else if (!strcasecmp(var->name, "application"))
1391                                         ast_copy_string(class->args, var->value, sizeof(class->args));
1392                                 else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
1393                                         class->digit = *var->value;
1394                                 else if (!strcasecmp(var->name, "random"))
1395                                         ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
1396                                 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
1397                                         ast_set_flag(class, MOH_RANDOMIZE);
1398                                 else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 
1399                                         ast_set_flag(class, MOH_SORTALPHA);
1400                                 else if (!strcasecmp(var->name, "format")) {
1401                                         class->format = ast_getformatbyname(var->value);
1402                                         if (!class->format) {
1403                                                 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1404                                                 class->format = AST_FORMAT_SLINEAR;
1405                                         }
1406                                 }
1407                                 var = var->next;
1408                         }
1409
1410                         if (ast_strlen_zero(class->dir)) {
1411                                 if (!strcasecmp(class->mode, "custom")) {
1412                                         strcpy(class->dir, "nodir");
1413                                 } else {
1414                                         ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1415                                         ast_free(class);
1416                                         continue;
1417                                 }
1418                         }
1419                         if (ast_strlen_zero(class->mode)) {
1420                                 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1421                                 ast_free(class);
1422                                 continue;
1423                         }
1424                         if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1425                                 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1426                                 ast_free(class);
1427                                 continue;
1428                         }
1429
1430                         /* Don't leak a class when it's already registered */
1431                         moh_register(class, is_reload);
1432
1433                         numclasses++;
1434                 }
1435         }
1436
1437         ast_config_destroy(cfg);
1438
1439         return numclasses;
1440 }
1441
1442 static int ast_moh_destroy_one(struct mohclass *moh)
1443 {
1444         char buff[8192];
1445         int bytes, tbytes = 0, stop_time = 0, pid = 0;
1446
1447         if (moh) {
1448                 if (moh->pid > 1) {
1449                         ast_debug(1, "killing %d!\n", moh->pid);
1450                         stop_time = time(NULL) + 2;
1451                         pid = moh->pid;
1452                         moh->pid = 0;
1453                         /* Back when this was just mpg123, SIGKILL was fine.  Now we need
1454                          * to give the process a reason and time enough to kill off its
1455                          * children. */
1456                         kill(pid, SIGHUP);
1457                         usleep(100000);
1458                         kill(pid, SIGTERM);
1459                         usleep(100000);
1460                         kill(pid, SIGKILL);
1461                         while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stop_time)
1462                                 tbytes = tbytes + bytes;
1463                         ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1464                         close(moh->srcfd);
1465                 }
1466                 ast_moh_free_class(&moh);
1467         }
1468
1469         return 0;
1470 }
1471
1472 static void ast_moh_destroy(void)
1473 {
1474         struct mohclass *moh;
1475
1476         ast_verb(2, "Destroying musiconhold processes\n");
1477
1478         AST_RWLIST_WRLOCK(&mohclasses);
1479         while ((moh = AST_RWLIST_REMOVE_HEAD(&mohclasses, list))) {
1480                 ast_moh_destroy_one(moh);
1481         }
1482         AST_RWLIST_UNLOCK(&mohclasses);
1483 }
1484
1485 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1486 {
1487         switch (cmd) {
1488         case CLI_INIT:
1489                 e->command = "moh reload";
1490                 e->usage =
1491                         "Usage: moh reload\n"
1492                         "       Reloads the MusicOnHold module.\n"
1493                         "       Alias for 'module reload res_musiconhold.so'\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         reload();
1503
1504         return CLI_SUCCESS;
1505 }
1506
1507 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1508 {
1509         int i;
1510         struct mohclass *class;
1511
1512         switch (cmd) {
1513         case CLI_INIT:
1514                 e->command = "moh show files";
1515                 e->usage =
1516                         "Usage: moh show files\n"
1517                         "       Lists all loaded file-based MusicOnHold classes and their\n"
1518                         "       files.\n";
1519                 return NULL;
1520         case CLI_GENERATE:
1521                 return NULL;
1522         }
1523
1524         if (a->argc != e->args)
1525                 return CLI_SHOWUSAGE;
1526
1527         AST_RWLIST_RDLOCK(&mohclasses);
1528         AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1529                 if (!class->total_files)
1530                         continue;
1531
1532                 ast_cli(a->fd, "Class: %s\n", class->name);
1533                 for (i = 0; i < class->total_files; i++)
1534                         ast_cli(a->fd, "\tFile: %s\n", class->filearray[i]);
1535         }
1536         AST_RWLIST_UNLOCK(&mohclasses);
1537
1538         return CLI_SUCCESS;
1539 }
1540
1541 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1542 {
1543         struct mohclass *class;
1544
1545         switch (cmd) {
1546         case CLI_INIT:
1547                 e->command = "moh show classes";
1548                 e->usage =
1549                         "Usage: moh show classes\n"
1550                         "       Lists all MusicOnHold classes.\n";
1551                 return NULL;
1552         case CLI_GENERATE:
1553                 return NULL;
1554         }
1555
1556         if (a->argc != e->args)
1557                 return CLI_SHOWUSAGE;
1558
1559         AST_RWLIST_RDLOCK(&mohclasses);
1560         AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1561                 ast_cli(a->fd, "Class: %s\n", class->name);
1562                 ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1563                 ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1564                 ast_cli(a->fd, "\tUse Count: %d\n", class->inuse);
1565                 if (class->digit)
1566                         ast_cli(a->fd, "\tDigit: %c\n", class->digit);
1567                 if (ast_test_flag(class, MOH_CUSTOM))
1568                         ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1569                 if (strcasecmp(class->mode, "files"))
1570                         ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
1571         }
1572         AST_RWLIST_UNLOCK(&mohclasses);
1573
1574         return CLI_SUCCESS;
1575 }
1576
1577 static struct ast_cli_entry cli_moh[] = {
1578         AST_CLI_DEFINE(handle_cli_moh_reload,       "Reload MusicOnHold"),
1579         AST_CLI_DEFINE(handle_cli_moh_show_classes, "List MusicOnHold classes"),
1580         AST_CLI_DEFINE(handle_cli_moh_show_files,   "List MusicOnHold file-based classes")
1581 };
1582
1583 static int init_classes(int is_reload) 
1584 {
1585         struct mohclass *moh;
1586     
1587         if (!load_moh_classes(is_reload))               /* Load classes from config */
1588                 return 0;                       /* Return if nothing is found */
1589
1590         AST_RWLIST_WRLOCK(&mohclasses);
1591         AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mohclasses, moh, list) {
1592                 if (is_reload && moh->delete) {
1593                         AST_RWLIST_REMOVE_CURRENT(list);
1594                         if (!moh->inuse)
1595                                 ast_moh_destroy_one(moh);
1596                 }
1597         }
1598         AST_RWLIST_TRAVERSE_SAFE_END
1599         AST_RWLIST_UNLOCK(&mohclasses);
1600
1601         return 1;
1602 }
1603
1604 static int load_module(void)
1605 {
1606         int res;
1607
1608         res = ast_register_application(play_moh, play_moh_exec, play_moh_syn, play_moh_desc);
1609         ast_register_atexit(ast_moh_destroy);
1610         ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
1611         if (!res)
1612                 res = ast_register_application(wait_moh, wait_moh_exec, wait_moh_syn, wait_moh_desc);
1613         if (!res)
1614                 res = ast_register_application(set_moh, set_moh_exec, set_moh_syn, set_moh_desc);
1615         if (!res)
1616                 res = ast_register_application(start_moh, start_moh_exec, start_moh_syn, start_moh_desc);
1617         if (!res)
1618                 res = ast_register_application(stop_moh, stop_moh_exec, stop_moh_syn, stop_moh_desc);
1619
1620         if (!init_classes(0)) {         /* No music classes configured, so skip it */
1621                 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.\n");
1622         } else {
1623                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1624         }
1625
1626         return AST_MODULE_LOAD_SUCCESS;
1627 }
1628
1629 static int reload(void)
1630 {
1631         if (init_classes(1))
1632                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1633
1634         return 0;
1635 }
1636
1637 static int unload_module(void)
1638 {
1639         int res = 0;
1640         struct mohclass *class = NULL;
1641
1642         AST_RWLIST_WRLOCK(&mohclasses);
1643         AST_LIST_TRAVERSE(&mohclasses, class, list) {
1644                 if (class->inuse > 0) {
1645                         res = -1;
1646                         break;
1647                 }
1648         }
1649         AST_RWLIST_UNLOCK(&mohclasses);
1650         if (res < 0) {
1651                 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
1652                 return res;
1653         }
1654
1655         ast_uninstall_music_functions();
1656         ast_moh_destroy();
1657         res = ast_unregister_application(play_moh);
1658         res |= ast_unregister_application(wait_moh);
1659         res |= ast_unregister_application(set_moh);
1660         res |= ast_unregister_application(start_moh);
1661         res |= ast_unregister_application(stop_moh);
1662         ast_cli_unregister_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
1663         return res;
1664 }
1665
1666 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Music On Hold Resource",
1667                 .load = load_module,
1668                 .unload = unload_module,
1669                 .reload = reload,
1670                );