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