2002-06-29 21:46:57 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk - - An open source telephony toolkit .
2002-06-29 21:46:57 +00:00
*
2006-04-24 17:11:45 +00:00
* Copyright ( C ) 1999 - 2006 , Digium , Inc .
2002-06-29 21:46:57 +00:00
*
2004-09-07 01:22:57 +00:00
* Mark Spencer < markster @ digium . com >
2002-06-29 21:46:57 +00:00
*
2005-09-14 20:46:50 +00:00
* See http : //www.asterisk.org for more information about
* the Asterisk project . Please do not directly contact
* any of the maintainers of this project for assistance ;
* the project provides a web site , mailing lists and IRC
* channels for your use .
*
2002-06-29 21:46:57 +00:00
* This program is free software , distributed under the terms of
2005-09-14 20:46:50 +00:00
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree .
*/
2005-10-24 20:12:06 +00:00
/*! \file
*
* \ brief Routines implementing music on hold
2005-09-14 20:46:50 +00:00
*
2005-10-24 20:12:06 +00:00
* \ arg See also \ ref Config_moh
2005-09-14 20:46:50 +00:00
*
2005-12-30 21:18:06 +00:00
* \ author Mark Spencer < markster @ digium . com >
2002-06-29 21:46:57 +00:00
*/
2006-04-24 17:11:45 +00:00
/*** MODULEINFO
< conflict > win32 < / conflict >
* * */
2006-06-07 18:54:56 +00:00
# include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , " $Revision$ " )
2002-06-29 21:46:57 +00:00
# include <stdlib.h>
# include <errno.h>
# include <unistd.h>
# include <string.h>
2003-09-08 16:48:07 +00:00
# include <signal.h>
2002-06-29 21:46:57 +00:00
# include <stdlib.h>
# include <stdio.h>
# include <sys/time.h>
# include <sys/signal.h>
# include <netinet/in.h>
# include <sys/stat.h>
# include <dirent.h>
# include <unistd.h>
# include <sys/ioctl.h>
2005-06-06 22:12:19 +00:00
2006-04-24 17:11:45 +00:00
# ifdef HAVE_ZAPTEL
# ifdef __linux__
# include <linux/zaptel.h>
# else
# include <zaptel.h>
# endif /* __linux__ */
# endif
2005-06-06 22:12:19 +00:00
# include "asterisk/lock.h"
# include "asterisk/file.h"
# include "asterisk/logger.h"
# include "asterisk/channel.h"
# include "asterisk/pbx.h"
# include "asterisk/options.h"
# include "asterisk/module.h"
# include "asterisk/translate.h"
# include "asterisk/say.h"
# include "asterisk/musiconhold.h"
# include "asterisk/config.h"
# include "asterisk/utils.h"
# include "asterisk/cli.h"
2006-02-01 23:05:28 +00:00
# include "asterisk/stringfields.h"
2006-04-29 13:15:05 +00:00
# include "asterisk/linkedlists.h"
2005-06-06 22:12:19 +00:00
2006-06-04 06:21:43 +00:00
# define INITIAL_NUM_FILES 8
2002-06-29 21:46:57 +00:00
static char * app0 = " MusicOnHold " ;
static char * app1 = " WaitMusicOnHold " ;
static char * app2 = " SetMusicOnHold " ;
2005-03-03 05:03:06 +00:00
static char * app3 = " StartMusicOnHold " ;
static char * app4 = " StopMusicOnHold " ;
2002-06-29 21:46:57 +00:00
static char * synopsis0 = " Play Music On Hold indefinitely " ;
static char * synopsis1 = " Wait, playing Music On Hold " ;
static char * synopsis2 = " Set default Music On Hold class " ;
2005-03-03 05:03:06 +00:00
static char * synopsis3 = " Play Music On Hold " ;
static char * synopsis4 = " Stop Playing Music On Hold " ;
2002-06-29 21:46:57 +00:00
static char * descrip0 = " MusicOnHold(class): "
" Plays hold music specified by class. If omitted, the default \n "
2004-01-11 09:23:26 +00:00
" music source for the channel will be used. Set the default \n "
" class with the SetMusicOnHold() application. \n "
" Returns -1 on hangup. \n "
2002-06-29 21:46:57 +00:00
" Never returns otherwise. \n " ;
static char * descrip1 = " WaitMusicOnHold(delay): "
" Plays hold music specified number of seconds. Returns 0 when \n "
" done, or -1 on hangup. If no hold music is available, the delay will \n "
" still occur with no sound. \n " ;
static char * descrip2 = " SetMusicOnHold(class): "
" Sets the default class for music on hold for a given channel. When \n "
" music on hold is activated, this class will be used to select which \n "
" music is played. \n " ;
2005-03-03 05:03:06 +00:00
static char * descrip3 = " StartMusicOnHold(class): "
" Starts playing music on hold, uses default music class for channel. \n "
" Starts playing music specified by class. If omitted, the default \n "
" music source for the channel will be used. Always returns 0. \n " ;
static char * descrip4 = " StopMusicOnHold: "
" Stops playing music on hold. \n " ;
2004-09-04 05:41:01 +00:00
static int respawn_time = 20 ;
2004-12-24 01:40:07 +00:00
struct moh_files_state {
struct mohclass * class ;
int origwfmt ;
int samples ;
int sample_queue ;
unsigned char pos ;
unsigned char save_pos ;
} ;
2005-01-09 09:00:41 +00:00
# define MOH_QUIET (1 << 0)
# define MOH_SINGLE (1 << 1)
# define MOH_CUSTOM (1 << 2)
# define MOH_RANDOMIZE (1 << 3)
2002-06-29 21:46:57 +00:00
struct mohclass {
2005-08-22 19:29:29 +00:00
char name [ MAX_MUSICCLASS ] ;
2006-02-24 10:50:43 +00:00
char dir [ 256 ] ;
2005-08-22 19:29:29 +00:00
char args [ 256 ] ;
char mode [ 80 ] ;
2006-06-04 06:21:43 +00:00
/*! A dynamically sized array to hold the list of filenames in "files" mode */
char * * filearray ;
/*! The current size of the filearray */
int allowed_files ;
/*! The current number of files loaded into the filearray */
2004-12-24 01:40:07 +00:00
int total_files ;
2006-06-04 06:21:43 +00:00
unsigned int flags ;
/*! The format from the MOH source, not applicable to "files" mode */
2005-08-22 19:29:29 +00:00
int format ;
2006-06-04 06:21:43 +00:00
/*! The pid of the external application delivering MOH */
int pid ;
2004-09-04 05:41:01 +00:00
time_t start ;
2002-06-29 21:46:57 +00:00
pthread_t thread ;
2006-06-04 06:21:43 +00:00
/*! Source of audio */
2002-06-29 21:46:57 +00:00
int srcfd ;
2006-06-04 06:21:43 +00:00
/*! FD for timing source */
2002-06-29 21:46:57 +00:00
int pseudofd ;
2006-04-29 13:15:05 +00:00
AST_LIST_HEAD_NOLOCK ( , mohdata ) members ;
AST_LIST_ENTRY ( mohclass ) list ;
2002-06-29 21:46:57 +00:00
} ;
struct mohdata {
int pipe [ 2 ] ;
int origwfmt ;
struct mohclass * parent ;
2006-04-29 13:15:05 +00:00
struct ast_frame f ;
AST_LIST_ENTRY ( mohdata ) list ;
2002-06-29 21:46:57 +00:00
} ;
2006-04-29 13:15:05 +00:00
AST_LIST_HEAD_STATIC ( mohclasses , mohclass ) ;
2002-06-29 21:46:57 +00:00
2003-09-16 19:35:57 +00:00
# define LOCAL_MPG_123 " / usr / local / bin / mpg123"
2002-06-29 21:46:57 +00:00
# define MPG_123 " / usr / bin / mpg123"
# define MAX_MP3S 256
2005-07-28 18:37:55 +00:00
2006-06-04 06:21:43 +00:00
static void ast_moh_free_class ( struct mohclass * * mohclass )
2005-07-28 18:37:55 +00:00
{
2006-04-29 13:15:05 +00:00
struct mohdata * member ;
2006-06-04 06:21:43 +00:00
struct mohclass * class = * mohclass ;
int i ;
2006-04-29 13:15:05 +00:00
2006-06-04 06:21:43 +00:00
while ( ( member = AST_LIST_REMOVE_HEAD ( & class - > members , list ) ) )
2006-04-29 13:15:05 +00:00
free ( member ) ;
2005-07-28 18:37:55 +00:00
2006-06-04 06:21:43 +00:00
if ( class - > thread ) {
pthread_cancel ( class - > thread ) ;
class - > thread = 0 ;
2006-04-17 17:17:41 +00:00
}
2006-04-29 13:15:05 +00:00
2006-06-04 06:21:43 +00:00
if ( class - > filearray ) {
for ( i = 0 ; i < class - > total_files ; i + + )
free ( class - > filearray [ i ] ) ;
free ( class - > filearray ) ;
}
free ( class ) ;
* mohclass = NULL ;
2005-07-28 18:37:55 +00:00
}
2004-12-24 01:40:07 +00:00
static void moh_files_release ( struct ast_channel * chan , void * data )
{
struct moh_files_state * state = chan - > music_state ;
if ( chan & & state ) {
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Stopped music on hold on %s \n " , chan - > name ) ;
if ( state - > origwfmt & & ast_set_write_format ( chan , state - > origwfmt ) ) {
ast_log ( LOG_WARNING , " Unable to restore channel '%s' to format '%d' \n " , chan - > name , state - > origwfmt ) ;
}
state - > save_pos = state - > pos + 1 ;
}
}
2005-01-10 22:12:58 +00:00
static int ast_moh_files_next ( struct ast_channel * chan )
{
2004-12-24 01:40:07 +00:00
struct moh_files_state * state = chan - > music_state ;
2005-01-10 22:12:58 +00:00
int tries ;
2004-12-24 01:40:07 +00:00
2005-01-09 09:00:41 +00:00
if ( state - > save_pos ) {
2004-12-24 01:40:07 +00:00
state - > pos = state - > save_pos - 1 ;
state - > save_pos = 0 ;
} else {
2005-01-10 22:12:58 +00:00
/* Try 20 times to find something good */
for ( tries = 0 ; tries < 20 ; tries + + ) {
state - > samples = 0 ;
if ( chan - > stream ) {
ast_closestream ( chan - > stream ) ;
chan - > stream = NULL ;
state - > pos + + ;
}
2005-04-13 04:47:39 +00:00
if ( ast_test_flag ( state - > class , MOH_RANDOMIZE ) )
2006-04-05 17:44:44 +00:00
state - > pos = ast_random ( ) ;
2005-04-13 04:47:39 +00:00
2006-06-03 17:04:51 +00:00
state - > pos % = state - > class - > total_files ;
2005-01-10 22:12:58 +00:00
/* check to see if this file's format can be opened */
if ( ast_fileexists ( state - > class - > filearray [ state - > pos ] , NULL , NULL ) ! = - 1 )
break ;
2004-12-24 01:40:07 +00:00
}
}
state - > pos = state - > pos % state - > class - > total_files ;
if ( ! ast_openstream_full ( chan , state - > class - > filearray [ state - > pos ] , chan - > language , 1 ) ) {
ast_log ( LOG_WARNING , " Unable to open file '%s': %s \n " , state - > class - > filearray [ state - > pos ] , strerror ( errno ) ) ;
state - > pos + + ;
return - 1 ;
}
2005-08-22 19:29:29 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " %s Opened file %d '%s' \n " , chan - > name , state - > pos , state - > class - > filearray [ state - > pos ] ) ;
2004-12-24 01:40:07 +00:00
if ( state - > samples )
ast_seekstream ( chan - > stream , state - > samples , SEEK_SET ) ;
2005-01-09 09:00:41 +00:00
return 0 ;
2004-12-24 01:40:07 +00:00
}
2005-03-03 05:03:06 +00:00
static struct ast_frame * moh_files_readframe ( struct ast_channel * chan )
{
2004-12-24 01:40:07 +00:00
struct ast_frame * f = NULL ;
2005-01-09 09:00:41 +00:00
if ( ! ( chan - > stream & & ( f = ast_readframe ( chan - > stream ) ) ) ) {
if ( ! ast_moh_files_next ( chan ) )
2004-12-24 01:40:07 +00:00
f = ast_readframe ( chan - > stream ) ;
}
return f ;
}
static int moh_files_generator ( struct ast_channel * chan , void * data , int len , int samples )
{
struct moh_files_state * state = chan - > music_state ;
struct ast_frame * f = NULL ;
int res = 0 ;
2005-09-01 19:34:49 +00:00
2004-12-24 01:40:07 +00:00
state - > sample_queue + = samples ;
2005-01-09 09:00:41 +00:00
while ( state - > sample_queue > 0 ) {
2004-12-24 01:40:07 +00:00
if ( ( f = moh_files_readframe ( chan ) ) ) {
state - > samples + = f - > samples ;
res = ast_write ( chan , f ) ;
2005-09-01 19:34:49 +00:00
state - > sample_queue - = f - > samples ;
2004-12-24 01:40:07 +00:00
ast_frfree ( f ) ;
2005-01-09 09:00:41 +00:00
if ( res < 0 ) {
2004-12-24 01:40:07 +00:00
ast_log ( LOG_WARNING , " Failed to write frame to '%s': %s \n " , chan - > name , strerror ( errno ) ) ;
return - 1 ;
}
} else
return - 1 ;
}
return res ;
}
static void * moh_files_alloc ( struct ast_channel * chan , void * params )
{
struct moh_files_state * state ;
struct mohclass * class = params ;
2006-01-21 22:09:06 +00:00
if ( ! chan - > music_state & & ( state = ast_calloc ( 1 , sizeof ( * state ) ) ) ) {
2004-12-24 01:40:07 +00:00
chan - > music_state = state ;
2006-01-21 22:09:06 +00:00
state - > class = class ;
2004-12-24 01:40:07 +00:00
} else
state = chan - > music_state ;
2005-01-09 09:00:41 +00:00
if ( state ) {
2006-01-21 22:09:06 +00:00
if ( state - > class ! = class ) {
2004-12-24 01:40:07 +00:00
/* initialize */
2006-01-21 22:09:06 +00:00
memset ( state , 0 , sizeof ( * state ) ) ;
2004-12-24 01:40:07 +00:00
state - > class = class ;
}
state - > origwfmt = chan - > writeformat ;
2006-06-01 21:47:49 +00:00
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Started music on hold, class '%s', on %s \n " , class - > name , chan - > name ) ;
2004-12-24 01:40:07 +00:00
}
return chan - > music_state ;
}
static struct ast_generator moh_file_stream =
{
alloc : moh_files_alloc ,
release : moh_files_release ,
generate : moh_files_generator ,
} ;
2002-06-29 21:46:57 +00:00
static int spawn_mp3 ( struct mohclass * class )
{
int fds [ 2 ] ;
2005-06-30 18:08:27 +00:00
int files = 0 ;
2002-12-04 16:42:58 +00:00
char fns [ MAX_MP3S ] [ 80 ] ;
2002-06-29 21:46:57 +00:00
char * argv [ MAX_MP3S + 50 ] ;
char xargs [ 256 ] ;
char * argptr ;
2004-07-02 23:11:14 +00:00
int argc = 0 ;
2005-06-30 18:08:27 +00:00
DIR * dir = NULL ;
2002-06-29 21:46:57 +00:00
struct dirent * de ;
2004-12-24 01:40:07 +00:00
2005-06-30 18:08:27 +00:00
if ( ! strcasecmp ( class - > dir , " nodir " ) ) {
files = 1 ;
} else {
dir = opendir ( class - > dir ) ;
if ( ! dir & & ! strstr ( class - > dir , " http:// " ) & & ! strstr ( class - > dir , " HTTP:// " ) ) {
ast_log ( LOG_WARNING , " %s is not a valid directory \n " , class - > dir ) ;
return - 1 ;
}
2004-12-24 01:40:07 +00:00
}
2004-04-29 03:36:46 +00:00
2005-01-09 09:00:41 +00:00
if ( ! ast_test_flag ( class , MOH_CUSTOM ) ) {
2004-07-17 20:12:28 +00:00
argv [ argc + + ] = " mpg123 " ;
argv [ argc + + ] = " -q " ;
argv [ argc + + ] = " -s " ;
argv [ argc + + ] = " --mono " ;
argv [ argc + + ] = " -r " ;
argv [ argc + + ] = " 8000 " ;
2005-01-09 09:00:41 +00:00
if ( ! ast_test_flag ( class , MOH_SINGLE ) ) {
2004-07-17 20:12:28 +00:00
argv [ argc + + ] = " -b " ;
argv [ argc + + ] = " 2048 " ;
}
2004-12-24 01:40:07 +00:00
2004-07-17 20:12:28 +00:00
argv [ argc + + ] = " -f " ;
2004-12-24 01:40:07 +00:00
2005-01-09 09:00:41 +00:00
if ( ast_test_flag ( class , MOH_QUIET ) )
2004-07-17 20:12:28 +00:00
argv [ argc + + ] = " 4096 " ;
else
argv [ argc + + ] = " 8192 " ;
2004-12-24 01:40:07 +00:00
2004-07-17 20:12:28 +00:00
/* Look for extra arguments and add them to the list */
2006-04-21 18:26:54 +00:00
ast_copy_string ( xargs , class - > args , sizeof ( xargs ) ) ;
2004-07-17 20:12:28 +00:00
argptr = xargs ;
2005-11-08 01:55:31 +00:00
while ( ! ast_strlen_zero ( argptr ) ) {
2004-07-17 20:12:28 +00:00
argv [ argc + + ] = argptr ;
2006-04-21 18:26:54 +00:00
strsep ( & argptr , " , " ) ;
2004-07-17 20:12:28 +00:00
}
2004-12-24 01:40:07 +00:00
} else {
2004-07-17 20:12:28 +00:00
/* Format arguments for argv vector */
2006-04-21 18:26:54 +00:00
ast_copy_string ( xargs , class - > args , sizeof ( xargs ) ) ;
2004-07-17 20:12:28 +00:00
argptr = xargs ;
2005-11-08 01:55:31 +00:00
while ( ! ast_strlen_zero ( argptr ) ) {
2004-07-17 20:12:28 +00:00
argv [ argc + + ] = argptr ;
2006-04-21 18:26:54 +00:00
strsep ( & argptr , " " ) ;
2002-06-29 21:46:57 +00:00
}
}
2004-07-02 22:40:09 +00:00
2005-06-30 18:08:27 +00:00
2004-12-24 01:40:07 +00:00
if ( strstr ( class - > dir , " http:// " ) | | strstr ( class - > dir , " HTTP:// " ) ) {
2006-04-21 18:26:54 +00:00
ast_copy_string ( fns [ files ] , class - > dir , sizeof ( fns [ files ] ) ) ;
2004-09-04 05:41:01 +00:00
argv [ argc + + ] = fns [ files ] ;
files + + ;
2005-06-30 18:08:27 +00:00
} else if ( dir ) {
2005-01-09 09:00:41 +00:00
while ( ( de = readdir ( dir ) ) & & ( files < MAX_MP3S ) ) {
2004-12-24 01:40:07 +00:00
if ( ( strlen ( de - > d_name ) > 3 ) & &
2005-01-09 09:00:41 +00:00
( ( ast_test_flag ( class , MOH_CUSTOM ) & &
( ! strcasecmp ( de - > d_name + strlen ( de - > d_name ) - 4 , " .raw " ) | |
! strcasecmp ( de - > d_name + strlen ( de - > d_name ) - 4 , " .sln " ) ) ) | |
! strcasecmp ( de - > d_name + strlen ( de - > d_name ) - 4 , " .mp3 " ) ) ) {
2006-04-21 18:26:54 +00:00
ast_copy_string ( fns [ files ] , de - > d_name , sizeof ( fns [ files ] ) ) ;
2004-09-04 05:41:01 +00:00
argv [ argc + + ] = fns [ files ] ;
files + + ;
}
2004-07-02 23:11:14 +00:00
}
}
argv [ argc ] = NULL ;
2005-06-30 18:08:27 +00:00
if ( dir ) {
closedir ( dir ) ;
}
2004-07-02 23:11:14 +00:00
if ( pipe ( fds ) ) {
ast_log ( LOG_WARNING , " Pipe failed \n " ) ;
return - 1 ;
}
if ( ! files ) {
2002-06-29 21:46:57 +00:00
ast_log ( LOG_WARNING , " Found no files in '%s' \n " , class - > dir ) ;
2003-11-04 23:23:00 +00:00
close ( fds [ 0 ] ) ;
close ( fds [ 1 ] ) ;
2002-06-29 21:46:57 +00:00
return - 1 ;
}
2004-09-04 05:41:01 +00:00
if ( time ( NULL ) - class - > start < respawn_time ) {
sleep ( respawn_time - ( time ( NULL ) - class - > start ) ) ;
}
time ( & class - > start ) ;
2002-06-29 21:46:57 +00:00
class - > pid = fork ( ) ;
if ( class - > pid < 0 ) {
close ( fds [ 0 ] ) ;
close ( fds [ 1 ] ) ;
ast_log ( LOG_WARNING , " Fork failed: %s \n " , strerror ( errno ) ) ;
return - 1 ;
}
2005-07-12 18:57:20 +00:00
if ( ! class - > pid ) {
int x ;
2006-05-01 21:48:30 +00:00
if ( ast_opt_high_priority )
ast_set_priority ( 0 ) ;
2005-07-12 18:57:20 +00:00
close ( fds [ 0 ] ) ;
/* Stdout goes to pipe */
dup2 ( fds [ 1 ] , STDOUT_FILENO ) ;
/* Close unused file descriptors */
for ( x = 3 ; x < 8192 ; x + + ) {
if ( - 1 ! = fcntl ( x , F_GETFL ) ) {
close ( x ) ;
2004-11-17 18:16:08 +00:00
}
}
2005-07-12 18:57:20 +00:00
/* Child */
2004-12-26 00:33:17 +00:00
chdir ( class - > dir ) ;
2005-01-09 09:00:41 +00:00
if ( ast_test_flag ( class , MOH_CUSTOM ) ) {
2004-07-17 20:12:28 +00:00
execv ( argv [ 0 ] , argv ) ;
2005-01-09 09:00:41 +00:00
} else {
/* Default install is /usr/local/bin */
execv ( LOCAL_MPG_123 , argv ) ;
/* Many places have it in /usr/bin */
execv ( MPG_123 , argv ) ;
/* Check PATH as a last-ditch effort */
execvp ( " mpg123 " , argv ) ;
}
2005-07-12 18:57:20 +00:00
ast_log ( LOG_WARNING , " Exec failed: %s \n " , strerror ( errno ) ) ;
close ( fds [ 1 ] ) ;
2002-06-29 21:46:57 +00:00
exit ( 1 ) ;
2005-07-12 18:57:20 +00:00
} else {
/* Parent */
close ( fds [ 1 ] ) ;
2002-06-29 21:46:57 +00:00
}
2005-07-12 18:57:20 +00:00
return fds [ 0 ] ;
2002-06-29 21:46:57 +00:00
}
static void * monmp3thread ( void * data )
{
2003-07-27 03:53:19 +00:00
# define MOH_MS_INTERVAL 100
2002-06-29 21:46:57 +00:00
struct mohclass * class = data ;
struct mohdata * moh ;
char buf [ 8192 ] ;
short sbuf [ 8192 ] ;
int res , res2 ;
2005-08-22 19:29:29 +00:00
int len ;
2005-07-15 23:00:47 +00:00
struct timeval tv , tv_tmp ;
2003-07-27 03:53:19 +00:00
tv . tv_sec = 0 ;
tv . tv_usec = 0 ;
2002-06-29 21:46:57 +00:00
for ( ; /* ever */ ; ) {
2006-04-17 17:17:41 +00:00
pthread_testcancel ( ) ;
2002-06-29 21:46:57 +00:00
/* Spawn mp3 player if it's not there */
2004-06-29 04:42:19 +00:00
if ( class - > srcfd < 0 ) {
2002-06-29 21:46:57 +00:00
if ( ( class - > srcfd = spawn_mp3 ( class ) ) < 0 ) {
2005-08-25 23:29:30 +00:00
ast_log ( LOG_WARNING , " Unable to spawn mp3player \n " ) ;
2002-06-29 21:46:57 +00:00
/* Try again later */
sleep ( 500 ) ;
2006-04-17 17:17:41 +00:00
pthread_testcancel ( ) ;
2002-06-29 21:46:57 +00:00
}
}
if ( class - > pseudofd > - 1 ) {
/* Pause some amount of time */
res = read ( class - > pseudofd , buf , sizeof ( buf ) ) ;
2006-04-17 17:17:41 +00:00
pthread_testcancel ( ) ;
2002-06-29 21:46:57 +00:00
} else {
2005-07-15 23:00:47 +00:00
long delta ;
2003-07-27 03:53:19 +00:00
/* Reliable sleep */
2005-07-15 23:00:47 +00:00
tv_tmp = ast_tvnow ( ) ;
if ( ast_tvzero ( tv ) )
tv = tv_tmp ;
delta = ast_tvdiff_ms ( tv_tmp , tv ) ;
if ( delta < MOH_MS_INTERVAL ) { /* too early */
tv = ast_tvadd ( tv , ast_samp2tv ( MOH_MS_INTERVAL , 1000 ) ) ; /* next deadline */
usleep ( 1000 * ( MOH_MS_INTERVAL - delta ) ) ;
2006-04-17 17:17:41 +00:00
pthread_testcancel ( ) ;
2003-07-27 03:53:19 +00:00
} else {
ast_log ( LOG_NOTICE , " Request to schedule in the past?!?! \n " ) ;
2005-07-15 23:00:47 +00:00
tv = tv_tmp ;
2003-07-27 03:53:19 +00:00
}
2005-07-15 23:00:47 +00:00
res = 8 * MOH_MS_INTERVAL ; /* 8 samples per millisecond */
2002-06-29 21:46:57 +00:00
}
2006-04-29 13:15:05 +00:00
if ( AST_LIST_EMPTY ( & class - > members ) )
2002-06-29 21:46:57 +00:00
continue ;
/* Read mp3 audio */
2005-08-22 19:29:29 +00:00
len = ast_codec_get_len ( class - > format , res ) ;
if ( ( res2 = read ( class - > srcfd , sbuf , len ) ) ! = len ) {
2002-06-29 21:46:57 +00:00
if ( ! res2 ) {
close ( class - > srcfd ) ;
class - > srcfd = - 1 ;
2006-04-17 17:17:41 +00:00
pthread_testcancel ( ) ;
2002-06-29 21:46:57 +00:00
if ( class - > pid ) {
2006-04-17 17:17:41 +00:00
kill ( class - > pid , SIGHUP ) ;
usleep ( 100000 ) ;
kill ( class - > pid , SIGTERM ) ;
usleep ( 100000 ) ;
2005-07-12 18:57:20 +00:00
kill ( class - > pid , SIGKILL ) ;
2002-06-29 21:46:57 +00:00
class - > pid = 0 ;
}
} else
2005-08-22 19:29:29 +00:00
ast_log ( LOG_DEBUG , " Read %d bytes of audio while expecting %d \n " , res2 , len ) ;
2002-06-29 21:46:57 +00:00
continue ;
}
2006-04-17 17:17:41 +00:00
pthread_testcancel ( ) ;
2006-04-29 13:15:05 +00:00
AST_LIST_LOCK ( & mohclasses ) ;
AST_LIST_TRAVERSE ( & class - > members , moh , list ) {
2002-06-29 21:46:57 +00:00
/* Write data */
2006-04-29 13:15:05 +00:00
if ( ( res = write ( moh - > pipe [ 1 ] , sbuf , res2 ) ) ! = res2 ) {
2002-06-29 21:46:57 +00:00
if ( option_debug )
2004-06-29 04:42:19 +00:00
ast_log ( LOG_DEBUG , " Only wrote %d of %d bytes to pipe \n " , res , res2 ) ;
2006-04-29 13:15:05 +00:00
}
2002-06-29 21:46:57 +00:00
}
2006-04-29 13:15:05 +00:00
AST_LIST_UNLOCK ( & mohclasses ) ;
2002-06-29 21:46:57 +00:00
}
return NULL ;
}
static int moh0_exec ( struct ast_channel * chan , void * data )
{
if ( ast_moh_start ( chan , data ) ) {
ast_log ( LOG_WARNING , " Unable to start music on hold (class '%s') on channel %s \n " , ( char * ) data , chan - > name ) ;
return - 1 ;
}
2005-01-09 09:00:41 +00:00
while ( ! ast_safe_sleep ( chan , 10000 ) ) ;
2005-01-06 04:18:15 +00:00
ast_moh_stop ( chan ) ;
2002-06-29 21:46:57 +00:00
return - 1 ;
}
static int moh1_exec ( struct ast_channel * chan , void * data )
{
int res ;
if ( ! data | | ! atoi ( data ) ) {
ast_log ( LOG_WARNING , " WaitMusicOnHold requires an argument (number of seconds to wait) \n " ) ;
return - 1 ;
}
if ( ast_moh_start ( chan , NULL ) ) {
2005-01-09 09:00:41 +00:00
ast_log ( LOG_WARNING , " Unable to start music on hold for %d seconds on channel %s \n " , atoi ( data ) , chan - > name ) ;
2002-06-29 21:46:57 +00:00
return - 1 ;
}
res = ast_safe_sleep ( chan , atoi ( data ) * 1000 ) ;
ast_moh_stop ( chan ) ;
return res ;
}
static int moh2_exec ( struct ast_channel * chan , void * data )
{
2005-11-08 01:55:31 +00:00
if ( ast_strlen_zero ( data ) ) {
2002-06-29 21:46:57 +00:00
ast_log ( LOG_WARNING , " SetMusicOnHold requires an argument (class) \n " ) ;
return - 1 ;
}
2006-02-01 23:05:28 +00:00
ast_string_field_set ( chan , musicclass , data ) ;
2002-06-29 21:46:57 +00:00
return 0 ;
}
2005-03-03 05:03:06 +00:00
static int moh3_exec ( struct ast_channel * chan , void * data )
{
char * class = NULL ;
if ( data & & strlen ( data ) )
class = data ;
if ( ast_moh_start ( chan , class ) )
ast_log ( LOG_NOTICE , " Unable to start music on hold class '%s' on channel %s \n " , class ? class : " default " , chan - > name ) ;
return 0 ;
}
static int moh4_exec ( struct ast_channel * chan , void * data )
{
ast_moh_stop ( chan ) ;
return 0 ;
}
2006-04-29 13:15:05 +00:00
/*! \note This function should be called with the mohclasses list locked */
2006-02-01 23:05:28 +00:00
static struct mohclass * get_mohbyname ( const char * name )
2002-06-29 21:46:57 +00:00
{
2006-04-29 13:15:05 +00:00
struct mohclass * moh = NULL ;
AST_LIST_TRAVERSE ( & mohclasses , moh , list ) {
2005-08-22 19:29:29 +00:00
if ( ! strcasecmp ( name , moh - > name ) )
2006-04-29 13:15:05 +00:00
break ;
2002-06-29 21:46:57 +00:00
}
2006-04-29 13:15:05 +00:00
return moh ;
2002-06-29 21:46:57 +00:00
}
static struct mohdata * mohalloc ( struct mohclass * cl )
{
struct mohdata * moh ;
2006-01-21 22:09:06 +00:00
long flags ;
2006-04-29 13:15:05 +00:00
2006-01-21 22:09:06 +00:00
if ( ! ( moh = ast_calloc ( 1 , sizeof ( * moh ) ) ) )
2002-06-29 21:46:57 +00:00
return NULL ;
2006-04-29 13:15:05 +00:00
2002-06-29 21:46:57 +00:00
if ( pipe ( moh - > pipe ) ) {
ast_log ( LOG_WARNING , " Failed to create pipe: %s \n " , strerror ( errno ) ) ;
free ( moh ) ;
return NULL ;
}
2006-04-29 13:15:05 +00:00
2002-06-29 21:46:57 +00:00
/* Make entirely non-blocking */
flags = fcntl ( moh - > pipe [ 0 ] , F_GETFL ) ;
fcntl ( moh - > pipe [ 0 ] , F_SETFL , flags | O_NONBLOCK ) ;
flags = fcntl ( moh - > pipe [ 1 ] , F_GETFL ) ;
fcntl ( moh - > pipe [ 1 ] , F_SETFL , flags | O_NONBLOCK ) ;
2006-04-29 13:15:05 +00:00
moh - > f . frametype = AST_FRAME_VOICE ;
moh - > f . subclass = cl - > format ;
moh - > f . offset = AST_FRIENDLY_OFFSET ;
2002-06-29 21:46:57 +00:00
moh - > parent = cl ;
2006-04-29 13:15:05 +00:00
AST_LIST_INSERT_HEAD ( & cl - > members , moh , list ) ;
2002-06-29 21:46:57 +00:00
return moh ;
}
static void moh_release ( struct ast_channel * chan , void * data )
{
2006-04-29 13:15:05 +00:00
struct mohdata * moh = data ;
2003-03-18 06:00:18 +00:00
int oldwfmt ;
2006-04-29 13:15:05 +00:00
AST_LIST_LOCK ( & mohclasses ) ;
AST_LIST_REMOVE ( & moh - > parent - > members , moh , list ) ;
AST_LIST_UNLOCK ( & mohclasses ) ;
2002-06-29 21:46:57 +00:00
close ( moh - > pipe [ 0 ] ) ;
close ( moh - > pipe [ 1 ] ) ;
oldwfmt = moh - > origwfmt ;
free ( moh ) ;
if ( chan ) {
2004-04-06 22:17:32 +00:00
if ( oldwfmt & & ast_set_write_format ( chan , oldwfmt ) )
2003-08-16 05:10:35 +00:00
ast_log ( LOG_WARNING , " Unable to restore channel '%s' to format %s \n " , chan - > name , ast_getformatname ( oldwfmt ) ) ;
2002-06-29 21:46:57 +00:00
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Stopped music on hold on %s \n " , chan - > name ) ;
}
}
static void * moh_alloc ( struct ast_channel * chan , void * params )
{
struct mohdata * res ;
2005-08-22 19:29:29 +00:00
struct mohclass * class = params ;
2005-01-09 09:00:41 +00:00
2006-01-21 22:09:06 +00:00
if ( ( res = mohalloc ( class ) ) ) {
2002-06-29 21:46:57 +00:00
res - > origwfmt = chan - > writeformat ;
2005-08-22 19:29:29 +00:00
if ( ast_set_write_format ( chan , class - > format ) ) {
ast_log ( LOG_WARNING , " Unable to set channel '%s' to format '%s' \n " , chan - > name , ast_codec2str ( class - > format ) ) ;
2002-06-29 21:46:57 +00:00
moh_release ( NULL , res ) ;
res = NULL ;
}
if ( option_verbose > 2 )
2005-08-22 19:29:29 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " Started music on hold, class '%s', on channel '%s' \n " , class - > name , chan - > name ) ;
2002-06-29 21:46:57 +00:00
}
return res ;
}
2002-12-04 16:42:58 +00:00
static int moh_generate ( struct ast_channel * chan , void * data , int len , int samples )
2002-06-29 21:46:57 +00:00
{
struct mohdata * moh = data ;
2003-07-27 03:53:19 +00:00
short buf [ 1280 + AST_FRIENDLY_OFFSET / 2 ] ;
2002-06-29 21:46:57 +00:00
int res ;
2005-01-09 09:00:41 +00:00
if ( ! moh - > parent - > pid )
return - 1 ;
2005-08-22 19:29:29 +00:00
len = ast_codec_get_len ( moh - > parent - > format , samples ) ;
2003-07-27 03:53:19 +00:00
if ( len > sizeof ( buf ) - AST_FRIENDLY_OFFSET ) {
2005-01-09 09:00:41 +00:00
ast_log ( LOG_WARNING , " Only doing %d of %d requested bytes on %s \n " , ( int ) sizeof ( buf ) , len , chan - > name ) ;
2003-07-27 03:53:19 +00:00
len = sizeof ( buf ) - AST_FRIENDLY_OFFSET ;
2002-06-29 21:46:57 +00:00
}
res = read ( moh - > pipe [ 0 ] , buf + AST_FRIENDLY_OFFSET / 2 , len ) ;
2005-01-09 09:00:41 +00:00
if ( res < = 0 )
return 0 ;
2006-04-29 13:15:05 +00:00
moh - > f . datalen = res ;
moh - > f . data = buf + AST_FRIENDLY_OFFSET / 2 ;
moh - > f . samples = ast_codec_get_samples ( & moh - > f ) ;
if ( ast_write ( chan , & moh - > f ) < 0 ) {
2005-01-09 09:00:41 +00:00
ast_log ( LOG_WARNING , " Failed to write frame to '%s': %s \n " , chan - > name , strerror ( errno ) ) ;
return - 1 ;
2002-06-29 21:46:57 +00:00
}
2005-08-22 19:29:29 +00:00
2002-06-29 21:46:57 +00:00
return 0 ;
}
static struct ast_generator mohgen =
{
alloc : moh_alloc ,
release : moh_release ,
generate : moh_generate ,
} ;
2006-06-04 06:21:43 +00:00
static int moh_add_file ( struct mohclass * class , const char * filepath )
{
if ( ! class - > allowed_files ) {
if ( ! ( class - > filearray = ast_calloc ( 1 , INITIAL_NUM_FILES * sizeof ( * class - > filearray ) ) ) )
return - 1 ;
class - > allowed_files = INITIAL_NUM_FILES ;
} else if ( class - > total_files = = class - > allowed_files ) {
if ( ! ( class - > filearray = ast_realloc ( class - > filearray , class - > allowed_files * sizeof ( * class - > filearray ) * 2 ) ) ) {
class - > allowed_files = 0 ;
class - > total_files = 0 ;
return - 1 ;
}
class - > allowed_files * = 2 ;
}
if ( ! ( class - > filearray [ class - > total_files ] = ast_strdup ( filepath ) ) )
return - 1 ;
class - > total_files + + ;
return 0 ;
}
2004-12-24 01:40:07 +00:00
static int moh_scan_files ( struct mohclass * class ) {
DIR * files_DIR ;
struct dirent * files_dirent ;
2006-06-09 20:26:25 +00:00
char path [ PATH_MAX ] ;
2006-06-04 06:21:43 +00:00
char filepath [ PATH_MAX ] ;
2005-01-03 00:38:01 +00:00
char * ext ;
2004-12-24 01:40:07 +00:00
struct stat statbuf ;
int dirnamelen ;
int i ;
files_DIR = opendir ( class - > dir ) ;
if ( ! files_DIR ) {
ast_log ( LOG_WARNING , " Cannot open dir %s or dir does not exist " , class - > dir ) ;
return - 1 ;
}
2006-06-04 06:21:43 +00:00
for ( i = 0 ; i < class - > total_files ; i + + )
free ( class - > filearray [ i ] ) ;
2004-12-24 01:40:07 +00:00
class - > total_files = 0 ;
dirnamelen = strlen ( class - > dir ) + 2 ;
2006-06-09 20:26:25 +00:00
getcwd ( path , sizeof ( path ) ) ;
2004-12-24 01:40:07 +00:00
chdir ( class - > dir ) ;
while ( ( files_dirent = readdir ( files_DIR ) ) ) {
2006-06-04 06:21:43 +00:00
/* The file name must be at least long enough to have the file type extension */
if ( ( strlen ( files_dirent - > d_name ) < 4 ) )
2004-12-24 01:40:07 +00:00
continue ;
2006-06-04 06:21:43 +00:00
snprintf ( filepath , sizeof ( filepath ) , " %s/%s " , class - > dir , files_dirent - > d_name ) ;
2004-12-24 01:40:07 +00:00
if ( stat ( filepath , & statbuf ) )
continue ;
if ( ! S_ISREG ( statbuf . st_mode ) )
continue ;
2005-01-04 18:26:17 +00:00
if ( ( ext = strrchr ( filepath , ' . ' ) ) ) {
2005-01-03 00:38:01 +00:00
* ext = ' \0 ' ;
2005-01-04 18:26:17 +00:00
ext + + ;
}
2005-01-03 00:38:01 +00:00
2004-12-24 01:40:07 +00:00
/* if the file is present in multiple formats, ensure we only put it into the list once */
for ( i = 0 ; i < class - > total_files ; i + + )
if ( ! strcmp ( filepath , class - > filearray [ i ] ) )
break ;
2006-06-04 06:21:43 +00:00
if ( i = = class - > total_files ) {
if ( moh_add_file ( class , filepath ) )
break ;
}
2004-12-24 01:40:07 +00:00
}
closedir ( files_DIR ) ;
chdir ( path ) ;
return class - > total_files ;
}
2005-11-16 17:48:18 +00:00
static int moh_register ( struct mohclass * moh , int reload )
2002-06-29 21:46:57 +00:00
{
2006-04-24 17:11:45 +00:00
# ifdef HAVE_ZAPTEL
2002-06-29 21:46:57 +00:00
int x ;
2003-09-08 16:48:07 +00:00
# endif
2006-04-29 13:15:05 +00:00
AST_LIST_LOCK ( & mohclasses ) ;
2005-08-22 19:29:29 +00:00
if ( get_mohbyname ( moh - > name ) ) {
2005-11-16 17:48:18 +00:00
if ( reload ) {
ast_log ( LOG_DEBUG , " Music on Hold class '%s' left alone from initial load. \n " , moh - > name ) ;
} else {
ast_log ( LOG_WARNING , " Music on Hold class '%s' already exists \n " , moh - > name ) ;
}
2005-08-22 19:29:29 +00:00
free ( moh ) ;
2006-04-29 13:15:05 +00:00
AST_LIST_UNLOCK ( & mohclasses ) ;
2002-06-29 21:46:57 +00:00
return - 1 ;
}
2006-04-29 13:15:05 +00:00
AST_LIST_UNLOCK ( & mohclasses ) ;
2005-08-22 19:29:29 +00:00
2004-09-04 05:41:01 +00:00
time ( & moh - > start ) ;
moh - > start - = respawn_time ;
2005-08-22 19:29:29 +00:00
if ( ! strcasecmp ( moh - > mode , " files " ) ) {
2004-12-24 01:40:07 +00:00
if ( ! moh_scan_files ( moh ) ) {
2005-07-28 18:37:55 +00:00
ast_moh_free_class ( & moh ) ;
2004-12-24 01:40:07 +00:00
return - 1 ;
}
2005-08-22 19:29:29 +00:00
if ( strchr ( moh - > args , ' r ' ) )
ast_set_flag ( moh , MOH_RANDOMIZE ) ;
} 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 " ) ) {
2004-12-24 01:40:07 +00:00
2005-08-22 19:29:29 +00:00
if ( ! strcasecmp ( moh - > mode , " custom " ) )
2005-01-09 09:00:41 +00:00
ast_set_flag ( moh , MOH_CUSTOM ) ;
2005-08-31 01:16:48 +00:00
else if ( ! strcasecmp ( moh - > mode , " mp3nb " ) )
2005-01-09 09:00:41 +00:00
ast_set_flag ( moh , MOH_SINGLE ) ;
2005-08-31 01:16:48 +00:00
else if ( ! strcasecmp ( moh - > mode , " quietmp3nb " ) )
ast_set_flag ( moh , MOH_SINGLE | MOH_QUIET ) ;
else if ( ! strcasecmp ( moh - > mode , " quietmp3 " ) )
2005-01-09 09:00:41 +00:00
ast_set_flag ( moh , MOH_QUIET ) ;
2004-12-24 01:40:07 +00:00
2002-06-29 21:46:57 +00:00
moh - > srcfd = - 1 ;
2006-04-24 17:11:45 +00:00
# ifdef HAVE_ZAPTEL
2005-08-22 19:29:29 +00:00
/* Open /dev/zap/pseudo for timing... Is
2002-06-29 21:46:57 +00:00
there a better , yet reliable way to do this ? */
moh - > pseudofd = open ( " /dev/zap/pseudo " , O_RDONLY ) ;
if ( moh - > pseudofd < 0 ) {
ast_log ( LOG_WARNING , " Unable to open pseudo channel for timing... Sound may be choppy. \n " ) ;
} else {
x = 320 ;
ioctl ( moh - > pseudofd , ZT_SET_BLOCKSIZE , & x ) ;
}
# else
moh - > pseudofd = - 1 ;
# endif
2004-08-08 17:15:02 +00:00
if ( ast_pthread_create ( & moh - > thread , NULL , monmp3thread , moh ) ) {
2002-06-29 21:46:57 +00:00
ast_log ( LOG_WARNING , " Unable to create moh... \n " ) ;
if ( moh - > pseudofd > - 1 )
close ( moh - > pseudofd ) ;
2005-07-28 18:37:55 +00:00
ast_moh_free_class ( & moh ) ;
2002-06-29 21:46:57 +00:00
return - 1 ;
}
} else {
2005-08-22 19:29:29 +00:00
ast_log ( LOG_WARNING , " Don't know how to do a mode '%s' music on hold \n " , moh - > mode ) ;
2005-07-28 18:37:55 +00:00
ast_moh_free_class ( & moh ) ;
2002-06-29 21:46:57 +00:00
return - 1 ;
}
2006-04-29 13:15:05 +00:00
AST_LIST_LOCK ( & mohclasses ) ;
AST_LIST_INSERT_HEAD ( & mohclasses , moh , list ) ;
AST_LIST_UNLOCK ( & mohclasses ) ;
2002-06-29 21:46:57 +00:00
return 0 ;
}
2004-12-24 01:40:07 +00:00
static void local_ast_moh_cleanup ( struct ast_channel * chan )
{
2005-01-09 09:00:41 +00:00
if ( chan - > music_state ) {
2004-12-24 01:40:07 +00:00
free ( chan - > music_state ) ;
chan - > music_state = NULL ;
}
}
2006-02-01 23:05:28 +00:00
static int local_ast_moh_start ( struct ast_channel * chan , const char * class )
2002-06-29 21:46:57 +00:00
{
2004-12-24 01:40:07 +00:00
struct mohclass * mohclass ;
2005-11-08 01:55:31 +00:00
if ( ast_strlen_zero ( class ) )
2002-06-29 21:46:57 +00:00
class = chan - > musicclass ;
2005-11-08 01:55:31 +00:00
if ( ast_strlen_zero ( class ) )
2002-06-29 21:46:57 +00:00
class = " default " ;
2006-04-29 13:15:05 +00:00
AST_LIST_LOCK ( & mohclasses ) ;
2004-12-24 01:40:07 +00:00
mohclass = get_mohbyname ( class ) ;
2006-04-29 13:15:05 +00:00
AST_LIST_UNLOCK ( & mohclasses ) ;
2004-12-24 01:40:07 +00:00
if ( ! mohclass ) {
ast_log ( LOG_WARNING , " No class: %s \n " , ( char * ) class ) ;
return - 1 ;
}
ast_set_flag ( chan , AST_FLAG_MOH ) ;
if ( mohclass - > total_files ) {
return ast_activate_generator ( chan , & moh_file_stream , mohclass ) ;
} else
return ast_activate_generator ( chan , & mohgen , mohclass ) ;
2002-06-29 21:46:57 +00:00
}
2004-12-09 19:55:01 +00:00
static void local_ast_moh_stop ( struct ast_channel * chan )
2002-06-29 21:46:57 +00:00
{
2004-12-24 01:40:07 +00:00
ast_clear_flag ( chan , AST_FLAG_MOH ) ;
2002-06-29 21:46:57 +00:00
ast_deactivate_generator ( chan ) ;
2004-12-24 01:40:07 +00:00
2005-01-09 09:00:41 +00:00
if ( chan - > music_state ) {
if ( chan - > stream ) {
2004-12-24 01:40:07 +00:00
ast_closestream ( chan - > stream ) ;
chan - > stream = NULL ;
}
}
2002-06-29 21:46:57 +00:00
}
2005-08-22 19:29:29 +00:00
static struct mohclass * moh_class_malloc ( void )
{
struct mohclass * class ;
2006-01-21 22:09:06 +00:00
if ( ( class = ast_calloc ( 1 , sizeof ( * class ) ) ) )
class - > format = AST_FORMAT_SLINEAR ;
2005-08-22 19:29:29 +00:00
return class ;
}
2005-11-16 17:48:18 +00:00
static int load_moh_classes ( int reload )
2002-06-29 21:46:57 +00:00
{
struct ast_config * cfg ;
struct ast_variable * var ;
2005-08-22 19:29:29 +00:00
struct mohclass * class ;
2002-06-29 21:46:57 +00:00
char * data ;
char * args ;
2005-08-22 19:29:29 +00:00
char * cat ;
int numclasses = 0 ;
static int dep_warning = 0 ;
2005-01-09 09:00:41 +00:00
2005-01-25 06:10:20 +00:00
cfg = ast_config_load ( " musiconhold.conf " ) ;
2005-01-09 09:00:41 +00:00
if ( ! cfg )
return 0 ;
2005-08-22 19:29:29 +00:00
cat = ast_category_browse ( cfg , NULL ) ;
for ( ; cat ; cat = ast_category_browse ( cfg , cat ) ) {
2006-01-21 22:09:06 +00:00
if ( strcasecmp ( cat , " classes " ) & & strcasecmp ( cat , " moh_files " ) ) {
if ( ! ( class = moh_class_malloc ( ) ) ) {
2005-08-22 19:29:29 +00:00
break ;
}
ast_copy_string ( class - > name , cat , sizeof ( class - > name ) ) ;
var = ast_variable_browse ( cfg , cat ) ;
while ( var ) {
if ( ! strcasecmp ( var - > name , " mode " ) )
2005-11-20 05:01:07 +00:00
ast_copy_string ( class - > mode , var - > value , sizeof ( class - > mode ) ) ;
2005-08-22 19:29:29 +00:00
else if ( ! strcasecmp ( var - > name , " directory " ) )
ast_copy_string ( class - > dir , var - > value , sizeof ( class - > dir ) ) ;
else if ( ! strcasecmp ( var - > name , " application " ) )
ast_copy_string ( class - > args , var - > value , sizeof ( class - > args ) ) ;
else if ( ! strcasecmp ( var - > name , " random " ) )
ast_set2_flag ( class , ast_true ( var - > value ) , MOH_RANDOMIZE ) ;
else if ( ! strcasecmp ( var - > name , " format " ) ) {
class - > format = ast_getformatbyname ( var - > value ) ;
if ( ! class - > format ) {
ast_log ( LOG_WARNING , " Unknown format '%s' -- defaulting to SLIN \n " , var - > value ) ;
class - > format = AST_FORMAT_SLINEAR ;
}
}
2006-03-14 07:09:57 +00:00
var = var - > next ;
2005-08-22 19:29:29 +00:00
}
if ( ast_strlen_zero ( class - > dir ) ) {
if ( ! strcasecmp ( class - > mode , " custom " ) ) {
strcpy ( class - > dir , " nodir " ) ;
} else {
ast_log ( LOG_WARNING , " A directory must be specified for class '%s'! \n " , class - > name ) ;
free ( class ) ;
continue ;
}
}
if ( ast_strlen_zero ( class - > mode ) ) {
ast_log ( LOG_WARNING , " A mode must be specified for class '%s'! \n " , class - > name ) ;
free ( class ) ;
continue ;
}
if ( ast_strlen_zero ( class - > args ) & & ! strcasecmp ( class - > mode , " custom " ) ) {
ast_log ( LOG_WARNING , " An application must be specified for class '%s'! \n " , class - > name ) ;
free ( class ) ;
continue ;
}
2005-11-16 17:48:18 +00:00
/* Don't leak a class when it's already registered */
moh_register ( class , reload ) ;
2005-08-22 19:29:29 +00:00
numclasses + + ;
}
}
/* Deprecated Old-School Configuration */
2005-01-09 09:00:41 +00:00
var = ast_variable_browse ( cfg , " classes " ) ;
while ( var ) {
2005-08-22 19:29:29 +00:00
if ( ! dep_warning ) {
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 " ) ;
dep_warning = 1 ;
}
2005-01-09 09:00:41 +00:00
data = strchr ( var - > value , ' : ' ) ;
if ( data ) {
* data + + = ' \0 ' ;
args = strchr ( data , ' , ' ) ;
if ( args )
* args + + = ' \0 ' ;
2006-01-21 22:09:06 +00:00
if ( ! ( get_mohbyname ( var - > name ) ) ) {
if ( ! ( class = moh_class_malloc ( ) ) ) {
2005-08-22 19:29:29 +00:00
return numclasses ;
}
ast_copy_string ( class - > name , var - > name , sizeof ( class - > name ) ) ;
ast_copy_string ( class - > dir , data , sizeof ( class - > dir ) ) ;
ast_copy_string ( class - > mode , var - > value , sizeof ( class - > mode ) ) ;
if ( args )
ast_copy_string ( class - > args , args , sizeof ( class - > args ) ) ;
2005-11-16 17:48:18 +00:00
moh_register ( class , reload ) ;
2005-08-22 19:29:29 +00:00
numclasses + + ;
2004-12-24 01:40:07 +00:00
}
}
2005-01-09 09:00:41 +00:00
var = var - > next ;
2002-06-29 21:46:57 +00:00
}
2005-01-09 09:00:41 +00:00
var = ast_variable_browse ( cfg , " moh_files " ) ;
while ( var ) {
2005-08-22 19:29:29 +00:00
if ( ! dep_warning ) {
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 " ) ;
dep_warning = 1 ;
}
2005-01-09 09:00:41 +00:00
if ( ! ( get_mohbyname ( var - > name ) ) ) {
args = strchr ( var - > value , ' , ' ) ;
if ( args )
2006-01-21 22:09:06 +00:00
* args + + = ' \0 ' ;
if ( ! ( class = moh_class_malloc ( ) ) ) {
2005-08-22 19:29:29 +00:00
return numclasses ;
}
ast_copy_string ( class - > name , var - > name , sizeof ( class - > name ) ) ;
ast_copy_string ( class - > dir , var - > value , sizeof ( class - > dir ) ) ;
strcpy ( class - > mode , " files " ) ;
if ( args )
ast_copy_string ( class - > args , args , sizeof ( class - > args ) ) ;
2005-11-16 17:48:18 +00:00
moh_register ( class , reload ) ;
2005-08-22 19:29:29 +00:00
numclasses + + ;
2005-01-09 09:00:41 +00:00
}
var = var - > next ;
}
2005-01-25 06:10:20 +00:00
ast_config_destroy ( cfg ) ;
2005-08-22 19:29:29 +00:00
return numclasses ;
2002-06-29 21:46:57 +00:00
}
2003-03-16 22:37:31 +00:00
static void ast_moh_destroy ( void )
2003-03-04 06:00:19 +00:00
{
2006-04-29 13:15:05 +00:00
struct mohclass * moh ;
2003-08-10 00:01:48 +00:00
char buff [ 8192 ] ;
2006-04-29 13:15:05 +00:00
int bytes , tbytes = 0 , stime = 0 , pid = 0 ;
2005-01-09 09:00:41 +00:00
2003-08-07 03:48:00 +00:00
if ( option_verbose > 1 )
2004-12-24 01:40:07 +00:00
ast_verbose ( VERBOSE_PREFIX_2 " Destroying musiconhold processes \n " ) ;
2006-04-29 13:15:05 +00:00
AST_LIST_LOCK ( & mohclasses ) ;
while ( ( moh = AST_LIST_REMOVE_HEAD ( & mohclasses , list ) ) ) {
2003-03-04 06:00:19 +00:00
if ( moh - > pid ) {
2003-08-10 00:01:48 +00:00
ast_log ( LOG_DEBUG , " killing %d! \n " , moh - > pid ) ;
2005-03-05 03:20:55 +00:00
stime = time ( NULL ) + 2 ;
2004-12-24 01:40:07 +00:00
pid = moh - > pid ;
moh - > pid = 0 ;
2006-04-17 17:17:41 +00:00
/* Back when this was just mpg123, SIGKILL was fine. Now we need
* to give the process a reason and time enough to kill off its
* children . */
kill ( pid , SIGHUP ) ;
usleep ( 100000 ) ;
kill ( pid , SIGTERM ) ;
usleep ( 100000 ) ;
2005-07-12 18:57:20 +00:00
kill ( pid , SIGKILL ) ;
2006-04-29 13:15:05 +00:00
while ( ( ast_wait_for_input ( moh - > srcfd , 100 ) > 0 ) & & ( bytes = read ( moh - > srcfd , buff , 8192 ) ) & & time ( NULL ) < stime )
2003-08-10 00:01:48 +00:00
tbytes = tbytes + bytes ;
2004-12-24 01:40:07 +00:00
ast_log ( LOG_DEBUG , " mpg123 pid %d and child died after %d bytes read \n " , pid , tbytes ) ;
2003-08-10 00:01:48 +00:00
close ( moh - > srcfd ) ;
2004-07-02 23:11:14 +00:00
}
2006-04-29 13:15:05 +00:00
ast_moh_free_class ( & moh ) ;
2003-03-04 06:00:19 +00:00
}
2006-04-29 13:15:05 +00:00
AST_LIST_UNLOCK ( & mohclasses ) ;
2003-03-04 06:00:19 +00:00
}
2005-06-06 02:29:18 +00:00
static void moh_on_off ( int on )
{
struct ast_channel * chan = NULL ;
2005-01-09 09:00:41 +00:00
2005-06-06 02:29:18 +00:00
while ( ( chan = ast_channel_walk_locked ( chan ) ) ! = NULL ) {
2005-01-09 09:00:41 +00:00
if ( ast_test_flag ( chan , AST_FLAG_MOH ) ) {
if ( on )
local_ast_moh_start ( chan , NULL ) ;
2004-12-24 01:40:07 +00:00
else
ast_deactivate_generator ( chan ) ;
}
2006-04-29 13:15:05 +00:00
ast_channel_unlock ( chan ) ;
2004-12-24 01:40:07 +00:00
}
}
static int moh_cli ( int fd , int argc , char * argv [ ] )
{
2005-01-09 09:00:41 +00:00
int x ;
2004-12-24 01:40:07 +00:00
moh_on_off ( 0 ) ;
ast_moh_destroy ( ) ;
2005-11-16 17:48:18 +00:00
x = load_moh_classes ( 1 ) ;
2004-12-24 01:40:07 +00:00
moh_on_off ( 1 ) ;
2005-01-09 09:00:41 +00:00
ast_cli ( fd , " \n %d class%s reloaded. \n " , x , x = = 1 ? " " : " es " ) ;
2004-12-24 01:40:07 +00:00
return 0 ;
}
2005-01-03 00:38:01 +00:00
static int cli_files_show ( int fd , int argc , char * argv [ ] )
{
int i ;
struct mohclass * class ;
2006-04-29 13:15:05 +00:00
AST_LIST_LOCK ( & mohclasses ) ;
AST_LIST_TRAVERSE ( & mohclasses , class , list ) {
2005-01-03 00:38:01 +00:00
if ( ! class - > total_files )
continue ;
2005-08-22 19:29:29 +00:00
ast_cli ( fd , " Class: %s \n " , class - > name ) ;
2005-01-03 00:38:01 +00:00
for ( i = 0 ; i < class - > total_files ; i + + )
ast_cli ( fd , " \t File: %s \n " , class - > filearray [ i ] ) ;
}
2006-04-29 13:15:05 +00:00
AST_LIST_UNLOCK ( & mohclasses ) ;
2005-01-03 00:38:01 +00:00
return 0 ;
}
2005-08-22 19:29:29 +00:00
static int moh_classes_show ( int fd , int argc , char * argv [ ] )
{
struct mohclass * class ;
2006-04-29 13:15:05 +00:00
AST_LIST_LOCK ( & mohclasses ) ;
AST_LIST_TRAVERSE ( & mohclasses , class , list ) {
2005-08-22 19:29:29 +00:00
ast_cli ( fd , " Class: %s \n " , class - > name ) ;
2006-05-19 03:11:35 +00:00
ast_cli ( fd , " \t Mode: %s \n " , S_OR ( class - > mode , " <none> " ) ) ;
2006-03-27 19:31:54 +00:00
ast_cli ( fd , " \t Directory: %s \n " , S_OR ( class - > dir , " <none> " ) ) ;
2005-08-22 19:29:29 +00:00
if ( ast_test_flag ( class , MOH_CUSTOM ) )
2006-03-27 19:31:54 +00:00
ast_cli ( fd , " \t Application: %s \n " , S_OR ( class - > args , " <none> " ) ) ;
2006-05-19 03:11:35 +00:00
if ( strcasecmp ( class - > mode , " files " ) )
ast_cli ( fd , " \t Format: %s \n " , ast_getformatname ( class - > format ) ) ;
2005-08-22 19:29:29 +00:00
}
2006-04-29 13:15:05 +00:00
AST_LIST_UNLOCK ( & mohclasses ) ;
2005-08-22 19:29:29 +00:00
return 0 ;
}
2004-12-24 01:40:07 +00:00
static struct ast_cli_entry cli_moh = { { " moh " , " reload " } , moh_cli , " Music On Hold " , " Music On Hold " , NULL } ;
2005-08-22 19:29:29 +00:00
static struct ast_cli_entry cli_moh_classes_show = { { " moh " , " classes " , " show " } , moh_classes_show , " List MOH classes " , " Lists all MOH classes " , NULL } ;
2005-01-03 00:38:01 +00:00
static struct ast_cli_entry cli_moh_files_show = { { " moh " , " files " , " show " } , cli_files_show , " List MOH file-based classes " , " Lists all loaded file-based MOH classes and their files " , NULL } ;
2005-11-16 17:48:18 +00:00
static int init_classes ( int reload )
2005-04-07 14:53:31 +00:00
{
2005-01-09 09:00:41 +00:00
struct mohclass * moh ;
2005-11-16 17:48:18 +00:00
if ( ! load_moh_classes ( reload ) ) /* Load classes from config */
2005-08-25 23:29:30 +00:00
return 0 ; /* Return if nothing is found */
2006-04-29 13:15:05 +00:00
AST_LIST_LOCK ( & mohclasses ) ;
AST_LIST_TRAVERSE ( & mohclasses , moh , list ) {
2005-01-09 09:00:41 +00:00
if ( moh - > total_files )
moh_scan_files ( moh ) ;
}
2006-04-29 13:15:05 +00:00
AST_LIST_UNLOCK ( & mohclasses ) ;
2005-08-25 23:29:30 +00:00
return 1 ;
2005-01-04 19:09:00 +00:00
}
2004-12-24 01:40:07 +00:00
2006-04-14 14:08:19 +00:00
static int load_module ( void * mod )
2002-06-29 21:46:57 +00:00
{
int res ;
2005-01-09 09:00:41 +00:00
2002-06-29 21:46:57 +00:00
res = ast_register_application ( app0 , moh0_exec , synopsis0 , descrip0 ) ;
2003-08-07 03:48:00 +00:00
ast_register_atexit ( ast_moh_destroy ) ;
2004-12-24 01:40:07 +00:00
ast_cli_register ( & cli_moh ) ;
2005-01-03 00:38:01 +00:00
ast_cli_register ( & cli_moh_files_show ) ;
2005-08-22 19:29:29 +00:00
ast_cli_register ( & cli_moh_classes_show ) ;
2002-06-29 21:46:57 +00:00
if ( ! res )
res = ast_register_application ( app1 , moh1_exec , synopsis1 , descrip1 ) ;
if ( ! res )
res = ast_register_application ( app2 , moh2_exec , synopsis2 , descrip2 ) ;
2005-03-03 05:03:06 +00:00
if ( ! res )
res = ast_register_application ( app3 , moh3_exec , synopsis3 , descrip3 ) ;
if ( ! res )
res = ast_register_application ( app4 , moh4_exec , synopsis4 , descrip4 ) ;
2004-12-24 01:40:07 +00:00
2005-11-16 17:48:18 +00:00
if ( ! init_classes ( 0 ) ) { /* No music classes configured, so skip it */
2005-08-25 23:29:30 +00:00
ast_log ( LOG_WARNING , " No music on hold classes configured, disabling music on hold. " ) ;
} else {
ast_install_music_functions ( local_ast_moh_start , local_ast_moh_stop , local_ast_moh_cleanup ) ;
}
2005-01-09 09:00:41 +00:00
return 0 ;
2002-06-29 21:46:57 +00:00
}
2006-04-14 14:08:19 +00:00
static int reload ( void * mod )
2004-12-24 01:40:07 +00:00
{
2005-11-16 17:48:18 +00:00
if ( init_classes ( 1 ) )
2005-08-25 23:29:30 +00:00
ast_install_music_functions ( local_ast_moh_start , local_ast_moh_stop , local_ast_moh_cleanup ) ;
2004-12-24 01:40:07 +00:00
2005-01-09 09:00:41 +00:00
return 0 ;
}
2004-12-24 01:40:07 +00:00
2006-04-14 14:08:19 +00:00
static int unload_module ( void * mod )
2002-06-29 21:46:57 +00:00
{
return - 1 ;
}
2006-04-14 14:08:19 +00:00
static const char * description ( void )
2002-06-29 21:46:57 +00:00
{
return " Music On Hold Resource " ;
}
2006-04-14 14:08:19 +00:00
static const char * key ( void )
2002-06-29 21:46:57 +00:00
{
return ASTERISK_GPL_KEY ;
}
2006-04-14 14:08:19 +00:00
STD_MOD ( MOD_0 | NO_USECOUNT | NO_UNLOAD , reload , NULL , NULL ) ;
2006-04-24 17:11:45 +00:00