Merge anthm's native MOH patch (bug #2639) he promises me he'll rid it of ast_flag_moh...
authorMark Spencer <markster@digium.com>
Fri, 24 Dec 2004 01:40:07 +0000 (01:40 +0000)
committerMark Spencer <markster@digium.com>
Fri, 24 Dec 2004 01:40:07 +0000 (01:40 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@4552 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channel.c
file.c
include/asterisk/channel.h
include/asterisk/file.h
include/asterisk/musiconhold.h
res/res_musiconhold.c

index 879e8fd..c30f383 100755 (executable)
--- 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 7880114..57b2b7a 100755 (executable)
--- a/file.c
+++ b/file.c
@@ -431,9 +431,13 @@ 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, '/');
index 0a1bf02..909e314 100755 (executable)
@@ -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;
index 2f67fc2..1686914 100755 (executable)
@@ -203,6 +203,15 @@ struct ast_filestream *ast_openstream(struct ast_channel *chan, const char *file
  * \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
+ * \param filename to use
+ * \param preflang prefered language to use
  * Returns a ast_filestream pointer if it opens the file, NULL on error
  */
 struct ast_filestream *ast_openvstream(struct ast_channel *chan, const char *filename, const char *preflang);
index 39c90bd..f6b46c9 100755 (executable)
@@ -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)
 }
index 53d25f3..470f7ff 100755 (executable)
@@ -16,6 +16,7 @@
 #include <asterisk/logger.h>
 #include <asterisk/channel.h>
 #include <asterisk/pbx.h>
+#include <../astconf.h>
 #include <asterisk/options.h>
 #include <asterisk/module.h>
 #include <asterisk/translate.h>
@@ -25,6 +26,7 @@
 #include <asterisk/config.h>
 #include <asterisk/utils.h>
 #include <stdlib.h>
+#include <asterisk/cli.h>
 #include <errno.h>
 #include <unistd.h>
 #include <string.h>
@@ -45,6 +47,8 @@
 #endif
 #include <unistd.h>
 #include <sys/ioctl.h>
+#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;