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