Modify the names of functions/variables in res_musiconhold to be useful.
[asterisk/asterisk.git] / res / res_musiconhold.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Routines implementing music on hold
22  *
23  * \arg See also \ref Config_moh
24  * 
25  * \author Mark Spencer <markster@digium.com>
26  */
27
28 /*** MODULEINFO
29         <conflict>win32</conflict>
30         <use>zaptel</use>
31  ***/
32
33 #include "asterisk.h"
34
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36
37 #include <stdlib.h>
38 #include <errno.h>
39 #include <ctype.h>
40 #include <unistd.h>
41 #include <string.h>
42 #include <signal.h>
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <sys/time.h>
46 #include <sys/signal.h>
47 #include <netinet/in.h>
48 #include <sys/stat.h>
49 #include <dirent.h>
50 #include <unistd.h>
51 #include <sys/ioctl.h>
52 #ifdef SOLARIS
53 #include <thread.h>
54 #endif
55
56 #include "asterisk/zapata.h"
57
58 #include "asterisk/lock.h"
59 #include "asterisk/file.h"
60 #include "asterisk/logger.h"
61 #include "asterisk/channel.h"
62 #include "asterisk/pbx.h"
63 #include "asterisk/options.h"
64 #include "asterisk/module.h"
65 #include "asterisk/translate.h"
66 #include "asterisk/say.h"
67 #include "asterisk/musiconhold.h"
68 #include "asterisk/config.h"
69 #include "asterisk/utils.h"
70 #include "asterisk/cli.h"
71 #include "asterisk/stringfields.h"
72 #include "asterisk/linkedlists.h"
73
74 #define INITIAL_NUM_FILES   8
75
76 static char *play_moh = "MusicOnHold";
77 static char *wait_moh = "WaitMusicOnHold";
78 static char *set_moh = "SetMusicOnHold";
79 static char *start_moh = "StartMusicOnHold";
80 static char *stop_moh = "StopMusicOnHold";
81
82 static char *play_moh_syn = "Play Music On Hold indefinitely";
83 static char *wait_moh_syn = "Wait, playing Music On Hold";
84 static char *set_moh_syn = "Set default Music On Hold class";
85 static char *start_moh_syn = "Play Music On Hold";
86 static char *stop_moh_syn = "Stop Playing Music On Hold";
87
88 static char *play_moh_desc = "MusicOnHold(class): "
89 "Plays hold music specified by class.  If omitted, the default\n"
90 "music source for the channel will be used. Set the default \n"
91 "class with the SetMusicOnHold() application.\n"
92 "Returns -1 on hangup.\n"
93 "Never returns otherwise.\n";
94
95 static char *wait_moh_desc = "WaitMusicOnHold(delay): "
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
100 static char *set_moh_desc = "SetMusicOnHold(class): "
101 "Sets the default class for music on hold for a given channel.  When\n"
102 "music on hold is activated, this class will be used to select which\n"
103 "music is played.\n";
104
105 static char *start_moh_desc = "StartMusicOnHold(class): "
106 "Starts playing music on hold, uses default music class for channel.\n"
107 "Starts playing music specified by class.  If omitted, the default\n"
108 "music source for the channel will be used.  Always returns 0.\n";
109
110 static char *stop_moh_desc = "StopMusicOnHold: "
111 "Stops playing music on hold.\n";
112
113 static int respawn_time = 20;
114
115 struct moh_files_state {
116         struct mohclass *class;
117         int origwfmt;
118         int samples;
119         int sample_queue;
120         unsigned char pos;
121         unsigned char save_pos;
122 };
123
124 #define MOH_QUIET               (1 << 0)
125 #define MOH_SINGLE              (1 << 1)
126 #define MOH_CUSTOM              (1 << 2)
127 #define MOH_RANDOMIZE           (1 << 3)
128
129 struct mohclass {
130         char name[MAX_MUSICCLASS];
131         char dir[256];
132         char args[256];
133         char mode[80];
134         char digit;
135         /*! A dynamically sized array to hold the list of filenames in "files" mode */
136         char **filearray;
137         /*! The current size of the filearray */
138         int allowed_files;
139         /*! The current number of files loaded into the filearray */
140         int total_files;
141         unsigned int flags;
142         /*! The format from the MOH source, not applicable to "files" mode */
143         int format;
144         /*! The pid of the external application delivering MOH */
145         int pid;
146         time_t start;
147         pthread_t thread;
148         /*! Source of audio */
149         int srcfd;
150         /*! FD for timing source */
151         int pseudofd;
152         AST_LIST_HEAD_NOLOCK(, mohdata) members;
153         AST_LIST_ENTRY(mohclass) list;
154 };
155
156 struct mohdata {
157         int pipe[2];
158         int origwfmt;
159         struct mohclass *parent;
160         struct ast_frame f;
161         AST_LIST_ENTRY(mohdata) list;
162 };
163
164 AST_RWLIST_HEAD_STATIC(mohclasses, mohclass);
165
166 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
167 #define MPG_123 "/usr/bin/mpg123"
168 #define MAX_MP3S 256
169
170
171 static void ast_moh_free_class(struct mohclass **mohclass) 
172 {
173         struct mohdata *member;
174         struct mohclass *class = *mohclass;
175         int i;
176         
177         while ((member = AST_LIST_REMOVE_HEAD(&class->members, list)))
178                 ast_free(member);
179         
180         if (class->thread) {
181                 pthread_cancel(class->thread);
182                 class->thread = 0;
183         }
184
185         if (class->filearray) {
186                 for (i = 0; i < class->total_files; i++)
187                         ast_free(class->filearray[i]);
188                 ast_free(class->filearray);
189         }
190
191         ast_free(class);
192         *mohclass = NULL;
193 }
194
195
196 static void moh_files_release(struct ast_channel *chan, void *data)
197 {
198         struct moh_files_state *state = chan->music_state;
199
200         if (chan && state) {
201                 if (chan->stream) {
202                         ast_closestream(chan->stream);
203                         chan->stream = NULL;
204                 }
205                 ast_verb(3, "Stopped music on hold on %s\n", chan->name);
206
207                 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
208                         ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
209                 }
210                 state->save_pos = state->pos;
211         }
212 }
213
214
215 static int ast_moh_files_next(struct ast_channel *chan) 
216 {
217         struct moh_files_state *state = chan->music_state;
218         int tries;
219
220         /* Discontinue a stream if it is running already */
221         if (chan->stream) {
222                 ast_closestream(chan->stream);
223                 chan->stream = NULL;
224         }
225
226         /* If a specific file has been saved, use it */
227         if (state->save_pos) {
228                 state->pos = state->save_pos;
229                 state->save_pos = 0;
230         } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
231                 /* Get a random file and ensure we can open it */
232                 for (tries = 0; tries < 20; tries++) {
233                         state->pos = rand() % state->class->total_files;
234                         if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
235                                 break;
236                 }
237                 state->samples = 0;
238         } else {
239                 /* This is easy, just increment our position and make sure we don't exceed the total file count */
240                 state->pos++;
241                 state->pos %= state->class->total_files;
242                 state->samples = 0;
243         }
244
245         if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
246                 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
247                 state->pos++;
248                 state->pos %= state->class->total_files;
249                 return -1;
250         }
251
252         ast_debug(1, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
253
254         if (state->samples)
255                 ast_seekstream(chan->stream, state->samples, SEEK_SET);
256
257         return 0;
258 }
259
260
261 static struct ast_frame *moh_files_readframe(struct ast_channel *chan) 
262 {
263         struct ast_frame *f = NULL;
264         
265         if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
266                 if (!ast_moh_files_next(chan))
267                         f = ast_readframe(chan->stream);
268         }
269
270         return f;
271 }
272
273 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
274 {
275         struct moh_files_state *state = chan->music_state;
276         struct ast_frame *f = NULL;
277         int res = 0;
278
279         state->sample_queue += samples;
280
281         while (state->sample_queue > 0) {
282                 if ((f = moh_files_readframe(chan))) {
283                         state->samples += f->samples;
284                         res = ast_write(chan, f);
285                         state->sample_queue -= f->samples;
286                         ast_frfree(f);
287                         if (res < 0) {
288                                 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
289                                 return -1;
290                         }
291                 } else
292                         return -1;      
293         }
294         return res;
295 }
296
297
298 static void *moh_files_alloc(struct ast_channel *chan, void *params)
299 {
300         struct moh_files_state *state;
301         struct mohclass *class = params;
302
303         if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
304                 chan->music_state = state;
305                 state->class = class;
306         } else 
307                 state = chan->music_state;
308
309         if (state) {
310                 if (state->class != class) {
311                         /* initialize */
312                         memset(state, 0, sizeof(*state));
313                         state->class = class;
314                         if (ast_test_flag(state->class, MOH_RANDOMIZE))
315                                 state->pos = ast_random() % class->total_files;
316                 }
317
318                 state->origwfmt = chan->writeformat;
319
320                 ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
321         }
322         
323         return chan->music_state;
324 }
325
326 /*! \note This function should be called with the mohclasses list locked */
327 static struct mohclass *get_mohbydigit(char digit)
328 {
329         struct mohclass *moh = NULL;
330
331         AST_RWLIST_TRAVERSE(&mohclasses, moh, list) {
332                 if (digit == moh->digit)
333                         break;
334         }
335
336         return moh;
337 }
338
339 static void moh_handle_digit(struct ast_channel *chan, char digit)
340 {
341         struct mohclass *moh;
342         const char *classname = NULL;
343
344         AST_RWLIST_RDLOCK(&mohclasses);
345         if ((moh = get_mohbydigit(digit)))
346                 classname = ast_strdupa(moh->name);
347         AST_RWLIST_UNLOCK(&mohclasses);
348
349         if (!moh)
350                 return;
351
352         ast_moh_stop(chan);
353         ast_moh_start(chan, classname, NULL);
354 }
355
356 static struct ast_generator moh_file_stream = 
357 {
358         alloc: moh_files_alloc,
359         release: moh_files_release,
360         generate: moh_files_generator,
361         digit: moh_handle_digit,
362 };
363
364 static int spawn_mp3(struct mohclass *class)
365 {
366         int fds[2];
367         int files = 0;
368         char fns[MAX_MP3S][80];
369         char *argv[MAX_MP3S + 50];
370         char xargs[256];
371         char *argptr;
372         int argc = 0;
373         DIR *dir = NULL;
374         struct dirent *de;
375         sigset_t signal_set, old_set;
376
377         
378         if (!strcasecmp(class->dir, "nodir")) {
379                 files = 1;
380         } else {
381                 dir = opendir(class->dir);
382                 if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) {
383                         ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
384                         return -1;
385                 }
386         }
387
388         if (!ast_test_flag(class, MOH_CUSTOM)) {
389                 argv[argc++] = "mpg123";
390                 argv[argc++] = "-q";
391                 argv[argc++] = "-s";
392                 argv[argc++] = "--mono";
393                 argv[argc++] = "-r";
394                 argv[argc++] = "8000";
395                 
396                 if (!ast_test_flag(class, MOH_SINGLE)) {
397                         argv[argc++] = "-b";
398                         argv[argc++] = "2048";
399                 }
400                 
401                 argv[argc++] = "-f";
402                 
403                 if (ast_test_flag(class, MOH_QUIET))
404                         argv[argc++] = "4096";
405                 else
406                         argv[argc++] = "8192";
407                 
408                 /* Look for extra arguments and add them to the list */
409                 ast_copy_string(xargs, class->args, sizeof(xargs));
410                 argptr = xargs;
411                 while (!ast_strlen_zero(argptr)) {
412                         argv[argc++] = argptr;
413                         strsep(&argptr, ",");
414                 }
415         } else  {
416                 /* Format arguments for argv vector */
417                 ast_copy_string(xargs, class->args, sizeof(xargs));
418                 argptr = xargs;
419                 while (!ast_strlen_zero(argptr)) {
420                         argv[argc++] = argptr;
421                         strsep(&argptr, " ");
422                 }
423         }
424
425
426         if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) {
427                 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
428                 argv[argc++] = fns[files];
429                 files++;
430         } else if (dir) {
431                 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
432                         if ((strlen(de->d_name) > 3) && 
433                             ((ast_test_flag(class, MOH_CUSTOM) && 
434                               (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 
435                                !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
436                              !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
437                                 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
438                                 argv[argc++] = fns[files];
439                                 files++;
440                         }
441                 }
442         }
443         argv[argc] = NULL;
444         if (dir) {
445                 closedir(dir);
446         }
447         if (pipe(fds)) {        
448                 ast_log(LOG_WARNING, "Pipe failed\n");
449                 return -1;
450         }
451         if (!files) {
452                 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
453                 close(fds[0]);
454                 close(fds[1]);
455                 return -1;
456         }
457         if (time(NULL) - class->start < respawn_time) {
458                 sleep(respawn_time - (time(NULL) - class->start));
459         }
460
461         /* Block signals during the fork() */
462         sigfillset(&signal_set);
463         pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
464
465         time(&class->start);
466         class->pid = fork();
467         if (class->pid < 0) {
468                 close(fds[0]);
469                 close(fds[1]);
470                 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
471                 return -1;
472         }
473         if (!class->pid) {
474                 int x;
475
476                 if (ast_opt_high_priority)
477                         ast_set_priority(0);
478
479                 /* Reset ignored signals back to default */
480                 signal(SIGPIPE, SIG_DFL);
481                 pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
482
483                 close(fds[0]);
484                 /* Stdout goes to pipe */
485                 dup2(fds[1], STDOUT_FILENO);
486                 /* Close unused file descriptors */
487                 for (x=3;x<8192;x++) {
488                         if (-1 != fcntl(x, F_GETFL)) {
489                                 close(x);
490                         }
491                 }
492                 /* Child */
493                 chdir(class->dir);
494                 if (ast_test_flag(class, MOH_CUSTOM)) {
495                         execv(argv[0], argv);
496                 } else {
497                         /* Default install is /usr/local/bin */
498                         execv(LOCAL_MPG_123, argv);
499                         /* Many places have it in /usr/bin */
500                         execv(MPG_123, argv);
501                         /* Check PATH as a last-ditch effort */
502                         execvp("mpg123", argv);
503                 }
504                 ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
505                 close(fds[1]);
506                 _exit(1);
507         } else {
508                 /* Parent */
509                 pthread_sigmask(SIG_SETMASK, &old_set, NULL);
510                 close(fds[1]);
511         }
512         return fds[0];
513 }
514
515 static void *monmp3thread(void *data)
516 {
517 #define MOH_MS_INTERVAL         100
518
519         struct mohclass *class = data;
520         struct mohdata *moh;
521         char buf[8192];
522         short sbuf[8192];
523         int res, res2;
524         int len;
525         struct timeval tv, tv_tmp;
526
527         tv.tv_sec = 0;
528         tv.tv_usec = 0;
529         for(;/* ever */;) {
530                 pthread_testcancel();
531                 /* Spawn mp3 player if it's not there */
532                 if (class->srcfd < 0) {
533                         if ((class->srcfd = spawn_mp3(class)) < 0) {
534                                 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
535                                 /* Try again later */
536                                 sleep(500);
537                                 pthread_testcancel();
538                         }
539                 }
540                 if (class->pseudofd > -1) {
541 #ifdef SOLARIS
542                         thr_yield();
543 #endif
544                         /* Pause some amount of time */
545                         res = read(class->pseudofd, buf, sizeof(buf));
546                         pthread_testcancel();
547                 } else {
548                         long delta;
549                         /* Reliable sleep */
550                         tv_tmp = ast_tvnow();
551                         if (ast_tvzero(tv))
552                                 tv = tv_tmp;
553                         delta = ast_tvdiff_ms(tv_tmp, tv);
554                         if (delta < MOH_MS_INTERVAL) {  /* too early */
555                                 tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
556                                 usleep(1000 * (MOH_MS_INTERVAL - delta));
557                                 pthread_testcancel();
558                         } else {
559                                 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
560                                 tv = tv_tmp;
561                         }
562                         res = 8 * MOH_MS_INTERVAL;      /* 8 samples per millisecond */
563                 }
564                 if (AST_LIST_EMPTY(&class->members))
565                         continue;
566                 /* Read mp3 audio */
567                 len = ast_codec_get_len(class->format, res);
568                 
569                 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
570                         if (!res2) {
571                                 close(class->srcfd);
572                                 class->srcfd = -1;
573                                 pthread_testcancel();
574                                 if (class->pid > 1) {
575                                         kill(class->pid, SIGHUP);
576                                         usleep(100000);
577                                         kill(class->pid, SIGTERM);
578                                         usleep(100000);
579                                         kill(class->pid, SIGKILL);
580                                         class->pid = 0;
581                                 }
582                         } else {
583                                 ast_debug(1, "Read %d bytes of audio while expecting %d\n", res2, len);
584                         }
585                         continue;
586                 }
587                 pthread_testcancel();
588                 AST_RWLIST_RDLOCK(&mohclasses);
589                 AST_RWLIST_TRAVERSE(&class->members, moh, list) {
590                         /* Write data */
591                         if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
592                                 ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
593                         }
594                 }
595                 AST_RWLIST_UNLOCK(&mohclasses);
596         }
597         return NULL;
598 }
599
600 static int play_moh_exec(struct ast_channel *chan, void *data)
601 {
602         if (ast_moh_start(chan, data, NULL)) {
603                 ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
604                 return 0;
605         }
606         while (!ast_safe_sleep(chan, 10000));
607         ast_moh_stop(chan);
608         return -1;
609 }
610
611 static int wait_moh_exec(struct ast_channel *chan, void *data)
612 {
613         int res;
614         if (!data || !atoi(data)) {
615                 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
616                 return -1;
617         }
618         if (ast_moh_start(chan, NULL, NULL)) {
619                 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
620                 return 0;
621         }
622         res = ast_safe_sleep(chan, atoi(data) * 1000);
623         ast_moh_stop(chan);
624         return res;
625 }
626
627 static int set_moh_exec(struct ast_channel *chan, void *data)
628 {
629         if (ast_strlen_zero(data)) {
630                 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
631                 return -1;
632         }
633         ast_string_field_set(chan, musicclass, data);
634         return 0;
635 }
636
637 static int start_moh_exec(struct ast_channel *chan, void *data)
638 {
639         char *class = NULL;
640         if (data && strlen(data))
641                 class = data;
642         if (ast_moh_start(chan, class, NULL)) 
643                 ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
644
645         return 0;
646 }
647
648 static int stop_moh_exec(struct ast_channel *chan, void *data)
649 {
650         ast_moh_stop(chan);
651
652         return 0;
653 }
654
655 /*! \note This function should be called with the mohclasses list locked */
656 static struct mohclass *get_mohbyname(const char *name, int warn)
657 {
658         struct mohclass *moh = NULL;
659
660         AST_RWLIST_TRAVERSE(&mohclasses, moh, list) {
661                 if (!strcasecmp(name, moh->name))
662                         break;
663         }
664
665         if (!moh && warn)
666                 ast_log(LOG_WARNING, "Music on Hold class '%s' not found\n", name);
667
668         return moh;
669 }
670
671 static struct mohdata *mohalloc(struct mohclass *cl)
672 {
673         struct mohdata *moh;
674         long flags;     
675         
676         if (!(moh = ast_calloc(1, sizeof(*moh))))
677                 return NULL;
678         
679         if (pipe(moh->pipe)) {
680                 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
681                 ast_free(moh);
682                 return NULL;
683         }
684
685         /* Make entirely non-blocking */
686         flags = fcntl(moh->pipe[0], F_GETFL);
687         fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
688         flags = fcntl(moh->pipe[1], F_GETFL);
689         fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
690
691         moh->f.frametype = AST_FRAME_VOICE;
692         moh->f.subclass = cl->format;
693         moh->f.offset = AST_FRIENDLY_OFFSET;
694
695         moh->parent = cl;
696
697         AST_RWLIST_WRLOCK(&mohclasses);
698         AST_LIST_INSERT_HEAD(&cl->members, moh, list);
699         AST_RWLIST_UNLOCK(&mohclasses);
700         
701         return moh;
702 }
703
704 static void moh_release(struct ast_channel *chan, void *data)
705 {
706         struct mohdata *moh = data;
707         int oldwfmt;
708
709         AST_RWLIST_WRLOCK(&mohclasses);
710         AST_RWLIST_REMOVE(&moh->parent->members, moh, list);    
711         AST_RWLIST_UNLOCK(&mohclasses);
712         
713         close(moh->pipe[0]);
714         close(moh->pipe[1]);
715         oldwfmt = moh->origwfmt;
716         ast_free(moh);
717         if (chan) {
718                 if (oldwfmt && ast_set_write_format(chan, oldwfmt)) 
719                         ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
720                 ast_verb(3, "Stopped music on hold on %s\n", chan->name);
721         }
722 }
723
724 static void *moh_alloc(struct ast_channel *chan, void *params)
725 {
726         struct mohdata *res;
727         struct mohclass *class = params;
728
729         if ((res = mohalloc(class))) {
730                 res->origwfmt = chan->writeformat;
731                 if (ast_set_write_format(chan, class->format)) {
732                         ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
733                         moh_release(NULL, res);
734                         res = NULL;
735                 }
736                 ast_verb(3, "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
737         }
738         return res;
739 }
740
741 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
742 {
743         struct mohdata *moh = data;
744         short buf[1280 + AST_FRIENDLY_OFFSET / 2];
745         int res;
746
747         if (!moh->parent->pid)
748                 return -1;
749
750         len = ast_codec_get_len(moh->parent->format, samples);
751
752         if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
753                 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
754                 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
755         }
756         res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
757         if (res <= 0)
758                 return 0;
759
760         moh->f.datalen = res;
761         moh->f.data = buf + AST_FRIENDLY_OFFSET / 2;
762         moh->f.samples = ast_codec_get_samples(&moh->f);
763
764         if (ast_write(chan, &moh->f) < 0) {
765                 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
766                 return -1;
767         }
768
769         return 0;
770 }
771
772 static struct ast_generator mohgen = 
773 {
774         alloc: moh_alloc,
775         release: moh_release,
776         generate: moh_generate,
777         digit: moh_handle_digit
778 };
779
780 static int moh_add_file(struct mohclass *class, const char *filepath)
781 {
782         if (!class->allowed_files) {
783                 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
784                         return -1;
785                 class->allowed_files = INITIAL_NUM_FILES;
786         } else if (class->total_files == class->allowed_files) {
787                 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
788                         class->allowed_files = 0;
789                         class->total_files = 0;
790                         return -1;
791                 }
792                 class->allowed_files *= 2;
793         }
794
795         if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
796                 return -1;
797
798         class->total_files++;
799
800         return 0;
801 }
802
803 static int moh_scan_files(struct mohclass *class) {
804
805         DIR *files_DIR;
806         struct dirent *files_dirent;
807         char path[PATH_MAX];
808         char filepath[PATH_MAX];
809         char *ext;
810         struct stat statbuf;
811         int dirnamelen;
812         int i;
813         
814         files_DIR = opendir(class->dir);
815         if (!files_DIR) {
816                 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir);
817                 return -1;
818         }
819
820         for (i = 0; i < class->total_files; i++)
821                 ast_free(class->filearray[i]);
822
823         class->total_files = 0;
824         dirnamelen = strlen(class->dir) + 2;
825         getcwd(path, sizeof(path));
826         chdir(class->dir);
827         while ((files_dirent = readdir(files_DIR))) {
828                 /* The file name must be at least long enough to have the file type extension */
829                 if ((strlen(files_dirent->d_name) < 4))
830                         continue;
831
832                 /* Skip files that starts with a dot */
833                 if (files_dirent->d_name[0] == '.')
834                         continue;
835
836                 /* Skip files without extensions... they are not audio */
837                 if (!strchr(files_dirent->d_name, '.'))
838                         continue;
839
840                 snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
841
842                 if (stat(filepath, &statbuf))
843                         continue;
844
845                 if (!S_ISREG(statbuf.st_mode))
846                         continue;
847
848                 if ((ext = strrchr(filepath, '.'))) {
849                         *ext = '\0';
850                         ext++;
851                 }
852
853                 /* if the file is present in multiple formats, ensure we only put it into the list once */
854                 for (i = 0; i < class->total_files; i++)
855                         if (!strcmp(filepath, class->filearray[i]))
856                                 break;
857
858                 if (i == class->total_files) {
859                         if (moh_add_file(class, filepath))
860                                 break;
861                 }
862         }
863
864         closedir(files_DIR);
865         chdir(path);
866         return class->total_files;
867 }
868
869 static int moh_register(struct mohclass *moh, int reload)
870 {
871 #ifdef HAVE_ZAPTEL
872         int x;
873 #endif
874         AST_RWLIST_WRLOCK(&mohclasses);
875         if (get_mohbyname(moh->name, 0)) {
876                 if (reload) {
877                         ast_debug(1, "Music on Hold class '%s' left alone from initial load.\n", moh->name);
878                 } else {
879                         ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
880                 }
881                 ast_free(moh);  
882                 AST_RWLIST_UNLOCK(&mohclasses);
883                 return -1;
884         }
885         AST_RWLIST_UNLOCK(&mohclasses);
886
887         time(&moh->start);
888         moh->start -= respawn_time;
889         
890         if (!strcasecmp(moh->mode, "files")) {
891                 if (!moh_scan_files(moh)) {
892                         ast_moh_free_class(&moh);
893                         return -1;
894                 }
895                 if (strchr(moh->args, 'r'))
896                         ast_set_flag(moh, MOH_RANDOMIZE);
897         } 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")) {
898
899                 if (!strcasecmp(moh->mode, "custom"))
900                         ast_set_flag(moh, MOH_CUSTOM);
901                 else if (!strcasecmp(moh->mode, "mp3nb"))
902                         ast_set_flag(moh, MOH_SINGLE);
903                 else if (!strcasecmp(moh->mode, "quietmp3nb"))
904                         ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
905                 else if (!strcasecmp(moh->mode, "quietmp3"))
906                         ast_set_flag(moh, MOH_QUIET);
907                 
908                 moh->srcfd = -1;
909 #ifdef HAVE_ZAPTEL
910                 /* Open /dev/zap/pseudo for timing...  Is
911                    there a better, yet reliable way to do this? */
912                 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
913                 if (moh->pseudofd < 0) {
914                         ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
915                 } else {
916                         x = 320;
917                         ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
918                 }
919 #else
920                 moh->pseudofd = -1;
921 #endif
922                 if (ast_pthread_create_background(&moh->thread, NULL, monmp3thread, moh)) {
923                         ast_log(LOG_WARNING, "Unable to create moh...\n");
924                         if (moh->pseudofd > -1)
925                                 close(moh->pseudofd);
926                         ast_moh_free_class(&moh);
927                         return -1;
928                 }
929         } else {
930                 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
931                 ast_moh_free_class(&moh);
932                 return -1;
933         }
934
935         AST_RWLIST_WRLOCK(&mohclasses);
936         AST_RWLIST_INSERT_HEAD(&mohclasses, moh, list);
937         AST_RWLIST_UNLOCK(&mohclasses);
938         
939         return 0;
940 }
941
942 static void local_ast_moh_cleanup(struct ast_channel *chan)
943 {
944         if (chan->music_state) {
945                 ast_free(chan->music_state);
946                 chan->music_state = NULL;
947         }
948 }
949
950 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
951 {
952         struct mohclass *mohclass = NULL;
953
954         /* The following is the order of preference for which class to use:
955          * 1) The channels explicitly set musicclass, which should *only* be
956          *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
957          * 2) The mclass argument. If a channel is calling ast_moh_start() as the
958          *    result of receiving a HOLD control frame, this should be the
959          *    payload that came with the frame.
960          * 3) The interpclass argument. This would be from the mohinterpret
961          *    option from channel drivers. This is the same as the old musicclass
962          *    option.
963          * 4) The default class.
964          */
965         AST_RWLIST_RDLOCK(&mohclasses);
966         if (!ast_strlen_zero(chan->musicclass))
967                 mohclass = get_mohbyname(chan->musicclass, 1);
968         if (!mohclass && !ast_strlen_zero(mclass))
969                 mohclass = get_mohbyname(mclass, 1);
970         if (!mohclass && !ast_strlen_zero(interpclass))
971                 mohclass = get_mohbyname(interpclass, 1);
972         if (!mohclass)  
973                 mohclass = get_mohbyname("default", 1);
974         AST_RWLIST_UNLOCK(&mohclasses);
975
976         if (!mohclass)
977                 return -1;
978
979         ast_set_flag(chan, AST_FLAG_MOH);
980         if (mohclass->total_files) {
981                 return ast_activate_generator(chan, &moh_file_stream, mohclass);
982         } else
983                 return ast_activate_generator(chan, &mohgen, mohclass);
984 }
985
986 static void local_ast_moh_stop(struct ast_channel *chan)
987 {
988         ast_clear_flag(chan, AST_FLAG_MOH);
989         ast_deactivate_generator(chan);
990
991         if (chan->music_state) {
992                 if (chan->stream) {
993                         ast_closestream(chan->stream);
994                         chan->stream = NULL;
995                 }
996         }
997 }
998
999 static struct mohclass *moh_class_malloc(void)
1000 {
1001         struct mohclass *class;
1002
1003         if ((class = ast_calloc(1, sizeof(*class))))            
1004                 class->format = AST_FORMAT_SLINEAR;
1005
1006         return class;
1007 }
1008
1009 static int load_moh_classes(int reload)
1010 {
1011         struct ast_config *cfg;
1012         struct ast_variable *var;
1013         struct mohclass *class; 
1014         char *cat;
1015         int numclasses = 0;
1016
1017         cfg = ast_config_load("musiconhold.conf");
1018
1019         if (!cfg)
1020                 return 0;
1021
1022         cat = ast_category_browse(cfg, NULL);
1023         for (; cat; cat = ast_category_browse(cfg, cat)) {
1024                 /* These names were deprecated in 1.4 and should not be used until after the next major release. */
1025                 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {                       
1026                         if (!(class = moh_class_malloc()))
1027                                 break;
1028
1029                         ast_copy_string(class->name, cat, sizeof(class->name)); 
1030                         var = ast_variable_browse(cfg, cat);
1031                         while (var) {
1032                                 if (!strcasecmp(var->name, "mode"))
1033                                         ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
1034                                 else if (!strcasecmp(var->name, "directory"))
1035                                         ast_copy_string(class->dir, var->value, sizeof(class->dir));
1036                                 else if (!strcasecmp(var->name, "application"))
1037                                         ast_copy_string(class->args, var->value, sizeof(class->args));
1038                                 else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
1039                                         class->digit = *var->value;
1040                                 else if (!strcasecmp(var->name, "random"))
1041                                         ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
1042                                 else if (!strcasecmp(var->name, "format")) {
1043                                         class->format = ast_getformatbyname(var->value);
1044                                         if (!class->format) {
1045                                                 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1046                                                 class->format = AST_FORMAT_SLINEAR;
1047                                         }
1048                                 }
1049                                 var = var->next;
1050                         }
1051
1052                         if (ast_strlen_zero(class->dir)) {
1053                                 if (!strcasecmp(class->mode, "custom")) {
1054                                         strcpy(class->dir, "nodir");
1055                                 } else {
1056                                         ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1057                                         ast_free(class);
1058                                         continue;
1059                                 }
1060                         }
1061                         if (ast_strlen_zero(class->mode)) {
1062                                 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1063                                 ast_free(class);
1064                                 continue;
1065                         }
1066                         if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1067                                 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1068                                 ast_free(class);
1069                                 continue;
1070                         }
1071
1072                         /* Don't leak a class when it's already registered */
1073                         moh_register(class, reload);
1074
1075                         numclasses++;
1076                 }
1077         }
1078
1079         ast_config_destroy(cfg);
1080
1081         return numclasses;
1082 }
1083
1084 static void ast_moh_destroy(void)
1085 {
1086         struct mohclass *moh;
1087         char buff[8192];
1088         int bytes, tbytes = 0, stime = 0, pid = 0;
1089
1090         ast_verb(2, "Destroying musiconhold processes\n");
1091
1092         AST_RWLIST_WRLOCK(&mohclasses);
1093         while ((moh = AST_RWLIST_REMOVE_HEAD(&mohclasses, list))) {
1094                 if (moh->pid > 1) {
1095                         ast_debug(1, "killing %d!\n", moh->pid);
1096                         stime = time(NULL) + 2;
1097                         pid = moh->pid;
1098                         moh->pid = 0;
1099                         /* Back when this was just mpg123, SIGKILL was fine.  Now we need
1100                          * to give the process a reason and time enough to kill off its
1101                          * children. */
1102                         kill(pid, SIGHUP);
1103                         usleep(100000);
1104                         kill(pid, SIGTERM);
1105                         usleep(100000);
1106                         kill(pid, SIGKILL);
1107                         while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
1108                                 tbytes = tbytes + bytes;
1109                         ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1110                         close(moh->srcfd);
1111                 }
1112                 ast_moh_free_class(&moh);
1113         }
1114         AST_RWLIST_UNLOCK(&mohclasses);
1115 }
1116
1117 static void moh_on_off(int on)
1118 {
1119         struct ast_channel *chan = NULL;
1120
1121         while ( (chan = ast_channel_walk_locked(chan)) != NULL) {
1122                 if (ast_test_flag(chan, AST_FLAG_MOH)) {
1123                         if (on)
1124                                 local_ast_moh_start(chan, NULL, NULL);
1125                         else
1126                                 ast_deactivate_generator(chan);
1127                 }
1128                 ast_channel_unlock(chan);
1129         }
1130 }
1131
1132 static int moh_cli(int fd, int argc, char *argv[]) 
1133 {
1134         int x;
1135
1136         moh_on_off(0);
1137         ast_moh_destroy();
1138         x = load_moh_classes(1);
1139         moh_on_off(1);
1140         ast_cli(fd, "\n%d class%s reloaded.\n", x, x == 1 ? "" : "es");
1141         return 0;
1142 }
1143
1144 static int cli_files_show(int fd, int argc, char *argv[])
1145 {
1146         int i;
1147         struct mohclass *class;
1148
1149         AST_RWLIST_RDLOCK(&mohclasses);
1150         AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1151                 if (!class->total_files)
1152                         continue;
1153
1154                 ast_cli(fd, "Class: %s\n", class->name);
1155                 for (i = 0; i < class->total_files; i++)
1156                         ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
1157         }
1158         AST_RWLIST_UNLOCK(&mohclasses);
1159
1160         return 0;
1161 }
1162
1163 static int moh_classes_show(int fd, int argc, char *argv[])
1164 {
1165         struct mohclass *class;
1166
1167         AST_RWLIST_RDLOCK(&mohclasses);
1168         AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
1169                 ast_cli(fd, "Class: %s\n", class->name);
1170                 ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1171                 ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1172                 if (class->digit)
1173                         ast_cli(fd, "\tDigit: %c\n", class->digit);
1174                 if (ast_test_flag(class, MOH_CUSTOM))
1175                         ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1176                 if (strcasecmp(class->mode, "files"))
1177                         ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
1178         }
1179         AST_RWLIST_UNLOCK(&mohclasses);
1180
1181         return 0;
1182 }
1183
1184 static struct ast_cli_entry cli_moh[] = {
1185         { { "moh", "reload"},
1186         moh_cli, "Music On Hold",
1187         "Music On Hold" },
1188
1189         { { "moh", "show", "classes"},
1190         moh_classes_show, "List MOH classes",
1191         "Lists all MOH classes" },
1192
1193         { { "moh", "show", "files"},
1194         cli_files_show, "List MOH file-based classes",
1195         "Lists all loaded file-based MOH classes and their files" },
1196 };
1197
1198 static int init_classes(int reload) 
1199 {
1200         struct mohclass *moh;
1201     
1202         if (!load_moh_classes(reload))          /* Load classes from config */
1203                 return 0;                       /* Return if nothing is found */
1204
1205         AST_RWLIST_WRLOCK(&mohclasses);
1206         AST_RWLIST_TRAVERSE(&mohclasses, moh, list) {
1207                 if (moh->total_files)
1208                         moh_scan_files(moh);
1209         }
1210         AST_RWLIST_UNLOCK(&mohclasses);
1211
1212         return 1;
1213 }
1214
1215 static int load_module(void)
1216 {
1217         int res;
1218
1219         res = ast_register_application(play_moh, play_moh_exec, play_moh_syn, play_moh_desc);
1220         ast_register_atexit(ast_moh_destroy);
1221         ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
1222         if (!res)
1223                 res = ast_register_application(wait_moh, wait_moh_exec, wait_moh_syn, wait_moh_desc);
1224         if (!res)
1225                 res = ast_register_application(set_moh, set_moh_exec, set_moh_syn, set_moh_desc);
1226         if (!res)
1227                 res = ast_register_application(start_moh, start_moh_exec, start_moh_syn, start_moh_desc);
1228         if (!res)
1229                 res = ast_register_application(stop_moh, stop_moh_exec, stop_moh_syn, stop_moh_desc);
1230
1231         if (!init_classes(0)) {         /* No music classes configured, so skip it */
1232                 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.\n");
1233         } else {
1234                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1235         }
1236
1237         return 0;
1238 }
1239
1240 static int reload(void)
1241 {
1242         if (init_classes(1))
1243                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1244
1245         return 0;
1246 }
1247
1248 static int unload_module(void)
1249 {
1250         return -1;
1251 }
1252
1253 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Music On Hold Resource",
1254                 .load = load_module,
1255                 .unload = unload_module,
1256                 .reload = reload,
1257                );