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