fix various other problems found by gcc 4.3
[asterisk-bristuff.git] / res / res_musiconhold.c
blobb1955825ad43449af95c2a23b24cae4d634e29ab
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;
125 #define MOH_QUIET (1 << 0)
126 #define MOH_SINGLE (1 << 1)
127 #define MOH_CUSTOM (1 << 2)
128 #define MOH_RANDOMIZE (1 << 3)
130 struct mohclass {
131 char name[MAX_MUSICCLASS];
132 char dir[256];
133 char args[256];
134 char mode[80];
135 /*! A dynamically sized array to hold the list of filenames in "files" mode */
136 char **filearray;
137 /*! The current size of the filearray */
138 int allowed_files;
139 /*! The current number of files loaded into the filearray */
140 int total_files;
141 unsigned int flags;
142 /*! The format from the MOH source, not applicable to "files" mode */
143 int format;
144 /*! The pid of the external application delivering MOH */
145 int pid;
146 time_t start;
147 pthread_t thread;
148 /*! Source of audio */
149 int srcfd;
150 /*! FD for timing source */
151 int pseudofd;
152 /*! Number of users */
153 int inuse;
154 unsigned int delete:1;
155 AST_LIST_HEAD_NOLOCK(, mohdata) members;
156 AST_LIST_ENTRY(mohclass) list;
159 struct mohdata {
160 int pipe[2];
161 int origwfmt;
162 struct mohclass *parent;
163 struct ast_frame f;
164 AST_LIST_ENTRY(mohdata) list;
167 AST_LIST_HEAD_STATIC(mohclasses, mohclass);
169 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
170 #define MPG_123 "/usr/bin/mpg123"
171 #define MAX_MP3S 256
173 static int ast_moh_destroy_one(struct mohclass *moh);
174 static int reload(void);
176 static void ast_moh_free_class(struct mohclass **mohclass)
178 struct mohdata *member;
179 struct mohclass *class = *mohclass;
180 int i;
182 while ((member = AST_LIST_REMOVE_HEAD(&class->members, list)))
183 free(member);
185 if (class->thread) {
186 pthread_cancel(class->thread);
187 class->thread = 0;
190 if (class->filearray) {
191 for (i = 0; i < class->total_files; i++)
192 free(class->filearray[i]);
193 free(class->filearray);
196 free(class);
197 *mohclass = NULL;
201 static void moh_files_release(struct ast_channel *chan, void *data)
203 struct moh_files_state *state = chan->music_state;
205 if (chan && 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);
223 static int ast_moh_files_next(struct ast_channel *chan)
225 struct moh_files_state *state = chan->music_state;
226 int tries;
228 /* Discontinue a stream if it is running already */
229 if (chan->stream) {
230 ast_closestream(chan->stream);
231 chan->stream = NULL;
234 if (!state->class->total_files) {
235 ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
236 return -1;
239 /* If a specific file has been saved, use it */
240 if (state->save_pos >= 0) {
241 state->pos = state->save_pos;
242 state->save_pos = -1;
243 } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
244 /* Get a random file and ensure we can open it */
245 for (tries = 0; tries < 20; tries++) {
246 state->pos = rand() % state->class->total_files;
247 if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
248 break;
250 state->samples = 0;
251 } else {
252 /* This is easy, just increment our position and make sure we don't exceed the total file count */
253 state->pos++;
254 state->pos %= state->class->total_files;
255 state->samples = 0;
258 if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
259 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
260 state->pos++;
261 state->pos %= state->class->total_files;
262 return -1;
265 if (option_debug)
266 ast_log(LOG_DEBUG, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
268 if (state->samples)
269 ast_seekstream(chan->stream, state->samples, SEEK_SET);
271 return 0;
275 static struct ast_frame *moh_files_readframe(struct ast_channel *chan)
277 struct ast_frame *f = NULL;
279 if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
280 if (!ast_moh_files_next(chan))
281 f = ast_readframe(chan->stream);
284 return f;
287 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
289 struct moh_files_state *state = chan->music_state;
290 struct ast_frame *f = NULL;
291 int res = 0;
293 state->sample_queue += samples;
295 while (state->sample_queue > 0) {
296 if ((f = moh_files_readframe(chan))) {
297 state->samples += f->samples;
298 res = ast_write(chan, f);
299 state->sample_queue -= f->samples;
300 ast_frfree(f);
301 if (res < 0) {
302 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
303 return -1;
305 } else
306 return -1;
308 return res;
312 static void *moh_files_alloc(struct ast_channel *chan, void *params)
314 struct moh_files_state *state;
315 struct mohclass *class = params;
317 if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
318 chan->music_state = state;
319 state->class = class;
320 state->save_pos = -1;
321 } else
322 state = chan->music_state;
324 if (state) {
325 if (state->class != class) {
326 /* initialize */
327 memset(state, 0, sizeof(*state));
328 state->class = class;
329 if (ast_test_flag(state->class, MOH_RANDOMIZE) && class->total_files)
330 state->pos = ast_random() % class->total_files;
333 state->origwfmt = chan->writeformat;
335 if (option_verbose > 2)
336 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", class->name, chan->name);
339 return chan->music_state;
342 static struct ast_generator moh_file_stream =
344 alloc: moh_files_alloc,
345 release: moh_files_release,
346 generate: moh_files_generator,
349 static int spawn_mp3(struct mohclass *class)
351 int fds[2];
352 int files = 0;
353 char fns[MAX_MP3S][80];
354 char *argv[MAX_MP3S + 50];
355 char xargs[256];
356 char *argptr;
357 int argc = 0;
358 DIR *dir = NULL;
359 struct dirent *de;
360 sigset_t signal_set, old_set;
363 if (!strcasecmp(class->dir, "nodir")) {
364 files = 1;
365 } else {
366 dir = opendir(class->dir);
367 if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) {
368 ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
369 return -1;
373 if (!ast_test_flag(class, MOH_CUSTOM)) {
374 argv[argc++] = "mpg123";
375 argv[argc++] = "-q";
376 argv[argc++] = "-s";
377 argv[argc++] = "--mono";
378 argv[argc++] = "-r";
379 argv[argc++] = "8000";
381 if (!ast_test_flag(class, MOH_SINGLE)) {
382 argv[argc++] = "-b";
383 argv[argc++] = "2048";
386 argv[argc++] = "-f";
388 if (ast_test_flag(class, MOH_QUIET))
389 argv[argc++] = "4096";
390 else
391 argv[argc++] = "8192";
393 /* Look for extra arguments and add them to the list */
394 ast_copy_string(xargs, class->args, sizeof(xargs));
395 argptr = xargs;
396 while (!ast_strlen_zero(argptr)) {
397 argv[argc++] = argptr;
398 strsep(&argptr, ",");
400 } else {
401 /* Format arguments for argv vector */
402 ast_copy_string(xargs, class->args, sizeof(xargs));
403 argptr = xargs;
404 while (!ast_strlen_zero(argptr)) {
405 argv[argc++] = argptr;
406 strsep(&argptr, " ");
411 if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) {
412 ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
413 argv[argc++] = fns[files];
414 files++;
415 } else if (dir) {
416 while ((de = readdir(dir)) && (files < MAX_MP3S)) {
417 if ((strlen(de->d_name) > 3) &&
418 ((ast_test_flag(class, MOH_CUSTOM) &&
419 (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
420 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
421 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
422 ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
423 argv[argc++] = fns[files];
424 files++;
428 argv[argc] = NULL;
429 if (dir) {
430 closedir(dir);
432 if (pipe(fds)) {
433 ast_log(LOG_WARNING, "Pipe failed\n");
434 return -1;
436 if (!files) {
437 ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
438 close(fds[0]);
439 close(fds[1]);
440 return -1;
442 if (time(NULL) - class->start < respawn_time) {
443 sleep(respawn_time - (time(NULL) - class->start));
446 /* Block signals during the fork() */
447 sigfillset(&signal_set);
448 pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
450 time(&class->start);
451 class->pid = fork();
452 if (class->pid < 0) {
453 close(fds[0]);
454 close(fds[1]);
455 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
456 return -1;
458 if (!class->pid) {
459 int x;
461 if (ast_opt_high_priority)
462 ast_set_priority(0);
464 /* Reset ignored signals back to default */
465 signal(SIGPIPE, SIG_DFL);
466 pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
468 close(fds[0]);
469 /* Stdout goes to pipe */
470 dup2(fds[1], STDOUT_FILENO);
471 /* Close unused file descriptors */
472 for (x=3;x<8192;x++) {
473 if (-1 != fcntl(x, F_GETFL)) {
474 close(x);
477 /* Child */
478 chdir(class->dir);
479 if (ast_test_flag(class, MOH_CUSTOM)) {
480 execv(argv[0], argv);
481 } else {
482 /* Default install is /usr/local/bin */
483 execv(LOCAL_MPG_123, argv);
484 /* Many places have it in /usr/bin */
485 execv(MPG_123, argv);
486 /* Check PATH as a last-ditch effort */
487 execvp("mpg123", argv);
489 ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
490 close(fds[1]);
491 _exit(1);
492 } else {
493 /* Parent */
494 pthread_sigmask(SIG_SETMASK, &old_set, NULL);
495 close(fds[1]);
497 return fds[0];
500 static void *monmp3thread(void *data)
502 #define MOH_MS_INTERVAL 100
504 struct mohclass *class = data;
505 struct mohdata *moh;
506 char buf[8192];
507 short sbuf[8192];
508 int res, res2;
509 int len;
510 struct timeval tv, tv_tmp;
512 tv.tv_sec = 0;
513 tv.tv_usec = 0;
514 for(;/* ever */;) {
515 pthread_testcancel();
516 /* Spawn mp3 player if it's not there */
517 if (class->srcfd < 0) {
518 if ((class->srcfd = spawn_mp3(class)) < 0) {
519 ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
520 /* Try again later */
521 sleep(500);
522 pthread_testcancel();
525 if (class->pseudofd > -1) {
526 #ifdef SOLARIS
527 thr_yield();
528 #endif
529 /* Pause some amount of time */
530 res = read(class->pseudofd, buf, sizeof(buf));
531 pthread_testcancel();
532 } else {
533 long delta;
534 /* Reliable sleep */
535 tv_tmp = ast_tvnow();
536 if (ast_tvzero(tv))
537 tv = tv_tmp;
538 delta = ast_tvdiff_ms(tv_tmp, tv);
539 if (delta < MOH_MS_INTERVAL) { /* too early */
540 tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000)); /* next deadline */
541 usleep(1000 * (MOH_MS_INTERVAL - delta));
542 pthread_testcancel();
543 } else {
544 ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
545 tv = tv_tmp;
547 res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
549 if (AST_LIST_EMPTY(&class->members))
550 continue;
551 /* Read mp3 audio */
552 len = ast_codec_get_len(class->format, res);
554 if ((res2 = read(class->srcfd, sbuf, len)) != len) {
555 if (!res2) {
556 close(class->srcfd);
557 class->srcfd = -1;
558 pthread_testcancel();
559 if (class->pid > 1) {
560 kill(class->pid, SIGHUP);
561 usleep(100000);
562 kill(class->pid, SIGTERM);
563 usleep(100000);
564 kill(class->pid, SIGKILL);
565 class->pid = 0;
567 } else
568 ast_log(LOG_DEBUG, "Read %d bytes of audio while expecting %d\n", res2, len);
569 continue;
571 pthread_testcancel();
572 AST_LIST_LOCK(&mohclasses);
573 AST_LIST_TRAVERSE(&class->members, moh, list) {
574 /* Write data */
575 if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
576 if (option_debug)
577 ast_log(LOG_DEBUG, "Only wrote %d of %d bytes to pipe\n", res, res2);
580 AST_LIST_UNLOCK(&mohclasses);
582 return NULL;
585 static int moh0_exec(struct ast_channel *chan, void *data)
587 if (ast_moh_start(chan, data, NULL)) {
588 ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
589 return 0;
591 while (!ast_safe_sleep(chan, 10000));
592 ast_moh_stop(chan);
593 return -1;
596 static int moh1_exec(struct ast_channel *chan, void *data)
598 int res;
599 if (!data || !atoi(data)) {
600 ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
601 return -1;
603 if (ast_moh_start(chan, NULL, NULL)) {
604 ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
605 return 0;
607 res = ast_safe_sleep(chan, atoi(data) * 1000);
608 ast_moh_stop(chan);
609 return res;
612 static int moh2_exec(struct ast_channel *chan, void *data)
614 if (ast_strlen_zero(data)) {
615 ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
616 return -1;
618 ast_string_field_set(chan, musicclass, data);
619 return 0;
622 static int moh3_exec(struct ast_channel *chan, void *data)
624 char *class = NULL;
625 if (data && strlen(data))
626 class = data;
627 if (ast_moh_start(chan, class, NULL))
628 ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
630 return 0;
633 static int moh4_exec(struct ast_channel *chan, void *data)
635 ast_moh_stop(chan);
637 return 0;
640 /*! \note This function should be called with the mohclasses list locked */
641 static struct mohclass *get_mohbyname(const char *name, int warn)
643 struct mohclass *moh = NULL;
645 AST_LIST_TRAVERSE(&mohclasses, moh, list) {
646 if (!strcasecmp(name, moh->name))
647 break;
650 if (!moh && warn)
651 ast_log(LOG_WARNING, "Music on Hold class '%s' not found\n", name);
653 return moh;
656 static struct mohdata *mohalloc(struct mohclass *cl)
658 struct mohdata *moh;
659 long flags;
661 if (!(moh = ast_calloc(1, sizeof(*moh))))
662 return NULL;
664 if (pipe(moh->pipe)) {
665 ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
666 free(moh);
667 return NULL;
670 /* Make entirely non-blocking */
671 flags = fcntl(moh->pipe[0], F_GETFL);
672 fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
673 flags = fcntl(moh->pipe[1], F_GETFL);
674 fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
676 moh->f.frametype = AST_FRAME_VOICE;
677 moh->f.subclass = cl->format;
678 moh->f.offset = AST_FRIENDLY_OFFSET;
680 moh->parent = cl;
682 AST_LIST_LOCK(&mohclasses);
683 AST_LIST_INSERT_HEAD(&cl->members, moh, list);
684 AST_LIST_UNLOCK(&mohclasses);
686 return moh;
689 static void moh_release(struct ast_channel *chan, void *data)
691 struct mohdata *moh = data;
692 int oldwfmt;
694 AST_LIST_LOCK(&mohclasses);
695 AST_LIST_REMOVE(&moh->parent->members, moh, list);
696 AST_LIST_UNLOCK(&mohclasses);
698 close(moh->pipe[0]);
699 close(moh->pipe[1]);
700 oldwfmt = moh->origwfmt;
701 if (moh->parent->delete && ast_atomic_dec_and_test(&moh->parent->inuse))
702 ast_moh_destroy_one(moh->parent);
703 free(moh);
704 if (chan) {
705 if (oldwfmt && ast_set_write_format(chan, oldwfmt))
706 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
707 if (option_verbose > 2)
708 ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
712 static void *moh_alloc(struct ast_channel *chan, void *params)
714 struct mohdata *res;
715 struct mohclass *class = params;
717 if ((res = mohalloc(class))) {
718 res->origwfmt = chan->writeformat;
719 if (ast_set_write_format(chan, class->format)) {
720 ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
721 moh_release(NULL, res);
722 res = NULL;
724 if (option_verbose > 2)
725 ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
727 return res;
730 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
732 struct mohdata *moh = data;
733 short buf[1280 + AST_FRIENDLY_OFFSET / 2];
734 int res;
736 if (!moh->parent->pid)
737 return -1;
739 len = ast_codec_get_len(moh->parent->format, samples);
741 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
742 ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
743 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
745 res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
746 if (res <= 0)
747 return 0;
749 moh->f.datalen = res;
750 moh->f.data = buf + AST_FRIENDLY_OFFSET / 2;
751 moh->f.samples = ast_codec_get_samples(&moh->f);
753 if (ast_write(chan, &moh->f) < 0) {
754 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
755 return -1;
758 return 0;
761 static struct ast_generator mohgen =
763 alloc: moh_alloc,
764 release: moh_release,
765 generate: moh_generate,
768 static int moh_add_file(struct mohclass *class, const char *filepath)
770 if (!class->allowed_files) {
771 if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
772 return -1;
773 class->allowed_files = INITIAL_NUM_FILES;
774 } else if (class->total_files == class->allowed_files) {
775 if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
776 class->allowed_files = 0;
777 class->total_files = 0;
778 return -1;
780 class->allowed_files *= 2;
783 if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
784 return -1;
786 class->total_files++;
788 return 0;
791 static int moh_scan_files(struct mohclass *class) {
793 DIR *files_DIR;
794 struct dirent *files_dirent;
795 char path[PATH_MAX];
796 char filepath[PATH_MAX];
797 char *ext;
798 struct stat statbuf;
799 int dirnamelen;
800 int i;
802 files_DIR = opendir(class->dir);
803 if (!files_DIR) {
804 ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir);
805 return -1;
808 for (i = 0; i < class->total_files; i++)
809 free(class->filearray[i]);
811 class->total_files = 0;
812 dirnamelen = strlen(class->dir) + 2;
813 getcwd(path, sizeof(path));
814 chdir(class->dir);
815 while ((files_dirent = readdir(files_DIR))) {
816 /* The file name must be at least long enough to have the file type extension */
817 if ((strlen(files_dirent->d_name) < 4))
818 continue;
820 /* Skip files that starts with a dot */
821 if (files_dirent->d_name[0] == '.')
822 continue;
824 /* Skip files without extensions... they are not audio */
825 if (!strchr(files_dirent->d_name, '.'))
826 continue;
828 snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
830 if (stat(filepath, &statbuf))
831 continue;
833 if (!S_ISREG(statbuf.st_mode))
834 continue;
836 if ((ext = strrchr(filepath, '.'))) {
837 *ext = '\0';
838 ext++;
841 /* if the file is present in multiple formats, ensure we only put it into the list once */
842 for (i = 0; i < class->total_files; i++)
843 if (!strcmp(filepath, class->filearray[i]))
844 break;
846 if (i == class->total_files) {
847 if (moh_add_file(class, filepath))
848 break;
852 closedir(files_DIR);
853 chdir(path);
854 return class->total_files;
857 static int moh_register(struct mohclass *moh, int reload)
859 #ifdef HAVE_ZAPTEL
860 int x;
861 #endif
862 struct mohclass *mohclass = NULL;
864 AST_LIST_LOCK(&mohclasses);
865 if ((mohclass = get_mohbyname(moh->name, 0))) {
866 mohclass->delete = 0;
867 if (reload) {
868 ast_log(LOG_DEBUG, "Music on Hold class '%s' left alone from initial load.\n", moh->name);
869 } else {
870 ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
872 free(moh);
873 AST_LIST_UNLOCK(&mohclasses);
874 return -1;
876 AST_LIST_UNLOCK(&mohclasses);
878 time(&moh->start);
879 moh->start -= respawn_time;
881 if (!strcasecmp(moh->mode, "files")) {
882 if (!moh_scan_files(moh)) {
883 ast_moh_free_class(&moh);
884 return -1;
886 if (strchr(moh->args, 'r'))
887 ast_set_flag(moh, MOH_RANDOMIZE);
888 } 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")) {
890 if (!strcasecmp(moh->mode, "custom"))
891 ast_set_flag(moh, MOH_CUSTOM);
892 else if (!strcasecmp(moh->mode, "mp3nb"))
893 ast_set_flag(moh, MOH_SINGLE);
894 else if (!strcasecmp(moh->mode, "quietmp3nb"))
895 ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
896 else if (!strcasecmp(moh->mode, "quietmp3"))
897 ast_set_flag(moh, MOH_QUIET);
899 moh->srcfd = -1;
900 #ifdef HAVE_ZAPTEL
901 /* Open /dev/zap/pseudo for timing... Is
902 there a better, yet reliable way to do this? */
903 moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
904 if (moh->pseudofd < 0) {
905 ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n");
906 } else {
907 x = 320;
908 ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
910 #else
911 moh->pseudofd = -1;
912 #endif
913 if (ast_pthread_create_background(&moh->thread, NULL, monmp3thread, moh)) {
914 ast_log(LOG_WARNING, "Unable to create moh...\n");
915 if (moh->pseudofd > -1)
916 close(moh->pseudofd);
917 ast_moh_free_class(&moh);
918 return -1;
920 } else {
921 ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
922 ast_moh_free_class(&moh);
923 return -1;
926 AST_LIST_LOCK(&mohclasses);
927 AST_LIST_INSERT_HEAD(&mohclasses, moh, list);
928 AST_LIST_UNLOCK(&mohclasses);
930 return 0;
933 static void local_ast_moh_cleanup(struct ast_channel *chan)
935 if (chan->music_state) {
936 free(chan->music_state);
937 chan->music_state = NULL;
941 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
943 struct mohclass *mohclass = NULL;
945 /* The following is the order of preference for which class to use:
946 * 1) The channels explicitly set musicclass, which should *only* be
947 * set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
948 * 2) The mclass argument. If a channel is calling ast_moh_start() as the
949 * result of receiving a HOLD control frame, this should be the
950 * payload that came with the frame.
951 * 3) The interpclass argument. This would be from the mohinterpret
952 * option from channel drivers. This is the same as the old musicclass
953 * option.
954 * 4) The default class.
956 AST_LIST_LOCK(&mohclasses);
957 if (!ast_strlen_zero(chan->musicclass))
958 mohclass = get_mohbyname(chan->musicclass, 1);
959 if (!mohclass && !ast_strlen_zero(mclass))
960 mohclass = get_mohbyname(mclass, 1);
961 if (!mohclass && !ast_strlen_zero(interpclass))
962 mohclass = get_mohbyname(interpclass, 1);
963 if (!mohclass)
964 mohclass = get_mohbyname("default", 1);
965 if (mohclass)
966 ast_atomic_fetchadd_int(&mohclass->inuse, +1);
967 AST_LIST_UNLOCK(&mohclasses);
969 if (!mohclass)
970 return -1;
972 ast_set_flag(chan, AST_FLAG_MOH);
973 if (mohclass->total_files) {
974 return ast_activate_generator(chan, &moh_file_stream, mohclass);
975 } else
976 return ast_activate_generator(chan, &mohgen, mohclass);
979 static void local_ast_moh_stop(struct ast_channel *chan)
981 ast_clear_flag(chan, AST_FLAG_MOH);
982 ast_deactivate_generator(chan);
984 if (chan->music_state) {
985 if (chan->stream) {
986 ast_closestream(chan->stream);
987 chan->stream = NULL;
992 static struct mohclass *moh_class_malloc(void)
994 struct mohclass *class;
996 if ((class = ast_calloc(1, sizeof(*class))))
997 class->format = AST_FORMAT_SLINEAR;
999 return class;
1002 static int load_moh_classes(int reload)
1004 struct ast_config *cfg;
1005 struct ast_variable *var;
1006 struct mohclass *class;
1007 char *data;
1008 char *args;
1009 char *cat;
1010 int numclasses = 0;
1011 static int dep_warning = 0;
1013 cfg = ast_config_load("musiconhold.conf");
1015 if (!cfg)
1016 return 0;
1018 if (reload) {
1019 AST_LIST_LOCK(&mohclasses);
1020 AST_LIST_TRAVERSE(&mohclasses, class, list)
1021 class->delete = 1;
1022 AST_LIST_UNLOCK(&mohclasses);
1025 cat = ast_category_browse(cfg, NULL);
1026 for (; cat; cat = ast_category_browse(cfg, cat)) {
1027 if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {
1028 if (!(class = moh_class_malloc())) {
1029 break;
1031 ast_copy_string(class->name, cat, sizeof(class->name));
1032 var = ast_variable_browse(cfg, cat);
1033 while (var) {
1034 if (!strcasecmp(var->name, "mode"))
1035 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1036 else if (!strcasecmp(var->name, "directory"))
1037 ast_copy_string(class->dir, var->value, sizeof(class->dir));
1038 else if (!strcasecmp(var->name, "application"))
1039 ast_copy_string(class->args, var->value, sizeof(class->args));
1040 else if (!strcasecmp(var->name, "random"))
1041 ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
1042 else if (!strcasecmp(var->name, "format")) {
1043 class->format = ast_getformatbyname(var->value);
1044 if (!class->format) {
1045 ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
1046 class->format = AST_FORMAT_SLINEAR;
1049 var = var->next;
1052 if (ast_strlen_zero(class->dir)) {
1053 if (!strcasecmp(class->mode, "custom")) {
1054 strcpy(class->dir, "nodir");
1055 } else {
1056 ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
1057 free(class);
1058 continue;
1061 if (ast_strlen_zero(class->mode)) {
1062 ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
1063 free(class);
1064 continue;
1066 if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
1067 ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
1068 free(class);
1069 continue;
1072 /* Don't leak a class when it's already registered */
1073 moh_register(class, reload);
1075 numclasses++;
1080 /* Deprecated Old-School Configuration */
1081 var = ast_variable_browse(cfg, "classes");
1082 while (var) {
1083 if (!dep_warning) {
1084 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");
1085 dep_warning = 1;
1087 data = strchr(var->value, ':');
1088 if (data) {
1089 *data++ = '\0';
1090 args = strchr(data, ',');
1091 if (args)
1092 *args++ = '\0';
1093 if (!(get_mohbyname(var->name, 0))) {
1094 if (!(class = moh_class_malloc())) {
1095 break;
1098 ast_copy_string(class->name, var->name, sizeof(class->name));
1099 ast_copy_string(class->dir, data, sizeof(class->dir));
1100 ast_copy_string(class->mode, var->value, sizeof(class->mode));
1101 if (args)
1102 ast_copy_string(class->args, args, sizeof(class->args));
1104 moh_register(class, reload);
1105 numclasses++;
1108 var = var->next;
1110 var = ast_variable_browse(cfg, "moh_files");
1111 while (var) {
1112 if (!dep_warning) {
1113 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");
1114 dep_warning = 1;
1116 if (!(get_mohbyname(var->name, 0))) {
1117 args = strchr(var->value, ',');
1118 if (args)
1119 *args++ = '\0';
1120 if (!(class = moh_class_malloc())) {
1121 break;
1124 ast_copy_string(class->name, var->name, sizeof(class->name));
1125 ast_copy_string(class->dir, var->value, sizeof(class->dir));
1126 strcpy(class->mode, "files");
1127 if (args)
1128 ast_copy_string(class->args, args, sizeof(class->args));
1130 moh_register(class, reload);
1131 numclasses++;
1133 var = var->next;
1136 ast_config_destroy(cfg);
1138 return numclasses;
1141 static int ast_moh_destroy_one(struct mohclass *moh)
1143 char buff[8192];
1144 int bytes, tbytes = 0, stime = 0, pid = 0;
1146 if (moh) {
1147 if (moh->pid > 1) {
1148 ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
1149 stime = time(NULL) + 2;
1150 pid = moh->pid;
1151 moh->pid = 0;
1152 /* Back when this was just mpg123, SIGKILL was fine. Now we need
1153 * to give the process a reason and time enough to kill off its
1154 * children. */
1155 kill(pid, SIGHUP);
1156 usleep(100000);
1157 kill(pid, SIGTERM);
1158 usleep(100000);
1159 kill(pid, SIGKILL);
1160 while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
1161 tbytes = tbytes + bytes;
1162 ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
1163 close(moh->srcfd);
1165 ast_moh_free_class(&moh);
1168 return 0;
1171 static void ast_moh_destroy(void)
1173 struct mohclass *moh;
1175 if (option_verbose > 1)
1176 ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
1178 AST_LIST_LOCK(&mohclasses);
1179 while ((moh = AST_LIST_REMOVE_HEAD(&mohclasses, list))) {
1180 ast_moh_destroy_one(moh);
1182 AST_LIST_UNLOCK(&mohclasses);
1185 static int moh_cli(int fd, int argc, char *argv[])
1187 reload();
1188 return 0;
1191 static int cli_files_show(int fd, int argc, char *argv[])
1193 int i;
1194 struct mohclass *class;
1196 AST_LIST_LOCK(&mohclasses);
1197 AST_LIST_TRAVERSE(&mohclasses, class, list) {
1198 if (!class->total_files)
1199 continue;
1201 ast_cli(fd, "Class: %s\n", class->name);
1202 for (i = 0; i < class->total_files; i++)
1203 ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
1205 AST_LIST_UNLOCK(&mohclasses);
1207 return 0;
1210 static int moh_classes_show(int fd, int argc, char *argv[])
1212 struct mohclass *class;
1214 AST_LIST_LOCK(&mohclasses);
1215 AST_LIST_TRAVERSE(&mohclasses, class, list) {
1216 ast_cli(fd, "Class: %s\n", class->name);
1217 ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
1218 ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
1219 ast_cli(fd, "\tUse Count: %d\n", class->inuse);
1220 if (ast_test_flag(class, MOH_CUSTOM))
1221 ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
1222 if (strcasecmp(class->mode, "files"))
1223 ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
1225 AST_LIST_UNLOCK(&mohclasses);
1227 return 0;
1230 static struct ast_cli_entry cli_moh_classes_show_deprecated = {
1231 { "moh", "classes", "show"},
1232 moh_classes_show, NULL,
1233 NULL };
1235 static struct ast_cli_entry cli_moh_files_show_deprecated = {
1236 { "moh", "files", "show"},
1237 cli_files_show, NULL,
1238 NULL };
1240 static struct ast_cli_entry cli_moh[] = {
1241 { { "moh", "reload"},
1242 moh_cli, "Music On Hold",
1243 "Music On Hold" },
1245 { { "moh", "show", "classes"},
1246 moh_classes_show, "List MOH classes",
1247 "Lists all MOH classes", NULL, &cli_moh_classes_show_deprecated },
1249 { { "moh", "show", "files"},
1250 cli_files_show, "List MOH file-based classes",
1251 "Lists all loaded file-based MOH classes and their files", NULL, &cli_moh_files_show_deprecated },
1254 static int init_classes(int reload)
1256 struct mohclass *moh;
1258 if (!load_moh_classes(reload)) /* Load classes from config */
1259 return 0; /* Return if nothing is found */
1261 AST_LIST_LOCK(&mohclasses);
1262 AST_LIST_TRAVERSE_SAFE_BEGIN(&mohclasses, moh, list) {
1263 if (reload && moh->delete) {
1264 AST_LIST_REMOVE_CURRENT(&mohclasses, list);
1265 if (!moh->inuse)
1266 ast_moh_destroy_one(moh);
1267 } else if (moh->total_files) {
1268 if (moh_scan_files(moh) <= 0) {
1269 ast_log(LOG_WARNING, "No files found for class '%s'\n", moh->name);
1270 moh->delete = 1;
1271 AST_LIST_REMOVE_CURRENT(&mohclasses, list);
1272 if (!moh->inuse)
1273 ast_moh_destroy_one(moh);
1277 AST_LIST_TRAVERSE_SAFE_END
1278 AST_LIST_UNLOCK(&mohclasses);
1280 return 1;
1283 static int load_module(void)
1285 int res;
1287 res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
1288 ast_register_atexit(ast_moh_destroy);
1289 ast_cli_register_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
1290 if (!res)
1291 res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
1292 if (!res)
1293 res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
1294 if (!res)
1295 res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
1296 if (!res)
1297 res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
1299 if (!init_classes(0)) { /* No music classes configured, so skip it */
1300 ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.\n");
1301 } else {
1302 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1305 return 0;
1308 static int reload(void)
1310 if (init_classes(1))
1311 ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
1313 return 0;
1316 static int unload_module(void)
1318 return -1;
1321 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Music On Hold Resource",
1322 .load = load_module,
1323 .unload = unload_module,
1324 .reload = reload,