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