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