From 05c1466c552ff0085a191038e034030a3681d53e Mon Sep 17 00:00:00 2001 From: markster Date: Fri, 24 Dec 2004 01:40:07 +0000 Subject: Merge anthm's native MOH patch (bug #2639) he promises me he'll rid it of ast_flag_moh... git-svn-id: http://svn.digium.com/svn/asterisk/trunk@4552 f38db490-d61c-443f-a65b-d21fe96a405b --- channel.c | 19 +- file.c | 17 +- include/asterisk/channel.h | 4 +- include/asterisk/file.h | 9 + include/asterisk/musiconhold.h | 5 +- res/res_musiconhold.c | 413 ++++++++++++++++++++++++++++++++++++----- 6 files changed, 411 insertions(+), 56 deletions(-) diff --git a/channel.c b/channel.c index 879e8fd08..c30f3838e 100755 --- a/channel.c +++ b/channel.c @@ -613,6 +613,10 @@ void ast_channel_free(struct ast_channel *chan) chan->monitor->stop( chan, 0 ); } + /* If there is native format music-on-hold state, free it */ + if(chan->music_state) + ast_moh_cleanup(chan); + /* Free translatosr */ if (chan->pvt->readtrans) ast_translator_free_path(chan->pvt->readtrans); @@ -2960,22 +2964,26 @@ unsigned int ast_get_group(char *s) return group; } - static int (*ast_moh_start_ptr)(struct ast_channel *, char *) = NULL; static void (*ast_moh_stop_ptr)(struct ast_channel *) = NULL; +static void (*ast_moh_cleanup_ptr)(struct ast_channel *) = NULL; void ast_install_music_functions(int (*start_ptr)(struct ast_channel *, char *), - void (*stop_ptr)(struct ast_channel *)) + void (*stop_ptr)(struct ast_channel *), + void (*cleanup_ptr)(struct ast_channel *) + ) { ast_moh_start_ptr = start_ptr; ast_moh_stop_ptr = stop_ptr; + ast_moh_cleanup_ptr = cleanup_ptr; } void ast_uninstall_music_functions(void) { ast_moh_start_ptr = NULL; ast_moh_stop_ptr = NULL; + ast_moh_cleanup_ptr = NULL; } /*! Turn on/off music on hold on a given channel */ @@ -2996,3 +3004,10 @@ void ast_moh_stop(struct ast_channel *chan) if(ast_moh_stop_ptr) ast_moh_stop_ptr(chan); } + +void ast_moh_cleanup(struct ast_channel *chan) +{ + if(ast_moh_cleanup_ptr) + ast_moh_cleanup_ptr(chan); +} + diff --git a/file.c b/file.c index 7880114e2..57b2b7adb 100755 --- a/file.c +++ b/file.c @@ -431,8 +431,12 @@ static int ast_filehelper(const char *filename, const char *filename2, const cha res = ret ? ret : -1; return res; } - struct ast_filestream *ast_openstream(struct ast_channel *chan, const char *filename, const char *preflang) +{ + return ast_openstream_full(chan, filename, preflang, 0); +} + +struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char *filename, const char *preflang, int asis) { /* This is a fairly complex routine. Essentially we should do the following: @@ -452,10 +456,13 @@ struct ast_filestream *ast_openstream(struct ast_channel *chan, const char *file char filename3[256]=""; char *endpart; int res; - ast_stopstream(chan); - /* do this first, otherwise we detect the wrong writeformat */ - if (chan->generator) - ast_deactivate_generator(chan); + + if (!asis) { + /* do this first, otherwise we detect the wrong writeformat */ + ast_stopstream(chan); + if (chan->generator) + ast_deactivate_generator(chan); + } if (preflang && !ast_strlen_zero(preflang)) { strncpy(filename3, filename, sizeof(filename3) - 1); endpart = strrchr(filename3, '/'); diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 0a1bf0222..909e314cc 100755 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -88,7 +88,8 @@ struct ast_channel { /*! Default music class */ char musicclass[MAX_LANGUAGE]; - + /*! Music State*/ + void *music_state; /*! Current generator data if there is any */ void *generatordata; /*! Current active data generator */ @@ -230,6 +231,7 @@ struct ast_channel { #define AST_FLAG_BLOCKING (1 << 3) /* if we are blocking */ #define AST_FLAG_ZOMBIE (1 << 4) /* if we are a zombie */ #define AST_FLAG_EXCEPTION (1 << 5) /* if there is a pending exception */ +#define AST_FLAG_MOH (1 << 6) /* XXX anthm promises me this will disappear XXX listening to moh */ struct ast_bridge_config { int play_to_caller; diff --git a/include/asterisk/file.h b/include/asterisk/file.h index 2f67fc23f..1686914df 100755 --- a/include/asterisk/file.h +++ b/include/asterisk/file.h @@ -198,6 +198,15 @@ int ast_closestream(struct ast_filestream *f); */ struct ast_filestream *ast_openstream(struct ast_channel *chan, const char *filename, const char *preflang); +//! Opens stream for use in seeking, playing +/*! + * \param chan channel to work with + * \param filename to use + * \param preflang prefered language to use + * \param asis if set, don't clear generators + * Returns a ast_filestream pointer if it opens the file, NULL on error + */ +struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char *filename, const char *preflang, int asis); //! Opens stream for use in seeking, playing /*! * \param chan channel to work with diff --git a/include/asterisk/musiconhold.h b/include/asterisk/musiconhold.h index 39c90bd85..f6b46c99d 100755 --- a/include/asterisk/musiconhold.h +++ b/include/asterisk/musiconhold.h @@ -25,8 +25,11 @@ extern int ast_moh_start(struct ast_channel *chan, char *mclass); extern void ast_moh_stop(struct ast_channel *chan); extern void ast_install_music_functions(int (*start_ptr)(struct ast_channel *, char *), - void (*stop_ptr)(struct ast_channel *)); + void (*stop_ptr)(struct ast_channel *), + void (*cleanup_ptr)(struct ast_channel *)); + extern void ast_uninstall_music_functions(void); +void ast_moh_cleanup(struct ast_channel *chan); #if defined(__cplusplus) || defined(c_plusplus) } diff --git a/res/res_musiconhold.c b/res/res_musiconhold.c index 53d25f3da..470f7ffc4 100755 --- a/res/res_musiconhold.c +++ b/res/res_musiconhold.c @@ -16,6 +16,7 @@ #include #include #include +#include <../astconf.h> #include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +47,8 @@ #endif #include #include +#define MAX_MOHFILES 512 +#define MAX_MOHFILE_LEN 128 static char *app0 = "MusicOnHold"; static char *app1 = "WaitMusicOnHold"; @@ -73,15 +77,28 @@ static char *descrip2 = "SetMusicOnHold(class): " static int respawn_time = 20; +struct moh_files_state { + struct mohclass *class; + struct ast_filestream *stream; + int origwfmt; + int samples; + int sample_queue; + unsigned char pos; + unsigned char save_pos; +}; + struct mohclass { char class[80]; char dir[256]; char miscargs[256]; + char filearray[MAX_MOHFILES][MAX_MOHFILE_LEN]; + int total_files; int destroyme; int pid; /* PID of mpg123 */ int quiet; int single; int custom; + int randomize; time_t start; pthread_t thread; struct mohdata *members; @@ -101,17 +118,154 @@ struct mohdata { static struct mohclass *mohclasses; - AST_MUTEX_DEFINE_STATIC(moh_lock); #define LOCAL_MPG_123 "/usr/local/bin/mpg123" #define MPG_123 "/usr/bin/mpg123" #define MAX_MP3S 256 +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; + } +} + + +static int ast_moh_files_next(struct ast_channel *chan) { + struct moh_files_state *state = chan->music_state; + + if(state->save_pos) { + state->pos = state->save_pos - 1; + state->save_pos = 0; + } else { + state->samples = 0; + if (chan->stream) { + ast_closestream(chan->stream); + chan->stream = NULL; + state->pos++; + } + + if (state->class->randomize) { + srand(time(NULL)+getpid()+strlen(chan->name)-state->class->total_files); + state->pos = rand(); + } + } + + state->pos = state->pos % state->class->total_files; + + if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) { + ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name); + return -1; + } + 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; + } + + if (option_verbose > 2) + ast_log(LOG_NOTICE, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]); + + + if (state->samples) + ast_seekstream(chan->stream, state->samples, SEEK_SET); + + return state->pos; +} + + +static struct ast_frame *moh_files_readframe(struct ast_channel *chan) { + struct ast_frame *f = NULL; + + if (!chan->stream || !(f = ast_readframe(chan->stream))) { + if (ast_moh_files_next(chan) > -1) + 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; + state->sample_queue += samples; + + while(state->sample_queue > 0) { + if ((f = moh_files_readframe(chan))) { + state->samples += f->samples; + res = ast_write(chan, f); + ast_frfree(f); + if(res < 0) { + ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno)); + return -1; + } + state->sample_queue -= f->samples; + } 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; + int allocated = 0; + + if ((!chan->music_state) && ((state = malloc(sizeof(struct moh_files_state))))) { + chan->music_state = state; + allocated = 1; + } else + state = chan->music_state; + + + if(state) { + if (allocated || state->class != class) { + /* initialize */ + memset(state, 0, sizeof(struct moh_files_state)); + state->class = class; + } + + state->origwfmt = chan->writeformat; + + if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) { + ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name); + free(chan->music_state); + chan->music_state = NULL; + } else { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", (char *)params, chan->name); + } + + + } + + return chan->music_state; +} + +static struct ast_generator moh_file_stream = +{ + alloc: moh_files_alloc, + release: moh_files_release, + generate: moh_files_generator, +}; + static int spawn_mp3(struct mohclass *class) { int fds[2]; - int files; + int files=0; char fns[MAX_MP3S][80]; char *argv[MAX_MP3S + 50]; char xargs[256]; @@ -119,11 +273,13 @@ static int spawn_mp3(struct mohclass *class) int argc = 0; DIR *dir; struct dirent *de; + + 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; - } + } if (!class->custom) { argv[argc++] = "mpg123"; @@ -137,14 +293,14 @@ static int spawn_mp3(struct mohclass *class) argv[argc++] = "-b"; argv[argc++] = "2048"; } - + argv[argc++] = "-f"; - + if (class->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; @@ -156,7 +312,7 @@ static int spawn_mp3(struct mohclass *class) argptr++; } } - } else { + } else { /* Format arguments for argv vector */ strncpy(xargs, class->miscargs, sizeof(xargs) - 1); argptr = xargs; @@ -171,16 +327,17 @@ static int spawn_mp3(struct mohclass *class) } files = 0; - if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) - { + if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) { strncpy(fns[files], class->dir, sizeof(fns[files]) - 1); argv[argc++] = fns[files]; files++; - } - else - { + } else { while((de = readdir(dir)) && (files < MAX_MP3S)) { - if ((strlen(de->d_name) > 3) && !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3")) { + if ((strlen(de->d_name) > 3) && + ((class->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"))) { strncpy(fns[files], de->d_name, sizeof(fns[files]) - 1); argv[argc++] = fns[files]; files++; @@ -189,6 +346,7 @@ static int spawn_mp3(struct mohclass *class) } argv[argc] = NULL; closedir(dir); + if (pipe(fds)) { ast_log(LOG_WARNING, "Pipe failed\n"); return -1; @@ -229,18 +387,19 @@ static int spawn_mp3(struct mohclass *class) close(x); } } - /* Child */ - chdir(class->dir); - if(class->custom) { + /* Child */ + + if(class->custom) { + chdir(class->dir); execv(argv[0], argv); - } 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); - } + } 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); + } ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno)); close(fds[1]); exit(1); @@ -456,8 +615,7 @@ static void *moh_alloc(struct ast_channel *chan, void *params) { struct mohdata *res; struct mohclass *class; - ast_mutex_lock(&moh_lock); - class = get_mohbyname(params); + class = params; if (class) res = mohalloc(class); else { @@ -465,7 +623,6 @@ static void *moh_alloc(struct ast_channel *chan, void *params) ast_log(LOG_WARNING, "No class: %s\n", (char *)params); res = NULL; } - ast_mutex_unlock(&moh_lock); if (res) { res->origwfmt = chan->writeformat; if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) { @@ -473,10 +630,6 @@ static void *moh_alloc(struct ast_channel *chan, void *params) moh_release(NULL, res); res = NULL; } -#if 0 - /* Allow writes to interrupt */ - chan->writeinterrupt = 1; -#endif if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", (char *)params, chan->name); } @@ -489,7 +642,8 @@ static int moh_generate(struct ast_channel *chan, void *data, int len, int sampl struct mohdata *moh = data; short buf[1280 + AST_FRIENDLY_OFFSET / 2]; int res; - + if(!moh->parent->pid) + return - 1; len = samples * 2; if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) { ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), (int)len, chan->name); @@ -525,6 +679,58 @@ static struct ast_generator mohgen = generate: moh_generate, }; +static int moh_scan_files(struct mohclass *class) { + + DIR *files_DIR; + struct dirent *files_dirent; + char path[512]; + char filepath[MAX_MOHFILE_LEN]; + char *scan; + 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; + } + + class->total_files = 0; + dirnamelen = strlen(class->dir) + 2; + getcwd(path, 512); + chdir(class->dir); + memset(class->filearray, 0, MAX_MOHFILES*MAX_MOHFILE_LEN); + + while ((files_dirent = readdir(files_DIR))) { + if ((strlen(files_dirent->d_name) < 4) || ((strlen(files_dirent->d_name) + dirnamelen) >= MAX_MOHFILE_LEN)) + continue; + + snprintf(filepath, MAX_MOHFILE_LEN, "%s/%s", class->dir, files_dirent->d_name); + + if (stat(filepath, &statbuf)) + continue; + + if (!S_ISREG(statbuf.st_mode)) + continue; + + if ((scan = strrchr(filepath, '.'))) + *scan = '\0'; + + /* 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; + + if (i == class->total_files) + strcpy(class->filearray[class->total_files++], filepath); + } + + closedir(files_DIR); + chdir(path); + return class->total_files; +} + static int moh_register(char *classname, char *mode, char *param, char *miscargs) { struct mohclass *moh; @@ -545,16 +751,30 @@ static int moh_register(char *classname, char *mode, char *param, char *miscargs time(&moh->start); moh->start -= respawn_time; strncpy(moh->class, classname, sizeof(moh->class) - 1); - if (miscargs) + 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 (strchr(miscargs,'r')) + moh->randomize=1; + } + if (!strcasecmp(mode, "files")) { + if (param) + strncpy(moh->dir, param, sizeof(moh->dir) - 1); + if (!moh_scan_files(moh)) { + free(moh); + return -1; + } + } else if (!strcasecmp(mode, "mp3") || !strcasecmp(mode, "mp3nb") || !strcasecmp(mode, "quietmp3") || !strcasecmp(mode, "quietmp3nb") || !strcasecmp(mode, "httpmp3") || !strcasecmp(mode, "custom")) { + + if (param) + strncpy(moh->dir, param, sizeof(moh->dir) - 1); + if (!strcasecmp(mode, "custom")) moh->custom = 1; - if (!strcasecmp(mode, "mp3nb") || !strcasecmp(mode, "quietmp3nb")) + else if (!strcasecmp(mode, "mp3nb") || !strcasecmp(mode, "quietmp3nb")) moh->single = 1; - if (!strcasecmp(mode, "quietmp3") || !strcasecmp(mode, "quietmp3nb")) + else 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 @@ -588,26 +808,58 @@ static int moh_register(char *classname, char *mode, char *param, char *miscargs return 0; } +static void local_ast_moh_cleanup(struct ast_channel *chan) +{ + if(chan->music_state) { + free(chan->music_state); + chan->music_state = NULL; + } +} + static int local_ast_moh_start(struct ast_channel *chan, char *class) { + struct mohclass *mohclass; + if (!class || ast_strlen_zero(class)) class = chan->musicclass; if (!class || ast_strlen_zero(class)) class = "default"; - return ast_activate_generator(chan, &mohgen, class); + ast_mutex_lock(&moh_lock); + mohclass = get_mohbyname(class); + ast_mutex_unlock(&moh_lock); + + 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); } static void local_ast_moh_stop(struct ast_channel *chan) { + ast_clear_flag(chan, AST_FLAG_MOH); ast_deactivate_generator(chan); + + if(chan->music_state) { + if(chan->stream) { + ast_closestream(chan->stream); + chan->stream = NULL; + } + } } -static void load_moh_classes(void) +static int load_moh_classes(void) { struct ast_config *cfg; struct ast_variable *var; char *data; char *args; + int x = 0; cfg = ast_load("musiconhold.conf"); if (cfg) { var = ast_variable_browse(cfg, "classes"); @@ -621,54 +873,121 @@ static void load_moh_classes(void) *args = '\0'; args++; } - moh_register(var->name, var->value, data,args); + if(!(get_mohbyname(var->name))) { + moh_register(var->name, var->value, data, args); + x++; + } } var = var->next; } + var = ast_variable_browse(cfg, "moh_files"); + while(var) { + if(!(get_mohbyname(var->name))) { + args = strchr(var->value, ','); + if (args) { + *args = '\0'; + args++; + } + moh_register(var->name, "files", var->value, args); + x++; + } + var = var->next; + } + ast_destroy(cfg); } + return x; } static void ast_moh_destroy(void) { - struct mohclass *moh; + struct mohclass *moh,*tmp; char buff[8192]; - int bytes, tbytes=0, stime = 0; + int bytes, tbytes=0, stime = 0, pid = 0; if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Destroying any remaining musiconhold processes\n"); + ast_verbose(VERBOSE_PREFIX_2 "Destroying 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); + pid = moh->pid; + moh->pid = 0; + kill(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); + ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes); close(moh->srcfd); - moh->pid = 0; } + tmp = moh; moh = moh->next; + free(tmp); } + mohclasses = NULL; ast_mutex_unlock(&moh_lock); } + +static void moh_on_off(int on) { + struct ast_channel *chan = ast_channel_walk_locked(NULL); + while(chan) { + if(ast_test_flag(chan, AST_FLAG_MOH)) { + if(on) + local_ast_moh_start(chan,NULL); + else + ast_deactivate_generator(chan); + } + ast_mutex_unlock(&chan->lock); + chan = ast_channel_walk_locked(chan); + } +} + +static int moh_cli(int fd, int argc, char *argv[]) +{ + int x = 0; + moh_on_off(0); + ast_moh_destroy(); + x = load_moh_classes(); + moh_on_off(1); + ast_cli(fd,"\n%d class%s reloaded.\n",x,x == 1 ? "" : "es"); + return 0; +} + +static struct ast_cli_entry cli_moh = { { "moh", "reload"}, moh_cli, "Music On Hold", "Music On Hold", NULL}; + + int load_module(void) { int res; load_moh_classes(); - ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop); + ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup); res = ast_register_application(app0, moh0_exec, synopsis0, descrip0); ast_register_atexit(ast_moh_destroy); + ast_cli_register(&cli_moh); if (!res) res = ast_register_application(app1, moh1_exec, synopsis1, descrip1); if (!res) res = ast_register_application(app2, moh2_exec, synopsis2, descrip2); + return res; } +int reload(void) +{ + struct mohclass *moh = mohclasses; + load_moh_classes(); + while(moh) { + if (moh->total_files) + moh_scan_files(moh); + moh = moh->next; + } + return 0; +} + + int unload_module(void) { return -1; -- cgit v1.2.3