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