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