Fix a small typo I found.
[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) {
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                 snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
782
783                 if (stat(filepath, &statbuf))
784                         continue;
785
786                 if (!S_ISREG(statbuf.st_mode))
787                         continue;
788
789                 if ((ext = strrchr(filepath, '.'))) {
790                         *ext = '\0';
791                         ext++;
792                 }
793
794                 /* if the file is present in multiple formats, ensure we only put it into the list once */
795                 for (i = 0; i < class->total_files; i++)
796                         if (!strcmp(filepath, class->filearray[i]))
797                                 break;
798
799                 if (i == class->total_files) {
800                         if (moh_add_file(class, filepath))
801                                 break;
802                 }
803         }
804
805         closedir(files_DIR);
806         chdir(path);
807         return class->total_files;
808 }
809
810 static int moh_register(struct mohclass *moh, int reload)
811 {
812 #ifdef HAVE_ZAPTEL
813         int x;
814 #endif
815         AST_LIST_LOCK(&mohclasses);
816         if (get_mohbyname(moh->name)) {
817                 if (reload) {
818                         ast_log(LOG_DEBUG, "Music on Hold class '%s' left alone from initial load.\n", moh->name);
819                 } else {
820                         ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
821                 }
822                 free(moh);      
823                 AST_LIST_UNLOCK(&mohclasses);
824                 return -1;
825         }
826         AST_LIST_UNLOCK(&mohclasses);
827
828         time(&moh->start);
829         moh->start -= respawn_time;
830         
831         if (!strcasecmp(moh->mode, "files")) {
832                 if (!moh_scan_files(moh)) {
833                         ast_moh_free_class(&moh);
834                         return -1;
835                 }
836                 if (strchr(moh->args, 'r'))
837                         ast_set_flag(moh, MOH_RANDOMIZE);
838         } 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")) {
839
840                 if (!strcasecmp(moh->mode, "custom"))
841                         ast_set_flag(moh, MOH_CUSTOM);
842                 else if (!strcasecmp(moh->mode, "mp3nb"))
843                         ast_set_flag(moh, MOH_SINGLE);
844                 else if (!strcasecmp(moh->mode, "quietmp3nb"))
845                         ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
846                 else if (!strcasecmp(moh->mode, "quietmp3"))
847                         ast_set_flag(moh, MOH_QUIET);
848                 
849                 moh->srcfd = -1;
850 #ifdef HAVE_ZAPTEL
851                 /* Open /dev/zap/pseudo for timing...  Is
852                    there a better, yet reliable way to do this? */
853                 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
854                 if (moh->pseudofd < 0) {
855                         ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
856                 } else {
857                         x = 320;
858                         ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
859                 }
860 #else
861                 moh->pseudofd = -1;
862 #endif
863                 if (ast_pthread_create(&moh->thread, NULL, monmp3thread, moh)) {
864                         ast_log(LOG_WARNING, "Unable to create moh...\n");
865                         if (moh->pseudofd > -1)
866                                 close(moh->pseudofd);
867                         ast_moh_free_class(&moh);
868                         return -1;
869                 }
870         } else {
871                 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
872                 ast_moh_free_class(&moh);
873                 return -1;
874         }
875
876         AST_LIST_LOCK(&mohclasses);
877         AST_LIST_INSERT_HEAD(&mohclasses, moh, list);
878         AST_LIST_UNLOCK(&mohclasses);
879         
880         return 0;
881 }
882
883 static void local_ast_moh_cleanup(struct ast_channel *chan)
884 {
885         if (chan->music_state) {
886                 free(chan->music_state);
887                 chan->music_state = NULL;
888         }
889 }
890
891 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
892 {
893         struct mohclass *mohclass;
894         const char *class;
895
896         /* The following is the order of preference for which class to use:
897          * 1) The channels explicitly set musicclass, which should *only* be
898          *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
899          * 2) The mclass argument. If a channel is calling ast_moh_start() as the
900          *    result of receiving a HOLD control frame, this should be the
901          *    payload that came with the frame.
902          * 3) The interpclass argument. This would be from the mohinterpret
903          *    option from channel drivers. This is the same as the old musicclass
904          *    option.
905          * 4) The default class.
906          */
907         if (!ast_strlen_zero(chan->musicclass))
908                 class = chan->musicclass;
909         else if (!ast_strlen_zero(mclass))
910                 class = mclass;
911         else if (!ast_strlen_zero(interpclass))
912                 class = interpclass;
913         else
914                 class = "default";
915
916         AST_LIST_LOCK(&mohclasses);
917         mohclass = get_mohbyname(class);
918         AST_LIST_UNLOCK(&mohclasses);
919
920         if (!mohclass) {
921                 ast_log(LOG_WARNING, "No class: %s\n", class);
922                 return -1;
923         }
924
925         ast_set_flag(chan, AST_FLAG_MOH);
926         if (mohclass->total_files) {
927                 return ast_activate_generator(chan, &moh_file_stream, mohclass);
928         } else
929                 return ast_activate_generator(chan, &mohgen, mohclass);
930 }
931
932 static void local_ast_moh_stop(struct ast_channel *chan)
933 {
934         ast_clear_flag(chan, AST_FLAG_MOH);
935         ast_deactivate_generator(chan);
936
937         if (chan->music_state) {
938                 if (chan->stream) {
939                         ast_closestream(chan->stream);
940                         chan->stream = NULL;
941                 }
942         }
943 }
944
945 static struct mohclass *moh_class_malloc(void)
946 {
947         struct mohclass *class;
948
949         if ((class = ast_calloc(1, sizeof(*class))))            
950                 class->format = AST_FORMAT_SLINEAR;
951
952         return class;
953 }
954
955 static int load_moh_classes(int reload)
956 {
957         struct ast_config *cfg;
958         struct ast_variable *var;
959         struct mohclass *class; 
960         char *data;
961         char *args;
962         char *cat;
963         int numclasses = 0;
964         static int dep_warning = 0;
965
966         cfg = ast_config_load("musiconhold.conf");
967
968         if (!cfg)
969                 return 0;
970
971         cat = ast_category_browse(cfg, NULL);
972         for (; cat; cat = ast_category_browse(cfg, cat)) {
973                 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {                       
974                         if (!(class = moh_class_malloc())) {
975                                 break;
976                         }                               
977                         ast_copy_string(class->name, cat, sizeof(class->name)); 
978                         var = ast_variable_browse(cfg, cat);
979                         while (var) {
980                                 if (!strcasecmp(var->name, "mode"))
981                                         ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
982                                 else if (!strcasecmp(var->name, "directory"))
983                                         ast_copy_string(class->dir, var->value, sizeof(class->dir));
984                                 else if (!strcasecmp(var->name, "application"))
985                                         ast_copy_string(class->args, var->value, sizeof(class->args));
986                                 else if (!strcasecmp(var->name, "random"))
987                                         ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
988                                 else if (!strcasecmp(var->name, "format")) {
989                                         class->format = ast_getformatbyname(var->value);
990                                         if (!class->format) {
991                                                 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
992                                                 class->format = AST_FORMAT_SLINEAR;
993                                         }
994                                 }
995                                 var = var->next;
996                         }
997
998                         if (ast_strlen_zero(class->dir)) {
999                                 if (!strcasecmp(class->mode, "custom")) {
1000                                         strcpy(class->dir, "nodir");
1001                                 } else {
1002                                         ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1003                                         free(class);
1004                                         continue;
1005                                 }
1006                         }
1007                         if (ast_strlen_zero(class->mode)) {
1008                                 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1009                                 free(class);
1010                                 continue;
1011                         }
1012                         if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1013                                 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1014                                 free(class);
1015                                 continue;
1016                         }
1017
1018                         /* Don't leak a class when it's already registered */
1019                         moh_register(class, reload);
1020
1021                         numclasses++;
1022                 }
1023         }
1024         
1025
1026         /* Deprecated Old-School Configuration */
1027         var = ast_variable_browse(cfg, "classes");
1028         while (var) {
1029                 if (!dep_warning) {
1030                         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");
1031                         dep_warning = 1;
1032                 }
1033                 data = strchr(var->value, ':');
1034                 if (data) {
1035                         *data++ = '\0';
1036                         args = strchr(data, ',');
1037                         if (args)
1038                                 *args++ = '\0';
1039                         if (!(get_mohbyname(var->name))) {                      
1040                                 if (!(class = moh_class_malloc())) {
1041                                         return numclasses;
1042                                 }
1043                                 
1044                                 ast_copy_string(class->name, var->name, sizeof(class->name));
1045                                 ast_copy_string(class->dir, data, sizeof(class->dir));
1046                                 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1047                                 if (args)
1048                                         ast_copy_string(class->args, args, sizeof(class->args));
1049                                 
1050                                 moh_register(class, reload);
1051                                 numclasses++;
1052                         }
1053                 }
1054                 var = var->next;
1055         }
1056         var = ast_variable_browse(cfg, "moh_files");
1057         while (var) {
1058                 if (!dep_warning) {
1059                         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");
1060                         dep_warning = 1;
1061                 }
1062                 if (!(get_mohbyname(var->name))) {
1063                         args = strchr(var->value, ',');
1064                         if (args)
1065                                 *args++ = '\0';                 
1066                         if (!(class = moh_class_malloc())) {
1067                                 return numclasses;
1068                         }
1069                         
1070                         ast_copy_string(class->name, var->name, sizeof(class->name));
1071                         ast_copy_string(class->dir, var->value, sizeof(class->dir));
1072                         strcpy(class->mode, "files");
1073                         if (args)       
1074                                 ast_copy_string(class->args, args, sizeof(class->args));
1075                         
1076                         moh_register(class, reload);
1077                         numclasses++;
1078                 }
1079                 var = var->next;
1080         }
1081
1082         ast_config_destroy(cfg);
1083
1084         return numclasses;
1085 }
1086
1087 static void ast_moh_destroy(void)
1088 {
1089         struct mohclass *moh;
1090         char buff[8192];
1091         int bytes, tbytes = 0, stime = 0, pid = 0;
1092
1093         if (option_verbose > 1)
1094                 ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
1095
1096         AST_LIST_LOCK(&mohclasses);
1097         while ((moh = AST_LIST_REMOVE_HEAD(&mohclasses, list))) {
1098                 if (moh->pid) {
1099                         ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
1100                         stime = time(NULL) + 2;
1101                         pid = moh->pid;
1102                         moh->pid = 0;
1103                         /* Back when this was just mpg123, SIGKILL was fine.  Now we need
1104                          * to give the process a reason and time enough to kill off its
1105                          * children. */
1106                         kill(pid, SIGHUP);
1107                         usleep(100000);
1108                         kill(pid, SIGTERM);
1109                         usleep(100000);
1110                         kill(pid, SIGKILL);
1111                         while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
1112                                 tbytes = tbytes + bytes;
1113                         ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1114                         close(moh->srcfd);
1115                 }
1116                 ast_moh_free_class(&moh);
1117         }
1118         AST_LIST_UNLOCK(&mohclasses);
1119 }
1120
1121 static void moh_on_off(int on)
1122 {
1123         struct ast_channel *chan = NULL;
1124
1125         while ( (chan = ast_channel_walk_locked(chan)) != NULL) {
1126                 if (ast_test_flag(chan, AST_FLAG_MOH)) {
1127                         if (on)
1128                                 local_ast_moh_start(chan, NULL, NULL);
1129                         else
1130                                 ast_deactivate_generator(chan);
1131                 }
1132                 ast_channel_unlock(chan);
1133         }
1134 }
1135
1136 static int moh_cli(int fd, int argc, char *argv[]) 
1137 {
1138         int x;
1139
1140         moh_on_off(0);
1141         ast_moh_destroy();
1142         x = load_moh_classes(1);
1143         moh_on_off(1);
1144         ast_cli(fd, "\n%d class%s reloaded.\n", x, x == 1 ? "" : "es");
1145         return 0;
1146 }
1147
1148 static int cli_files_show(int fd, int argc, char *argv[])
1149 {
1150         int i;
1151         struct mohclass *class;
1152
1153         AST_LIST_LOCK(&mohclasses);
1154         AST_LIST_TRAVERSE(&mohclasses, class, list) {
1155                 if (!class->total_files)
1156                         continue;
1157
1158                 ast_cli(fd, "Class: %s\n", class->name);
1159                 for (i = 0; i < class->total_files; i++)
1160                         ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
1161         }
1162         AST_LIST_UNLOCK(&mohclasses);
1163
1164         return 0;
1165 }
1166
1167 static int moh_classes_show(int fd, int argc, char *argv[])
1168 {
1169         struct mohclass *class;
1170
1171         AST_LIST_LOCK(&mohclasses);
1172         AST_LIST_TRAVERSE(&mohclasses, class, list) {
1173                 ast_cli(fd, "Class: %s\n", class->name);
1174                 ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1175                 ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1176                 if (ast_test_flag(class, MOH_CUSTOM))
1177                         ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1178                 if (strcasecmp(class->mode, "files"))
1179                         ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
1180         }
1181         AST_LIST_UNLOCK(&mohclasses);
1182
1183         return 0;
1184 }
1185
1186 static struct ast_cli_entry  cli_moh = { { "moh", "reload"}, moh_cli, "Music On Hold", "Music On Hold", NULL};
1187
1188 static struct ast_cli_entry  cli_moh_classes_show = { { "moh", "classes", "show"}, moh_classes_show, "List MOH classes", "Lists all MOH classes", NULL};
1189
1190 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};
1191
1192 static int init_classes(int reload) 
1193 {
1194         struct mohclass *moh;
1195     
1196         if (!load_moh_classes(reload))          /* Load classes from config */
1197                 return 0;                       /* Return if nothing is found */
1198
1199         AST_LIST_LOCK(&mohclasses);
1200         AST_LIST_TRAVERSE(&mohclasses, moh, list) {
1201                 if (moh->total_files)
1202                         moh_scan_files(moh);
1203         }
1204         AST_LIST_UNLOCK(&mohclasses);
1205
1206         return 1;
1207 }
1208
1209 static int load_module(void)
1210 {
1211         int res;
1212
1213         res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
1214         ast_register_atexit(ast_moh_destroy);
1215         ast_cli_register(&cli_moh);
1216         ast_cli_register(&cli_moh_files_show);
1217         ast_cli_register(&cli_moh_classes_show);
1218         if (!res)
1219                 res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
1220         if (!res)
1221                 res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
1222         if (!res)
1223                 res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
1224         if (!res)
1225                 res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
1226
1227         if (!init_classes(0)) {         /* No music classes configured, so skip it */
1228                 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.");
1229         } else {
1230                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1231         }
1232
1233         return 0;
1234 }
1235
1236 static int reload(void)
1237 {
1238         if (init_classes(1))
1239                 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1240
1241         return 0;
1242 }
1243
1244 static int unload_module(void)
1245 {
1246         return -1;
1247 }
1248
1249 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Music On Hold Resource",
1250                 .load = load_module,
1251                 .unload = unload_module,
1252                 .reload = reload,
1253                );