Don't give up on attempting an outbound registration if we receive a 408 Timeout.
[asterisk-bristuff.git] / res / res_musiconhold.c
blobfaab84cae399ed4997d891d0f702fd4db95ea41c
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>zaptel</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 #ifdef HAVE_ZAPTEL
56 #include <zaptel/zaptel.h>
57 #endif
59 #include "asterisk/lock.h"
60 #include "asterisk/file.h"
61 #include "asterisk/logger.h"
62 #include "asterisk/channel.h"
63 #include "asterisk/pbx.h"
64 #include "asterisk/options.h"
65 #include "asterisk/module.h"
66 #include "asterisk/translate.h"
67 #include "asterisk/say.h"
68 #include "asterisk/musiconhold.h"
69 #include "asterisk/config.h"
70 #include "asterisk/utils.h"
71 #include "asterisk/cli.h"
72 #include "asterisk/stringfields.h"
73 #include "asterisk/linkedlists.h"
75 #define INITIAL_NUM_FILES 8
77 static char *app0 = "MusicOnHold";
78 static char *app1 = "WaitMusicOnHold";
79 static char *app2 = "SetMusicOnHold";
80 static char *app3 = "StartMusicOnHold";
81 static char *app4 = "StopMusicOnHold";
83 static char *synopsis0 = "Play Music On Hold indefinitely";
84 static char *synopsis1 = "Wait, playing Music On Hold";
85 static char *synopsis2 = "Set default Music On Hold class";
86 static char *synopsis3 = "Play Music On Hold";
87 static char *synopsis4 = "Stop Playing Music On Hold";
89 static char *descrip0 = "MusicOnHold(class): "
90 "Plays hold music specified by class. If omitted, the default\n"
91 "music source for the channel will be used. Set the default \n"
92 "class with the SetMusicOnHold() application.\n"
93 "Returns -1 on hangup.\n"
94 "Never returns otherwise.\n";
96 static char *descrip1 = "WaitMusicOnHold(delay): "
97 "Plays hold music specified number of seconds. Returns 0 when\n"
98 "done, or -1 on hangup. If no hold music is available, the delay will\n"
99 "still occur with no sound.\n";
101 static char *descrip2 = "SetMusicOnHold(class): "
102 "Sets the default class for music on hold for a given channel. When\n"
103 "music on hold is activated, this class will be used to select which\n"
104 "music is played.\n";
106 static char *descrip3 = "StartMusicOnHold(class): "
107 "Starts playing music on hold, uses default music class for channel.\n"
108 "Starts playing music specified by class. If omitted, the default\n"
109 "music source for the channel will be used. Always returns 0.\n";
111 static char *descrip4 = "StopMusicOnHold: "
112 "Stops playing music on hold.\n";
114 static int respawn_time = 20;
116 struct moh_files_state {
117 struct mohclass *class;
118 int origwfmt;
119 int samples;
120 int sample_queue;
121 int pos;
122 int save_pos;
123 char *save_pos_filename;
126 #define MOH_QUIET (1 << 0)
127 #define MOH_SINGLE (1 << 1)
128 #define MOH_CUSTOM (1 << 2)
129 #define MOH_RANDOMIZE (1 << 3)
131 struct mohclass {
132 char name[MAX_MUSICCLASS];
133 char dir[256];
134 char args[256];
135 char mode[80];
136 /*! A dynamically sized array to hold the list of filenames in "files" mode */
137 char **filearray;
138 /*! The current size of the filearray */
139 int allowed_files;
140 /*! The current number of files loaded into the filearray */
141 int total_files;
142 unsigned int flags;
143 /*! The format from the MOH source, not applicable to "files" mode */
144 int format;
145 /*! The pid of the external application delivering MOH */
146 int pid;
147 time_t start;
148 pthread_t thread;
149 /*! Source of audio */
150 int srcfd;
151 /*! FD for timing source */
152 int pseudofd;
153 /*! Number of users */
154 int inuse;
155 unsigned int delete:1;
156 AST_LIST_HEAD_NOLOCK(, mohdata) members;
157 AST_LIST_ENTRY(mohclass) list;
160 struct mohdata {
161 int pipe[2];
162 int origwfmt;
163 struct mohclass *parent;
164 struct ast_frame f;
165 AST_LIST_ENTRY(mohdata) list;
168 AST_LIST_HEAD_STATIC(mohclasses, mohclass);
170 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
171 #define MPG_123 "/usr/bin/mpg123"
172 #define MAX_MP3S 256
174 static int ast_moh_destroy_one(struct mohclass *moh);
175 static int reload(void);
177 static void ast_moh_free_class(struct mohclass **mohclass)
179 struct mohdata *member;
180 struct mohclass *class = *mohclass;
181 int i;
183 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list)))
184 free(member);
186 if (class->thread) {
187 pthread_cancel(class->thread);
188 class->thread = 0;
191 if (class->filearray) {
192 for (i = 0; i < class->total_files; i++)
193 free(class->filearray[i]);
194 free(class->filearray);
197 free(class);
198 *mohclass = NULL;
202 static void moh_files_release(struct ast_channel *chan, void *data)
204 struct moh_files_state *state;
206 if (chan) {
207 if ((state = chan->music_state)) {
208 if (chan->stream) {
209 ast_closestream(chan->stream);
210 chan->stream = NULL;
212 if (option_verbose > 2)
213 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
215 if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
216 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
218 state->save_pos = state->pos;
220 if (ast_atomic_dec_and_test(&state->class->inuse) && state->class->delete)
221 ast_moh_destroy_one(state->class);
227 static int ast_moh_files_next(struct ast_channel *chan)
229 struct moh_files_state *state = chan->music_state;
230 int tries;
232 /* Discontinue a stream if it is running already */
233 if (chan->stream) {
234 ast_closestream(chan->stream);
235 chan->stream = NULL;
238 if (!state->class->total_files) {
239 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
240 return -1;
243 /* If a specific file has been saved confirm it still exists and that it is still valid */
244 if (state->save_pos >= 0 && state->save_pos < state->class->total_files && state->class->filearray[state->save_pos] == state->save_pos_filename) {
245 state->pos = state->save_pos;
246 state->save_pos = -1;
247 } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
248 /* Get a random file and ensure we can open it */
249 for (tries = 0; tries < 20; tries++) {
250 state->pos = ast_random() % state->class->total_files;
251 if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
252 break;
254 state->save_pos = -1;
255 state->samples = 0;
256 } else {
257 /* This is easy, just increment our position and make sure we don't exceed the total file count */
258 state->pos++;
259 state->pos %= state->class->total_files;
260 state->save_pos = -1;
261 state->samples = 0;
264 if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
265 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
266 state->pos++;
267 state->pos %= state->class->total_files;
268 return -1;
271 /* Record the pointer to the filename for position resuming later */
272 state->save_pos_filename = state->class->filearray[state->pos];
274 if (option_debug)
275 ast_log(LOG_DEBUG, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
277 if (state->samples)
278 ast_seekstream(chan->stream, state->samples, SEEK_SET);
280 return 0;
284 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
286 struct ast_frame *f = NULL;
288 if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
289 if (!ast_moh_files_next(chan))
290 f = ast_readframe(chan->stream);
293 return f;
296 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
298 struct moh_files_state *state = chan->music_state;
299 struct ast_frame *f = NULL;
300 int res = 0;
302 state->sample_queue += samples;
304 while (state->sample_queue > 0) {
305 if ((f = moh_files_readframe(chan))) {
306 state->samples += f->samples;
307 state->sample_queue -= f->samples;
308 res = ast_write(chan, f);
309 ast_frfree(f);
310 if (res < 0) {
311 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
312 return -1;
314 } else
315 return -1;
317 return res;
321 static void *moh_files_alloc(struct ast_channel *chan, void *params)
323 struct moh_files_state *state;
324 struct mohclass *class = params;
326 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
327 chan->music_state = state;
328 state->class = class;
329 state->save_pos = -1;
330 } else
331 state = chan->music_state;
333 if (state) {
334 if (state->class != class) {
335 /* initialize */
336 memset(state, 0, sizeof(*state));
337 state->class = class;
338 if (ast_test_flag(state->class, MOH_RANDOMIZE) && class->total_files)
339 state->pos = ast_random() % class->total_files;
342 state->origwfmt = chan->writeformat;
344 if (option_verbose > 2)
345 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", class->name, chan->name);
348 return chan->music_state;
351 static struct ast_generator moh_file_stream =
353 alloc: moh_files_alloc,
354 release: moh_files_release,
355 generate: moh_files_generator,
358 static int spawn_mp3(struct mohclass *class)
360 int fds[2];
361 int files = 0;
362 char fns[MAX_MP3S][80];
363 char *argv[MAX_MP3S + 50];
364 char xargs[256];
365 char *argptr;
366 int argc = 0;
367 DIR *dir = NULL;
368 struct dirent *de;
369 sigset_t signal_set, old_set;
372 if (!strcasecmp(class->dir, "nodir")) {
373 files = 1;
374 } else {
375 dir = opendir(class->dir);
376 if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) {
377 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
378 return -1;
382 if (!ast_test_flag(class, MOH_CUSTOM)) {
383 argv[argc++] = "mpg123";
384 argv[argc++] = "-q";
385 argv[argc++] = "-s";
386 argv[argc++] = "--mono";
387 argv[argc++] = "-r";
388 argv[argc++] = "8000";
390 if (!ast_test_flag(class, MOH_SINGLE)) {
391 argv[argc++] = "-b";
392 argv[argc++] = "2048";
395 argv[argc++] = "-f";
397 if (ast_test_flag(class, MOH_QUIET))
398 argv[argc++] = "4096";
399 else
400 argv[argc++] = "8192";
402 /* Look for extra arguments and add them to the list */
403 ast_copy_string(xargs, class->args, sizeof(xargs));
404 argptr = xargs;
405 while (!ast_strlen_zero(argptr)) {
406 argv[argc++] = argptr;
407 strsep(&argptr, ",");
409 } else {
410 /* Format arguments for argv vector */
411 ast_copy_string(xargs, class->args, sizeof(xargs));
412 argptr = xargs;
413 while (!ast_strlen_zero(argptr)) {
414 argv[argc++] = argptr;
415 strsep(&argptr, " ");
420 if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) {
421 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
422 argv[argc++] = fns[files];
423 files++;
424 } else if (dir) {
425 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
426 if ((strlen(de->d_name) > 3) &&
427 ((ast_test_flag(class, MOH_CUSTOM) &&
428 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
429 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
430 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
431 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
432 argv[argc++] = fns[files];
433 files++;
437 argv[argc] = NULL;
438 if (dir) {
439 closedir(dir);
441 if (pipe(fds)) {
442 ast_log(LOG_WARNING, "Pipe failed\n");
443 return -1;
445 if (!files) {
446 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
447 close(fds[0]);
448 close(fds[1]);
449 return -1;
451 if (time(NULL) - class->start < respawn_time) {
452 sleep(respawn_time - (time(NULL) - class->start));
455 /* Block signals during the fork() */
456 sigfillset(&signal_set);
457 pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
459 time(&class->start);
460 class->pid = fork();
461 if (class->pid < 0) {
462 close(fds[0]);
463 close(fds[1]);
464 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
465 return -1;
467 if (!class->pid) {
468 int x;
470 if (ast_opt_high_priority)
471 ast_set_priority(0);
473 /* Reset ignored signals back to default */
474 signal(SIGPIPE, SIG_DFL);
475 pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
477 close(fds[0]);
478 /* Stdout goes to pipe */
479 dup2(fds[1], STDOUT_FILENO);
480 /* Close unused file descriptors */
481 for (x=3;x<8192;x++) {
482 if (-1 != fcntl(x, F_GETFL)) {
483 close(x);
486 /* Child */
487 chdir(class->dir);
488 if (ast_test_flag(class, MOH_CUSTOM)) {
489 execv(argv[0], argv);
490 } else {
491 /* Default install is /usr/local/bin */
492 execv(LOCAL_MPG_123, argv);
493 /* Many places have it in /usr/bin */
494 execv(MPG_123, argv);
495 /* Check PATH as a last-ditch effort */
496 execvp("mpg123", argv);
498 ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
499 close(fds[1]);
500 _exit(1);
501 } else {
502 /* Parent */
503 pthread_sigmask(SIG_SETMASK, &old_set, NULL);
504 close(fds[1]);
506 return fds[0];
509 static void *monmp3thread(void *data)
511 #define MOH_MS_INTERVAL 100
513 struct mohclass *class = data;
514 struct mohdata *moh;
515 char buf[8192];
516 short sbuf[8192];
517 int res, res2;
518 int len;
519 struct timeval tv, tv_tmp;
521 tv.tv_sec = 0;
522 tv.tv_usec = 0;
523 for(;/* ever */;) {
524 pthread_testcancel();
525 /* Spawn mp3 player if it's not there */
526 if (class->srcfd < 0) {
527 if ((class->srcfd = spawn_mp3(class)) < 0) {
528 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
529 /* Try again later */
530 sleep(500);
531 pthread_testcancel();
534 if (class->pseudofd > -1) {
535 #ifdef SOLARIS
536 thr_yield();
537 #endif
538 /* Pause some amount of time */
539 res = read(class->pseudofd, buf, sizeof(buf));
540 pthread_testcancel();
541 } else {
542 long delta;
543 /* Reliable sleep */
544 tv_tmp = ast_tvnow();
545 if (ast_tvzero(tv))
546 tv = tv_tmp;
547 delta = ast_tvdiff_ms(tv_tmp, tv);
548 if (delta < MOH_MS_INTERVAL) { /* too early */
549 tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
550 usleep(1000 * (MOH_MS_INTERVAL - delta));
551 pthread_testcancel();
552 } else {
553 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
554 tv = tv_tmp;
556 res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
558 if (AST_LIST_EMPTY(&class->members))
559 continue;
560 /* Read mp3 audio */
561 len = ast_codec_get_len(class->format, res);
563 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
564 if (!res2) {
565 close(class->srcfd);
566 class->srcfd = -1;
567 pthread_testcancel();
568 if (class->pid > 1) {
569 kill(class->pid, SIGHUP);
570 usleep(100000);
571 kill(class->pid, SIGTERM);
572 usleep(100000);
573 kill(class->pid, SIGKILL);
574 class->pid = 0;
576 } else
577 ast_log(LOG_DEBUG, "Read %d bytes of audio while expecting %d\n", res2, len);
578 continue;
580 pthread_testcancel();
581 AST_LIST_LOCK(&mohclasses);
582 AST_LIST_TRAVERSE(&class->members, moh, list) {
583 /* Write data */
584 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
585 if (option_debug)
586 ast_log(LOG_DEBUG, "Only wrote %d of %d bytes to pipe\n", res, res2);
589 AST_LIST_UNLOCK(&mohclasses);
591 return NULL;
594 static int moh0_exec(struct ast_channel *chan, void *data)
596 if (ast_moh_start(chan, data, NULL)) {
597 ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
598 return 0;
600 while (!ast_safe_sleep(chan, 10000));
601 ast_moh_stop(chan);
602 return -1;
605 static int moh1_exec(struct ast_channel *chan, void *data)
607 int res;
608 if (!data || !atoi(data)) {
609 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
610 return -1;
612 if (ast_moh_start(chan, NULL, NULL)) {
613 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
614 return 0;
616 res = ast_safe_sleep(chan, atoi(data) * 1000);
617 ast_moh_stop(chan);
618 return res;
621 static int moh2_exec(struct ast_channel *chan, void *data)
623 if (ast_strlen_zero(data)) {
624 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
625 return -1;
627 ast_string_field_set(chan, musicclass, data);
628 return 0;
631 static int moh3_exec(struct ast_channel *chan, void *data)
633 char *class = NULL;
634 if (data && strlen(data))
635 class = data;
636 if (ast_moh_start(chan, class, NULL))
637 ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
639 return 0;
642 static int moh4_exec(struct ast_channel *chan, void *data)
644 ast_moh_stop(chan);
646 return 0;
649 /*! \note This function should be called with the mohclasses list locked */
650 static struct mohclass *get_mohbyname(const char *name, int warn)
652 struct mohclass *moh = NULL;
654 AST_LIST_TRAVERSE(&mohclasses, moh, list) {
655 if (!strcasecmp(name, moh->name))
656 break;
659 if (!moh && warn)
660 ast_log(LOG_WARNING, "Music on Hold class '%s' not found\n", name);
662 return moh;
665 static struct mohdata *mohalloc(struct mohclass *cl)
667 struct mohdata *moh;
668 long flags;
670 if (!(moh = ast_calloc(1, sizeof(*moh))))
671 return NULL;
673 if (pipe(moh->pipe)) {
674 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
675 free(moh);
676 return NULL;
679 /* Make entirely non-blocking */
680 flags = fcntl(moh->pipe[0], F_GETFL);
681 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
682 flags = fcntl(moh->pipe[1], F_GETFL);
683 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
685 moh->f.frametype = AST_FRAME_VOICE;
686 moh->f.subclass = cl->format;
687 moh->f.offset = AST_FRIENDLY_OFFSET;
689 moh->parent = cl;
691 AST_LIST_LOCK(&mohclasses);
692 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
693 AST_LIST_UNLOCK(&mohclasses);
695 return moh;
698 static void moh_release(struct ast_channel *chan, void *data)
700 struct mohdata *moh = data;
701 int oldwfmt;
703 AST_LIST_LOCK(&mohclasses);
704 AST_LIST_REMOVE(&moh->parent->members, moh, list);
705 AST_LIST_UNLOCK(&mohclasses);
707 close(moh->pipe[0]);
708 close(moh->pipe[1]);
709 oldwfmt = moh->origwfmt;
710 if (moh->parent->delete && ast_atomic_dec_and_test(&moh->parent->inuse))
711 ast_moh_destroy_one(moh->parent);
712 free(moh);
713 if (chan) {
714 if (oldwfmt && ast_set_write_format(chan, oldwfmt))
715 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
716 if (option_verbose > 2)
717 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
721 static void *moh_alloc(struct ast_channel *chan, void *params)
723 struct mohdata *res;
724 struct mohclass *class = params;
726 if ((res = mohalloc(class))) {
727 res->origwfmt = chan->writeformat;
728 if (ast_set_write_format(chan, class->format)) {
729 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
730 moh_release(NULL, res);
731 res = NULL;
733 if (option_verbose > 2)
734 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
736 return res;
739 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
741 struct mohdata *moh = data;
742 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
743 int res;
745 if (!moh->parent->pid)
746 return -1;
748 len = ast_codec_get_len(moh->parent->format, samples);
750 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
751 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
752 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
754 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
755 if (res <= 0)
756 return 0;
758 moh->f.datalen = res;
759 moh->f.data = buf + AST_FRIENDLY_OFFSET / 2;
760 moh->f.samples = ast_codec_get_samples(&moh->f);
762 if (ast_write(chan, &moh->f) < 0) {
763 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
764 return -1;
767 return 0;
770 static struct ast_generator mohgen =
772 alloc: moh_alloc,
773 release: moh_release,
774 generate: moh_generate,
777 static int moh_add_file(struct mohclass *class, const char *filepath)
779 if (!class->allowed_files) {
780 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
781 return -1;
782 class->allowed_files = INITIAL_NUM_FILES;
783 } else if (class->total_files == class->allowed_files) {
784 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
785 class->allowed_files = 0;
786 class->total_files = 0;
787 return -1;
789 class->allowed_files *= 2;
792 if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
793 return -1;
795 class->total_files++;
797 return 0;
800 static int moh_scan_files(struct mohclass *class) {
802 DIR *files_DIR;
803 struct dirent *files_dirent;
804 char path[PATH_MAX];
805 char filepath[PATH_MAX];
806 char *ext;
807 struct stat statbuf;
808 int dirnamelen;
809 int i;
811 files_DIR = opendir(class->dir);
812 if (!files_DIR) {
813 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir);
814 return -1;
817 for (i = 0; i < class->total_files; i++)
818 free(class->filearray[i]);
820 class->total_files = 0;
821 dirnamelen = strlen(class->dir) + 2;
822 getcwd(path, sizeof(path));
823 chdir(class->dir);
824 while ((files_dirent = readdir(files_DIR))) {
825 /* The file name must be at least long enough to have the file type extension */
826 if ((strlen(files_dirent->d_name) < 4))
827 continue;
829 /* Skip files that starts with a dot */
830 if (files_dirent->d_name[0] == '.')
831 continue;
833 /* Skip files without extensions... they are not audio */
834 if (!strchr(files_dirent->d_name, '.'))
835 continue;
837 snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
839 if (stat(filepath, &statbuf))
840 continue;
842 if (!S_ISREG(statbuf.st_mode))
843 continue;
845 if ((ext = strrchr(filepath, '.'))) {
846 *ext = '\0';
847 ext++;
850 /* if the file is present in multiple formats, ensure we only put it into the list once */
851 for (i = 0; i < class->total_files; i++)
852 if (!strcmp(filepath, class->filearray[i]))
853 break;
855 if (i == class->total_files) {
856 if (moh_add_file(class, filepath))
857 break;
861 closedir(files_DIR);
862 chdir(path);
863 return class->total_files;
866 static int moh_register(struct mohclass *moh, int reload)
868 #ifdef HAVE_ZAPTEL
869 int x;
870 #endif
871 struct mohclass *mohclass = NULL;
872 int res = 0;
874 AST_LIST_LOCK(&mohclasses);
875 if ((mohclass = get_mohbyname(moh->name, 0))) {
876 if (!mohclass->delete) {
877 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
878 free(moh);
879 AST_LIST_UNLOCK(&mohclasses);
880 return -1;
883 AST_LIST_UNLOCK(&mohclasses);
885 time(&moh->start);
886 moh->start -= respawn_time;
888 if (!strcasecmp(moh->mode, "files")) {
889 res = moh_scan_files(moh);
890 if (res <= 0) {
891 if (res == 0) {
892 if (option_verbose > 2)
893 ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n", moh->dir, moh->name);
895 ast_moh_free_class(&moh);
896 return -1;
898 if (strchr(moh->args, 'r'))
899 ast_set_flag(moh, MOH_RANDOMIZE);
900 } 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")) {
902 if (!strcasecmp(moh->mode, "custom"))
903 ast_set_flag(moh, MOH_CUSTOM);
904 else if (!strcasecmp(moh->mode, "mp3nb"))
905 ast_set_flag(moh, MOH_SINGLE);
906 else if (!strcasecmp(moh->mode, "quietmp3nb"))
907 ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
908 else if (!strcasecmp(moh->mode, "quietmp3"))
909 ast_set_flag(moh, MOH_QUIET);
911 moh->srcfd = -1;
912 #ifdef HAVE_ZAPTEL
913 /* Open /dev/zap/pseudo for timing... Is
914 there a better, yet reliable way to do this? */
915 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
916 if (moh->pseudofd < 0) {
917 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
918 } else {
919 x = 320;
920 ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
922 #else
923 moh->pseudofd = -1;
924 #endif
925 if (ast_pthread_create_background(&moh->thread, NULL, monmp3thread, moh)) {
926 ast_log(LOG_WARNING, "Unable to create moh...\n");
927 if (moh->pseudofd > -1)
928 close(moh->pseudofd);
929 ast_moh_free_class(&moh);
930 return -1;
932 } else {
933 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
934 ast_moh_free_class(&moh);
935 return -1;
938 AST_LIST_LOCK(&mohclasses);
939 AST_LIST_INSERT_HEAD(&mohclasses, moh, list);
940 AST_LIST_UNLOCK(&mohclasses);
942 return 0;
945 static void local_ast_moh_cleanup(struct ast_channel *chan)
947 if (chan->music_state) {
948 free(chan->music_state);
949 chan->music_state = NULL;
953 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
955 struct mohclass *mohclass = NULL;
957 /* The following is the order of preference for which class to use:
958 * 1) The channels explicitly set musicclass, which should *only* be
959 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
960 * 2) The mclass argument. If a channel is calling ast_moh_start() as the
961 * result of receiving a HOLD control frame, this should be the
962 * payload that came with the frame.
963 * 3) The interpclass argument. This would be from the mohinterpret
964 * option from channel drivers. This is the same as the old musicclass
965 * option.
966 * 4) The default class.
968 AST_LIST_LOCK(&mohclasses);
969 if (!ast_strlen_zero(chan->musicclass))
970 mohclass = get_mohbyname(chan->musicclass, 1);
971 if (!mohclass && !ast_strlen_zero(mclass))
972 mohclass = get_mohbyname(mclass, 1);
973 if (!mohclass && !ast_strlen_zero(interpclass))
974 mohclass = get_mohbyname(interpclass, 1);
975 if (!mohclass)
976 mohclass = get_mohbyname("default", 1);
977 if (mohclass)
978 ast_atomic_fetchadd_int(&mohclass->inuse, +1);
979 AST_LIST_UNLOCK(&mohclasses);
981 if (!mohclass)
982 return -1;
984 ast_set_flag(chan, AST_FLAG_MOH);
985 if (mohclass->total_files) {
986 return ast_activate_generator(chan, &moh_file_stream, mohclass);
987 } else
988 return ast_activate_generator(chan, &mohgen, mohclass);
991 static void local_ast_moh_stop(struct ast_channel *chan)
993 ast_clear_flag(chan, AST_FLAG_MOH);
994 ast_deactivate_generator(chan);
996 if (chan->music_state) {
997 if (chan->stream) {
998 ast_closestream(chan->stream);
999 chan->stream = NULL;
1004 static struct mohclass *moh_class_malloc(void)
1006 struct mohclass *class;
1008 if ((class = ast_calloc(1, sizeof(*class))))
1009 class->format = AST_FORMAT_SLINEAR;
1011 return class;
1014 static int load_moh_classes(int reload)
1016 struct ast_config *cfg;
1017 struct ast_variable *var;
1018 struct mohclass *class;
1019 char *data;
1020 char *args;
1021 char *cat;
1022 int numclasses = 0;
1023 static int dep_warning = 0;
1025 cfg = ast_config_load("musiconhold.conf");
1027 if (!cfg)
1028 return 0;
1030 if (reload) {
1031 AST_LIST_LOCK(&mohclasses);
1032 AST_LIST_TRAVERSE(&mohclasses, class, list)
1033 class->delete = 1;
1034 AST_LIST_UNLOCK(&mohclasses);
1037 cat = ast_category_browse(cfg, NULL);
1038 for (; cat; cat = ast_category_browse(cfg, cat)) {
1039 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {
1040 if (!(class = moh_class_malloc())) {
1041 break;
1043 ast_copy_string(class->name, cat, sizeof(class->name));
1044 var = ast_variable_browse(cfg, cat);
1045 while (var) {
1046 if (!strcasecmp(var->name, "mode"))
1047 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1048 else if (!strcasecmp(var->name, "directory"))
1049 ast_copy_string(class->dir, var->value, sizeof(class->dir));
1050 else if (!strcasecmp(var->name, "application"))
1051 ast_copy_string(class->args, var->value, sizeof(class->args));
1052 else if (!strcasecmp(var->name, "random"))
1053 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
1054 else if (!strcasecmp(var->name, "format")) {
1055 class->format = ast_getformatbyname(var->value);
1056 if (!class->format) {
1057 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1058 class->format = AST_FORMAT_SLINEAR;
1061 var = var->next;
1064 if (ast_strlen_zero(class->dir)) {
1065 if (!strcasecmp(class->mode, "custom")) {
1066 strcpy(class->dir, "nodir");
1067 } else {
1068 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1069 free(class);
1070 continue;
1073 if (ast_strlen_zero(class->mode)) {
1074 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1075 free(class);
1076 continue;
1078 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1079 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1080 free(class);
1081 continue;
1084 /* Don't leak a class when it's already registered */
1085 moh_register(class, reload);
1087 numclasses++;
1092 /* Deprecated Old-School Configuration */
1093 var = ast_variable_browse(cfg, "classes");
1094 while (var) {
1095 if (!dep_warning) {
1096 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");
1097 dep_warning = 1;
1099 data = strchr(var->value, ':');
1100 if (data) {
1101 *data++ = '\0';
1102 args = strchr(data, ',');
1103 if (args)
1104 *args++ = '\0';
1105 if (!(get_mohbyname(var->name, 0))) {
1106 if (!(class = moh_class_malloc())) {
1107 break;
1110 ast_copy_string(class->name, var->name, sizeof(class->name));
1111 ast_copy_string(class->dir, data, sizeof(class->dir));
1112 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1113 if (args)
1114 ast_copy_string(class->args, args, sizeof(class->args));
1116 moh_register(class, reload);
1117 numclasses++;
1120 var = var->next;
1122 var = ast_variable_browse(cfg, "moh_files");
1123 while (var) {
1124 if (!dep_warning) {
1125 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");
1126 dep_warning = 1;
1128 if (!(get_mohbyname(var->name, 0))) {
1129 args = strchr(var->value, ',');
1130 if (args)
1131 *args++ = '\0';
1132 if (!(class = moh_class_malloc())) {
1133 break;
1136 ast_copy_string(class->name, var->name, sizeof(class->name));
1137 ast_copy_string(class->dir, var->value, sizeof(class->dir));
1138 strcpy(class->mode, "files");
1139 if (args)
1140 ast_copy_string(class->args, args, sizeof(class->args));
1142 moh_register(class, reload);
1143 numclasses++;
1145 var = var->next;
1148 ast_config_destroy(cfg);
1150 return numclasses;
1153 static int ast_moh_destroy_one(struct mohclass *moh)
1155 char buff[8192];
1156 int bytes, tbytes = 0, stime = 0, pid = 0;
1158 if (moh) {
1159 if (moh->pid > 1) {
1160 ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
1161 stime = time(NULL) + 2;
1162 pid = moh->pid;
1163 moh->pid = 0;
1164 /* Back when this was just mpg123, SIGKILL was fine. Now we need
1165 * to give the process a reason and time enough to kill off its
1166 * children. */
1167 kill(pid, SIGHUP);
1168 usleep(100000);
1169 kill(pid, SIGTERM);
1170 usleep(100000);
1171 kill(pid, SIGKILL);
1172 while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
1173 tbytes = tbytes + bytes;
1174 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1175 close(moh->srcfd);
1177 ast_moh_free_class(&moh);
1180 return 0;
1183 static void ast_moh_destroy(void)
1185 struct mohclass *moh;
1187 if (option_verbose > 1)
1188 ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
1190 AST_LIST_LOCK(&mohclasses);
1191 while ((moh = AST_LIST_REMOVE_HEAD(&mohclasses, list))) {
1192 ast_moh_destroy_one(moh);
1194 AST_LIST_UNLOCK(&mohclasses);
1197 static int moh_cli(int fd, int argc, char *argv[])
1199 reload();
1200 return 0;
1203 static int cli_files_show(int fd, int argc, char *argv[])
1205 int i;
1206 struct mohclass *class;
1208 AST_LIST_LOCK(&mohclasses);
1209 AST_LIST_TRAVERSE(&mohclasses, class, list) {
1210 if (!class->total_files)
1211 continue;
1213 ast_cli(fd, "Class: %s\n", class->name);
1214 for (i = 0; i < class->total_files; i++)
1215 ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
1217 AST_LIST_UNLOCK(&mohclasses);
1219 return 0;
1222 static int moh_classes_show(int fd, int argc, char *argv[])
1224 struct mohclass *class;
1226 AST_LIST_LOCK(&mohclasses);
1227 AST_LIST_TRAVERSE(&mohclasses, class, list) {
1228 ast_cli(fd, "Class: %s\n", class->name);
1229 ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1230 ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1231 ast_cli(fd, "\tUse Count: %d\n", class->inuse);
1232 if (ast_test_flag(class, MOH_CUSTOM))
1233 ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1234 if (strcasecmp(class->mode, "files"))
1235 ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
1237 AST_LIST_UNLOCK(&mohclasses);
1239 return 0;
1242 static struct ast_cli_entry cli_moh_classes_show_deprecated = {
1243 { "moh", "classes", "show"},
1244 moh_classes_show, NULL,
1245 NULL };
1247 static struct ast_cli_entry cli_moh_files_show_deprecated = {
1248 { "moh", "files", "show"},
1249 cli_files_show, NULL,
1250 NULL };
1252 static struct ast_cli_entry cli_moh[] = {
1253 { { "moh", "reload"},
1254 moh_cli, "Music On Hold",
1255 "Usage: moh reload\n Rereads configuration\n" },
1257 { { "moh", "show", "classes"},
1258 moh_classes_show, "List MOH classes",
1259 "Usage: moh show classes\n Lists all MOH classes\n", NULL, &cli_moh_classes_show_deprecated },
1261 { { "moh", "show", "files"},
1262 cli_files_show, "List MOH file-based classes",
1263 "Usage: moh show files\n Lists all loaded file-based MOH classes and their files\n", NULL, &cli_moh_files_show_deprecated },
1266 static int init_classes(int reload)
1268 struct mohclass *moh;
1270 if (!load_moh_classes(reload)) /* Load classes from config */
1271 return 0; /* Return if nothing is found */
1273 AST_LIST_LOCK(&mohclasses);
1274 AST_LIST_TRAVERSE_SAFE_BEGIN(&mohclasses, moh, list) {
1275 if (reload && moh->delete) {
1276 AST_LIST_REMOVE_CURRENT(&mohclasses, list);
1277 if (!moh->inuse)
1278 ast_moh_destroy_one(moh);
1281 AST_LIST_TRAVERSE_SAFE_END
1282 AST_LIST_UNLOCK(&mohclasses);
1284 return 1;
1287 static int load_module(void)
1289 int res;
1291 res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
1292 ast_register_atexit(ast_moh_destroy);
1293 ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
1294 if (!res)
1295 res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
1296 if (!res)
1297 res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
1298 if (!res)
1299 res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
1300 if (!res)
1301 res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
1303 if (!init_classes(0)) { /* No music classes configured, so skip it */
1304 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.\n");
1305 } else {
1306 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1309 return 0;
1312 static int reload(void)
1314 if (init_classes(1))
1315 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1317 return 0;
1320 static int unload_module(void)
1322 int res = 0;
1323 struct mohclass *class = NULL;
1325 AST_LIST_LOCK(&mohclasses);
1326 AST_LIST_TRAVERSE(&mohclasses, class, list) {
1327 if (class->inuse > 0) {
1328 res = -1;
1329 break;
1332 AST_LIST_UNLOCK(&mohclasses);
1333 if (res < 0) {
1334 ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
1335 return res;
1338 ast_uninstall_music_functions();
1339 ast_moh_destroy();
1340 res = ast_unregister_application(app0);
1341 res |= ast_unregister_application(app1);
1342 res |= ast_unregister_application(app2);
1343 res |= ast_unregister_application(app3);
1344 res |= ast_unregister_application(app4);
1345 ast_cli_unregister_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
1346 return res;
1349 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Music On Hold Resource",
1350 .load = load_module,
1351 .unload = unload_module,
1352 .reload = reload,