Let's also include aclocal.m4
[asterisk-bristuff.git] / res / res_musiconhold.c
blobed2b55f0e181f2f789e08914063a52c0103516ab
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
19 /*! \file
21 * \brief Routines implementing music on hold
23 * \arg See also \ref Config_moh
25 * \author Mark Spencer <markster@digium.com>
28 /*** MODULEINFO
29 <conflict>win32</conflict>
30 <use>dahdi</use>
31 ***/
33 #include "asterisk.h"
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
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 #ifdef SOLARIS
52 #include <thread.h>
53 #endif
55 #include "asterisk/lock.h"
56 #include "asterisk/file.h"
57 #include "asterisk/logger.h"
58 #include "asterisk/channel.h"
59 #include "asterisk/pbx.h"
60 #include "asterisk/options.h"
61 #include "asterisk/module.h"
62 #include "asterisk/translate.h"
63 #include "asterisk/say.h"
64 #include "asterisk/musiconhold.h"
65 #include "asterisk/config.h"
66 #include "asterisk/utils.h"
67 #include "asterisk/cli.h"
68 #include "asterisk/stringfields.h"
69 #include "asterisk/linkedlists.h"
71 #include "asterisk/dahdi_compat.h"
73 #define INITIAL_NUM_FILES 8
75 static char *app0 = "MusicOnHold";
76 static char *app1 = "WaitMusicOnHold";
77 static char *app2 = "SetMusicOnHold";
78 static char *app3 = "StartMusicOnHold";
79 static char *app4 = "StopMusicOnHold";
81 static char *synopsis0 = "Play Music On Hold indefinitely";
82 static char *synopsis1 = "Wait, playing Music On Hold";
83 static char *synopsis2 = "Set default Music On Hold class";
84 static char *synopsis3 = "Play Music On Hold";
85 static char *synopsis4 = "Stop Playing Music On Hold";
87 static char *descrip0 = "MusicOnHold(class): "
88 "Plays hold music specified by class. If omitted, the default\n"
89 "music source for the channel will be used. Set the default \n"
90 "class with the SetMusicOnHold() application.\n"
91 "Returns -1 on hangup.\n"
92 "Never returns otherwise.\n";
94 static char *descrip1 = "WaitMusicOnHold(delay): "
95 "Plays hold music specified number of seconds. Returns 0 when\n"
96 "done, or -1 on hangup. If no hold music is available, the delay will\n"
97 "still occur with no sound.\n";
99 static char *descrip2 = "SetMusicOnHold(class): "
100 "Sets the default class for music on hold for a given channel. When\n"
101 "music on hold is activated, this class will be used to select which\n"
102 "music is played.\n";
104 static char *descrip3 = "StartMusicOnHold(class): "
105 "Starts playing music on hold, uses default music class for channel.\n"
106 "Starts playing music specified by class. If omitted, the default\n"
107 "music source for the channel will be used. Always returns 0.\n";
109 static char *descrip4 = "StopMusicOnHold: "
110 "Stops playing music on hold.\n";
112 static int respawn_time = 20;
114 struct moh_files_state {
115 struct mohclass *class;
116 int origwfmt;
117 int samples;
118 int sample_queue;
119 int pos;
120 int save_pos;
121 char *save_pos_filename;
124 #define MOH_QUIET (1 << 0)
125 #define MOH_SINGLE (1 << 1)
126 #define MOH_CUSTOM (1 << 2)
127 #define MOH_RANDOMIZE (1 << 3)
129 struct mohclass {
130 char name[MAX_MUSICCLASS];
131 char dir[256];
132 char args[256];
133 char mode[80];
134 /*! A dynamically sized array to hold the list of filenames in "files" mode */
135 char **filearray;
136 /*! The current size of the filearray */
137 int allowed_files;
138 /*! The current number of files loaded into the filearray */
139 int total_files;
140 unsigned int flags;
141 /*! The format from the MOH source, not applicable to "files" mode */
142 int format;
143 /*! The pid of the external application delivering MOH */
144 int pid;
145 time_t start;
146 pthread_t thread;
147 /*! Source of audio */
148 int srcfd;
149 /*! FD for timing source */
150 int pseudofd;
151 /*! Number of users */
152 int inuse;
153 unsigned int delete:1;
154 AST_LIST_HEAD_NOLOCK(, mohdata) members;
155 AST_LIST_ENTRY(mohclass) list;
158 struct mohdata {
159 int pipe[2];
160 int origwfmt;
161 struct mohclass *parent;
162 struct ast_frame f;
163 AST_LIST_ENTRY(mohdata) list;
166 AST_LIST_HEAD_STATIC(mohclasses, mohclass);
168 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
169 #define MPG_123 "/usr/bin/mpg123"
170 #define MAX_MP3S 256
172 static int ast_moh_destroy_one(struct mohclass *moh);
173 static int reload(void);
175 static void ast_moh_free_class(struct mohclass **mohclass)
177 struct mohdata *member;
178 struct mohclass *class = *mohclass;
179 int i;
181 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list)))
182 free(member);
184 if (class->thread) {
185 pthread_cancel(class->thread);
186 class->thread = 0;
189 if (class->filearray) {
190 for (i = 0; i < class->total_files; i++)
191 free(class->filearray[i]);
192 free(class->filearray);
195 free(class);
196 *mohclass = NULL;
200 static void moh_files_release(struct ast_channel *chan, void *data)
202 struct moh_files_state *state;
204 if (chan) {
205 if ((state = chan->music_state)) {
206 if (chan->stream) {
207 ast_closestream(chan->stream);
208 chan->stream = NULL;
210 if (option_verbose > 2)
211 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
213 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
214 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
216 state->save_pos = state->pos;
218 if (ast_atomic_dec_and_test(&state->class->inuse) && state->class->delete)
219 ast_moh_destroy_one(state->class);
225 static int ast_moh_files_next(struct ast_channel *chan)
227 struct moh_files_state *state = chan->music_state;
228 int tries;
230 /* Discontinue a stream if it is running already */
231 if (chan->stream) {
232 ast_closestream(chan->stream);
233 chan->stream = NULL;
236 if (!state->class->total_files) {
237 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
238 return -1;
241 /* If a specific file has been saved confirm it still exists and that it is still valid */
242 if (state->save_pos >= 0 && state->save_pos < state->class->total_files && state->class->filearray[state->save_pos] == state->save_pos_filename) {
243 state->pos = state->save_pos;
244 state->save_pos = -1;
245 } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
246 /* Get a random file and ensure we can open it */
247 for (tries = 0; tries < 20; tries++) {
248 state->pos = ast_random() % state->class->total_files;
249 if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
250 break;
252 state->save_pos = -1;
253 state->samples = 0;
254 } else {
255 /* This is easy, just increment our position and make sure we don't exceed the total file count */
256 state->pos++;
257 state->pos %= state->class->total_files;
258 state->save_pos = -1;
259 state->samples = 0;
262 if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
263 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
264 state->pos++;
265 state->pos %= state->class->total_files;
266 return -1;
269 /* Record the pointer to the filename for position resuming later */
270 state->save_pos_filename = state->class->filearray[state->pos];
272 if (option_debug)
273 ast_log(LOG_DEBUG, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
275 if (state->samples)
276 ast_seekstream(chan->stream, state->samples, SEEK_SET);
278 return 0;
282 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
284 struct ast_frame *f = NULL;
286 if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
287 if (!ast_moh_files_next(chan))
288 f = ast_readframe(chan->stream);
291 return f;
294 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
296 struct moh_files_state *state = chan->music_state;
297 struct ast_frame *f = NULL;
298 int res = 0;
300 state->sample_queue += samples;
302 while (state->sample_queue > 0) {
303 if ((f = moh_files_readframe(chan))) {
304 state->samples += f->samples;
305 state->sample_queue -= f->samples;
306 res = ast_write(chan, f);
307 ast_frfree(f);
308 if (res < 0) {
309 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
310 return -1;
312 } else
313 return -1;
315 return res;
319 static void *moh_files_alloc(struct ast_channel *chan, void *params)
321 struct moh_files_state *state;
322 struct mohclass *class = params;
324 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
325 chan->music_state = state;
326 state->class = class;
327 state->save_pos = -1;
328 } else
329 state = chan->music_state;
331 if (state) {
332 if (state->class != class) {
333 /* initialize */
334 memset(state, 0, sizeof(*state));
335 state->class = class;
336 if (ast_test_flag(state->class, MOH_RANDOMIZE) && class->total_files)
337 state->pos = ast_random() % class->total_files;
340 state->origwfmt = chan->writeformat;
342 if (option_verbose > 2)
343 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", class->name, chan->name);
346 return chan->music_state;
349 static struct ast_generator moh_file_stream =
351 alloc: moh_files_alloc,
352 release: moh_files_release,
353 generate: moh_files_generator,
356 static int spawn_mp3(struct mohclass *class)
358 int fds[2];
359 int files = 0;
360 char fns[MAX_MP3S][80];
361 char *argv[MAX_MP3S + 50];
362 char xargs[256];
363 char *argptr;
364 int argc = 0;
365 DIR *dir = NULL;
366 struct dirent *de;
367 sigset_t signal_set, old_set;
370 if (!strcasecmp(class->dir, "nodir")) {
371 files = 1;
372 } else {
373 dir = opendir(class->dir);
374 if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) {
375 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
376 return -1;
380 if (!ast_test_flag(class, MOH_CUSTOM)) {
381 argv[argc++] = "mpg123";
382 argv[argc++] = "-q";
383 argv[argc++] = "-s";
384 argv[argc++] = "--mono";
385 argv[argc++] = "-r";
386 argv[argc++] = "8000";
388 if (!ast_test_flag(class, MOH_SINGLE)) {
389 argv[argc++] = "-b";
390 argv[argc++] = "2048";
393 argv[argc++] = "-f";
395 if (ast_test_flag(class, MOH_QUIET))
396 argv[argc++] = "4096";
397 else
398 argv[argc++] = "8192";
400 /* Look for extra arguments and add them to the list */
401 ast_copy_string(xargs, class->args, sizeof(xargs));
402 argptr = xargs;
403 while (!ast_strlen_zero(argptr)) {
404 argv[argc++] = argptr;
405 strsep(&argptr, ",");
407 } else {
408 /* Format arguments for argv vector */
409 ast_copy_string(xargs, class->args, sizeof(xargs));
410 argptr = xargs;
411 while (!ast_strlen_zero(argptr)) {
412 argv[argc++] = argptr;
413 strsep(&argptr, " ");
418 if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) {
419 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
420 argv[argc++] = fns[files];
421 files++;
422 } else if (dir) {
423 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
424 if ((strlen(de->d_name) > 3) &&
425 ((ast_test_flag(class, MOH_CUSTOM) &&
426 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
427 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
428 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
429 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
430 argv[argc++] = fns[files];
431 files++;
435 argv[argc] = NULL;
436 if (dir) {
437 closedir(dir);
439 if (pipe(fds)) {
440 ast_log(LOG_WARNING, "Pipe failed\n");
441 return -1;
443 if (!files) {
444 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
445 close(fds[0]);
446 close(fds[1]);
447 return -1;
449 if (time(NULL) - class->start < respawn_time) {
450 sleep(respawn_time - (time(NULL) - class->start));
453 /* Block signals during the fork() */
454 sigfillset(&signal_set);
455 pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
457 time(&class->start);
458 class->pid = fork();
459 if (class->pid < 0) {
460 close(fds[0]);
461 close(fds[1]);
462 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
463 return -1;
465 if (!class->pid) {
466 int x;
468 if (ast_opt_high_priority)
469 ast_set_priority(0);
471 /* Reset ignored signals back to default */
472 signal(SIGPIPE, SIG_DFL);
473 pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
475 close(fds[0]);
476 /* Stdout goes to pipe */
477 dup2(fds[1], STDOUT_FILENO);
478 /* Close unused file descriptors */
479 for (x=3;x<8192;x++) {
480 if (-1 != fcntl(x, F_GETFL)) {
481 close(x);
484 /* Child */
485 chdir(class->dir);
486 if (ast_test_flag(class, MOH_CUSTOM)) {
487 execv(argv[0], argv);
488 } else {
489 /* Default install is /usr/local/bin */
490 execv(LOCAL_MPG_123, argv);
491 /* Many places have it in /usr/bin */
492 execv(MPG_123, argv);
493 /* Check PATH as a last-ditch effort */
494 execvp("mpg123", argv);
496 ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
497 close(fds[1]);
498 _exit(1);
499 } else {
500 /* Parent */
501 pthread_sigmask(SIG_SETMASK, &old_set, NULL);
502 close(fds[1]);
504 return fds[0];
507 static void *monmp3thread(void *data)
509 #define MOH_MS_INTERVAL 100
511 struct mohclass *class = data;
512 struct mohdata *moh;
513 char buf[8192];
514 short sbuf[8192];
515 int res, res2;
516 int len;
517 struct timeval tv, tv_tmp;
519 tv.tv_sec = 0;
520 tv.tv_usec = 0;
521 for(;/* ever */;) {
522 pthread_testcancel();
523 /* Spawn mp3 player if it's not there */
524 if (class->srcfd < 0) {
525 if ((class->srcfd = spawn_mp3(class)) < 0) {
526 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
527 /* Try again later */
528 sleep(500);
529 pthread_testcancel();
532 if (class->pseudofd > -1) {
533 #ifdef SOLARIS
534 thr_yield();
535 #endif
536 /* Pause some amount of time */
537 res = read(class->pseudofd, buf, sizeof(buf));
538 pthread_testcancel();
539 } else {
540 long delta;
541 /* Reliable sleep */
542 tv_tmp = ast_tvnow();
543 if (ast_tvzero(tv))
544 tv = tv_tmp;
545 delta = ast_tvdiff_ms(tv_tmp, tv);
546 if (delta < MOH_MS_INTERVAL) { /* too early */
547 tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
548 usleep(1000 * (MOH_MS_INTERVAL - delta));
549 pthread_testcancel();
550 } else {
551 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
552 tv = tv_tmp;
554 res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
556 if (AST_LIST_EMPTY(&class->members))
557 continue;
558 /* Read mp3 audio */
559 len = ast_codec_get_len(class->format, res);
561 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
562 if (!res2) {
563 close(class->srcfd);
564 class->srcfd = -1;
565 pthread_testcancel();
566 if (class->pid > 1) {
567 kill(class->pid, SIGHUP);
568 usleep(100000);
569 kill(class->pid, SIGTERM);
570 usleep(100000);
571 kill(class->pid, SIGKILL);
572 class->pid = 0;
574 } else
575 ast_log(LOG_DEBUG, "Read %d bytes of audio while expecting %d\n", res2, len);
576 continue;
578 pthread_testcancel();
579 AST_LIST_LOCK(&mohclasses);
580 AST_LIST_TRAVERSE(&class->members, moh, list) {
581 /* Write data */
582 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
583 if (option_debug)
584 ast_log(LOG_DEBUG, "Only wrote %d of %d bytes to pipe\n", res, res2);
587 AST_LIST_UNLOCK(&mohclasses);
589 return NULL;
592 static int moh0_exec(struct ast_channel *chan, void *data)
594 if (ast_moh_start(chan, data, NULL)) {
595 ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
596 return 0;
598 while (!ast_safe_sleep(chan, 10000));
599 ast_moh_stop(chan);
600 return -1;
603 static int moh1_exec(struct ast_channel *chan, void *data)
605 int res;
606 if (!data || !atoi(data)) {
607 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
608 return -1;
610 if (ast_moh_start(chan, NULL, NULL)) {
611 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
612 return 0;
614 res = ast_safe_sleep(chan, atoi(data) * 1000);
615 ast_moh_stop(chan);
616 return res;
619 static int moh2_exec(struct ast_channel *chan, void *data)
621 if (ast_strlen_zero(data)) {
622 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
623 return -1;
625 ast_string_field_set(chan, musicclass, data);
626 return 0;
629 static int moh3_exec(struct ast_channel *chan, void *data)
631 char *class = NULL;
632 if (data && strlen(data))
633 class = data;
634 if (ast_moh_start(chan, class, NULL))
635 ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
637 return 0;
640 static int moh4_exec(struct ast_channel *chan, void *data)
642 ast_moh_stop(chan);
644 return 0;
647 /*! \note This function should be called with the mohclasses list locked */
648 static struct mohclass *get_mohbyname(const char *name, int warn)
650 struct mohclass *moh = NULL;
652 AST_LIST_TRAVERSE(&mohclasses, moh, list) {
653 if (!strcasecmp(name, moh->name))
654 break;
657 if (!moh && warn)
658 ast_log(LOG_WARNING, "Music on Hold class '%s' not found\n", name);
660 return moh;
663 static struct mohdata *mohalloc(struct mohclass *cl)
665 struct mohdata *moh;
666 long flags;
668 if (!(moh = ast_calloc(1, sizeof(*moh))))
669 return NULL;
671 if (pipe(moh->pipe)) {
672 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
673 free(moh);
674 return NULL;
677 /* Make entirely non-blocking */
678 flags = fcntl(moh->pipe[0], F_GETFL);
679 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
680 flags = fcntl(moh->pipe[1], F_GETFL);
681 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
683 moh->f.frametype = AST_FRAME_VOICE;
684 moh->f.subclass = cl->format;
685 moh->f.offset = AST_FRIENDLY_OFFSET;
687 moh->parent = cl;
689 AST_LIST_LOCK(&mohclasses);
690 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
691 AST_LIST_UNLOCK(&mohclasses);
693 return moh;
696 static void moh_release(struct ast_channel *chan, void *data)
698 struct mohdata *moh = data;
699 int oldwfmt;
701 AST_LIST_LOCK(&mohclasses);
702 AST_LIST_REMOVE(&moh->parent->members, moh, list);
703 AST_LIST_UNLOCK(&mohclasses);
705 close(moh->pipe[0]);
706 close(moh->pipe[1]);
707 oldwfmt = moh->origwfmt;
708 if (moh->parent->delete && ast_atomic_dec_and_test(&moh->parent->inuse))
709 ast_moh_destroy_one(moh->parent);
710 free(moh);
711 if (chan) {
712 if (oldwfmt && ast_set_write_format(chan, oldwfmt))
713 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
714 if (option_verbose > 2)
715 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
719 static void *moh_alloc(struct ast_channel *chan, void *params)
721 struct mohdata *res;
722 struct mohclass *class = params;
724 if ((res = mohalloc(class))) {
725 res->origwfmt = chan->writeformat;
726 if (ast_set_write_format(chan, class->format)) {
727 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
728 moh_release(NULL, res);
729 res = NULL;
731 if (option_verbose > 2)
732 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
734 return res;
737 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
739 struct mohdata *moh = data;
740 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
741 int res;
743 if (!moh->parent->pid)
744 return -1;
746 len = ast_codec_get_len(moh->parent->format, samples);
748 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
749 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
750 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
752 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
753 if (res <= 0)
754 return 0;
756 moh->f.datalen = res;
757 moh->f.data = buf + AST_FRIENDLY_OFFSET / 2;
758 moh->f.samples = ast_codec_get_samples(&moh->f);
760 if (ast_write(chan, &moh->f) < 0) {
761 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
762 return -1;
765 return 0;
768 static struct ast_generator mohgen =
770 alloc: moh_alloc,
771 release: moh_release,
772 generate: moh_generate,
775 static int moh_add_file(struct mohclass *class, const char *filepath)
777 if (!class->allowed_files) {
778 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
779 return -1;
780 class->allowed_files = INITIAL_NUM_FILES;
781 } else if (class->total_files == class->allowed_files) {
782 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
783 class->allowed_files = 0;
784 class->total_files = 0;
785 return -1;
787 class->allowed_files *= 2;
790 if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
791 return -1;
793 class->total_files++;
795 return 0;
798 static int moh_scan_files(struct mohclass *class) {
800 DIR *files_DIR;
801 struct dirent *files_dirent;
802 char path[PATH_MAX];
803 char filepath[PATH_MAX];
804 char *ext;
805 struct stat statbuf;
806 int dirnamelen;
807 int i;
809 files_DIR = opendir(class->dir);
810 if (!files_DIR) {
811 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir);
812 return -1;
815 for (i = 0; i < class->total_files; i++)
816 free(class->filearray[i]);
818 class->total_files = 0;
819 dirnamelen = strlen(class->dir) + 2;
820 getcwd(path, sizeof(path));
821 chdir(class->dir);
822 while ((files_dirent = readdir(files_DIR))) {
823 /* The file name must be at least long enough to have the file type extension */
824 if ((strlen(files_dirent->d_name) < 4))
825 continue;
827 /* Skip files that starts with a dot */
828 if (files_dirent->d_name[0] == '.')
829 continue;
831 /* Skip files without extensions... they are not audio */
832 if (!strchr(files_dirent->d_name, '.'))
833 continue;
835 snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
837 if (stat(filepath, &statbuf))
838 continue;
840 if (!S_ISREG(statbuf.st_mode))
841 continue;
843 if ((ext = strrchr(filepath, '.'))) {
844 *ext = '\0';
845 ext++;
848 /* if the file is present in multiple formats, ensure we only put it into the list once */
849 for (i = 0; i < class->total_files; i++)
850 if (!strcmp(filepath, class->filearray[i]))
851 break;
853 if (i == class->total_files) {
854 if (moh_add_file(class, filepath))
855 break;
859 closedir(files_DIR);
860 chdir(path);
861 return class->total_files;
864 static int moh_register(struct mohclass *moh, int reload)
866 #ifdef HAVE_DAHDI
867 int x;
868 #endif
869 struct mohclass *mohclass = NULL;
870 int res = 0;
872 AST_LIST_LOCK(&mohclasses);
873 if ((mohclass = get_mohbyname(moh->name, 0))) {
874 if (!mohclass->delete) {
875 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
876 free(moh);
877 AST_LIST_UNLOCK(&mohclasses);
878 return -1;
881 AST_LIST_UNLOCK(&mohclasses);
883 time(&moh->start);
884 moh->start -= respawn_time;
886 if (!strcasecmp(moh->mode, "files")) {
887 res = moh_scan_files(moh);
888 if (res <= 0) {
889 if (res == 0) {
890 if (option_verbose > 2)
891 ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n", moh->dir, moh->name);
893 ast_moh_free_class(&moh);
894 return -1;
896 if (strchr(moh->args, 'r'))
897 ast_set_flag(moh, MOH_RANDOMIZE);
898 } 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")) {
900 if (!strcasecmp(moh->mode, "custom"))
901 ast_set_flag(moh, MOH_CUSTOM);
902 else if (!strcasecmp(moh->mode, "mp3nb"))
903 ast_set_flag(moh, MOH_SINGLE);
904 else if (!strcasecmp(moh->mode, "quietmp3nb"))
905 ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
906 else if (!strcasecmp(moh->mode, "quietmp3"))
907 ast_set_flag(moh, MOH_QUIET);
909 moh->srcfd = -1;
910 #ifdef HAVE_DAHDI
911 /* Open /dev/zap/pseudo for timing... Is
912 there a better, yet reliable way to do this? */
913 #ifdef HAVE_ZAPTEL
914 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
915 #else
916 moh->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY);
917 #endif
918 if (moh->pseudofd < 0) {
919 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
920 } else {
921 x = 320;
922 ioctl(moh->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
924 #else
925 moh->pseudofd = -1;
926 #endif
927 if (ast_pthread_create_background(&moh->thread, NULL, monmp3thread, moh)) {
928 ast_log(LOG_WARNING, "Unable to create moh...\n");
929 if (moh->pseudofd > -1)
930 close(moh->pseudofd);
931 ast_moh_free_class(&moh);
932 return -1;
934 } else {
935 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
936 ast_moh_free_class(&moh);
937 return -1;
940 AST_LIST_LOCK(&mohclasses);
941 AST_LIST_INSERT_HEAD(&mohclasses, moh, list);
942 AST_LIST_UNLOCK(&mohclasses);
944 return 0;
947 static void local_ast_moh_cleanup(struct ast_channel *chan)
949 if (chan->music_state) {
950 free(chan->music_state);
951 chan->music_state = NULL;
955 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
957 struct mohclass *mohclass = NULL;
959 /* The following is the order of preference for which class to use:
960 * 1) The channels explicitly set musicclass, which should *only* be
961 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
962 * 2) The mclass argument. If a channel is calling ast_moh_start() as the
963 * result of receiving a HOLD control frame, this should be the
964 * payload that came with the frame.
965 * 3) The interpclass argument. This would be from the mohinterpret
966 * option from channel drivers. This is the same as the old musicclass
967 * option.
968 * 4) The default class.
970 AST_LIST_LOCK(&mohclasses);
971 if (!ast_strlen_zero(chan->musicclass))
972 mohclass = get_mohbyname(chan->musicclass, 1);
973 if (!mohclass && !ast_strlen_zero(mclass))
974 mohclass = get_mohbyname(mclass, 1);
975 if (!mohclass && !ast_strlen_zero(interpclass))
976 mohclass = get_mohbyname(interpclass, 1);
977 if (!mohclass)
978 mohclass = get_mohbyname("default", 1);
979 if (mohclass)
980 ast_atomic_fetchadd_int(&mohclass->inuse, +1);
981 AST_LIST_UNLOCK(&mohclasses);
983 if (!mohclass)
984 return -1;
986 ast_set_flag(chan, AST_FLAG_MOH);
987 if (mohclass->total_files) {
988 return ast_activate_generator(chan, &moh_file_stream, mohclass);
989 } else
990 return ast_activate_generator(chan, &mohgen, mohclass);
993 static void local_ast_moh_stop(struct ast_channel *chan)
995 ast_clear_flag(chan, AST_FLAG_MOH);
996 ast_deactivate_generator(chan);
998 if (chan->music_state) {
999 if (chan->stream) {
1000 ast_closestream(chan->stream);
1001 chan->stream = NULL;
1006 static struct mohclass *moh_class_malloc(void)
1008 struct mohclass *class;
1010 if ((class = ast_calloc(1, sizeof(*class))))
1011 class->format = AST_FORMAT_SLINEAR;
1013 return class;
1016 static int load_moh_classes(int reload)
1018 struct ast_config *cfg;
1019 struct ast_variable *var;
1020 struct mohclass *class;
1021 char *data;
1022 char *args;
1023 char *cat;
1024 int numclasses = 0;
1025 static int dep_warning = 0;
1027 cfg = ast_config_load("musiconhold.conf");
1029 if (!cfg)
1030 return 0;
1032 if (reload) {
1033 AST_LIST_LOCK(&mohclasses);
1034 AST_LIST_TRAVERSE(&mohclasses, class, list)
1035 class->delete = 1;
1036 AST_LIST_UNLOCK(&mohclasses);
1039 cat = ast_category_browse(cfg, NULL);
1040 for (; cat; cat = ast_category_browse(cfg, cat)) {
1041 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {
1042 if (!(class = moh_class_malloc())) {
1043 break;
1045 ast_copy_string(class->name, cat, sizeof(class->name));
1046 var = ast_variable_browse(cfg, cat);
1047 while (var) {
1048 if (!strcasecmp(var->name, "mode"))
1049 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1050 else if (!strcasecmp(var->name, "directory"))
1051 ast_copy_string(class->dir, var->value, sizeof(class->dir));
1052 else if (!strcasecmp(var->name, "application"))
1053 ast_copy_string(class->args, var->value, sizeof(class->args));
1054 else if (!strcasecmp(var->name, "random"))
1055 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
1056 else if (!strcasecmp(var->name, "format")) {
1057 class->format = ast_getformatbyname(var->value);
1058 if (!class->format) {
1059 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1060 class->format = AST_FORMAT_SLINEAR;
1063 var = var->next;
1066 if (ast_strlen_zero(class->dir)) {
1067 if (!strcasecmp(class->mode, "custom")) {
1068 strcpy(class->dir, "nodir");
1069 } else {
1070 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1071 free(class);
1072 continue;
1075 if (ast_strlen_zero(class->mode)) {
1076 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1077 free(class);
1078 continue;
1080 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1081 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1082 free(class);
1083 continue;
1086 /* Don't leak a class when it's already registered */
1087 moh_register(class, reload);
1089 numclasses++;
1094 /* Deprecated Old-School Configuration */
1095 var = ast_variable_browse(cfg, "classes");
1096 while (var) {
1097 if (!dep_warning) {
1098 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");
1099 dep_warning = 1;
1101 data = strchr(var->value, ':');
1102 if (data) {
1103 *data++ = '\0';
1104 args = strchr(data, ',');
1105 if (args)
1106 *args++ = '\0';
1107 if (!(get_mohbyname(var->name, 0))) {
1108 if (!(class = moh_class_malloc())) {
1109 break;
1112 ast_copy_string(class->name, var->name, sizeof(class->name));
1113 ast_copy_string(class->dir, data, sizeof(class->dir));
1114 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1115 if (args)
1116 ast_copy_string(class->args, args, sizeof(class->args));
1118 moh_register(class, reload);
1119 numclasses++;
1122 var = var->next;
1124 var = ast_variable_browse(cfg, "moh_files");
1125 while (var) {
1126 if (!dep_warning) {
1127 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");
1128 dep_warning = 1;
1130 if (!(get_mohbyname(var->name, 0))) {
1131 args = strchr(var->value, ',');
1132 if (args)
1133 *args++ = '\0';
1134 if (!(class = moh_class_malloc())) {
1135 break;
1138 ast_copy_string(class->name, var->name, sizeof(class->name));
1139 ast_copy_string(class->dir, var->value, sizeof(class->dir));
1140 strcpy(class->mode, "files");
1141 if (args)
1142 ast_copy_string(class->args, args, sizeof(class->args));
1144 moh_register(class, reload);
1145 numclasses++;
1147 var = var->next;
1150 ast_config_destroy(cfg);
1152 return numclasses;
1155 static int ast_moh_destroy_one(struct mohclass *moh)
1157 char buff[8192];
1158 int bytes, tbytes = 0, stime = 0, pid = 0;
1160 if (moh) {
1161 if (moh->pid > 1) {
1162 ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
1163 stime = time(NULL) + 2;
1164 pid = moh->pid;
1165 moh->pid = 0;
1166 /* Back when this was just mpg123, SIGKILL was fine. Now we need
1167 * to give the process a reason and time enough to kill off its
1168 * children. */
1169 kill(pid, SIGHUP);
1170 usleep(100000);
1171 kill(pid, SIGTERM);
1172 usleep(100000);
1173 kill(pid, SIGKILL);
1174 while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
1175 tbytes = tbytes + bytes;
1176 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1177 close(moh->srcfd);
1179 ast_moh_free_class(&moh);
1182 return 0;
1185 static void ast_moh_destroy(void)
1187 struct mohclass *moh;
1189 if (option_verbose > 1)
1190 ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
1192 AST_LIST_LOCK(&mohclasses);
1193 while ((moh = AST_LIST_REMOVE_HEAD(&mohclasses, list))) {
1194 ast_moh_destroy_one(moh);
1196 AST_LIST_UNLOCK(&mohclasses);
1199 static int moh_cli(int fd, int argc, char *argv[])
1201 reload();
1202 return 0;
1205 static int cli_files_show(int fd, int argc, char *argv[])
1207 int i;
1208 struct mohclass *class;
1210 AST_LIST_LOCK(&mohclasses);
1211 AST_LIST_TRAVERSE(&mohclasses, class, list) {
1212 if (!class->total_files)
1213 continue;
1215 ast_cli(fd, "Class: %s\n", class->name);
1216 for (i = 0; i < class->total_files; i++)
1217 ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
1219 AST_LIST_UNLOCK(&mohclasses);
1221 return 0;
1224 static int moh_classes_show(int fd, int argc, char *argv[])
1226 struct mohclass *class;
1228 AST_LIST_LOCK(&mohclasses);
1229 AST_LIST_TRAVERSE(&mohclasses, class, list) {
1230 ast_cli(fd, "Class: %s\n", class->name);
1231 ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1232 ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1233 ast_cli(fd, "\tUse Count: %d\n", class->inuse);
1234 if (ast_test_flag(class, MOH_CUSTOM))
1235 ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1236 if (strcasecmp(class->mode, "files"))
1237 ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
1239 AST_LIST_UNLOCK(&mohclasses);
1241 return 0;
1244 static struct ast_cli_entry cli_moh_classes_show_deprecated = {
1245 { "moh", "classes", "show"},
1246 moh_classes_show, NULL,
1247 NULL };
1249 static struct ast_cli_entry cli_moh_files_show_deprecated = {
1250 { "moh", "files", "show"},
1251 cli_files_show, NULL,
1252 NULL };
1254 static struct ast_cli_entry cli_moh[] = {
1255 { { "moh", "reload"},
1256 moh_cli, "Music On Hold",
1257 "Usage: moh reload\n Rereads configuration\n" },
1259 { { "moh", "show", "classes"},
1260 moh_classes_show, "List MOH classes",
1261 "Usage: moh show classes\n Lists all MOH classes\n", NULL, &cli_moh_classes_show_deprecated },
1263 { { "moh", "show", "files"},
1264 cli_files_show, "List MOH file-based classes",
1265 "Usage: moh show files\n Lists all loaded file-based MOH classes and their files\n", NULL, &cli_moh_files_show_deprecated },
1268 static int init_classes(int reload)
1270 struct mohclass *moh;
1272 if (!load_moh_classes(reload)) /* Load classes from config */
1273 return 0; /* Return if nothing is found */
1275 AST_LIST_LOCK(&mohclasses);
1276 AST_LIST_TRAVERSE_SAFE_BEGIN(&mohclasses, moh, list) {
1277 if (reload && moh->delete) {
1278 AST_LIST_REMOVE_CURRENT(&mohclasses, list);
1279 if (!moh->inuse)
1280 ast_moh_destroy_one(moh);
1283 AST_LIST_TRAVERSE_SAFE_END
1284 AST_LIST_UNLOCK(&mohclasses);
1286 return 1;
1289 static int load_module(void)
1291 int res;
1293 res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
1294 ast_register_atexit(ast_moh_destroy);
1295 ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
1296 if (!res)
1297 res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
1298 if (!res)
1299 res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
1300 if (!res)
1301 res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
1302 if (!res)
1303 res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
1305 if (!init_classes(0)) { /* No music classes configured, so skip it */
1306 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.\n");
1307 } else {
1308 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1311 return 0;
1314 static int reload(void)
1316 if (init_classes(1))
1317 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1319 return 0;
1322 static int unload_module(void)
1324 int res = 0;
1325 struct mohclass *class = NULL;
1327 AST_LIST_LOCK(&mohclasses);
1328 AST_LIST_TRAVERSE(&mohclasses, class, list) {
1329 if (class->inuse > 0) {
1330 res = -1;
1331 break;
1334 AST_LIST_UNLOCK(&mohclasses);
1335 if (res < 0) {
1336 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
1337 return res;
1340 ast_uninstall_music_functions();
1341 ast_moh_destroy();
1342 res = ast_unregister_application(app0);
1343 res |= ast_unregister_application(app1);
1344 res |= ast_unregister_application(app2);
1345 res |= ast_unregister_application(app3);
1346 res |= ast_unregister_application(app4);
1347 ast_cli_unregister_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
1348 return res;
1351 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Music On Hold Resource",
1352 .load = load_module,
1353 .unload = unload_module,
1354 .reload = reload,