Index: res/res_musiconhold.c =================================================================== RCS file: /usr/cvsroot/asterisk/res/res_musiconhold.c,v retrieving revision 1.37 diff -u -r1.37 res_musiconhold.c --- res/res_musiconhold.c 8 Aug 2004 17:15:02 -0000 1.37 +++ res/res_musiconhold.c 1 Sep 2004 07:41:22 -0000 @@ -46,6 +46,20 @@ #include #include +#define EXTRA_LOG 1 +#define ELEMOF(a) (sizeof(a)/sizeof(a[0])) + +#ifdef POSTGRES_MOH +#define SQL_MOH 1 +#include +#endif /* POSTGRES_MOH */ + +#ifdef MYSQL_MOH +#define SQL_MOH 1 +#include +#error "MYSQL support is not implemented yet" +#endif /* MYSQL_MOH */ + static char *app0 = "MusicOnHold"; static char *app1 = "WaitMusicOnHold"; static char *app2 = "SetMusicOnHold"; @@ -84,8 +98,15 @@ struct mohdata *members; /* Source of audio */ int srcfd; +#ifdef ZAPATA_MOH /* FD for timing source */ int pseudofd; +#endif + ast_mutex_t lock; +#ifdef POSTGRES_MOH + int deadinsyncing; + struct mohclass* new_mohclass; +#endif struct mohclass *next; }; @@ -104,24 +125,84 @@ #define MPG_123 "/usr/bin/mpg123" #define MAX_MP3S 256 -static int spawn_mp3(struct mohclass *class) -{ - int fds[2]; +/********************************************************************************* + * class moh_class (as much as you get get to a class in C :)) + ********************************************************************************/ +static struct mohclass* mohclass_new( + const char *name, + const char *mode, + const char *dir, + const char *miscargs ) { + struct mohclass* _this = NULL; + if( !strcasecmp(mode, "mp3") || + !strcasecmp(mode, "mp3nb") || + !strcasecmp(mode, "quietmp3") || + !strcasecmp(mode, "quietmp3nb") || + !strcasecmp(mode, "httpmp3") || + !strcasecmp(mode, "custom")) { + _this = malloc(sizeof(struct mohclass)); + if( _this ) { + memset(_this,0,sizeof(struct mohclass)); + strncpy(_this->class, name, sizeof(_this->class) - 1); + if (miscargs) + strncpy(_this->miscargs, miscargs, sizeof(_this->miscargs) - 1); + if (!strcasecmp(mode, "custom")) + _this->custom = 1; + if (!strcasecmp(mode, "mp3nb") || !strcasecmp(mode, "quietmp3nb")) + _this->single = 1; + if (!strcasecmp(mode, "quietmp3") || !strcasecmp(mode, "quietmp3nb")) + _this->quiet = 1; + strncpy(_this->dir, dir, sizeof(_this->dir) - 1); + _this->srcfd = -1; +#ifdef ZAPATA_MOH + /* It's an MP3 Moh -- Open /dev/zap/pseudo for timing... Is + there a better, yet reliable way to do this? */ + _this->pseudofd = open("/dev/zap/pseudo", O_RDONLY); + if (_this->pseudofd < 0) { + ast_log(LOG_WARNING, "Unable to open pseudo channel for timing... Sound may be choppy.\n"); + } else { + int x = 320; + ioctl(_this->pseudofd, ZT_SET_BLOCKSIZE, &x); + } +#endif + ast_mutex_init(&_this->lock); + return _this; + } else { + ast_log(LOG_WARNING,"Unable to allocate %d bytes for mohclass\n",sizeof(struct mohclass)); + } + } + else { + ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mode); + } + return _this; +} + +static struct mohclass* mohclass_find( struct mohclass* _this, const char* name ) { + struct mohclass* tmp; + for( tmp=_this; tmp; tmp=tmp->next ) { + if( !strcasecmp(name,tmp->class) ) + return tmp; + } + return NULL; +} + +static int mohclass_make_mp3_args( struct mohclass* _this, int argvsize, char* argv[] ) { + int files; - char fns[MAX_MP3S][80]; - char *argv[MAX_MP3S + 50]; - char xargs[256]; - char *argptr; + DIR* dir; int argc = 0; - DIR *dir; struct dirent *de; - dir = opendir(class->dir); + char xargs[256]; + char *argptr; + char fns[MAX_MP3S][80]; + + dir = opendir(_this->dir); if (!dir) { - ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir); + ast_log(LOG_WARNING, "%s is not a valid directory\n", _this->dir); return -1; } - if (!class->custom) { + if (!_this->custom) { argv[argc++] = "mpg123"; argv[argc++] = "-q"; argv[argc++] = "-s"; @@ -129,45 +210,33 @@ argv[argc++] = "-r"; argv[argc++] = "8000"; - if (!class->single) { + if (!_this->single) { argv[argc++] = "-b"; argv[argc++] = "2048"; } argv[argc++] = "-f"; - if (class->quiet) + if (_this->quiet) argv[argc++] = "4096"; else argv[argc++] = "8192"; + } - /* Look for extra arguments and add them to the list */ - strncpy(xargs, class->miscargs, sizeof(xargs) - 1); - argptr = xargs; - while(argptr && !ast_strlen_zero(argptr)) { - argv[argc++] = argptr; - argptr = strchr(argptr, ','); - if (argptr) { - *argptr = '\0'; - argptr++; - } - } - } else { - /* Format arguments for argv vector */ - strncpy(xargs, class->miscargs, sizeof(xargs) - 1); - argptr = xargs; - while(argptr && !ast_strlen_zero(argptr)) { - argv[argc++] = argptr; - argptr = strchr(argptr, ' '); - if (argptr) { - *argptr = '\0'; - argptr++; - } + /* Look for extra arguments and add them to the list */ + strncpy(xargs, _this->miscargs, sizeof(xargs) - 1); + argptr = xargs; + while(argptr && !ast_strlen_zero(argptr) && (argcd_name) > 3) && !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3")) { strncpy(fns[files], de->d_name, sizeof(fns[files]) - 1); argv[argc++] = fns[files]; @@ -176,32 +245,56 @@ } argv[argc] = NULL; closedir(dir); - if (pipe(fds)) { - ast_log(LOG_WARNING, "Pipe failed\n"); - return -1; - } -#if 0 - printf("%d files total, %d args total\n", files, argc); + +#if EXTRA_LOG + fprintf(stderr,"%d files total, %d args total\n", files, argc); { int x; for (x=0;argv[x];x++) - printf("arg%d: %s\n", x, argv[x]); + fprintf(stderr,"arg%d: %s\n", x, argv[x]); } #endif if (!files) { - ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir); - close(fds[0]); - close(fds[1]); + ast_log(LOG_WARNING, "Found no files in '%s'\n", _this->dir); + return -1; + } + + return 0; +} + +static int mohclass_is_equal( struct mohclass* _this, struct mohclass* _that ) { + if( strcasecmp(_this->class,_that->class) ) return 0; + if( strcmp(_this->dir,_that->dir) ) return 0; + if( _this->quiet!=_that->quiet ) return 0; + if( _this->single!=_that->single ) return 0; + if( _this->custom!=_that->custom ) return 0; + return 1; +} + +static int mohclass_spawn_mp3( struct mohclass* _this ) { + int fds[2]; + char *argv[MAX_MP3S + 50]; + + if( !_this ) + return -1; + + if( mohclass_make_mp3_args(_this,ELEMOF(argv),argv) ) { + ast_log(LOG_WARNING,"Cannot make mp3 arguments\n"); return -1; } - class->pid = fork(); - if (class->pid < 0) { + + if (pipe(fds)) { + ast_log(LOG_WARNING, "Pipe failed\n"); + return -1; + } + _this->pid = fork(); + if (_this->pid < 0) { close(fds[0]); close(fds[1]); ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno)); return -1; } - if (!class->pid) { + if (!_this->pid) { int x; close(fds[0]); /* Stdout goes to pipe */ @@ -210,8 +303,8 @@ for (x=3;x<8192;x++) close(x); /* Child */ - chdir(class->dir); - if(class->custom) { + chdir(_this->dir); + if(_this->custom) { execv(argv[0], argv); } else { /* Default install is /usr/local/bin */ @@ -231,39 +324,81 @@ return fds[0]; } -static void *monmp3thread(void *data) +static int mohclass_kill_mp3( struct mohclass* _this ) { + int result = 0; + if( _this->pid ) { + ast_log(LOG_DEBUG, "killing %d!\n", _this->pid); + if( kill(_this->pid,SIGKILL) && (errno!=ESRCH) ) { + ast_log(LOG_WARNING,"Unable to kill moh process, %d\n",errno); + result++; + } + else { + time_t stime = time(NULL); + char buff[8192]; + int bytes, tbytes=0; + while( (bytes=read(_this->srcfd, buff, sizeof(buff))) && (time(NULL)<(stime+5)) ) { + tbytes += tbytes; + } + ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", _this->pid, tbytes); + close(_this->srcfd); + _this->srcfd = -1; + _this->pid = 0; + } + } + else { + ast_log(LOG_WARNING,"Attempt to kill a not running mp3 process for moh class '%s'\n",_this->class); + } + return result; +} + +static void mohclass_set_destroyme( struct mohclass* _this, int destroyme ) { + ast_mutex_lock(&_this->lock); + _this->destroyme = destroyme; + ast_mutex_unlock(&_this->lock); +} + +static int mohclass_get_destroyme( struct mohclass* _this ) { + int destroyme; + ast_mutex_lock(&_this->lock); + destroyme = _this->destroyme; + ast_mutex_unlock(&_this->lock); + return destroyme; +} + +static void* mohclass_monmp3thread(void *data) { #define MOH_MS_INTERVAL 100 - struct mohclass *class = data; + struct mohclass *_this = data; struct mohdata *moh; - char buf[8192]; short sbuf[8192]; int res, res2; struct timeval tv; struct timeval tv_tmp; long error_sec, error_usec; long delay; - tv_tmp.tv_sec = 0; tv_tmp.tv_usec = 0; tv.tv_sec = 0; tv.tv_usec = 0; error_sec = 0; error_usec = 0; - for(;/* ever */;) { + while( !mohclass_get_destroyme(_this) ) { /* Spawn mp3 player if it's not there */ - if (class->srcfd < 0) { - if ((class->srcfd = spawn_mp3(class)) < 0) { + if (_this->srcfd < 0) { + if ((_this->srcfd = mohclass_spawn_mp3(_this)) < 0) { ast_log(LOG_WARNING, "unable to spawn mp3player\n"); /* Try again later */ sleep(500); } } - if (class->pseudofd > -1) { +#if ZAPATA_MOH + if (_this->pseudofd > -1) { /* Pause some amount of time */ - res = read(class->pseudofd, buf, sizeof(buf)); + char buf[8192]; + res = read(_this->pseudofd, buf, sizeof(buf)); } else { +#endif /* Reliable sleep */ if (gettimeofday(&tv_tmp, NULL) < 0) { ast_log(LOG_NOTICE, "gettimeofday() failed!\n"); @@ -298,36 +433,317 @@ if (delay > 0) usleep(delay * 1000); res = 800; /* 800 samples */ +#ifdef ZAPATA_MOH } - if (!class->members) +#endif + if (!_this->members) continue; /* Read mp3 audio */ - if ((res2 = read(class->srcfd, sbuf, res * 2)) != res * 2) { - if (!res2) { - close(class->srcfd); - class->srcfd = -1; - if (class->pid) { - kill(class->pid, SIGKILL); - class->pid = 0; - } - } else + if ((res2 = read(_this->srcfd, sbuf, res * 2)) != res * 2) { + if (!res2) + mohclass_kill_mp3(_this); + else ast_log(LOG_DEBUG, "Read %d bytes of audio while expecting %d\n", res2, res * 2); continue; } - ast_mutex_lock(&moh_lock); - moh = class->members; + ast_mutex_lock(&_this->lock); + moh = _this->members; while(moh) { /* Write data */ - if ((res = write(moh->pipe[1], sbuf, res2)) != res2) - if (option_debug) - ast_log(LOG_DEBUG, "Only wrote %d of %d bytes to pipe\n", res, res2); + if ((res = write(moh->pipe[1], sbuf, res2)) != res2) { + if( errno==EAGAIN ) { + /* the reader thread is too busy */ + } + else { +#if EXTRA_LOG + fprintf(stderr,"mohclass_monmp3thread:unable to write to pipe: res=%d,res2=%d,errno=%d\n",res,res2,errno); +#endif + if (option_debug) + ast_log(LOG_DEBUG, "Only wrote %d of %d bytes to pipe\n", res, res2); + } + } moh = moh->next; } - ast_mutex_unlock(&moh_lock); + ast_mutex_unlock(&_this->lock); } + mohclass_kill_mp3(_this); return NULL; } +static int mohclass_start( struct mohclass* _this ) { + int result = -1; + if( _this->thread==0 ) { + result = ast_pthread_create(&_this->thread,NULL,mohclass_monmp3thread,_this); + } + else { + ast_log(LOG_WARNING,"An attempt to start a running thread for moh class '%s'\n",_this->class); + } + return result; + +} + +static int mohclass_stop( struct mohclass* _this ) { + int result = 0; + if( _this->thread==0 ) { + ast_log(LOG_WARNING,"An attempt to stop a not running thread for moh class '%s'\n",_this->class); + result++; + } + else { + mohclass_set_destroyme(_this,1); + if( pthread_join(_this->thread,NULL) ) { + ast_log(LOG_WARNING,"Unable to join moh thread, %d\n",errno); + result++; + } + else { + _this->thread = 0; + } + } + return result; +} + +static struct mohclass* mohclass_delete( struct mohclass* _this ) { + struct mohclass* result = _this->next; + mohclass_stop(_this); + ast_mutex_destroy(&_this->lock); + free(_this); + return result; +} + +#ifdef POSTGRES_MOH +/********************************************************************************* + * changes to integrate MOH with postgres database + ********************************************************************************/ +static PGconn* _dbconn; +static char _dbget_mohclasses_query[512]; + +static int sql_connect(void) { + struct ast_config *cfg; + struct ast_variable *var; + cfg = ast_load("musiconhold.conf"); + if (cfg) { + PGconn* dbconn; + char dboption[512] = {0}; + char dbget_mohclasses_query[sizeof(_dbget_mohclasses_query)] = {0}; + var = ast_variable_browse(cfg,"general"); + while(var) { + if( !strcasecmp(var->name,"dboption") ) { + strncpy(dboption,var->value,sizeof(dboption)-1); + } + else if( !strcasecmp(var->name,"get_mohclasses_query") ) { + strncpy(dbget_mohclasses_query,var->value,sizeof(dbget_mohclasses_query)-1); + } + var = var->next; + } + /* try to open the database coonection */ + if( (dbconn=PQconnectdb(dboption))!=NULL ) { + if( PQstatus(dbconn)==CONNECTION_OK ) { + _dbconn = dbconn; + strcpy(_dbget_mohclasses_query,dbget_mohclasses_query); + } + else { + /* Danger!!!! the database password may appear in the logs here */ + ast_log(LOG_WARNING,"Unable to open database connection to '%s'\n",dboption); + PQfinish(dbconn); + } + } + ast_destroy(cfg); + } + else { + ast_log(LOG_WARNING,"Unable to read musiconhold.conf\n"); + } + return _dbconn ? 0 : -1; +} + +static void sql_disconnect(void) { + if( _dbconn ) { + PQfinish(_dbconn); + _dbconn = NULL; + } +} + +static int read_moh_classes( struct mohclass** pmoh_classes ) { + int result = -1; + PGresult* pgresult; + if( (pgresult=PQexec(_dbconn,_dbget_mohclasses_query))!=NULL ) { + ExecStatusType pgstatus = PQresultStatus(pgresult); + if( (pgstatus!=PGRES_BAD_RESPONSE) && (pgstatus!=PGRES_NONFATAL_ERROR) && (pgstatus!=PGRES_FATAL_ERROR) ) { + int nameColumn = PQfnumber(pgresult,"name"); + int modeColumn = PQfnumber(pgresult,"mode"); + int dirColumn = PQfnumber(pgresult,"dir"); + int miscargsColumn = PQfnumber(pgresult,"miscargs"); + if( (nameColumn>=0) && (modeColumn>=0) && (dirColumn>=0) && (miscargsColumn>=0) ) { + int n; + int rows = PQntuples(pgresult); + int errors_so_far = 0; + int errors_to_tolerate = 2; + struct mohclass** presult = pmoh_classes; + *pmoh_classes = NULL; + for( n=0; nnext = 0; + presult = &((*presult)->next); + } + else if( ++errors_so_far>errors_to_tolerate ) { + ast_log(LOG_WARNING,"Musiconhold database misconfiguration detected. Giving up\n"); + break; + } + else { + ast_log(LOG_WARNING,"Cannot read moh class\n"); + } + } + if( n==rows ) { + result = 0; + } + else { + while( *pmoh_classes ) { + *pmoh_classes = mohclass_delete(*pmoh_classes); + } + } + } + else { + ast_log(LOG_WARNING,"Required columns are missing\n"); + } + } + else { + ast_log(LOG_WARNING,"%s\n",PQcmdStatus(pgresult)); + } + PQclear(pgresult); + } + else { + ast_log(LOG_WARNING,"SQL query '%s' returned NULL\n",_dbget_mohclasses_query); + } + return result; +} + +static int sync_moh_classes(void) { + int result; + struct mohclass* old_mohclasses = mohclasses; /* This is the global variable */ + struct mohclass* new_mohclasses; + if( (result=read_moh_classes(&new_mohclasses))==0 ) { + + struct mohclass* new_mohclass; + struct mohclass* old_mohclass; + struct mohclass* prev_new_mohclass = NULL; + + /* mark the old classes as dead (they likely are to be replaced) */ + for( old_mohclass=old_mohclasses; old_mohclass; old_mohclass=old_mohclass->next ) { + old_mohclass->deadinsyncing = 1; + } + + /* start the new moh classes */ + for( new_mohclass=new_mohclasses; new_mohclass; ) { + old_mohclass = mohclass_find(old_mohclasses,new_mohclass->class); + if( !old_mohclass && !mohclass_start(new_mohclass) ) { +#if EXTRA_LOG + fprintf(stderr,"Started new moh class %s\n",new_mohclass->class); +#endif + prev_new_mohclass = new_mohclass; + new_mohclass = new_mohclass->next; + } + else if( !mohclass_is_equal(old_mohclass,new_mohclass) && !mohclass_start(new_mohclass) ) { +#if EXTRA_LOG + fprintf(stderr,"Started changed moh class %s\n",new_mohclass->class); +#endif + old_mohclass->new_mohclass = new_mohclass; /* will pass the members over */ + prev_new_mohclass = new_mohclass; + new_mohclass = new_mohclass->next; + } + else { +#if EXTRA_LOG + fprintf(stderr,"Ignoring new class '%s' (it is probably equal)\n",new_mohclass->class); +#endif + old_mohclass->deadinsyncing = 0; + if( prev_new_mohclass ) + prev_new_mohclass->next = new_mohclass->next; + else + new_mohclasses = new_mohclass->next; + new_mohclass = mohclass_delete(new_mohclass); + } + } + + /* destroy all dead old moh classes, Living classes merge into the new classes */ + ast_mutex_lock(&moh_lock); + for( old_mohclass=old_mohclasses; old_mohclass; ) { + if( old_mohclass->deadinsyncing ) { + /* destroy the old class, possibly passing members over */ + if( old_mohclass->new_mohclass ) { + ast_mutex_lock(&old_mohclass->lock); + old_mohclass->new_mohclass->members = old_mohclass->members; + old_mohclass->members = 0; + ast_mutex_unlock(&old_mohclass->lock); + } + old_mohclass = mohclass_delete(old_mohclass); + } + else { + /* stick it into the beginning of new_mohclasses */ + struct mohclass* tmp = old_mohclass->next; + old_mohclass->next = new_mohclasses; + new_mohclasses = old_mohclass; + old_mohclass = tmp; + } + } + mohclasses = new_mohclasses; + ast_mutex_unlock(&moh_lock); + } + else { + /* a proper error message is already logged */ + } + return result; +} + +#endif + +static struct mohclass *get_mohbyname(const char *name) +{ +#ifdef POSTGRES_MOH + struct mohclass* result; + if( !(result=mohclass_find(mohclasses,name)) ) { + /* sync and try again */ + if( !sync_moh_classes() ) { + return mohclass_find(mohclasses,name); + } + } + return result; +#else + return mohclass_find(mohclasses,name); +#endif +} + +#ifndef POSTGRES_MOH +static int moh_register(char *classname, char *mode, char *param, char *miscargs) +{ + struct mohclass *mclass; + ast_mutex_lock(&moh_lock); + mclass = get_mohbyname(classname); + ast_mutex_unlock(&moh_lock); + if (mclass) { + ast_log(LOG_WARNING, "Music on Hold '%s' already exists\n", classname); + return -1; + } + mclass = mohclass_new(classname,mode,param,miscargs); + if (!mclass) + return -1; + if (mohclass_start(mclass)) { + ast_log(LOG_WARNING, "Unable to start moh class...\n"); + mohclass_delete(mclass); + return -1; + } + ast_mutex_lock(&moh_lock); + mclass->next = mohclasses; + mohclasses = mclass; + ast_mutex_unlock(&moh_lock); + return 0; +} +#endif /* POSTGRES_MOH */ + +/********************************************************************************* + * + ********************************************************************************/ static int moh0_exec(struct ast_channel *chan, void *data) { if (ast_moh_start(chan, data)) { @@ -364,18 +780,6 @@ return 0; } -static struct mohclass *get_mohbyname(char *name) -{ - struct mohclass *moh; - moh = mohclasses; - while(moh) { - if (!strcasecmp(name, moh->class)) - return moh; - moh = moh->next; - } - return NULL; -} - static struct mohdata *mohalloc(struct mohclass *cl) { struct mohdata *moh; @@ -396,7 +800,9 @@ fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK); moh->parent = cl; moh->next = cl->members; + ast_mutex_lock(&cl->lock); cl->members = moh; + ast_mutex_unlock(&cl->lock); return moh; } @@ -404,7 +810,7 @@ { struct mohdata *moh = data, *prev, *cur; int oldwfmt; - ast_mutex_lock(&moh_lock); + ast_mutex_lock(&moh->parent->lock); /* Unlink */ prev = NULL; cur = moh->parent->members; @@ -419,7 +825,7 @@ prev = cur; cur = cur->next; } - ast_mutex_unlock(&moh_lock); + ast_mutex_unlock(&moh->parent->lock); close(moh->pipe[0]); close(moh->pipe[1]); oldwfmt = moh->origwfmt; @@ -494,7 +900,22 @@ ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno)); return -1; } +#if EXTRA_LOG>1 + else { + fprintf(stderr,"moh_generate:Wrote %d bytes to channel\n",res); + } +#endif } +#if EXTRA_LOG + else if( res<0 ) { + if( errno==EAGAIN ) { + /* the writer thread hasn't written anything yet */ + } + else { + fprintf(stderr,"moh_generate: unable to read from pipe,res=%d,errno=%d\n",res,errno); + } + } +#endif return 0; } @@ -505,68 +926,6 @@ generate: moh_generate, }; -static int moh_register(char *classname, char *mode, char *param, char *miscargs) -{ - struct mohclass *moh; -#ifdef ZAPATA_MOH - int x; -#endif - ast_mutex_lock(&moh_lock); - moh = get_mohbyname(classname); - ast_mutex_unlock(&moh_lock); - if (moh) { - ast_log(LOG_WARNING, "Music on Hold '%s' already exists\n", classname); - return -1; - } - moh = malloc(sizeof(struct mohclass)); - if (!moh) - return -1; - memset(moh, 0, sizeof(struct mohclass)); - - strncpy(moh->class, classname, sizeof(moh->class) - 1); - if (miscargs) - strncpy(moh->miscargs, miscargs, sizeof(moh->miscargs) - 1); - if (!strcasecmp(mode, "mp3") || !strcasecmp(mode, "mp3nb") || !strcasecmp(mode, "quietmp3") || !strcasecmp(mode, "quietmp3nb") || !strcasecmp(mode, "httpmp3") || !strcasecmp(mode, "custom")) { - if (!strcasecmp(mode, "custom")) - moh->custom = 1; - if (!strcasecmp(mode, "mp3nb") || !strcasecmp(mode, "quietmp3nb")) - moh->single = 1; - if (!strcasecmp(mode, "quietmp3") || !strcasecmp(mode, "quietmp3nb")) - moh->quiet = 1; - strncpy(moh->dir, param, sizeof(moh->dir) - 1); - moh->srcfd = -1; -#ifdef ZAPATA_MOH - /* It's an MP3 Moh -- Open /dev/zap/pseudo for timing... Is - 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 - if (ast_pthread_create(&moh->thread, NULL, monmp3thread, moh)) { - ast_log(LOG_WARNING, "Unable to create moh...\n"); - if (moh->pseudofd > -1) - close(moh->pseudofd); - free(moh); - return -1; - } - } else { - ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mode); - free(moh); - return -1; - } - ast_mutex_lock(&moh_lock); - moh->next = mohclasses; - mohclasses = moh; - ast_mutex_unlock(&moh_lock); - return 0; -} - int ast_moh_start(struct ast_channel *chan, char *class) { if (!class || ast_strlen_zero(class)) @@ -581,8 +940,13 @@ ast_deactivate_generator(chan); } -static void load_moh_classes(void) +static int load_moh_classes(void) { +#ifdef POSTGRES_MOH + /* Do not really have to start moh classes here - this can be done in a + lazy fashion when the first get_mohbyname is called */ + return sql_connect(); +#else struct ast_config *cfg; struct ast_variable *var; char *data; @@ -600,50 +964,45 @@ *args = '\0'; args++; } - moh_register(var->name, var->value, data,args); + if( moh_register(var->name, var->value, data,args)!=0 ) { + ast_destroy(cfg); + return -1; + } } var = var->next; } ast_destroy(cfg); } + return 0; +#endif } static void ast_moh_destroy(void) { - struct mohclass *moh; - char buff[8192]; - int bytes, tbytes=0, stime = 0; if (option_verbose > 1) ast_verbose(VERBOSE_PREFIX_2 "Destroying any remaining musiconhold processes\n"); ast_mutex_lock(&moh_lock); - moh = mohclasses; - while(moh) { - if (moh->pid) { - ast_log(LOG_DEBUG, "killing %d!\n", moh->pid); - stime = time(NULL); - kill(moh->pid, SIGKILL); - while ((bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime + 5) { - tbytes = tbytes + bytes; - } - ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", moh->pid, tbytes); - close(moh->srcfd); - moh->pid = 0; - } - moh = moh->next; + while( mohclasses ) { + mohclasses = mohclass_delete(mohclasses); } ast_mutex_unlock(&moh_lock); +#ifdef POSTGRES_MOH + sql_disconnect(); +#endif } int load_module(void) { - int res; - load_moh_classes(); - res = ast_register_application(app0, moh0_exec, synopsis0, descrip0); - ast_register_atexit(ast_moh_destroy); - if (!res) - res = ast_register_application(app1, moh1_exec, synopsis1, descrip1); - if (!res) - res = ast_register_application(app2, moh2_exec, synopsis2, descrip2); + int res = load_moh_classes(); + if(!res) { + ast_register_atexit(ast_moh_destroy); + res = ast_register_application(app0, moh0_exec, synopsis0, descrip0); + if (!res) { + res = ast_register_application(app1, moh1_exec, synopsis1, descrip1); + if (!res) + res = ast_register_application(app2, moh2_exec, synopsis2, descrip2); + } + } return res; }