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