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