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