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