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