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