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